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: