diff options
33 files changed, 375 insertions, 194 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bd29a266ccd..144063f208f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -66,6 +66,7 @@ stages: paths: - knapsack/ - rspec_flaky/ + - rspec_profiling/ .use-pg: &use-pg services: @@ -159,6 +160,7 @@ stages: - coverage/ - knapsack/ - rspec_flaky/ + - rspec_profiling/ - tmp/capybara/ reports: junit: junit_rspec.xml @@ -336,6 +338,7 @@ retrieve-tests-metadata: - wget -O $KNAPSACK_RSPEC_SUITE_REPORT_PATH http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/$KNAPSACK_RSPEC_SUITE_REPORT_PATH || rm $KNAPSACK_RSPEC_SUITE_REPORT_PATH - '[[ -f $KNAPSACK_RSPEC_SUITE_REPORT_PATH ]] || echo "{}" > ${KNAPSACK_RSPEC_SUITE_REPORT_PATH}' - mkdir -p rspec_flaky/ + - mkdir -p rspec_profiling/ - wget -O $FLAKY_RSPEC_SUITE_REPORT_PATH http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/$FLAKY_RSPEC_SUITE_REPORT_PATH || rm $FLAKY_RSPEC_SUITE_REPORT_PATH - '[[ -f $FLAKY_RSPEC_SUITE_REPORT_PATH ]] || echo "{}" > ${FLAKY_RSPEC_SUITE_REPORT_PATH}' @@ -350,7 +353,7 @@ update-tests-metadata: - rspec_flaky/ policy: push script: - - retry gem install fog-aws mime-types activesupport --no-document + - retry gem install fog-aws mime-types activesupport rspec_profiling postgres-copy --no-document - scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json - scripts/merge-reports ${FLAKY_RSPEC_SUITE_REPORT_PATH} rspec_flaky/all_*_*.json - FLAKY_RSPEC_GENERATE_REPORT=1 scripts/prune-old-flaky-specs ${FLAKY_RSPEC_SUITE_REPORT_PATH} @@ -358,6 +361,7 @@ update-tests-metadata: - '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $FLAKY_RSPEC_SUITE_REPORT_PATH' - rm -f knapsack/${CI_PROJECT_NAME}/*_node_*.json - rm -f rspec_flaky/all_*.json rspec_flaky/new_*.json + - scripts/insert-rspec-profiling-data flaky-examples-check: <<: *dedicated-runner @@ -484,6 +488,9 @@ setup-test-env: build-qa-image: <<: *review-docker + variables: + <<: *review-docker-variables + GIT_DEPTH: "20" stage: prepare script: - time docker build --cache-from ${LATEST_QA_IMAGE} --tag ${QA_IMAGE} ./qa/ diff --git a/.prettierrc b/.prettierrc index 3384551aea5..5e2863a11f6 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,13 +1,5 @@ { "printWidth": 100, "singleQuote": true, - "trailingComma": "es5", - "overrides": [ - { - "files": ["**/app/**/*", "**/spec/**/*"], - "options": { - "trailingComma": "all" - } - } - ] + "trailingComma": "all" } diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 5ff8c4f5d2a..5db08bf2dc5 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -1.26.0 +1.27.0 diff --git a/app/assets/javascripts/emoji/index.js b/app/assets/javascripts/emoji/index.js index 36542315c4c..bb5085a1911 100644 --- a/app/assets/javascripts/emoji/index.js +++ b/app/assets/javascripts/emoji/index.js @@ -3,6 +3,7 @@ import createFlash from '~/flash'; import { s__ } from '~/locale'; import emojiAliases from 'emojis/aliases.json'; import axios from '../lib/utils/axios_utils'; +import csrf from '../lib/utils/csrf'; import AccessorUtilities from '../lib/utils/accessor'; @@ -24,7 +25,14 @@ export function initEmojiMap() { resolve(emojiMap); } else { // We load the JSON from server - axios + const axiosInstance = axios.create(); + + // If the static JSON file is on a CDN we don't want to send the default CSRF token + if (gon.asset_host) { + delete axiosInstance.defaults.headers.common[csrf.headerKey]; + } + + axiosInstance .get( `${gon.asset_host || ''}${gon.relative_url_root || ''}/-/emojis/${EMOJI_VERSION}/emojis.json`, diff --git a/app/assets/javascripts/environments/components/container.vue b/app/assets/javascripts/environments/components/container.vue index 6ece8b92a30..be80661223c 100644 --- a/app/assets/javascripts/environments/components/container.vue +++ b/app/assets/javascripts/environments/components/container.vue @@ -1,14 +1,16 @@ <script> import { GlLoadingIcon } from '@gitlab/ui'; -import tablePagination from '../../vue_shared/components/table_pagination.vue'; -import environmentTable from '../components/environments_table.vue'; +import TablePagination from '~/vue_shared/components/table_pagination.vue'; +import containerMixin from 'ee_else_ce/environments/mixins/container_mixin'; +import EnvironmentTable from '../components/environments_table.vue'; export default { components: { - environmentTable, - tablePagination, + EnvironmentTable, + TablePagination, GlLoadingIcon, }, + mixins: [containerMixin], props: { isLoading: { type: Boolean, @@ -47,7 +49,15 @@ export default { <slot name="emptyState"></slot> <div v-if="!isLoading && environments.length > 0" class="table-holder"> - <environment-table :environments="environments" :can-read-environment="canReadEnvironment" /> + <environment-table + :environments="environments" + :can-read-environment="canReadEnvironment" + :canary-deployment-feature-id="canaryDeploymentFeatureId" + :show-canary-deployment-callout="showCanaryDeploymentCallout" + :user-callouts-path="userCalloutsPath" + :lock-promotion-svg-path="lockPromotionSvgPath" + :help-canary-deployments-path="helpCanaryDeploymentsPath" + /> <table-pagination v-if="pagination && pagination.totalPages > 1" diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue index 1e89dce69cb..a092bdfbc6c 100644 --- a/app/assets/javascripts/environments/components/environment_item.vue +++ b/app/assets/javascripts/environments/components/environment_item.vue @@ -4,6 +4,7 @@ import _ from 'underscore'; import { GlTooltipDirective } from '@gitlab/ui'; import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; import Icon from '~/vue_shared/components/icon.vue'; +import environmentItemMixin from 'ee_else_ce/environments/mixins/environment_item_mixin'; import ActionsComponent from './environment_actions.vue'; import ExternalUrlComponent from './environment_external_url.vue'; import StopComponent from './environment_stop.vue'; @@ -34,10 +35,10 @@ export default { TerminalButtonComponent, MonitoringButtonComponent, }, - directives: { GlTooltip: GlTooltipDirective, }, + mixins: [environmentItemMixin], props: { model: { @@ -467,9 +468,18 @@ export default { <div v-if="!model.isFolder" class="table-mobile-header" role="rowheader"> {{ s__('Environments|Environment') }} </div> + + <span v-if="shouldRenderDeployBoard" class="deploy-board-icon" @click="toggleDeployBoard"> + <icon :name="deployIconName" /> + </span> + <span v-if="!model.isFolder" class="environment-name table-mobile-content"> <a class="qa-environment-link" :href="environmentPath"> {{ model.name }} </a> + <span v-if="isProtected" class="badge badge-success"> + {{ s__('Environments|protected') }} + </span> </span> + <span v-else class="folder-name" role="button" @click="onClickFolder"> <icon :name="folderIconName" class="folder-icon" /> diff --git a/app/assets/javascripts/environments/mixins/container_mixin.js b/app/assets/javascripts/environments/mixins/container_mixin.js new file mode 100644 index 00000000000..f2907c120f8 --- /dev/null +++ b/app/assets/javascripts/environments/mixins/container_mixin.js @@ -0,0 +1,29 @@ +export default { + props: { + canaryDeploymentFeatureId: { + type: String, + required: false, + default: null, + }, + showCanaryDeploymentCallout: { + type: Boolean, + required: false, + default: false, + }, + userCalloutsPath: { + type: String, + required: false, + default: null, + }, + lockPromotionSvgPath: { + type: String, + required: false, + default: null, + }, + helpCanaryDeploymentsPath: { + type: String, + required: false, + default: null, + }, + }, +}; diff --git a/app/assets/javascripts/environments/mixins/environment_item_mixin.js b/app/assets/javascripts/environments/mixins/environment_item_mixin.js new file mode 100644 index 00000000000..2dfed36ec99 --- /dev/null +++ b/app/assets/javascripts/environments/mixins/environment_item_mixin.js @@ -0,0 +1,13 @@ +export default { + computed: { + deployIconName() { + return ''; + }, + shouldRenderDeployBoard() { + return false; + }, + }, + methods: { + toggleDeployBoard() {}, + }, +}; diff --git a/changelogs/unreleased/10029-env-item.yml b/changelogs/unreleased/10029-env-item.yml new file mode 100644 index 00000000000..f4e742d3e17 --- /dev/null +++ b/changelogs/unreleased/10029-env-item.yml @@ -0,0 +1,5 @@ +--- +title: Removes EE differences for environment_item.vue +merge_request: +author: +type: other diff --git a/config/initializers/rspec_profiling.rb b/config/initializers/rspec_profiling.rb index 2de310753a9..715e17057e0 100644 --- a/config/initializers/rspec_profiling.rb +++ b/config/initializers/rspec_profiling.rb @@ -1,7 +1,28 @@ +# frozen_string_literal: true + +return unless Rails.env.test? + module RspecProfilingExt - module PSQL - def establish_connection - ::RspecProfiling::Collectors::PSQL::Result.establish_connection(ENV['RSPEC_PROFILING_POSTGRES_URL']) + module Collectors + class CSVWithTimestamps < ::RspecProfiling::Collectors::CSV + TIMESTAMP_FIELDS = %w(created_at updated_at).freeze + HEADERS = (::RspecProfiling::Collectors::CSV::HEADERS + TIMESTAMP_FIELDS).freeze + + def insert(attributes) + output << HEADERS.map do |field| + if TIMESTAMP_FIELDS.include?(field) + Time.now + else + attributes.fetch(field.to_sym) + end + end + end + + private + + def output + @output ||= ::CSV.open(path, "w").tap { |csv| csv << HEADERS } + end end end @@ -10,9 +31,13 @@ module RspecProfilingExt if ENV['CI_COMMIT_REF_NAME'] "#{defined?(Gitlab::License) ? 'ee' : 'ce'}:#{ENV['CI_COMMIT_REF_NAME']}" else - super + super&.chomp end end + + def sha + super&.chomp + end end module Run @@ -30,16 +55,11 @@ module RspecProfilingExt end end -if Rails.env.test? - RspecProfiling.configure do |config| - if ENV['RSPEC_PROFILING_POSTGRES_URL'].present? - RspecProfiling::Collectors::PSQL.prepend(RspecProfilingExt::PSQL) - config.collector = RspecProfiling::Collectors::PSQL - end - - if ENV.key?('CI') - RspecProfiling::VCS::Git.prepend(RspecProfilingExt::Git) - RspecProfiling::Run.prepend(RspecProfilingExt::Run) - end +RspecProfiling.configure do |config| + if ENV.key?('CI') || ENV.key?('RSPEC_PROFILING') + RspecProfiling::VCS::Git.prepend(RspecProfilingExt::Git) + RspecProfiling::Run.prepend(RspecProfilingExt::Run) + config.collector = RspecProfilingExt::Collectors::CSVWithTimestamps + config.csv_path = -> { "rspec_profiling/#{Time.now.to_i}-#{SecureRandom.hex(8)}-rspec-data.csv" } end end diff --git a/config/karma.config.js b/config/karma.config.js index 1012a713eb6..23eae40dceb 100644 --- a/config/karma.config.js +++ b/config/karma.config.js @@ -26,7 +26,7 @@ webpackConfig.devtool = 'cheap-inline-source-map'; webpackConfig.plugins.push( new webpack.DefinePlugin({ 'process.env.BABEL_ENV': JSON.stringify(process.env.BABEL_ENV || process.env.NODE_ENV || null), - }) + }), ); const specFilters = argumentsParser @@ -37,7 +37,7 @@ const specFilters = argumentsParser memo.push(filter, filter.replace(/\/?$/, '/**/*.js')); return memo; }, - [] + [], ) .parse(process.argv).filterSpec; @@ -51,7 +51,7 @@ if (specFilters.length) { root: ROOT_PATH, matchBase: true, }) - .filter(path => path.endsWith('spec.js')) + .filter(path => path.endsWith('spec.js')), ); // flatten @@ -78,8 +78,8 @@ if (specFilters.length) { new webpack.ContextReplacementPlugin( /spec[\\\/]javascripts$/, path.join(ROOT_PATH, 'spec/javascripts'), - newContext - ) + newContext, + ), ); } diff --git a/config/webpack.config.js b/config/webpack.config.js index 64e6ec49219..11970b620bc 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -251,7 +251,7 @@ module.exports = { } else { resource.request = path.join( ROOT_PATH, - 'app/assets/javascripts/vue_shared/components/empty_component.js' + 'app/assets/javascripts/vue_shared/components/empty_component.js', ); } }), @@ -267,7 +267,7 @@ module.exports = { const missingDeps = Array.from(compilation.missingDependencies); const nodeModulesPath = path.join(ROOT_PATH, 'node_modules'); const hasMissingNodeModules = missingDeps.some( - file => file.indexOf(nodeModulesPath) !== -1 + file => file.indexOf(nodeModulesPath) !== -1, ); // watch for changes to missing node_modules @@ -278,7 +278,7 @@ module.exports = { // report our auto-generated bundle count console.log( - `${autoEntriesCount} entries from '/pages' automatically added to webpack output.` + `${autoEntriesCount} entries from '/pages' automatically added to webpack output.`, ); callback(); diff --git a/db/fixtures/development/03_settings.rb b/db/fixtures/development/02_settings.rb index 3a4a5d436bf..3a4a5d436bf 100644 --- a/db/fixtures/development/03_settings.rb +++ b/db/fixtures/development/02_settings.rb diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/03_project.rb index 9a5f7cf8175..46018cf68aa 100644 --- a/db/fixtures/development/04_project.rb +++ b/db/fixtures/development/03_project.rb @@ -60,7 +60,7 @@ Sidekiq::Testing.inline! do path: group_path ) group.description = FFaker::Lorem.sentence - group.save + group.save! group.add_owner(User.first) end diff --git a/db/fixtures/development/04_labels.rb b/db/fixtures/development/04_labels.rb new file mode 100644 index 00000000000..b9ae4098d76 --- /dev/null +++ b/db/fixtures/development/04_labels.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +require 'digest/md5' + +class Gitlab::Seeder::GroupLabels + def initialize(group, label_per_group: 10) + @group = group + @label_per_group = label_per_group + end + + def seed! + @label_per_group.times do + label_title = FFaker::Product.brand + Labels::CreateService + .new(title: label_title, color: "##{Digest::MD5.hexdigest(label_title)[0..5]}") + .execute(group: @group) + print '.' + end + end +end + +class Gitlab::Seeder::ProjectLabels + def initialize(project, label_per_project: 5) + @project = project + @label_per_project = label_per_project + end + + def seed! + @label_per_project.times do + label_title = FFaker::Vehicle.model + Labels::CreateService + .new(title: label_title, color: "##{Digest::MD5.hexdigest(label_title)[0..5]}") + .execute(project: @project) + print '.' + end + end +end + +Gitlab::Seeder.quiet do + puts "\nGenerating group labels" + Group.all.find_each do |group| + Gitlab::Seeder::GroupLabels.new(group).seed! + end + + puts "\nGenerating project labels" + Project.all.find_each do |project| + Gitlab::Seeder::ProjectLabels.new(project).seed! + end +end diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb index 16243b72f9a..926401d8b9e 100644 --- a/db/fixtures/development/09_issues.rb +++ b/db/fixtures/development/09_issues.rb @@ -3,13 +3,17 @@ require './spec/support/sidekiq' Gitlab::Seeder.quiet do Project.all.each do |project| 10.times do + label_ids = project.labels.pluck(:id).sample(3) + label_ids += project.group.labels.sample(3) if project.group + issue_params = { title: FFaker::Lorem.sentence(6), description: FFaker::Lorem.sentence, state: ['opened', 'closed'].sample, milestone: project.milestones.sample, assignees: [project.team.users.sample], - created_at: rand(12).months.ago + created_at: rand(12).months.ago, + label_ids: label_ids } Issues::CreateService.new(project, project.team.users.sample, issue_params).execute diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb index 2051bcff8f0..1952f84ed62 100644 --- a/db/fixtures/development/10_merge_requests.rb +++ b/db/fixtures/development/10_merge_requests.rb @@ -12,13 +12,17 @@ Gitlab::Seeder.quiet do source_branch = branches.pop target_branch = branches.pop + label_ids = project.labels.pluck(:id).sample(3) + label_ids += project.group.labels.sample(3) if project.group + params = { source_branch: source_branch, target_branch: target_branch, title: FFaker::Lorem.sentence(6), description: FFaker::Lorem.sentences(3).join(" "), milestone: project.milestones.sample, - assignee: project.team.users.sample + assignee: project.team.users.sample, + label_ids: label_ids } # Only create MRs with users that are allowed to create MRs diff --git a/db/fixtures/development/22_labeled_issues_seed.rb b/db/fixtures/development/22_labeled_issues_seed.rb deleted file mode 100644 index 3730e9c7958..00000000000 --- a/db/fixtures/development/22_labeled_issues_seed.rb +++ /dev/null @@ -1,103 +0,0 @@ -# Creates a project with labeled issues for an user. -# Run this single seed file using: rake db:seed_fu FILTER=labeled USER_ID=74. -# If an USER_ID is not provided it will use the last created user. -require './spec/support/sidekiq' - -class Gitlab::Seeder::LabeledIssues - include ::Gitlab::Utils - - def initialize(user) - @user = user - end - - def seed! - Sidekiq::Testing.inline! do - group = create_group - - create_projects(group) - create_labels(group) - create_issues(group) - end - - print '.' - end - - private - - def create_group - group_name = "group_of_#{@user.username}_#{SecureRandom.hex(4)}" - - group_params = { - name: group_name, - path: group_name, - description: FFaker::Lorem.sentence - } - - Groups::CreateService.new(@user, group_params).execute - end - - def create_projects(group) - 5.times do - project_name = "project_#{SecureRandom.hex(6)}" - - params = { - namespace_id: group.id, - name: project_name, - description: FFaker::Lorem.sentence, - visibility_level: Gitlab::VisibilityLevel.values.sample - } - - Projects::CreateService.new(@user, params).execute - end - end - - def create_labels(group) - group.projects.each do |project| - 5.times do - label_title = FFaker::Vehicle.model - Labels::CreateService.new(title: label_title, color: "#69D100").execute(project: project) - end - end - - 10.times do - label_title = FFaker::Product.brand - Labels::CreateService.new(title: label_title).execute(group: group) - end - end - - def create_issues(group) - # Get only group labels - group_labels = - LabelsFinder.new(@user, group_id: group.id).execute.where.not(group_id: nil) - - group.projects.each do |project| - label_ids = project.labels.pluck(:id).sample(5) - label_ids.push(*group.labels.sample(4)) - - 20.times do - issue_params = { - title: FFaker::Lorem.sentence(6), - description: FFaker::Lorem.sentence, - state: 'opened', - label_ids: label_ids - - } - - Issues::CreateService.new(project, @user, issue_params).execute if project.project_feature.present? - end - end - end -end - -Gitlab::Seeder.quiet do - user_id = ENV['USER_ID'] - - user = - if user_id.present? - User.find(user_id) - else - User.last - end - - Gitlab::Seeder::LabeledIssues.new(user).seed! -end diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md index bceccf4d40d..398b017277f 100644 --- a/doc/ci/triggers/README.md +++ b/doc/ci/triggers/README.md @@ -17,6 +17,12 @@ The following methods of authentication are supported. A unique trigger token can be obtained when [adding a new trigger](#adding-a-new-trigger). +DANGER: **Danger:** +Passing plain text tokens in public projects is a security issue. Potential +attackers can impersonate the user that exposed their trigger token publicly in +their `.gitlab-ci.yml` file. Use [variables](../variables/README.md#variables) +to protect trigger tokens. + ## Adding a new trigger You can add a new trigger by going to your project's @@ -53,9 +59,6 @@ The action is irreversible. > > - Valid refs are only the branches and tags. If you pass a commit SHA as a ref, > it will not trigger a job. -> - If your project is public, passing the token in plain text is probably not the -> wisest idea, so you might want to use a -> [variable](../variables/README.md#variables) for that purpose. To trigger a job you need to send a `POST` request to GitLab's API endpoint: diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index f170323059a..a44f4b62a0e 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -367,10 +367,11 @@ job: - branches@gitlab-org/gitlab-ce except: - master@gitlab-org/gitlab-ce + - release/.*@gitlab-org/gitlab-ce ``` The above example will run `job` for all branches on `gitlab-org/gitlab-ce`, -except master. +except `master` and those with names prefixed with `release/`. If a job does not have an `only` rule, `only: ['branches', 'tags']` is set by default. If it doesn't have an `except` rule, it is empty. @@ -1756,7 +1757,7 @@ include: ``` All [nested includes](#nested-includes) will be executed in the scope of the target project, -so it is possible to used local (relative to target project), project, remote +so it is possible to use local (relative to target project), project, remote or template includes. #### `include:template` @@ -1792,9 +1793,17 @@ include: All nested includes will be executed without context as public user, so only another remote, or public project, or template is allowed. +NOTE: **Note:** +Changes to remote includes will not have effect on already created pipelines, +because the include is being evaluated at the time of pipeline creation. +This is when full definition of CI yaml is being expanded in order to create +pipeline with stages with jobs. You always retry job that is already created, +thus created after pipeline creation. To re-include all (thus re-evaluate the +configuration), you have to re-create pipeline. + #### Nested includes -> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/53903) in GitLab 11.7. +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/56836) in GitLab 11.9. Nested includes allow you to compose a set of includes. A total of 50 includes is allowed. diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md index 5b66e513a76..9bfb1e69f9e 100644 --- a/doc/development/testing_guide/frontend_testing.md +++ b/doc/development/testing_guide/frontend_testing.md @@ -35,15 +35,16 @@ If your test exceeds that time, it will fail. If you cannot improve the performance of the tests, you can increase the timeout for a specific test using -[`jest.setTimeout`](https://jestjs.io/docs/en/jest-object#jestsettimeouttimeout). +[`setTestTimeout`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/frontend/helpers/timeout.js). ```javascript -beforeAll(() => { - jest.setTimeout(500); -}); +import { setTestTimeout } from 'helpers/timeout'; describe('Component', () => { - // ... + it('does something amazing', () => { + setTestTimeout(500); + // ... + }); }); ``` @@ -281,25 +282,6 @@ Information on setting up and running RSpec integration tests with ## Gotchas -### Errors due to use of unsupported JavaScript features - -Similar errors will be thrown if you're using JavaScript features not yet -supported by the PhantomJS test runner which is used for both Karma and RSpec -tests. We polyfill some JavaScript objects for older browsers, but some -features are still unavailable: - -- Array.from -- Array.first -- Async functions -- Generators -- Array destructuring -- For..Of -- Symbol/Symbol.iterator -- Spread - -Until these are polyfilled appropriately, they should not be used. Please -update this list with additional unsupported features. - ### RSpec errors due to JavaScript By default RSpec unit tests will not run JavaScript in the headless browser diff --git a/doc/user/instance_statistics/convdev.md b/doc/user/instance_statistics/convdev.md index 247be1fb392..2c9e0ecbf65 100644 --- a/doc/user/instance_statistics/convdev.md +++ b/doc/user/instance_statistics/convdev.md @@ -2,7 +2,7 @@ > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/30469) in GitLab 9.3. -NOTE: **NOTE** +NOTE: **Note:** Your GitLab instance's [usage ping](../admin_area/settings/usage_statistics.md#usage-ping-core-only) must be activated in order to use this feature. The Conversational Development Index (ConvDev Index) gives you an overview of your entire diff --git a/lib/gitlab/danger/teammate.rb b/lib/gitlab/danger/teammate.rb index 4b822aa86c5..bfada512727 100644 --- a/lib/gitlab/danger/teammate.rb +++ b/lib/gitlab/danger/teammate.rb @@ -21,21 +21,21 @@ module Gitlab # Traintainers also count as reviewers def reviewer?(project, category) - capabilities(project) == "reviewer #{category}" || traintainer?(project, category) + capabilities(project).include?("reviewer #{category}") || traintainer?(project, category) end def traintainer?(project, category) - capabilities(project) == "trainee_maintainer #{category}" + capabilities(project).include?("trainee_maintainer #{category}") end def maintainer?(project, category) - capabilities(project) == "maintainer #{category}" + capabilities(project).include?("maintainer #{category}") end private def capabilities(project) - projects.fetch(project, '') + Array(projects.fetch(project, [])) end end end diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb index 48c113a8b14..0a371889af2 100644 --- a/lib/gitlab/gitaly_client.rb +++ b/lib/gitlab/gitaly_client.rb @@ -257,8 +257,7 @@ module Gitlab # This is this actual number of times this call was made. Used for information purposes only actual_call_count = increment_call_count("gitaly_#{call_site}_actual") - # Do no enforce limits in production - return if Rails.env.production? || ENV["GITALY_DISABLE_REQUEST_LIMITS"] + return unless enforce_gitaly_request_limits? # Check if this call is nested within a allow_n_plus_1_calls # block and skip check if it is @@ -275,6 +274,19 @@ module Gitlab raise TooManyInvocationsError.new(call_site, actual_call_count, max_call_count, max_stacks) end + def self.enforce_gitaly_request_limits? + # We typically don't want to enforce request limits in production + # However, we have some production-like test environments, i.e., ones + # where `Rails.env.production?` returns `true`. We do want to be able to + # check if the limit is being exceeded while testing in those environments + # In that case we can use a feature flag to indicate that we do want to + # enforce request limits. + return true if feature_enabled?('enforce_requests_limits') + + !(Rails.env.production? || ENV["GITALY_DISABLE_REQUEST_LIMITS"]) + end + private_class_method :enforce_gitaly_request_limits? + def self.allow_n_plus_1_calls return yield unless Gitlab::SafeRequestStore.active? diff --git a/locale/gitlab.pot b/locale/gitlab.pot index df69e86c1ad..4751b7264a5 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3196,6 +3196,9 @@ msgstr "" msgid "Environments|You don't have any environments right now" msgstr "" +msgid "Environments|protected" +msgstr "" + msgid "Epic" msgstr "" diff --git a/package.json b/package.json index e9abe6a76d7..60b3203741e 100644 --- a/package.json +++ b/package.json @@ -169,7 +169,7 @@ "nodemon": "^1.18.9", "pixelmatch": "^4.0.2", "postcss": "^7.0.14", - "prettier": "1.16.1", + "prettier": "1.16.4", "stylelint": "^9.10.1", "stylelint-config-recommended": "^2.1.0", "stylelint-scss": "^3.5.3", diff --git a/scripts/frontend/postinstall.js b/scripts/frontend/postinstall.js index 682039a41b3..94977e459e3 100644 --- a/scripts/frontend/postinstall.js +++ b/scripts/frontend/postinstall.js @@ -13,7 +13,7 @@ if (process.platform === 'darwin') { ensure that it is supported by the fsevents library. You can try installing again with \`${chalk.cyan('yarn install --force')}\` - `) + `), ); process.exit(1); } diff --git a/scripts/frontend/prettier.js b/scripts/frontend/prettier.js index ffb09ea9779..bf0e98da139 100644 --- a/scripts/frontend/prettier.js +++ b/scripts/frontend/prettier.js @@ -32,7 +32,7 @@ let globDir = process.argv[3] || ''; if (globDir && globDir.charAt(globDir.length - 1) !== '/') globDir += '/'; console.log( - `Loading all ${allFiles ? '' : 'staged '}files ${globDir ? `within ${globDir} ` : ''}...` + `Loading all ${allFiles ? '' : 'staged '}files ${globDir ? `within ${globDir} ` : ''}...`, ); const globPatterns = matchExtensions.map(ext => `${globDir}**/*.${ext}`); @@ -105,7 +105,7 @@ Promise.all(matchedFiles.map(checkFileWithPrettierConfig)) .then(() => { const failAction = shouldSave ? 'fixed' : 'failed'; console.log( - `\nSummary:\n ${matchedCount} files processed (${passedCount} passed, ${failedCount} ${failAction}, ${ignoredCount} ignored)\n` + `\nSummary:\n ${matchedCount} files processed (${passedCount} passed, ${failedCount} ${failAction}, ${ignoredCount} ignored)\n`, ); if (didWarn) process.exit(1); diff --git a/scripts/insert-rspec-profiling-data b/scripts/insert-rspec-profiling-data new file mode 100755 index 00000000000..10e337b9972 --- /dev/null +++ b/scripts/insert-rspec-profiling-data @@ -0,0 +1,47 @@ +#!/usr/bin/env ruby + +require 'csv' +require 'rspec_profiling' +require 'postgres-copy' + +module RspecProfiling + module Collectors + class PSQL + def establish_connection + # This disables the automatic creation of the database and + # table. In the future, we may want a way to specify the host of + # the database to connect so that we can call #install. + Result.establish_connection(results_url) + end + + def prepared? + connection.data_source_exists?(table) + end + + def results_url + ENV['RSPEC_PROFILING_POSTGRES_URL'] + end + + class Result < ActiveRecord::Base + acts_as_copy_target + end + end + end +end + +def insert_data(path) + puts "#{Time.now} Inserting CI stats..." + + collector = RspecProfiling::Collectors::PSQL.new + collector.install + + files = Dir[File.join(path, "*.csv")] + + files.each do |filename| + puts "#{Time.now} Inserting #{filename}..." + result = RspecProfiling::Collectors::PSQL::Result.copy_from(filename) + puts "#{Time.now} Inserted #{result.cmd_tuples} lines in #{filename}, DB response: #{result.cmd_status}" + end +end + +insert_data('rspec_profiling') if ENV['RSPEC_PROFILING_POSTGRES_URL'].present? diff --git a/spec/javascripts/fixtures/emojis.rb b/spec/javascripts/fixtures/emojis.rb index f5fb008c7b3..4dab697e5e2 100644 --- a/spec/javascripts/fixtures/emojis.rb +++ b/spec/javascripts/fixtures/emojis.rb @@ -9,6 +9,8 @@ describe 'Emojis (JavaScript fixtures)' do it 'emojis/emojis.json' do |example| JavaScriptFixturesHelpers::FIXTURE_PATHS.each do |fixture_path| + next unless File.directory?(fixture_path) + # Copying the emojis.json from the public folder fixture_file_name = File.expand_path('emojis/emojis.json', fixture_path) FileUtils.mkdir_p(File.dirname(fixture_file_name)) diff --git a/spec/lib/gitlab/danger/teammate_spec.rb b/spec/lib/gitlab/danger/teammate_spec.rb new file mode 100644 index 00000000000..4bc0a4c1398 --- /dev/null +++ b/spec/lib/gitlab/danger/teammate_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +describe Gitlab::Danger::Teammate do + subject { described_class.new({ 'projects' => projects }) } + let(:projects) { { project => capabilities } } + let(:project) { double } + + describe 'multiple roles project project' do + let(:capabilities) { ['reviewer backend', 'maintainer frontend', 'trainee_maintainer database'] } + + it '#reviewer? supports multiple roles per project' do + expect(subject.reviewer?(project, 'backend')).to be_truthy + end + + it '#traintainer? supports multiple roles per project' do + expect(subject.traintainer?(project, 'database')).to be_truthy + end + + it '#maintainer? supports multiple roles per project' do + expect(subject.maintainer?(project, 'frontend')).to be_truthy + end + end + + describe 'one role project project' do + let(:capabilities) { 'reviewer backend' } + + it '#reviewer? supports one role per project' do + expect(subject.reviewer?(project, 'backend')).to be_truthy + end + + it '#traintainer? supports one role per project' do + expect(subject.traintainer?(project, 'database')).to be_falsey + end + + it '#maintainer? supports one role per project' do + expect(subject.maintainer?(project, 'frontend')).to be_falsey + end + end +end diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb index cf12baf1a93..f1acb1d9bc4 100644 --- a/spec/lib/gitlab/gitaly_client_spec.rb +++ b/spec/lib/gitlab/gitaly_client_spec.rb @@ -149,11 +149,21 @@ describe Gitlab::GitalyClient do end end - context 'when RequestStore is enabled', :request_store do + context 'when RequestStore is enabled and the maximum number of calls is not enforced by a feature flag', :request_store do + before do + stub_feature_flags(gitaly_enforce_requests_limits: false) + end + it 'allows up the maximum number of allowed calls' do expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS) }.not_to raise_error end + it 'allows the maximum number of calls to be exceeded if GITALY_DISABLE_REQUEST_LIMITS is set' do + stub_env('GITALY_DISABLE_REQUEST_LIMITS', 'true') + + expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS + 1) }.not_to raise_error + end + context 'when the maximum number of calls has been reached' do before do call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS) @@ -189,6 +199,32 @@ describe Gitlab::GitalyClient do end end + context 'in production and when RequestStore is enabled', :request_store do + before do + allow(Rails.env).to receive(:production?).and_return(true) + end + + context 'when the maximum number of calls is enforced by a feature flag' do + before do + stub_feature_flags(gitaly_enforce_requests_limits: true) + end + + it 'does not allow the maximum number of calls to be exceeded' do + expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS + 1) }.to raise_error(Gitlab::GitalyClient::TooManyInvocationsError) + end + end + + context 'when the maximum number of calls is not enforced by a feature flag' do + before do + stub_feature_flags(gitaly_enforce_requests_limits: false) + end + + it 'allows the maximum number of calls to be exceeded' do + expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS + 1) }.not_to raise_error + end + end + end + context 'when RequestStore is not active' do it 'does not raise errors when the maximum number of allowed calls is exceeded' do expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS + 2) }.not_to raise_error diff --git a/yarn.lock b/yarn.lock index 9908d829b3a..4456810ec43 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8086,16 +8086,16 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= -prettier@1.16.1: - version "1.16.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.1.tgz#534c2c9d7853f8845e5e078384e71973bd74089f" - integrity sha512-XXUITwIkGb3CPJ2hforHah/zTINRyie5006Jd2HKy2qz7snEJXl0KLfsJZW/wst9g6R2rFvqba3VpNYdu1hDcA== - prettier@1.16.3: version "1.16.3" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.3.tgz#8c62168453badef702f34b45b6ee899574a6a65d" integrity sha512-kn/GU6SMRYPxUakNXhpP0EedT/KmaPzr0H5lIsDogrykbaxOpOfAFfk5XA7DZrJyMAv1wlMV3CPcZruGXVVUZw== +prettier@1.16.4: + version "1.16.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.4.tgz#73e37e73e018ad2db9c76742e2647e21790c9717" + integrity sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g== + pretty-format@^24.0.0: version "24.0.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.0.0.tgz#cb6599fd73ac088e37ed682f61291e4678f48591" |