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