diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 92ff9151f93..3d008ee2c83 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-aaafc8c4691520f9391a704476f933ffc2fe82fe
+687bc5d8f36102e2c8033cce76094d5d318cd961
diff --git a/app/assets/javascripts/helpers/cve_id_request_helper.js b/app/assets/javascripts/helpers/cve_id_request_helper.js
new file mode 100644
index 00000000000..71d3fd4c4fe
--- /dev/null
+++ b/app/assets/javascripts/helpers/cve_id_request_helper.js
@@ -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
+
+
+
+\`\`\`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"
+ `;
+}
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
index 4af6af16980..62b565a4856 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
@@ -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]"
/>
+
+
+
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
diff --git a/app/graphql/mutations/notes/update/base.rb b/app/graphql/mutations/notes/update/base.rb
index 4edb7429b97..571001981a4 100644
--- a/app/graphql/mutations/notes/update/base.rb
+++ b/app/graphql/mutations/notes/update/base.rb
@@ -6,12 +6,18 @@ 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,
- ::Types::GlobalIDType[::Note],
- required: true,
- description: 'The global ID of the note to update.'
+ ::Types::GlobalIDType[::Note],
+ required: true,
+ description: 'The global ID of the note to update.'
def resolve(args)
note = authorized_find!(id: args[:id])
diff --git a/app/graphql/mutations/notes/update/image_diff_note.rb b/app/graphql/mutations/notes/update/image_diff_note.rb
index f4533cd9edb..6160ee03f4e 100644
--- a/app/graphql/mutations/notes/update/image_diff_note.rb
+++ b/app/graphql/mutations/notes/update/image_diff_note.rb
@@ -5,16 +5,20 @@ 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,
- required: false,
- description: copy_field_description(Types::Notes::NoteType, :body)
+ GraphQL::STRING_TYPE,
+ required: false,
+ description: copy_field_description(Types::Notes::NoteType, :body)
argument :position,
- Types::Notes::UpdateDiffImagePositionInputType,
- required: false,
- description: copy_field_description(Types::Notes::NoteType, :position)
+ Types::Notes::UpdateDiffImagePositionInputType,
+ required: false,
+ description: copy_field_description(Types::Notes::NoteType, :position)
def ready?(**args)
# As both arguments are optional, validate here that one of the
@@ -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)
diff --git a/app/graphql/mutations/notes/update/note.rb b/app/graphql/mutations/notes/update/note.rb
index 73b9b9bc49a..11d8c6e2cb9 100644
--- a/app/graphql/mutations/notes/update/note.rb
+++ b/app/graphql/mutations/notes/update/note.rb
@@ -5,16 +5,17 @@ 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,
- required: false,
- description: copy_field_description(Types::Notes::NoteType, :body)
+ GraphQL::STRING_TYPE,
+ required: false,
+ description: copy_field_description(Types::Notes::NoteType, :body)
argument :confidential,
- GraphQL::BOOLEAN_TYPE,
- required: false,
- description: 'The confidentiality flag of a note. Default is false.'
+ GraphQL::BOOLEAN_TYPE,
+ required: false,
+ description: 'The confidentiality flag of a note. Default is false.'
private
diff --git a/app/graphql/types/merge_strategy_enum.rb b/app/graphql/types/merge_strategy_enum.rb
new file mode 100644
index 00000000000..2120dc576eb
--- /dev/null
+++ b/app/graphql/types/merge_strategy_enum.rb
@@ -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
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index f0cc36f8b14..c8671080461 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -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
diff --git a/app/policies/merge_request_policy.rb b/app/policies/merge_request_policy.rb
index d5ba42d750c..e3fb54172f8 100644
--- a/app/policies/merge_request_policy.rb
+++ b/app/policies/merge_request_policy.rb
@@ -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')
diff --git a/app/views/shared/deploy_keys/_index.html.haml b/app/views/shared/deploy_keys/_index.html.haml
index 50d24acd126..be6fe94e497 100644
--- a/app/views/shared/deploy_keys/_index.html.haml
+++ b/app/views/shared/deploy_keys/_index.html.haml
@@ -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
diff --git a/app/views/shared/deploy_keys/_project_group_form.html.haml b/app/views/shared/deploy_keys/_project_group_form.html.haml
index bad25086d9f..25357ccdc65 100644
--- a/app/views/shared/deploy_keys/_project_group_form.html.haml
+++ b/app/views/shared/deploy_keys/_project_group_form.html.haml
@@ -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"
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 076ccd93360..8a16a9396dc 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -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
diff --git a/changelogs/unreleased/205484-10-project-settings-headers-repository-deploy-keys.yml b/changelogs/unreleased/205484-10-project-settings-headers-repository-deploy-keys.yml
new file mode 100644
index 00000000000..acc49458164
--- /dev/null
+++ b/changelogs/unreleased/205484-10-project-settings-headers-repository-deploy-keys.yml
@@ -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
diff --git a/changelogs/unreleased/322745-add-missing-token-names.yml b/changelogs/unreleased/322745-add-missing-token-names.yml
new file mode 100644
index 00000000000..2f54e6d46b3
--- /dev/null
+++ b/changelogs/unreleased/322745-add-missing-token-names.yml
@@ -0,0 +1,5 @@
+---
+title: Update cluster agent tokens with null names
+merge_request: 55673
+author:
+type: changed
diff --git a/changelogs/unreleased/add_request_cve_issue.yml b/changelogs/unreleased/add_request_cve_issue.yml
new file mode 100644
index 00000000000..45b2c1bf384
--- /dev/null
+++ b/changelogs/unreleased/add_request_cve_issue.yml
@@ -0,0 +1,5 @@
+---
+title: Adds Request CVE ID button to issue sidebar
+merge_request: 41203
+author:
+type: added
diff --git a/changelogs/unreleased/ajk-graphql-accept-mr.yml b/changelogs/unreleased/ajk-graphql-accept-mr.yml
new file mode 100644
index 00000000000..de9001f00a9
--- /dev/null
+++ b/changelogs/unreleased/ajk-graphql-accept-mr.yml
@@ -0,0 +1,5 @@
+---
+title: Add mutation to accept merge requests
+merge_request: 54758
+author:
+type: added
diff --git a/changelogs/unreleased/btn-confirm-shared-deploy-keys.yml b/changelogs/unreleased/btn-confirm-shared-deploy-keys.yml
new file mode 100644
index 00000000000..36968fff64f
--- /dev/null
+++ b/changelogs/unreleased/btn-confirm-shared-deploy-keys.yml
@@ -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
diff --git a/changelogs/unreleased/mo-enable-cq-comparision-backend.yml b/changelogs/unreleased/mo-enable-cq-comparision-backend.yml
new file mode 100644
index 00000000000..74536b16d6f
--- /dev/null
+++ b/changelogs/unreleased/mo-enable-cq-comparision-backend.yml
@@ -0,0 +1,5 @@
+---
+title: Enable codequality report comparison with backend
+merge_request: 54241
+author:
+type: performance
diff --git a/config/feature_flags/development/codequality_backend_comparison.yml b/config/feature_flags/development/codequality_backend_comparison.yml
index f7c3c14f674..9383ecb97d7 100644
--- a/config/feature_flags/development/codequality_backend_comparison.yml
+++ b/config/feature_flags/development/codequality_backend_comparison.yml
@@ -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
diff --git a/config/metrics/settings/20210204124924_elasticsearch_enabled.yml b/config/metrics/settings/20210204124924_elasticsearch_enabled.yml
deleted file mode 100644
index a9d1646d8d8..00000000000
--- a/config/metrics/settings/20210204124924_elasticsearch_enabled.yml
+++ /dev/null
@@ -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
diff --git a/db/migrate/20200816133024_add_cve_id_request_project_setting.rb b/db/migrate/20200816133024_add_cve_id_request_project_setting.rb
new file mode 100644
index 00000000000..c4fe86845eb
--- /dev/null
+++ b/db/migrate/20200816133024_add_cve_id_request_project_setting.rb
@@ -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
diff --git a/db/migrate/20210303165301_add_not_null_constraint_to_cluster_token_name.rb b/db/migrate/20210303165301_add_not_null_constraint_to_cluster_token_name.rb
new file mode 100644
index 00000000000..5fc8970b8f9
--- /dev/null
+++ b/db/migrate/20210303165301_add_not_null_constraint_to_cluster_token_name.rb
@@ -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
diff --git a/db/post_migrate/20210303165302_cleanup_cluster_tokens_with_null_name.rb b/db/post_migrate/20210303165302_cleanup_cluster_tokens_with_null_name.rb
new file mode 100644
index 00000000000..80ea1748eed
--- /dev/null
+++ b/db/post_migrate/20210303165302_cleanup_cluster_tokens_with_null_name.rb
@@ -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
diff --git a/db/schema_migrations/20200816133024 b/db/schema_migrations/20200816133024
new file mode 100644
index 00000000000..1637f166857
--- /dev/null
+++ b/db/schema_migrations/20200816133024
@@ -0,0 +1 @@
+37196d54d03791f7509e411d5c545f22aa70f7c07d1f13d76f70008a06e72b57
\ No newline at end of file
diff --git a/db/schema_migrations/20210303165301 b/db/schema_migrations/20210303165301
new file mode 100644
index 00000000000..c040288a283
--- /dev/null
+++ b/db/schema_migrations/20210303165301
@@ -0,0 +1 @@
+fa82a0f6c57a527a143da56ae0d70245a7d711b5e5ff3eb959fd6b2cf5872dac
\ No newline at end of file
diff --git a/db/schema_migrations/20210303165302 b/db/schema_migrations/20210303165302
new file mode 100644
index 00000000000..f140d636482
--- /dev/null
+++ b/db/schema_migrations/20210303165302
@@ -0,0 +1 @@
+1cb74abdc7134c3252425c3ceb8cd9dc4b157d64b1a2ff7928153e78b05d9121
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index e34c96dad03..3d0e8b903cb 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -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;
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index eceff6d09c2..15ca941eef0 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -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.
diff --git a/doc/development/usage_ping/dictionary.md b/doc/development/usage_ping/dictionary.md
index 43707e49ab4..48a77f582aa 100644
--- a/doc/development/usage_ping/dictionary.md
+++ b/doc/development/usage_ping/dictionary.md
@@ -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`
diff --git a/lib/gitlab/blame.rb b/lib/gitlab/blame.rb
index 5382bdab7eb..78a8f39e143 100644
--- a/lib/gitlab/blame.rb
+++ b/lib/gitlab/blame.rb
@@ -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
diff --git a/lib/gitlab/git/blame.rb b/lib/gitlab/git/blame.rb
index b118eda37f8..9e24306c05e 100644
--- a/lib/gitlab/git/blame.rb
+++ b/lib/gitlab/git/blame.rb
@@ -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
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index f06428df0c0..2a208044b89 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -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 ""
diff --git a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js
index d7c754fd3cc..bee628c3a56 100644
--- a/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js
+++ b/spec/frontend/pages/projects/shared/permissions/components/settings_panel_spec.js
@@ -29,6 +29,7 @@ const defaultProps = {
showDefaultAwardEmojis: true,
allowEditingCommitMessages: false,
},
+ isGitlabCom: true,
canDisableEmails: true,
canChangeVisibilityLevel: true,
allowedVisibilityOptions: [0, 10, 20],
diff --git a/spec/graphql/mutations/merge_requests/accept_spec.rb b/spec/graphql/mutations/merge_requests/accept_spec.rb
new file mode 100644
index 00000000000..db75c64a447
--- /dev/null
+++ b/spec/graphql/mutations/merge_requests/accept_spec.rb
@@ -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
diff --git a/spec/requests/api/graphql/mutations/merge_requests/accept_spec.rb b/spec/requests/api/graphql/mutations/merge_requests/accept_spec.rb
new file mode 100644
index 00000000000..2725b33d528
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/merge_requests/accept_spec.rb
@@ -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
diff --git a/spec/requests/api/project_attributes.yml b/spec/requests/api/project_attributes.yml
index 54045e2393d..08e04af816d 100644
--- a/spec/requests/api/project_attributes.yml
+++ b/spec/requests/api/project_attributes.yml
@@ -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: