Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									b08b3719a1
								
							
						
					
					
						commit
						d865630025
					
				|  | @ -1 +1 @@ | |||
| aaafc8c4691520f9391a704476f933ffc2fe82fe | ||||
| 687bc5d8f36102e2c8033cce76094d5d318cd961 | ||||
|  |  | |||
|  | @ -0,0 +1,50 @@ | |||
| export function createCveIdRequestIssueBody(fullPath, iid) { | ||||
|   return `### Vulnerability Submission
 | ||||
| 
 | ||||
| **NOTE:** Only maintainers of GitLab-hosted projects may request a CVE for | ||||
| a vulnerability within their project. | ||||
| 
 | ||||
| Project issue: ${fullPath}#${iid} | ||||
| 
 | ||||
| #### Publishing Schedule | ||||
| 
 | ||||
| After a CVE request is validated, a CVE identifier will be assigned. On what | ||||
| schedule should the details of the CVE be published? | ||||
| 
 | ||||
| * [ ] Publish immediately | ||||
| * [ ] Wait to publish | ||||
| 
 | ||||
| <!-- | ||||
| Please fill out the yaml codeblock below | ||||
| --> | ||||
| 
 | ||||
| \`\`\`yaml
 | ||||
| reporter: | ||||
|   name: "TODO" # "First Last" | ||||
|   email: "TODO" # "email@domain.tld" | ||||
| vulnerability: | ||||
|   description: "TODO" # "[VULNTYPE] in [COMPONENT] in [VENDOR][PRODUCT] [VERSION] allows [ATTACKER] to [IMPACT] via [VECTOR]" | ||||
|   cwe: "TODO" # "CWE-22" # Path Traversal | ||||
|   product: | ||||
|     gitlab_path: "${fullPath}" | ||||
|     vendor: "TODO" # "Deluxe Sandwich Maker Company" | ||||
|     name: "TODO" # "Deluxe Sandwich Maker 2" | ||||
|     affected_versions: | ||||
|       - "TODO" # "1.2.3" | ||||
|       - "TODO" # ">1.3.0, <=1.3.9" | ||||
|     fixed_versions: | ||||
|       - "TODO" # "1.2.4" | ||||
|       - "TODO" # "1.3.10" | ||||
|   impact: "TODO" # "CVSS v3 string" # https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator
 | ||||
|   solution: "TODO" # "Upgrade to version 1.2.4 or 1.3.10" | ||||
|   credit: "TODO" | ||||
|   references: | ||||
|     - "TODO" # "https://some.domain.tld/a/reference" | ||||
| \`\`\` | ||||
| 
 | ||||
| CVSS scores can be computed by means of the [NVD CVSS Calculator](https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator).
 | ||||
| 
 | ||||
| /relate ${fullPath}#${iid} | ||||
| /label ~"devops::secure" ~"group::vulnerability research" ~"vulnerability research::cve" ~"advisory::queued" | ||||
|   `;
 | ||||
| } | ||||
|  | @ -11,6 +11,7 @@ import { | |||
|   featureAccessLevelEveryone, | ||||
|   featureAccessLevel, | ||||
|   featureAccessLevelNone, | ||||
|   CVE_ID_REQUEST_BUTTON_I18N, | ||||
| } from '../constants'; | ||||
| import { toggleHiddenClassBySelector } from '../external'; | ||||
| import projectFeatureSetting from './project_feature_setting.vue'; | ||||
|  | @ -19,6 +20,10 @@ import projectSettingRow from './project_setting_row.vue'; | |||
| const PAGE_FEATURE_ACCESS_LEVEL = s__('ProjectSettings|Everyone'); | ||||
| 
 | ||||
| export default { | ||||
|   i18n: { | ||||
|     ...CVE_ID_REQUEST_BUTTON_I18N, | ||||
|   }, | ||||
| 
 | ||||
|   components: { | ||||
|     projectFeatureSetting, | ||||
|     projectSettingRow, | ||||
|  | @ -31,6 +36,11 @@ export default { | |||
|   mixins: [settingsMixin, glFeatureFlagsMixin()], | ||||
| 
 | ||||
|   props: { | ||||
|     requestCveAvailable: { | ||||
|       type: Boolean, | ||||
|       required: false, | ||||
|       default: false, | ||||
|     }, | ||||
|     currentSettings: { | ||||
|       type: Object, | ||||
|       required: true, | ||||
|  | @ -99,6 +109,11 @@ export default { | |||
|       required: false, | ||||
|       default: '', | ||||
|     }, | ||||
|     cveIdRequestHelpPath: { | ||||
|       type: String, | ||||
|       required: false, | ||||
|       default: '', | ||||
|     }, | ||||
|     registryHelpPath: { | ||||
|       type: String, | ||||
|       required: false, | ||||
|  | @ -152,6 +167,7 @@ export default { | |||
|       requestAccessEnabled: true, | ||||
|       highlightChangesClass: false, | ||||
|       emailsDisabled: false, | ||||
|       cveIdRequestEnabled: true, | ||||
|       featureAccessLevelEveryone, | ||||
|       featureAccessLevelMembers, | ||||
|     }; | ||||
|  | @ -230,6 +246,9 @@ export default { | |||
|         'ProjectSettings|View and edit files in this project. Non-project members will only have read access.', | ||||
|       ); | ||||
|     }, | ||||
|     cveIdRequestIsDisabled() { | ||||
|       return this.visibilityLevel !== visibilityOptions.PUBLIC; | ||||
|     }, | ||||
|   }, | ||||
| 
 | ||||
|   watch: { | ||||
|  | @ -417,6 +436,19 @@ export default { | |||
|           :options="featureAccessLevelOptions" | ||||
|           name="project[project_feature_attributes][issues_access_level]" | ||||
|         /> | ||||
|         <project-setting-row | ||||
|           v-if="requestCveAvailable" | ||||
|           :help-path="cveIdRequestHelpPath" | ||||
|           :help-text="$options.i18n.cve_request_toggle_label" | ||||
|         > | ||||
|           <gl-toggle | ||||
|             v-model="cveIdRequestEnabled" | ||||
|             class="gl-my-2" | ||||
|             :disabled="cveIdRequestIsDisabled" | ||||
|             name="project[project_setting_attributes][cve_id_request_enabled]" | ||||
|             data-testid="cve_id_request_toggle" | ||||
|           /> | ||||
|         </project-setting-row> | ||||
|       </project-setting-row> | ||||
|       <project-setting-row | ||||
|         ref="repository-settings" | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import { __ } from '~/locale'; | ||||
| import { s__, __ } from '~/locale'; | ||||
| 
 | ||||
| export const visibilityOptions = { | ||||
|   PRIVATE: 0, | ||||
|  | @ -42,3 +42,7 @@ export const featureAccessLevelEveryone = [ | |||
|   featureAccessLevel.EVERYONE, | ||||
|   featureAccessLevelDescriptions[featureAccessLevel.EVERYONE], | ||||
| ]; | ||||
| 
 | ||||
| export const CVE_ID_REQUEST_BUTTON_I18N = { | ||||
|   cve_request_toggle_label: s__('CVE|Enable CVE ID requests in the issue sidebar'), | ||||
| }; | ||||
|  |  | |||
|  | @ -0,0 +1,88 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module Mutations | ||||
|   module MergeRequests | ||||
|     class Accept < Base | ||||
|       NOT_MERGEABLE = 'This branch cannot be merged' | ||||
|       HOOKS_VALIDATION_ERROR = 'Pre-merge hooks failed' | ||||
|       SHA_MISMATCH = 'The merge-head is not at the anticipated SHA' | ||||
|       MERGE_FAILED = 'The merge failed' | ||||
|       ALREADY_SCHEDULED = 'The merge request is already scheduled to be merged' | ||||
| 
 | ||||
|       graphql_name 'MergeRequestAccept' | ||||
|       authorize :accept_merge_request | ||||
|       description <<~DESC | ||||
|         Accepts a merge request. | ||||
|         When accepted, the source branch will be merged into the target branch, either | ||||
|         immediately if possible, or using one of the automatic merge strategies. | ||||
|       DESC | ||||
| 
 | ||||
|       argument :strategy, | ||||
|                ::Types::MergeStrategyEnum, | ||||
|                required: false, | ||||
|                as: :auto_merge_strategy, | ||||
|                description: 'How to merge this merge request.' | ||||
| 
 | ||||
|       argument :commit_message, ::GraphQL::STRING_TYPE, | ||||
|                required: false, | ||||
|                description: 'Custom merge commit message.' | ||||
|       argument :squash_commit_message, ::GraphQL::STRING_TYPE, | ||||
|                required: false, | ||||
|                description: 'Custom squash commit message (if squash is true).' | ||||
|       argument :sha, ::GraphQL::STRING_TYPE, | ||||
|                required: true, | ||||
|                description: 'The HEAD SHA at the time when this merge was requested.' | ||||
| 
 | ||||
|       argument :should_remove_source_branch, ::GraphQL::BOOLEAN_TYPE, | ||||
|                required: false, | ||||
|                description: 'Should the source branch be removed.' | ||||
|       argument :squash, ::GraphQL::BOOLEAN_TYPE, | ||||
|                required: false, | ||||
|                default_value: false, | ||||
|                description: 'Squash commits on the source branch before merge.' | ||||
| 
 | ||||
|       def resolve(project_path:, iid:, **args) | ||||
|         Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42317') | ||||
|         merge_request = authorized_find!(project_path: project_path, iid: iid) | ||||
|         project = merge_request.target_project | ||||
|         merge_params = args.compact.with_indifferent_access | ||||
|         merge_service = ::MergeRequests::MergeService.new(project, current_user, merge_params) | ||||
| 
 | ||||
|         if error = validate(merge_request, merge_service, merge_params) | ||||
|           return { merge_request: merge_request, errors: [error] } | ||||
|         end | ||||
| 
 | ||||
|         merge_request.update(merge_error: nil, squash: merge_params[:squash]) | ||||
| 
 | ||||
|         result = if merge_params.key?(:auto_merge_strategy) | ||||
|                    service = AutoMergeService.new(project, current_user, merge_params) | ||||
|                    service.execute(merge_request, merge_params[:auto_merge_strategy]) | ||||
|                  else | ||||
|                    merge_service.execute(merge_request) | ||||
|                  end | ||||
| 
 | ||||
|         { | ||||
|           merge_request: merge_request, | ||||
|           errors: result == :failed ? [MERGE_FAILED] : [] | ||||
|         } | ||||
|       rescue ::MergeRequests::MergeBaseService::MergeError => e | ||||
|         { | ||||
|           merge_request: merge_request, | ||||
|           errors: [e.message] | ||||
|         } | ||||
|       end | ||||
| 
 | ||||
|       def validate(merge_request, merge_service, merge_params) | ||||
|         if merge_request.auto_merge_enabled? | ||||
|           ALREADY_SCHEDULED | ||||
|         elsif !merge_request.mergeable?(skip_ci_check: merge_params.key?(:auto_merge_strategy)) | ||||
|           NOT_MERGEABLE | ||||
|         elsif !merge_service.hooks_validation_pass?(merge_request) | ||||
|           HOOKS_VALIDATION_ERROR | ||||
|         elsif merge_params[:sha] != merge_request.diff_head_sha | ||||
|           SHA_MISMATCH | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -6,6 +6,12 @@ module Mutations | |||
|       # This is a Base class for the Note update mutations and is not | ||||
|       # mounted as a GraphQL mutation itself. | ||||
|       class Base < Mutations::Notes::Base | ||||
|         QUICK_ACTION_ONLY_WARNING = <<~NB | ||||
|           If the body of the Note contains only quick actions, | ||||
|           the Note will be destroyed during the update, and no Note will be | ||||
|           returned. | ||||
|         NB | ||||
| 
 | ||||
|         authorize :admin_note | ||||
| 
 | ||||
|         argument :id, | ||||
|  |  | |||
|  | @ -5,6 +5,10 @@ module Mutations | |||
|     module Update | ||||
|       class ImageDiffNote < Mutations::Notes::Update::Base | ||||
|         graphql_name 'UpdateImageDiffNote' | ||||
|         description <<~DESC | ||||
|           Updates a DiffNote on an image (a `Note` where the `position.positionType` is `"image"`). | ||||
|           #{QUICK_ACTION_ONLY_WARNING} | ||||
|         DESC | ||||
| 
 | ||||
|         argument :body, | ||||
|                  GraphQL::STRING_TYPE, | ||||
|  | @ -34,10 +38,9 @@ module Mutations | |||
|         private | ||||
| 
 | ||||
|         def pre_update_checks!(note, _args) | ||||
|           unless note.is_a?(DiffNote) && note.position.on_image? | ||||
|             raise Gitlab::Graphql::Errors::ResourceNotAvailable, | ||||
|                   'Resource is not an ImageDiffNote' | ||||
|           end | ||||
|           return if note.is_a?(DiffNote) && note.position.on_image? | ||||
| 
 | ||||
|           raise Gitlab::Graphql::Errors::ResourceNotAvailable, 'Resource is not an ImageDiffNote' | ||||
|         end | ||||
| 
 | ||||
|         def note_params(note, args) | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ module Mutations | |||
|     module Update | ||||
|       class Note < Mutations::Notes::Update::Base | ||||
|         graphql_name 'UpdateNote' | ||||
|         description "Updates a Note.\n#{QUICK_ACTION_ONLY_WARNING}" | ||||
| 
 | ||||
|         argument :body, | ||||
|                  GraphQL::STRING_TYPE, | ||||
|  |  | |||
|  | @ -0,0 +1,9 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| module Types | ||||
|   class MergeStrategyEnum < BaseEnum | ||||
|     AutoMergeService.all_strategies_ordered_by_preference.each do |strat| | ||||
|       value strat.upcase, value: strat, description: "Use the #{strat} merge strategy." | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -44,6 +44,7 @@ module Types | |||
|     mount_mutation Mutations::Issues::Update | ||||
|     mount_mutation Mutations::Issues::Move | ||||
|     mount_mutation Mutations::Labels::Create | ||||
|     mount_mutation Mutations::MergeRequests::Accept | ||||
|     mount_mutation Mutations::MergeRequests::Create | ||||
|     mount_mutation Mutations::MergeRequests::Update | ||||
|     mount_mutation Mutations::MergeRequests::SetLabels | ||||
|  | @ -58,14 +59,8 @@ module Types | |||
|     mount_mutation Mutations::Notes::Create::Note, calls_gitaly: true | ||||
|     mount_mutation Mutations::Notes::Create::DiffNote, calls_gitaly: true | ||||
|     mount_mutation Mutations::Notes::Create::ImageDiffNote, calls_gitaly: true | ||||
|     mount_mutation Mutations::Notes::Update::Note, | ||||
|                    description: 'Updates a Note. If the body of the Note contains only quick actions, ' \ | ||||
|                                 'the Note will be destroyed during the update, and no Note will be ' \ | ||||
|                                 'returned' | ||||
|     mount_mutation Mutations::Notes::Update::ImageDiffNote, | ||||
|                    description: 'Updates a DiffNote on an image (a `Note` where the `position.positionType` is `"image"`). ' \ | ||||
|                                 'If the body of the Note contains only quick actions, the Note will be ' \ | ||||
|                                 'destroyed during the update, and no Note will be returned' | ||||
|     mount_mutation Mutations::Notes::Update::Note | ||||
|     mount_mutation Mutations::Notes::Update::ImageDiffNote | ||||
|     mount_mutation Mutations::Notes::RepositionImageDiffNote | ||||
|     mount_mutation Mutations::Notes::Destroy | ||||
|     mount_mutation Mutations::Releases::Create | ||||
|  |  | |||
|  | @ -9,7 +9,10 @@ class MergeRequestPolicy < IssuablePolicy | |||
|   # Although :read_merge_request is computed in the policy context, | ||||
|   # it would not be safe to prevent :create_note there, since | ||||
|   # note permissions are shared, and this would apply too broadly. | ||||
|   rule { ~can?(:read_merge_request) }.prevent :create_note | ||||
|   rule { ~can?(:read_merge_request) }.policy do | ||||
|     prevent :create_note | ||||
|     prevent :accept_merge_request | ||||
|   end | ||||
| 
 | ||||
|   rule { can?(:update_merge_request) }.policy do | ||||
|     enable :approve_merge_request | ||||
|  | @ -18,6 +21,12 @@ class MergeRequestPolicy < IssuablePolicy | |||
|   rule { ~anonymous & can?(:read_merge_request) }.policy do | ||||
|     enable :create_todo | ||||
|   end | ||||
| 
 | ||||
|   condition(:can_merge) { @subject.can_be_merged_by?(@user) } | ||||
| 
 | ||||
|   rule { can_merge }.policy do | ||||
|     enable :accept_merge_request | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| MergeRequestPolicy.prepend_if_ee('EE::MergeRequestPolicy') | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| - expanded = expanded_by_default? | ||||
| %section.qa-deploy-keys-settings.settings.no-animate#js-deploy-keys-settings{ class: ('expanded' if expanded), data: { qa_selector: 'deploy_keys_settings_content' } } | ||||
|   .settings-header | ||||
|     %h4= _('Deploy keys') | ||||
|     %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Deploy keys') | ||||
|     %button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' } | ||||
|       = expanded ? 'Collapse' : 'Expand' | ||||
|     %p | ||||
|  |  | |||
|  | @ -21,4 +21,4 @@ | |||
|         = _('Allow this key to push to this repository') | ||||
| 
 | ||||
|   .form-group.row | ||||
|     = f.submit _("Add key"), class: "btn-success btn" | ||||
|     = f.submit _("Add key"), class: "btn gl-button btn-confirm" | ||||
|  |  | |||
|  | @ -118,6 +118,8 @@ | |||
|         %script#js-confidential-issue-data{ type: "application/json" }= { is_confidential: issuable_sidebar[:confidential], is_editable: can_edit_issuable }.to_json.html_safe | ||||
|         #js-confidential-entry-point | ||||
| 
 | ||||
|         = render_if_exists 'shared/issuable/sidebar_cve_id_request', issuable_sidebar: issuable_sidebar | ||||
| 
 | ||||
|       %script#js-lock-issue-data{ type: "application/json" }= { is_locked: !!issuable_sidebar[:discussion_locked], is_editable: can_edit_issuable }.to_json.html_safe | ||||
|       #js-lock-entry-point | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Project Settings Repository Deploy keys header expands/collapses on click / tap | ||||
| merge_request: 55234 | ||||
| author: Daniel Schömer | ||||
| type: changed | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Update cluster agent tokens with null names | ||||
| merge_request: 55673 | ||||
| author: | ||||
| type: changed | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Adds Request CVE ID button to issue sidebar | ||||
| merge_request: 41203 | ||||
| author: | ||||
| type: added | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Add mutation to accept merge requests | ||||
| merge_request: 54758 | ||||
| author: | ||||
| type: added | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Move from btn-success to btn-confirm in shared/deploy_keys directory | ||||
| merge_request: 55299 | ||||
| author: Yogi (@yo) | ||||
| type: changed | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Enable codequality report comparison with backend | ||||
| merge_request: 54241 | ||||
| author: | ||||
| type: performance | ||||
|  | @ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/300796 | |||
| milestone: '13.9' | ||||
| type: development | ||||
| group: group::testing | ||||
| default_enabled: false | ||||
| default_enabled: true | ||||
|  |  | |||
|  | @ -1,16 +0,0 @@ | |||
| --- | ||||
| key_path: elasticsearch_enabled | ||||
| description: Whether Elasticsearch is enabled | ||||
| product_section: growth | ||||
| product_stage: growth | ||||
| product_group: group::product intelligence | ||||
| product_category: collection | ||||
| value_type: boolean | ||||
| status: data_available | ||||
| time_frame: none | ||||
| data_source: | ||||
| distribution: | ||||
| - ce | ||||
| tier: | ||||
| - free | ||||
| skip_validation: true | ||||
|  | @ -0,0 +1,13 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class AddCveIdRequestProjectSetting < ActiveRecord::Migration[6.0] | ||||
|   DOWNTIME = false | ||||
| 
 | ||||
|   def up | ||||
|     add_column :project_settings, :cve_id_request_enabled, :boolean, default: true, null: false | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     remove_column :project_settings, :cve_id_request_enabled | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,18 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class AddNotNullConstraintToClusterTokenName < ActiveRecord::Migration[6.0] | ||||
|   include Gitlab::Database::MigrationHelpers | ||||
| 
 | ||||
|   DOWNTIME = false | ||||
| 
 | ||||
|   disable_ddl_transaction! | ||||
| 
 | ||||
|   def up | ||||
|     # This will add the `NOT NULL` constraint WITHOUT validating it | ||||
|     add_not_null_constraint :cluster_agent_tokens, :name, validate: false | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     remove_not_null_constraint :cluster_agent_tokens, :name | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,25 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class CleanupClusterTokensWithNullName < ActiveRecord::Migration[6.0] | ||||
|   include Gitlab::Database::MigrationHelpers | ||||
| 
 | ||||
|   BATCH_SIZE = 1000 | ||||
| 
 | ||||
|   disable_ddl_transaction! | ||||
| 
 | ||||
|   class AgentToken < ActiveRecord::Base | ||||
|     include EachBatch | ||||
| 
 | ||||
|     self.table_name = 'cluster_agent_tokens' | ||||
|   end | ||||
| 
 | ||||
|   def up | ||||
|     AgentToken.each_batch(of: BATCH_SIZE) do |relation| | ||||
|       relation.where('name IS NULL').update_all("name = 'agent-token-' || id") | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def down | ||||
|     # no-op : can't go back to `NULL` without first dropping the `NOT NULL` constraint | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1 @@ | |||
| 37196d54d03791f7509e411d5c545f22aa70f7c07d1f13d76f70008a06e72b57 | ||||
|  | @ -0,0 +1 @@ | |||
| fa82a0f6c57a527a143da56ae0d70245a7d711b5e5ff3eb959fd6b2cf5872dac | ||||
|  | @ -0,0 +1 @@ | |||
| 1cb74abdc7134c3252425c3ceb8cd9dc4b157d64b1a2ff7928153e78b05d9121 | ||||
|  | @ -16270,6 +16270,7 @@ CREATE TABLE project_settings ( | |||
|     has_vulnerabilities boolean DEFAULT false NOT NULL, | ||||
|     allow_editing_commit_messages boolean DEFAULT false NOT NULL, | ||||
|     prevent_merge_without_jira_issue boolean DEFAULT false NOT NULL, | ||||
|     cve_id_request_enabled boolean DEFAULT true NOT NULL, | ||||
|     CONSTRAINT check_bde223416c CHECK ((show_default_award_emojis IS NOT NULL)) | ||||
| ); | ||||
| 
 | ||||
|  | @ -19952,6 +19953,9 @@ ALTER TABLE ONLY chat_names | |||
| ALTER TABLE ONLY chat_teams | ||||
|     ADD CONSTRAINT chat_teams_pkey PRIMARY KEY (id); | ||||
| 
 | ||||
| ALTER TABLE cluster_agent_tokens | ||||
|     ADD CONSTRAINT check_0fb634d04d CHECK ((name IS NOT NULL)) NOT VALID; | ||||
| 
 | ||||
| ALTER TABLE vulnerability_scanners | ||||
|     ADD CONSTRAINT check_37608c9db5 CHECK ((char_length(vendor) <= 255)) NOT VALID; | ||||
| 
 | ||||
|  |  | |||
|  | @ -2784,6 +2784,16 @@ Autogenerated return type of MarkAsSpamSnippet. | |||
| | `webUrl` | String | Web URL of the merge request. | | ||||
| | `workInProgress` | Boolean! | Indicates if the merge request is a draft. | | ||||
| 
 | ||||
| ### `MergeRequestAcceptPayload` | ||||
| 
 | ||||
| Autogenerated return type of MergeRequestAccept. | ||||
| 
 | ||||
| | Field | Type | Description | | ||||
| | ----- | ---- | ----------- | | ||||
| | `clientMutationId` | String | A unique identifier for the client performing the mutation. | | ||||
| | `errors` | String! => Array | Errors encountered during execution of the mutation. | | ||||
| | `mergeRequest` | MergeRequest | The merge request after mutation. | | ||||
| 
 | ||||
| ### `MergeRequestCreatePayload` | ||||
| 
 | ||||
| Autogenerated return type of MergeRequestCreate. | ||||
|  | @ -5636,6 +5646,14 @@ State of a GitLab merge request. | |||
| | `merged` | Merge Request has been merged. | | ||||
| | `opened` | In open state. | | ||||
| 
 | ||||
| ### `MergeStrategyEnum` | ||||
| 
 | ||||
| | Value | Description | | ||||
| | ----- | ----------- | | ||||
| | `ADD_TO_MERGE_TRAIN_WHEN_PIPELINE_SUCCEEDS` | Use the add_to_merge_train_when_pipeline_succeeds merge strategy. | | ||||
| | `MERGE_TRAIN` | Use the merge_train merge strategy. | | ||||
| | `MERGE_WHEN_PIPELINE_SUCCEEDS` | Use the merge_when_pipeline_succeeds merge strategy. | | ||||
| 
 | ||||
| ### `MilestoneStateEnum` | ||||
| 
 | ||||
| Current state of milestone. | ||||
|  |  | |||
|  | @ -6360,13 +6360,13 @@ Tiers: `free`, `premium`, `ultimate` | |||
| 
 | ||||
| Whether Elasticsearch is enabled | ||||
| 
 | ||||
| [YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/settings/20210204124924_elasticsearch_enabled.yml) | ||||
| [YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/config/metrics/settings/20210204124924_elasticsearch_enabled.yml) | ||||
| 
 | ||||
| Group: `group::product intelligence` | ||||
| Group: `group::global search` | ||||
| 
 | ||||
| Status: `data_available` | ||||
| 
 | ||||
| Tiers: `free` | ||||
| Tiers: `premium`, `ultimate` | ||||
| 
 | ||||
| ### `g_project_management_epic_created_monthly` | ||||
| 
 | ||||
|  |  | |||
|  | @ -19,16 +19,14 @@ module Gitlab | |||
|         commit = Commit.new(commit, project) | ||||
|         commit.lazy_author # preload author | ||||
| 
 | ||||
|         sha = commit.sha | ||||
|         if prev_sha != sha | ||||
|         if prev_sha != commit.sha | ||||
|           groups << current_group if current_group | ||||
|           current_group = { commit: commit, lines: [] } | ||||
|         end | ||||
| 
 | ||||
|         line = highlighted_lines[i].html_safe if highlight | ||||
|         current_group[:lines] << line | ||||
|         current_group[:lines] << (highlight ? highlighted_lines[i].html_safe : line) | ||||
| 
 | ||||
|         prev_sha = sha | ||||
|         prev_sha = commit.sha | ||||
|         i += 1 | ||||
|       end | ||||
|       groups << current_group if current_group | ||||
|  |  | |||
|  | @ -38,9 +38,11 @@ module Gitlab | |||
|           if line[0, 1] == "\t" | ||||
|             lines << line[1, line.size] | ||||
|           elsif m = /^(\w{40}) (\d+) (\d+)/.match(line) | ||||
|             commit_id, old_lineno, lineno = m[1], m[2].to_i, m[3].to_i | ||||
|             # Removed these instantiations for performance but keeping them for reference: | ||||
|             # commit_id, old_lineno, lineno = m[1], m[2].to_i, m[3].to_i | ||||
|             commit_id = m[1] | ||||
|             commits[commit_id] = nil unless commits.key?(commit_id) | ||||
|             info[lineno] = [commit_id, old_lineno] | ||||
|             info[m[3].to_i] = [commit_id, m[2].to_i] | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|  | @ -50,8 +52,7 @@ module Gitlab | |||
| 
 | ||||
|         # get it together | ||||
|         info.sort.each do |lineno, (commit_id, old_lineno)| | ||||
|           commit = commits[commit_id] | ||||
|           final << BlameLine.new(lineno, old_lineno, commit, lines[lineno - 1]) | ||||
|           final << BlameLine.new(lineno, old_lineno, commits[commit_id], lines[lineno - 1]) | ||||
|         end | ||||
| 
 | ||||
|         @lines = final | ||||
|  |  | |||
|  | @ -5312,6 +5312,27 @@ msgstr "" | |||
| msgid "CPU" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "CVE|As a maintainer, requesting a CVE for a vulnerability in your project will help your users stay secure and informed." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "CVE|CVE ID Request" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "CVE|Common Vulnerability Enumeration (CVE) identifiers are used to track distinct vulnerabilities in specific versions of code." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "CVE|Create CVE ID Request" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "CVE|Enable CVE ID requests in the issue sidebar" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "CVE|Request CVE ID" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "CVE|Why Request a CVE ID?" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Callback URL" | ||||
| msgstr "" | ||||
| 
 | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ const defaultProps = { | |||
|     showDefaultAwardEmojis: true, | ||||
|     allowEditingCommitMessages: false, | ||||
|   }, | ||||
|   isGitlabCom: true, | ||||
|   canDisableEmails: true, | ||||
|   canChangeVisibilityLevel: true, | ||||
|   allowedVisibilityOptions: [0, 10, 20], | ||||
|  |  | |||
|  | @ -0,0 +1,171 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe Mutations::MergeRequests::Accept do | ||||
|   include AfterNextHelpers | ||||
| 
 | ||||
|   let_it_be(:user) { create(:user) } | ||||
|   let(:project) { create(:project, :public, :repository) } | ||||
| 
 | ||||
|   subject(:mutation) { described_class.new(context: context, object: nil, field: nil) } | ||||
| 
 | ||||
|   let_it_be(:context) do | ||||
|     GraphQL::Query::Context.new( | ||||
|       query: OpenStruct.new(schema: GitlabSchema), | ||||
|       values: { current_user: user }, | ||||
|       object: nil | ||||
|     ) | ||||
|   end | ||||
| 
 | ||||
|   before do | ||||
|     project.repository.expire_all_method_caches | ||||
|   end | ||||
| 
 | ||||
|   describe '#resolve' do | ||||
|     before do | ||||
|       project.add_maintainer(user) | ||||
|     end | ||||
| 
 | ||||
|     def common_args(merge_request) | ||||
|       { | ||||
|         project_path: project.full_path, | ||||
|         iid: merge_request.iid.to_s, | ||||
|         sha: merge_request.diff_head_sha, | ||||
|         squash: false # default value | ||||
|       } | ||||
|     end | ||||
| 
 | ||||
|     it 'merges the merge request' do | ||||
|       merge_request = create(:merge_request, source_project: project) | ||||
| 
 | ||||
|       result = mutation.resolve(**common_args(merge_request)) | ||||
| 
 | ||||
|       expect(result).to include(errors: be_empty, merge_request: be_merged) | ||||
|     end | ||||
| 
 | ||||
|     it 'rejects the mutation if the SHA is a mismatch' do | ||||
|       merge_request = create(:merge_request, source_project: project) | ||||
|       args = common_args(merge_request).merge(sha: 'not a good sha') | ||||
| 
 | ||||
|       result = mutation.resolve(**args) | ||||
| 
 | ||||
|       expect(result).not_to include(merge_request: be_merged) | ||||
|       expect(result).to include(errors: [described_class::SHA_MISMATCH]) | ||||
|     end | ||||
| 
 | ||||
|     it 'respects the merge commit message' do | ||||
|       merge_request = create(:merge_request, source_project: project) | ||||
|       args = common_args(merge_request).merge(commit_message: 'my super custom message') | ||||
| 
 | ||||
|       result = mutation.resolve(**args) | ||||
| 
 | ||||
|       expect(result).to include(merge_request: be_merged) | ||||
|       expect(project.repository.commit(merge_request.target_branch)).to have_attributes( | ||||
|         message: args[:commit_message] | ||||
|       ) | ||||
|     end | ||||
| 
 | ||||
|     it 'respects the squash flag' do | ||||
|       merge_request = create(:merge_request, source_project: project) | ||||
|       args = common_args(merge_request).merge(squash: true) | ||||
| 
 | ||||
|       result = mutation.resolve(**args) | ||||
| 
 | ||||
|       expect(result).to include(merge_request: be_merged) | ||||
|       expect(result[:merge_request].squash_commit_sha).to be_present | ||||
|     end | ||||
| 
 | ||||
|     it 'respects the squash_commit_message argument' do | ||||
|       merge_request = create(:merge_request, source_project: project) | ||||
|       args = common_args(merge_request).merge(squash: true, squash_commit_message: 'squish') | ||||
| 
 | ||||
|       result = mutation.resolve(**args) | ||||
|       sha = result[:merge_request].squash_commit_sha | ||||
| 
 | ||||
|       expect(result).to include(merge_request: be_merged) | ||||
|       expect(project.repository.commit(sha)).to have_attributes(message: "squish\n") | ||||
|     end | ||||
| 
 | ||||
|     it 'respects the should_remove_source_branch argument when true' do | ||||
|       b = project.repository.add_branch(user, generate(:branch), 'master') | ||||
|       merge_request = create(:merge_request, source_branch: b.name, source_project: project) | ||||
|       args = common_args(merge_request).merge(should_remove_source_branch: true) | ||||
| 
 | ||||
|       expect(::MergeRequests::DeleteSourceBranchWorker).to receive(:perform_async) | ||||
| 
 | ||||
|       result = mutation.resolve(**args) | ||||
| 
 | ||||
|       expect(result).to include(merge_request: be_merged) | ||||
|     end | ||||
| 
 | ||||
|     it 'respects the should_remove_source_branch argument when false' do | ||||
|       b = project.repository.add_branch(user, generate(:branch), 'master') | ||||
|       merge_request = create(:merge_request, source_branch: b.name, source_project: project) | ||||
|       args = common_args(merge_request).merge(should_remove_source_branch: false) | ||||
| 
 | ||||
|       expect(::MergeRequests::DeleteSourceBranchWorker).not_to receive(:perform_async) | ||||
| 
 | ||||
|       result = mutation.resolve(**args) | ||||
| 
 | ||||
|       expect(result).to include(merge_request: be_merged) | ||||
|     end | ||||
| 
 | ||||
|     it 'rejects unmergeable MRs' do | ||||
|       merge_request = create(:merge_request, :closed, source_project: project) | ||||
|       args = common_args(merge_request) | ||||
| 
 | ||||
|       result = mutation.resolve(**args) | ||||
| 
 | ||||
|       expect(result).not_to include(merge_request: be_merged) | ||||
|       expect(result).to include(errors: [described_class::NOT_MERGEABLE]) | ||||
|     end | ||||
| 
 | ||||
|     it 'rejects merges when we cannot validate the hooks' do | ||||
|       merge_request = create(:merge_request, source_project: project) | ||||
|       args = common_args(merge_request) | ||||
|       expect_next(::MergeRequests::MergeService) | ||||
|         .to receive(:hooks_validation_pass?).with(merge_request).and_return(false) | ||||
| 
 | ||||
|       result = mutation.resolve(**args) | ||||
| 
 | ||||
|       expect(result).not_to include(merge_request: be_merged) | ||||
|       expect(result).to include(errors: [described_class::HOOKS_VALIDATION_ERROR]) | ||||
|     end | ||||
| 
 | ||||
|     it 'rejects merges when the merge service returns an error' do | ||||
|       merge_request = create(:merge_request, source_project: project) | ||||
|       args = common_args(merge_request) | ||||
|       expect_next(::MergeRequests::MergeService) | ||||
|         .to receive(:execute).with(merge_request).and_return(:failed) | ||||
| 
 | ||||
|       result = mutation.resolve(**args) | ||||
| 
 | ||||
|       expect(result).not_to include(merge_request: be_merged) | ||||
|       expect(result).to include(errors: [described_class::MERGE_FAILED]) | ||||
|     end | ||||
| 
 | ||||
|     it 'rejects merges when the merge service raises merge error' do | ||||
|       merge_request = create(:merge_request, source_project: project) | ||||
|       args = common_args(merge_request) | ||||
|       expect_next(::MergeRequests::MergeService) | ||||
|         .to receive(:execute).and_raise(::MergeRequests::MergeBaseService::MergeError, 'boom') | ||||
| 
 | ||||
|       result = mutation.resolve(**args) | ||||
| 
 | ||||
|       expect(result).not_to include(merge_request: be_merged) | ||||
|       expect(result).to include(errors: ['boom']) | ||||
|     end | ||||
| 
 | ||||
|     it "can use the MERGE_WHEN_PIPELINE_SUCCEEDS strategy" do | ||||
|       enum = ::Types::MergeStrategyEnum.values['MERGE_WHEN_PIPELINE_SUCCEEDS'] | ||||
|       merge_request = create(:merge_request, :with_head_pipeline, source_project: project) | ||||
|       args = common_args(merge_request).merge(auto_merge_strategy: enum.value) | ||||
| 
 | ||||
|       result = mutation.resolve(**args) | ||||
| 
 | ||||
|       expect(result).not_to include(merge_request: be_merged) | ||||
|       expect(result).to include(errors: be_empty, merge_request: be_auto_merge_enabled) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -0,0 +1,44 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| RSpec.describe 'accepting a merge request', :request_store do | ||||
|   include GraphqlHelpers | ||||
| 
 | ||||
|   let_it_be(:current_user) { create(:user) } | ||||
|   let_it_be(:project) { create(:project, :public, :repository) } | ||||
|   let!(:merge_request) { create(:merge_request, source_project: project) } | ||||
|   let(:input) do | ||||
|     { | ||||
|       project_path: project.full_path, | ||||
|       iid: merge_request.iid.to_s, | ||||
|       sha: merge_request.diff_head_sha | ||||
|     } | ||||
|   end | ||||
| 
 | ||||
|   let(:mutation) { graphql_mutation(:merge_request_accept, input, 'mergeRequest { state }') } | ||||
|   let(:mutation_response) { graphql_mutation_response(:merge_request_accept) } | ||||
| 
 | ||||
|   context 'when the user is not allowed to accept a merge request' do | ||||
|     before do | ||||
|       project.add_reporter(current_user) | ||||
|     end | ||||
| 
 | ||||
|     it_behaves_like 'a mutation that returns a top-level access error' | ||||
|   end | ||||
| 
 | ||||
|   context 'when user has permissions to create a merge request' do | ||||
|     before do | ||||
|       project.add_maintainer(current_user) | ||||
|     end | ||||
| 
 | ||||
|     it 'merges the merge request' do | ||||
|       post_graphql_mutation(mutation, current_user: current_user) | ||||
| 
 | ||||
|       expect(response).to have_gitlab_http_status(:success) | ||||
|       expect(mutation_response['mergeRequest']).to include( | ||||
|         'state' => 'merged' | ||||
|       ) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -141,6 +141,7 @@ project_setting: | |||
|     - show_default_award_emojis | ||||
|     - squash_option | ||||
|     - updated_at | ||||
|     - cve_id_request_enabled | ||||
| 
 | ||||
| build_service_desk_setting: # service_desk_setting | ||||
|   unexposed_attributes: | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue