From e1e7e4ae8034d25b34c159b9088022458b9cb9b0 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Sat, 9 Jan 2021 00:10:30 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../components/text_editor.vue | 10 +- .../javascripts/pipeline_editor/index.js | 9 +- .../pipeline_editor/pipeline_editor_app.vue | 12 +- .../ci/pipeline_editor/show.html.haml | 4 +- ..._i_testing_metrics_report_widget_total.yml | 8 ++ doc/.vale/gitlab/SubstitutionSuggestions.yml | 2 +- doc/ci/docker/using_docker_build.md | 2 +- doc/ci/docker/using_kaniko.md | 33 +++++ doc/ci/pipelines/index.md | 4 +- doc/user/packages/conan_repository/index.md | 16 +++ doc/user/permissions.md | 1 + doc/user/project/quick_actions.md | 7 +- lib/bulk_imports/importers/group_importer.rb | 13 +- .../quick_actions/merge_request_actions.rb | 99 +++++++++++++++ .../known_events/common.yml | 5 + locale/gitlab.pot | 27 ++++ .../components/text_editor_spec.js | 12 +- .../pipeline_editor/graphql/resolvers_spec.js | 6 +- spec/frontend/pipeline_editor/mock_data.js | 6 +- .../pipeline_editor_app_spec.js | 30 +++-- .../importers/group_importer_spec.rb | 4 +- spec/lib/gitlab/usage_data_spec.rb | 2 +- .../quick_actions/interpret_service_spec.rb | 120 ++++++++++++++++++ 23 files changed, 383 insertions(+), 49 deletions(-) create mode 100644 config/feature_flags/development/usage_data_i_testing_metrics_report_widget_total.yml diff --git a/app/assets/javascripts/pipeline_editor/components/text_editor.vue b/app/assets/javascripts/pipeline_editor/components/text_editor.vue index 6954ba3d6ff..6ef0ef591d8 100644 --- a/app/assets/javascripts/pipeline_editor/components/text_editor.vue +++ b/app/assets/javascripts/pipeline_editor/components/text_editor.vue @@ -7,6 +7,7 @@ export default { EditorLite, }, inheritAttrs: false, + inject: ['projectPath', 'projectNamespace'], props: { ciConfigPath: { type: String, @@ -17,20 +18,15 @@ export default { required: false, default: null, }, - projectPath: { - type: String, - required: true, - }, }, methods: { onEditorReady() { const editorInstance = this.$refs.editor.getEditor(); - const [projectNamespace, projectPath] = this.projectPath.split('/'); editorInstance.use(new CiSchemaExtension()); editorInstance.registerCiSchema({ - projectPath, - projectNamespace, + projectPath: this.projectPath, + projectNamespace: this.projectNamespace, ref: this.commitSha, }); }, diff --git a/app/assets/javascripts/pipeline_editor/index.js b/app/assets/javascripts/pipeline_editor/index.js index df093be9b92..583ba555080 100644 --- a/app/assets/javascripts/pipeline_editor/index.js +++ b/app/assets/javascripts/pipeline_editor/index.js @@ -15,12 +15,17 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => { } const { + // props ciConfigPath, commitSha, defaultBranch, newMergeRequestPath, + + // `provide/inject` data lintHelpPagePath, + projectFullPath, projectPath, + projectNamespace, ymlHelpPagePath, } = el?.dataset; @@ -35,6 +40,9 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => { apolloProvider, provide: { lintHelpPagePath, + projectFullPath, + projectPath, + projectNamespace, ymlHelpPagePath, }, render(h) { @@ -44,7 +52,6 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => { commitSha, defaultBranch, newMergeRequestPath, - projectPath, }, }); }, diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue index 87327a46f17..2880d649075 100644 --- a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue +++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue @@ -39,11 +39,8 @@ export default { ValidationSegment, }, mixins: [glFeatureFlagsMixin()], + inject: ['projectFullPath'], props: { - projectPath: { - type: String, - required: true, - }, defaultBranch: { type: String, required: false, @@ -84,7 +81,7 @@ export default { query: getBlobContent, variables() { return { - projectPath: this.projectPath, + projectPath: this.projectFullPath, path: this.ciConfigPath, ref: this.defaultBranch, }; @@ -107,7 +104,7 @@ export default { }, variables() { return { - projectPath: this.projectPath, + projectPath: this.projectFullPath, content: this.contentModel, }; }, @@ -244,7 +241,7 @@ export default { } = await this.$apollo.mutate({ mutation: commitCiFileMutation, variables: { - projectPath: this.projectPath, + projectPath: this.projectFullPath, branch, startBranch: this.defaultBranch, message, @@ -318,7 +315,6 @@ export default { v-model="contentModel" :ci-config-path="ciConfigPath" :commit-sha="lastCommitSha" - :project-path="projectPath" /> @project.full_path, + "project-path" => @project.path, + "project-full-path" => @project.full_path, + "project-namespace" => @project.namespace.full_path, "default-branch" => @project.default_branch, "commit-sha" => @project.commit ? @project.commit.sha : '', "new-merge-request-path" => namespace_project_new_merge_request_path, diff --git a/config/feature_flags/development/usage_data_i_testing_metrics_report_widget_total.yml b/config/feature_flags/development/usage_data_i_testing_metrics_report_widget_total.yml new file mode 100644 index 00000000000..2c086b211ba --- /dev/null +++ b/config/feature_flags/development/usage_data_i_testing_metrics_report_widget_total.yml @@ -0,0 +1,8 @@ +--- +name: usage_data_i_testing_metrics_report_widget_total +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50790 +rollout_issue_url: +milestone: '13.8' +type: development +group: group::testing +default_enabled: true diff --git a/doc/.vale/gitlab/SubstitutionSuggestions.yml b/doc/.vale/gitlab/SubstitutionSuggestions.yml index 735a39119d7..ab6658f0943 100644 --- a/doc/.vale/gitlab/SubstitutionSuggestions.yml +++ b/doc/.vale/gitlab/SubstitutionSuggestions.yml @@ -13,7 +13,7 @@ ignorecase: true swap: active user: '"billable user"' active users: '"billable users"' - docs: documentation + docs: '"documentation"' once that: '"after that"' once the: '"after the"' once you: '"after you"' diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index 7b6466adbde..af88a006156 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -404,7 +404,7 @@ To make Docker available in the context of the image: commands are siblings of the runner rather than children of the runner.** This may have complications and limitations that are unsuitable for your workflow. - Your `config.toml` file should not have an entry like this: + Your `config.toml` file should now have an entry like this: ```toml [[runners]] diff --git a/doc/ci/docker/using_kaniko.md b/doc/ci/docker/using_kaniko.md index 89722dac419..7eb2a8286c7 100644 --- a/doc/ci/docker/using_kaniko.md +++ b/doc/ci/docker/using_kaniko.md @@ -70,6 +70,39 @@ build: - if: $CI_COMMIT_TAG ``` +### Building an image with kaniko behind a proxy + +If you use a custom GitLab Runner behind an http(s) proxy, kaniko needs to be set +up accordingly. This means: + +- Adding the proxy to `/kaniko/.docker/config.json` +- Passing the `http_proxy` environment variables as build args so the Dockerfile + instructions can use the proxy when building the image. + +The previous example can be extended as follows: + +```yaml +build: + stage: build + image: + name: gcr.io/kaniko-project/executor:debug + entrypoint: [""] + script: + - mkdir -p /kaniko/.docker + - |- + KANIKOPROXYBUILDARGS="" + KANIKOCFG="{ \"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}" + if [ "x${http_proxy}" != "x" -o "x${https_proxy}" != "x" ]; then + KANIKOCFG="${KANIKOCFG}, \"proxies\": { \"default\": { \"httpProxy\": \"${http_proxy}\", \"httpsProxy\": \"${https_proxy}\", \"noProxy\": \"${no_proxy}\"}}" + KANIKOPROXYBUILDARGS="--build-arg http_proxy=${http_proxy} --build-arg https_proxy=${https_proxy} --build-arg no_proxy=${no_proxy}" + fi + KANIKOCFG="${KANIKOCFG} }" + echo "${KANIKOCFG}" > /kaniko/.docker/config.json + - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile $KANIKOPROXYBUILDARGS --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG + only: + - tags +``` + ## Using a registry with a custom certificate When trying to push to a Docker registry that uses a certificate that is signed diff --git a/doc/ci/pipelines/index.md b/doc/ci/pipelines/index.md index 160b10aefa2..fc615333b9f 100644 --- a/doc/ci/pipelines/index.md +++ b/doc/ci/pipelines/index.md @@ -133,8 +133,8 @@ You might do this if the results of a pipeline (for example, a code build) are r operation of the pipeline. [In GitLab 13.7 and later](https://gitlab.com/gitlab-org/gitlab/-/issues/30101), -the variables fields are pre-filled with any global variables defined in the -`.gitlab-ci.yml` file. +all global variables with descriptions defined in the `.gitlab-ci.yml` file are +displayed in the variable fields. To execute a pipeline manually: diff --git a/doc/user/packages/conan_repository/index.md b/doc/user/packages/conan_repository/index.md index bf7b402ad1c..2068338c878 100644 --- a/doc/user/packages/conan_repository/index.md +++ b/doc/user/packages/conan_repository/index.md @@ -283,6 +283,9 @@ Additional Conan images to use as the basis of your CI file are available in the Install a Conan package from the Package Registry so you can use it as a dependency. +WARNING: +Project-level packages [cannot be downloaded currently](https://gitlab.com/gitlab-org/gitlab/-/issues/270129). + Conan packages are often installed as dependencies by using the `conanfile.txt` file. @@ -384,3 +387,16 @@ The GitLab Conan repository supports the following Conan CLI commands: packages you have permission to view. - `conan info`: View the information on a given package from the Package Registry. - `conan remove`: Delete the package from the Package Registry. + +## Troubleshooting Conan packages + +### `ERROR: was not found in remote ` + +When you attempt to install a Conan package, you might receive a `404` error +like `ERROR: was not found in remote `. + +This issue occurs when you request a download from the project-level Conan API. +The resulting URL is missing is project's `/` and Conan commands, like +`conan install`, fail. + +For more information, see [issue 270129](https://gitlab.com/gitlab-org/gitlab/-/issues/270129). diff --git a/doc/user/permissions.md b/doc/user/permissions.md index 71c299a37a7..816b0e5ab82 100644 --- a/doc/user/permissions.md +++ b/doc/user/permissions.md @@ -75,6 +75,7 @@ The following table depicts the various user permission levels in a project. | Manage user-starred metrics dashboards (*7*) | ✓ | ✓ | ✓ | ✓ | ✓ | | View confidential issues | (*2*) | ✓ | ✓ | ✓ | ✓ | | Assign issues | | ✓ | ✓ | ✓ | ✓ | +| Assign reviewers | | ✓ | ✓ | ✓ | ✓ | | Label issues | | ✓ | ✓ | ✓ | ✓ | | Set issue weight | | ✓ | ✓ | ✓ | ✓ | | Lock issue threads | | ✓ | ✓ | ✓ | ✓ | diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md index c758b2775d2..56299f80ef0 100644 --- a/doc/user/project/quick_actions.md +++ b/doc/user/project/quick_actions.md @@ -31,6 +31,9 @@ The following quick actions are applicable to descriptions, discussions and thre | `/assign @user` | ✓ | ✓ | | Assign one user. | | `/assign @user1 @user2` | ✓ | ✓ | | Assign multiple users. **(STARTER)** | | `/assign me` | ✓ | ✓ | | Assign yourself. | +| `/assign_reviewer @user` | | ✓ | | Assign one user as a reviewer. | +| `/assign_reviewer @user1 @user2` | | ✓ | | Assign multiple users as reviewers. **(STARTER)** | +| `/assign_reviewer me` | | ✓ | | Assign yourself as a reviewer. | | `/award :emoji:` | ✓ | ✓ | ✓ | Toggle emoji award. | | `/child_epic ` | | | ✓ | Add child epic to ``. The `` value should be in the format of `&epic`, `group&epic`, or a URL to an epic ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab/-/issues/7330)). **(ULTIMATE)** | | `/clear_weight` | ✓ | | | Clear weight. **(STARTER)** | @@ -79,7 +82,9 @@ The following quick actions are applicable to descriptions, discussions and thre | `/title ` | ✓ | ✓ | ✓ | Change title. | | `/todo` | ✓ | ✓ | ✓ | Add a to-do item. | | `/unassign @user1 @user2` | ✓ | ✓ | | Remove specific assignees. **(STARTER)** | -| `/unassign` | ✓ | ✓ | | Remove all assignees. | +| `/unassign` | | ✓ | | Remove all assignees. | +| `/unassign_reviewer @user1 @user2` | | ✓ | | Remove specific reviewers. **(STARTER)** | +| `/unassign_reviewer` | | ✓ | | Remove all reviewers. | | `/unlabel ~label1 ~label2` or `/remove_label ~label1 ~label2` | ✓ | ✓ | ✓ | Remove specified labels. | | `/unlabel` or `/remove_label` | ✓ | ✓ | ✓ | Remove all labels. | | `/unlock` | ✓ | ✓ | | Unlock the discussions. | diff --git a/lib/bulk_imports/importers/group_importer.rb b/lib/bulk_imports/importers/group_importer.rb index 82cb1ca03a2..3dbcba18c94 100644 --- a/lib/bulk_imports/importers/group_importer.rb +++ b/lib/bulk_imports/importers/group_importer.rb @@ -18,9 +18,7 @@ module BulkImports configuration: configuration ) - BulkImports::Groups::Pipelines::GroupPipeline.new.run(context) - 'BulkImports::EE::Groups::Pipelines::EpicsPipeline'.constantize.new.run(context) if Gitlab.ee? - BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline.new.run(context) + pipelines.each { |pipeline| pipeline.new.run(context) } entity.finish! end @@ -28,6 +26,15 @@ module BulkImports private attr_reader :entity + + def pipelines + [ + BulkImports::Groups::Pipelines::GroupPipeline, + BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline + ] + end end end end + +BulkImports::Importers::GroupImporter.prepend_if_ee('EE::BulkImports::Importers::GroupImporter') diff --git a/lib/gitlab/quick_actions/merge_request_actions.rb b/lib/gitlab/quick_actions/merge_request_actions.rb index ffce109d808..70c581c6454 100644 --- a/lib/gitlab/quick_actions/merge_request_actions.rb +++ b/lib/gitlab/quick_actions/merge_request_actions.rb @@ -149,6 +149,105 @@ module Gitlab @execution_message[:approve] = _('Approved the current merge request.') end + + desc do + if quick_action_target.allows_multiple_reviewers? + _('Assign reviewer(s)') + else + _('Assign reviewer') + end + end + explanation do |users| + _('Assigns %{reviewer_users_sentence} as %{reviewer_text}.') % { reviewer_users_sentence: reviewer_users_sentence(users), + reviewer_text: 'reviewer'.pluralize(users.size) } + end + execution_message do |users = nil| + if users.blank? + _("Failed to assign a reviewer because no user was found.") + else + users = [users.first] unless quick_action_target.allows_multiple_reviewers? + _('Assigned %{reviewer_users_sentence} as %{reviewer_text}.') % { reviewer_users_sentence: reviewer_users_sentence(users), + reviewer_text: 'reviewer'.pluralize(users.size) } + end + end + params do + quick_action_target.allows_multiple_reviewers? ? '@user1 @user2' : '@user' + end + types MergeRequest + condition do + Feature.enabled?(:merge_request_reviewers, project, default_enabled: :yaml) && + current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project) + end + parse_params do |reviewer_param| + extract_users(reviewer_param) + end + command :assign_reviewer do |users| + next if users.empty? + + if quick_action_target.allows_multiple_reviewers? + @updates[:reviewer_ids] ||= quick_action_target.reviewers.map(&:id) + @updates[:reviewer_ids] |= users.map(&:id) + else + @updates[:reviewer_ids] = [users.first.id] + end + end + + desc do + if quick_action_target.allows_multiple_reviewers? + _('Remove all or specific reviewer(s)') + else + _('Remove reviewer') + end + end + explanation do |users = nil| + reviewers = reviewers_for_removal(users) + _("Removes %{reviewer_text} %{reviewer_references}.") % + { reviewer_text: 'reviewer'.pluralize(reviewers.size), reviewer_references: reviewers.map(&:to_reference).to_sentence } + end + execution_message do |users = nil| + reviewers = reviewers_for_removal(users) + _("Removed %{reviewer_text} %{reviewer_references}.") % + { reviewer_text: 'reviewer'.pluralize(reviewers.size), reviewer_references: reviewers.map(&:to_reference).to_sentence } + end + params do + quick_action_target.allows_multiple_reviewers? ? '@user1 @user2' : '' + end + types MergeRequest + condition do + quick_action_target.persisted? && + Feature.enabled?(:merge_request_reviewers, project, default_enabled: :yaml) && + quick_action_target.reviewers.any? && + current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project) + end + parse_params do |unassign_reviewer_param| + # When multiple users are assigned, all will be unassigned if multiple reviewers are no longer allowed + extract_users(unassign_reviewer_param) if quick_action_target.allows_multiple_reviewers? + end + command :unassign_reviewer do |users = nil| + if quick_action_target.allows_multiple_reviewers? && users&.any? + @updates[:reviewer_ids] ||= quick_action_target.reviewers.map(&:id) + @updates[:reviewer_ids] -= users.map(&:id) + else + @updates[:reviewer_ids] = [] + end + end + end + + def reviewer_users_sentence(users) + if quick_action_target.allows_multiple_reviewers? + users + else + [users.first] + end.map(&:to_reference).to_sentence + end + + def reviewers_for_removal(users) + reviewers = quick_action_target.reviewers + if users.present? && quick_action_target.allows_multiple_reviewers? + users + else + reviewers + end end def merge_orchestration_service diff --git a/lib/gitlab/usage_data_counters/known_events/common.yml b/lib/gitlab/usage_data_counters/known_events/common.yml index fdbbbc7a029..cc26753eeaa 100644 --- a/lib/gitlab/usage_data_counters/known_events/common.yml +++ b/lib/gitlab/usage_data_counters/known_events/common.yml @@ -248,6 +248,11 @@ redis_slot: testing aggregation: weekly feature_flag: usage_data_i_testing_test_case_parsed +- name: i_testing_metrics_report_widget_total + category: testing + redis_slot: testing + aggregation: weekly + feature_flag: usage_data_i_testing_metrics_report_widget_total # Project Management group - name: g_project_management_issue_title_changed category: issues_edit diff --git a/locale/gitlab.pot b/locale/gitlab.pot index d5e4f5e6200..8124bd6b0f8 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3889,6 +3889,12 @@ msgstr "" msgid "Assign milestone" msgstr "" +msgid "Assign reviewer" +msgstr "" + +msgid "Assign reviewer(s)" +msgstr "" + msgid "Assign some issues to this milestone." msgstr "" @@ -3907,6 +3913,9 @@ msgstr "" msgid "Assigned %{assignee_users_sentence}." msgstr "" +msgid "Assigned %{reviewer_users_sentence} as %{reviewer_text}." +msgstr "" + msgid "Assigned Issues" msgstr "" @@ -3954,6 +3963,9 @@ msgstr "" msgid "Assigns %{assignee_users_sentence}." msgstr "" +msgid "Assigns %{reviewer_users_sentence} as %{reviewer_text}." +msgstr "" + msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules." msgstr "" @@ -11748,6 +11760,9 @@ msgstr "" msgid "Failed to apply commands." msgstr "" +msgid "Failed to assign a reviewer because no user was found." +msgstr "" + msgid "Failed to assign a user because no user was found." msgstr "" @@ -23359,6 +23374,9 @@ msgstr "" msgid "Remove all or specific label(s)" msgstr "" +msgid "Remove all or specific reviewer(s)" +msgstr "" + msgid "Remove approver" msgstr "" @@ -23437,6 +23455,9 @@ msgstr "" msgid "Remove report" msgstr "" +msgid "Remove reviewer" +msgstr "" + msgid "Remove secondary node" msgstr "" @@ -23473,6 +23494,9 @@ msgstr "" msgid "Removed %{milestone_reference} milestone." msgstr "" +msgid "Removed %{reviewer_text} %{reviewer_references}." +msgstr "" + msgid "Removed %{type} with id %{id}" msgstr "" @@ -23518,6 +23542,9 @@ msgstr "" msgid "Removes %{milestone_reference} milestone." msgstr "" +msgid "Removes %{reviewer_text} %{reviewer_references}." +msgstr "" + msgid "Removes all labels." msgstr "" diff --git a/spec/frontend/pipeline_editor/components/text_editor_spec.js b/spec/frontend/pipeline_editor/components/text_editor_spec.js index 3d3bb4f78c5..9221d64c44b 100644 --- a/spec/frontend/pipeline_editor/components/text_editor_spec.js +++ b/spec/frontend/pipeline_editor/components/text_editor_spec.js @@ -4,8 +4,7 @@ import { mockCiYml, mockCommitSha, mockProjectPath, - mockNamespace, - mockProjectName, + mockProjectNamespace, } from '../mock_data'; import TextEditor from '~/pipeline_editor/components/text_editor.vue'; @@ -33,10 +32,13 @@ describe('~/pipeline_editor/components/text_editor.vue', () => { const createComponent = (opts = {}, mountFn = shallowMount) => { wrapper = mountFn(TextEditor, { + provide: { + projectPath: mockProjectPath, + projectNamespace: mockProjectNamespace, + }, propsData: { ciConfigPath: mockCiConfigPath, commitSha: mockCommitSha, - projectPath: mockProjectPath, }, attrs: { value: mockCiYml, @@ -77,8 +79,8 @@ describe('~/pipeline_editor/components/text_editor.vue', () => { expect(mockUse).toHaveBeenCalledTimes(1); expect(mockRegisterCiSchema).toHaveBeenCalledTimes(1); expect(mockRegisterCiSchema).toHaveBeenCalledWith({ - projectNamespace: mockNamespace, - projectPath: mockProjectName, + projectNamespace: mockProjectNamespace, + projectPath: mockProjectPath, ref: mockCommitSha, }); }); diff --git a/spec/frontend/pipeline_editor/graphql/resolvers_spec.js b/spec/frontend/pipeline_editor/graphql/resolvers_spec.js index b531f8af797..3e008527415 100644 --- a/spec/frontend/pipeline_editor/graphql/resolvers_spec.js +++ b/spec/frontend/pipeline_editor/graphql/resolvers_spec.js @@ -5,7 +5,7 @@ import { mockCiYml, mockDefaultBranch, mockLintResponse, - mockProjectPath, + mockProjectFullPath, } from '../mock_data'; import httpStatus from '~/lib/utils/http_status'; import axios from '~/lib/utils/axios_utils'; @@ -32,12 +32,12 @@ describe('~/pipeline_editor/graphql/resolvers', () => { it('resolves lint data with type names', async () => { const result = resolvers.Query.blobContent(null, { - projectPath: mockProjectPath, + projectPath: mockProjectFullPath, path: mockCiConfigPath, ref: mockDefaultBranch, }); - expect(Api.getRawFile).toHaveBeenCalledWith(mockProjectPath, mockCiConfigPath, { + expect(Api.getRawFile).toHaveBeenCalledWith(mockProjectFullPath, mockCiConfigPath, { ref: mockDefaultBranch, }); diff --git a/spec/frontend/pipeline_editor/mock_data.js b/spec/frontend/pipeline_editor/mock_data.js index ac23ee9a2bc..624800bdec1 100644 --- a/spec/frontend/pipeline_editor/mock_data.js +++ b/spec/frontend/pipeline_editor/mock_data.js @@ -1,9 +1,9 @@ import { CI_CONFIG_STATUS_VALID } from '~/pipeline_editor/constants'; import { unwrapStagesWithNeeds } from '~/pipelines/components/unwrapping_utils'; -export const mockNamespace = 'user1'; -export const mockProjectName = 'project1'; -export const mockProjectPath = `${mockNamespace}/${mockProjectName}`; +export const mockProjectNamespace = 'user1'; +export const mockProjectPath = 'project1'; +export const mockProjectFullPath = `${mockProjectNamespace}/${mockProjectPath}`; export const mockDefaultBranch = 'master'; export const mockNewMergeRequestPath = '/-/merge_requests/new'; export const mockCommitSha = 'aabbccdd'; diff --git a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js index 2f8a1e12616..6f59803d78f 100644 --- a/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js +++ b/spec/frontend/pipeline_editor/pipeline_editor_app_spec.js @@ -15,6 +15,8 @@ import { mockCommitMessage, mockDefaultBranch, mockProjectPath, + mockProjectFullPath, + mockProjectNamespace, mockNewMergeRequestPath, } from './mock_data'; @@ -39,6 +41,15 @@ const MockEditorLite = { template: '
', }; +const mockProvide = { + projectFullPath: mockProjectFullPath, + projectPath: mockProjectPath, + projectNamespace: mockProjectNamespace, + glFeatures: { + ciConfigVisualizationTab: true, + }, +}; + describe('~/pipeline_editor/pipeline_editor_app.vue', () => { let wrapper; @@ -53,11 +64,7 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { lintLoading = false, options = {}, mountFn = shallowMount, - provide = { - glFeatures: { - ciConfigVisualizationTab: true, - }, - }, + provide = mockProvide, } = {}) => { mockMutate = jest.fn().mockResolvedValue({ data: { @@ -75,7 +82,6 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { ciConfigPath: mockCiConfigPath, commitSha: mockCommitSha, defaultBranch: mockDefaultBranch, - projectPath: mockProjectPath, newMergeRequestPath: mockNewMergeRequestPath, ...props, }, @@ -211,7 +217,12 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { describe('with feature flag off', () => { beforeEach(() => { - createComponent({ provide: { glFeatures: { ciConfigVisualizationTab: false } } }); + createComponent({ + provide: { + ...mockProvide, + glFeatures: { ciConfigVisualizationTab: false }, + }, + }); }); it('does not display the tab', () => { @@ -242,7 +253,6 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { it('configures text editor', () => { expect(findTextEditor().props('commitSha')).toBe(mockCommitSha); - expect(findTextEditor().props('projectPath')).toBe(mockProjectPath); }); describe('commit form', () => { @@ -251,7 +261,7 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { filePath: mockCiConfigPath, lastCommitId: mockCommitSha, message: mockCommitMessage, - projectPath: mockProjectPath, + projectPath: mockProjectFullPath, startBranch: mockDefaultBranch, }; @@ -435,7 +445,7 @@ describe('~/pipeline_editor/pipeline_editor_app.vue', () => { expect(mockCiConfigData).toHaveBeenCalledWith({ content: mockCiYml, - projectPath: mockProjectPath, + projectPath: mockProjectFullPath, }); }); diff --git a/spec/lib/bulk_imports/importers/group_importer_spec.rb b/spec/lib/bulk_imports/importers/group_importer_spec.rb index 95dca7fc486..81732496bc1 100644 --- a/spec/lib/bulk_imports/importers/group_importer_spec.rb +++ b/spec/lib/bulk_imports/importers/group_importer_spec.rb @@ -18,14 +18,14 @@ RSpec.describe BulkImports::Importers::GroupImporter do subject { described_class.new(bulk_import_entity) } before do - allow(Gitlab).to receive(:ee?).and_return(false) allow(BulkImports::Pipeline::Context).to receive(:new).and_return(context) end describe '#execute' do it 'starts the entity and run its pipelines' do - expect(bulk_import_entity).to receive(:start).and_call_original + expect(bulk_import_entity).to receive(:start!).and_call_original expect_to_run_pipeline BulkImports::Groups::Pipelines::GroupPipeline, context: context + expect_to_run_pipeline('EE::BulkImports::Groups::Pipelines::EpicsPipeline'.constantize, context: context) if Gitlab.ee? expect_to_run_pipeline BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline, context: context subject.execute diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index fcf48878a62..fd02521622c 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -1262,7 +1262,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do subject { described_class.redis_hll_counters } let(:categories) { ::Gitlab::UsageDataCounters::HLLRedisCounter.categories } - let(:ineligible_total_categories) { %w[source_code testing ci_secrets_management incident_management_alerts snippets terraform] } + let(:ineligible_total_categories) { %w[source_code ci_secrets_management incident_management_alerts snippets terraform] } it 'has all known_events' do expect(subject).to have_key(:redis_hll_counters) diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index c80fafe1c5d..e95f24c4200 100644 --- a/spec/services/quick_actions/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -8,6 +8,7 @@ RSpec.describe QuickActions::InterpretService do let_it_be(:project) { public_project } let_it_be(:developer) { create(:user) } let_it_be(:developer2) { create(:user) } + let_it_be(:developer3) { create(:user) } let_it_be_with_reload(:issue) { create(:issue, project: project) } let(:milestone) { create(:milestone, project: project, title: '9.10') } let(:commit) { create(:commit, project: project) } @@ -23,6 +24,7 @@ RSpec.describe QuickActions::InterpretService do before do stub_licensed_features(multiple_issue_assignees: false, + multiple_merge_request_reviewers: false, multiple_merge_request_assignees: false) end @@ -665,6 +667,20 @@ RSpec.describe QuickActions::InterpretService do end end + shared_examples 'assign_reviewer command' do + it 'assigns a reviewer to a single user' do + _, updates, _ = service.execute(content, issuable) + + expect(updates).to eq(reviewer_ids: [developer.id]) + end + + it 'returns the assign reviewer message' do + _, _, message = service.execute(content, issuable) + + expect(message).to eq("Assigned #{developer.to_reference} as reviewer.") + end + end + it_behaves_like 'reopen command' do let(:content) { '/reopen' } let(:issuable) { issue } @@ -859,6 +875,110 @@ RSpec.describe QuickActions::InterpretService do let(:issuable) { issue } end + context 'when the merge_request_reviewers flag is enabled' do + context 'assign_reviewer command with one user' do + it_behaves_like 'assign_reviewer command' do + let(:content) { "/assign_reviewer @#{developer.username}" } + let(:issuable) { merge_request } + end + + it_behaves_like 'empty command' do + let(:content) { "/assign_reviewer @#{developer.username}" } + let(:issuable) { issue } + end + end + + # CE does not have multiple reviewers + context 'assign_reviewer command with multiple assignees' do + let(:issuable) { merge_request } + + it 'assigns only the first reviewer to the merge request' do + content = "/assign_reviewer @#{developer.username} @#{developer2.username}" + _, updates, _ = service.execute(content, issuable) + + expect(updates).to eq(reviewer_ids: [developer.id]) + end + end + + context 'assign_reviewer command with me alias' do + it_behaves_like 'assign_reviewer command' do + let(:content) { '/assign_reviewer me' } + let(:issuable) { merge_request } + end + end + + context 'assign_reviewer command with me alias and whitespace' do + it_behaves_like 'assign_reviewer command' do + let(:content) { '/assign_reviewer me ' } + let(:issuable) { merge_request } + end + end + + it_behaves_like 'empty command', "Failed to assign a reviewer because no user was found." do + let(:content) { '/assign_reviewer @abcd1234' } + let(:issuable) { merge_request } + end + + it_behaves_like 'empty command', "Failed to assign a reviewer because no user was found." do + let(:content) { '/assign_reviewer' } + let(:issuable) { merge_request } + end + + describe 'assign_reviewer command' do + let(:content) { "/assign_reviewer @#{developer.username} do it!" } + + it 'includes only the user reference' do + _, explanations = service.explain(content, merge_request) + + expect(explanations).to eq(["Assigns @#{developer.username} as reviewer."]) + end + end + + describe 'unassign_reviewer command' do + let(:content) { '/unassign_reviewer' } + let(:merge_request) { create(:merge_request, reviewers: [developer]) } + + it 'includes current reviewer reference' do + _, explanations = service.explain(content, merge_request) + + expect(explanations).to eq(["Removes reviewer @#{developer.username}."]) + end + + it 'populates reviewer_ids: [] if content contains /unassign_reviewer' do + _, updates, _ = service.execute(content, merge_request) + + expect(updates).to eq(reviewer_ids: []) + end + + it 'returns the unassign reviewer message for all the reviewers if content contains /unassign_reviewer' do + merge_request.update!(reviewer_ids: [developer.id, developer2.id]) + _, _, message = service.execute(content, merge_request) + + expect(message).to eq("Removed reviewers #{developer.to_reference} and #{developer2.to_reference}.") + end + end + end + + context 'when the merge_request_reviewers flag is disabled' do + before do + stub_feature_flags(merge_request_reviewers: false) + end + + describe 'assign_reviewer command' do + it_behaves_like 'empty command' do + let(:content) { "/assign_reviewer @#{developer.username}" } + let(:issuable) { merge_request } + end + end + + describe 'unassign_reviewer command' do + it_behaves_like 'empty command' do + let(:content) { "/unassign_reviewer @#{developer.username}" } + let(:issuable) { merge_request } + end + end + end + context 'unassign command' do let(:content) { '/unassign' }