Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-03-09 00:09:23 +00:00
parent b08b3719a1
commit d865630025
38 changed files with 592 additions and 61 deletions

View file

@ -1 +1 @@
aaafc8c4691520f9391a704476f933ffc2fe82fe
687bc5d8f36102e2c8033cce76094d5d318cd961

View file

@ -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"
`;
}

View file

@ -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"

View file

@ -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'),
};

View file

@ -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

View file

@ -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,

View file

@ -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)

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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')

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,5 @@
---
title: Update cluster agent tokens with null names
merge_request: 55673
author:
type: changed

View file

@ -0,0 +1,5 @@
---
title: Adds Request CVE ID button to issue sidebar
merge_request: 41203
author:
type: added

View file

@ -0,0 +1,5 @@
---
title: Add mutation to accept merge requests
merge_request: 54758
author:
type: added

View file

@ -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

View file

@ -0,0 +1,5 @@
---
title: Enable codequality report comparison with backend
merge_request: 54241
author:
type: performance

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1 @@
37196d54d03791f7509e411d5c545f22aa70f7c07d1f13d76f70008a06e72b57

View file

@ -0,0 +1 @@
fa82a0f6c57a527a143da56ae0d70245a7d711b5e5ff3eb959fd6b2cf5872dac

View file

@ -0,0 +1 @@
1cb74abdc7134c3252425c3ceb8cd9dc4b157d64b1a2ff7928153e78b05d9121

View file

@ -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;

View file

@ -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.

View file

@ -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`

View file

@ -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

View file

@ -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

View file

@ -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 ""

View file

@ -29,6 +29,7 @@ const defaultProps = {
showDefaultAwardEmojis: true,
allowEditingCommitMessages: false,
},
isGitlabCom: true,
canDisableEmails: true,
canChangeVisibilityLevel: true,
allowedVisibilityOptions: [0, 10, 20],

View file

@ -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

View file

@ -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

View file

@ -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: