From da07b341fd8d97bac03ce5cd1781c64af2175b87 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Mon, 5 Apr 2021 21:09:19 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .rubocop_manual_todo.yml | 6 -- .../javascripts/diffs/components/app.vue | 6 ++ app/assets/javascripts/diffs/index.js | 2 + app/assets/javascripts/diffs/store/actions.js | 2 + .../diffs/store/modules/diff_state.js | 1 + .../javascripts/diffs/store/mutations.js | 2 + app/assets/javascripts/ide/stores/getters.js | 2 +- app/controllers/ide_controller.rb | 1 + .../merge_requests/by_approvals_finder.rb | 8 +- app/graphql/types/merge_request_state_enum.rb | 2 +- app/graphql/types/user_type.rb | 6 +- app/models/project_services/bamboo_service.rb | 16 ++-- app/models/timelog.rb | 4 +- .../dev_ops_report/metric_presenter.rb | 2 +- .../migrate_from_legacy_storage_service.rb | 7 +- ...te_legacy_storage_to_deployment_service.rb | 7 +- app/services/pages/zip_directory_service.rb | 8 +- app/views/groups/merge_requests.html.haml | 2 +- .../shared/milestones/_milestone.html.haml | 2 +- app/views/shared/milestones/_tabs.html.haml | 2 +- app/views/shared/projects/_project.html.haml | 2 +- .../batched_background_migration_worker.rb | 12 ++- ...tion_mark_as_not_deployed-feature-flag.yml | 6 ++ .../new-epic-form-deprecated-button.yml | 5 ++ changelogs/unreleased/ui-text-bamboo-ci.yml | 5 ++ ... => reject_unsigned_commits_by_gitlab.yml} | 12 +-- danger/pipeline/Dangerfile | 4 +- doc/api/graphql/reference/index.md | 8 +- doc/api/vulnerabilities.md | 6 +- .../documentation/feature_flags.md | 20 +++-- doc/development/i18n/externalization.md | 4 +- doc/development/i18n/merging_translations.md | 11 +-- .../testing_guide/frontend_testing.md | 9 +++ doc/user/feature_flags.md | 20 ++++- .../img/scim_name_identifier_mapping.png | Bin 59409 -> 0 bytes .../saml_sso/img/scim_provisioning_status.png | Bin 7756 -> 0 bytes doc/user/group/saml_sso/scim_setup.md | 46 ++++------- doc/user/project/import/perforce.md | 40 +++++----- doc/user/project/integrations/bamboo.md | 32 ++++---- .../filter/abstract_reference_filter.rb | 2 +- lib/support/nginx/gitlab | 4 +- lib/support/nginx/gitlab-ssl | 4 +- lib/tasks/gitlab/pages.rake | 9 ++- locale/gitlab.pot | 30 ++++--- spec/features/dashboard/milestones_spec.rb | 2 +- spec/features/groups/milestone_spec.rb | 6 +- .../milestones/user_views_milestone_spec.rb | 4 +- .../milestones/user_views_milestones_spec.rb | 3 +- ...user_activates_atlassian_bamboo_ci_spec.rb | 6 +- spec/frontend/diffs/store/mutations_spec.js | 14 ++-- spec/frontend/ide/stores/getters_spec.js | 28 ++++++- ...igrate_from_legacy_storage_service_spec.rb | 74 +++++++++++------- ...gacy_storage_to_deployment_service_spec.rb | 56 ++++++------- .../pages/zip_directory_service_spec.rb | 9 --- spec/tasks/gitlab/pages_rake_spec.rb | 26 +++++- ...atched_background_migration_worker_spec.rb | 29 +++++-- 56 files changed, 384 insertions(+), 252 deletions(-) create mode 100644 changelogs/unreleased/295187-roll-out-pages_migration_mark_as_not_deployed-feature-flag.yml create mode 100644 changelogs/unreleased/new-epic-form-deprecated-button.yml create mode 100644 changelogs/unreleased/ui-text-bamboo-ci.yml rename config/feature_flags/development/{pages_migration_mark_as_not_deployed.yml => reject_unsigned_commits_by_gitlab.yml} (53%) delete mode 100644 doc/user/group/saml_sso/img/scim_name_identifier_mapping.png delete mode 100644 doc/user/group/saml_sso/img/scim_provisioning_status.png diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index a8e2f98e174..7718cf3be7c 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -731,14 +731,8 @@ RSpec/EmptyLineAfterFinalLetItBe: - ee/spec/services/external_approval_rules/create_service_spec.rb - ee/spec/services/external_approval_rules/destroy_service_spec.rb - ee/spec/services/external_approval_rules/update_service_spec.rb - - ee/spec/services/geo/blob_download_service_spec.rb - - ee/spec/services/geo/design_repository_sync_service_spec.rb - - ee/spec/services/geo/event_service_spec.rb - - ee/spec/services/geo/file_registry_removal_service_spec.rb - - ee/spec/services/geo/project_housekeeping_service_spec.rb - ee/spec/services/gitlab_subscriptions/activate_service_spec.rb - ee/spec/services/gitlab_subscriptions/apply_trial_service_spec.rb - - ee/spec/services/groups/autocomplete_service_spec.rb - ee/spec/services/ide/schemas_config_service_spec.rb - ee/spec/services/incident_management/incidents/upload_metric_service_spec.rb - ee/spec/services/incident_management/oncall_rotations/edit_service_spec.rb diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue index a1358cdc40e..02fb5df07e8 100644 --- a/app/assets/javascripts/diffs/components/app.vue +++ b/app/assets/javascripts/diffs/components/app.vue @@ -89,6 +89,11 @@ export default { required: false, default: '', }, + endpointUpdateUser: { + type: String, + required: false, + default: '', + }, projectPath: { type: String, required: true, @@ -283,6 +288,7 @@ export default { endpointBatch: this.endpointBatch, endpointCoverage: this.endpointCoverage, endpointCodequality: this.endpointCodequality, + endpointUpdateUser: this.endpointUpdateUser, projectPath: this.projectPath, dismissEndpoint: this.dismissEndpoint, showSuggestPopover: this.showSuggestPopover, diff --git a/app/assets/javascripts/diffs/index.js b/app/assets/javascripts/diffs/index.js index c278baf2d2c..5a8862c2b70 100644 --- a/app/assets/javascripts/diffs/index.js +++ b/app/assets/javascripts/diffs/index.js @@ -74,6 +74,7 @@ export default function initDiffsApp(store) { endpointBatch: dataset.endpointBatch || '', endpointCoverage: dataset.endpointCoverage || '', endpointCodequality: dataset.endpointCodequality || '', + endpointUpdateUser: dataset.updateCurrentUserPath, projectPath: dataset.projectPath, helpPagePath: dataset.helpPagePath, currentUser: JSON.parse(dataset.currentUserData) || {}, @@ -116,6 +117,7 @@ export default function initDiffsApp(store) { endpointBatch: this.endpointBatch, endpointCoverage: this.endpointCoverage, endpointCodequality: this.endpointCodequality, + endpointUpdateUser: this.endpointUpdateUser, currentUser: this.currentUser, projectPath: this.projectPath, helpPagePath: this.helpPagePath, diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js index 8e036dc32d0..87e922886b5 100644 --- a/app/assets/javascripts/diffs/store/actions.js +++ b/app/assets/javascripts/diffs/store/actions.js @@ -62,6 +62,7 @@ export const setBaseConfig = ({ commit }, options) => { endpointBatch, endpointCoverage, endpointCodequality, + endpointUpdateUser, projectPath, dismissEndpoint, showSuggestPopover, @@ -75,6 +76,7 @@ export const setBaseConfig = ({ commit }, options) => { endpointBatch, endpointCoverage, endpointCodequality, + endpointUpdateUser, projectPath, dismissEndpoint, showSuggestPopover, diff --git a/app/assets/javascripts/diffs/store/modules/diff_state.js b/app/assets/javascripts/diffs/store/modules/diff_state.js index b673a5387ca..e5bbc9ed8f8 100644 --- a/app/assets/javascripts/diffs/store/modules/diff_state.js +++ b/app/assets/javascripts/diffs/store/modules/diff_state.js @@ -23,6 +23,7 @@ export default () => ({ addedLines: null, removedLines: null, endpoint: '', + endpointUpdateUser: '', basePath: '', commit: null, startVersion: null, // Null unless a target diff is selected for comparison that is not the "base" diff diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js index a676b1bdb37..eacf76234fc 100644 --- a/app/assets/javascripts/diffs/store/mutations.js +++ b/app/assets/javascripts/diffs/store/mutations.js @@ -34,6 +34,7 @@ export default { endpointBatch, endpointCoverage, endpointCodequality, + endpointUpdateUser, projectPath, dismissEndpoint, showSuggestPopover, @@ -47,6 +48,7 @@ export default { endpointBatch, endpointCoverage, endpointCodequality, + endpointUpdateUser, projectPath, dismissEndpoint, showSuggestPopover, diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js index 1db6ccb9e9d..e8b1a0ea494 100644 --- a/app/assets/javascripts/ide/stores/getters.js +++ b/app/assets/javascripts/ide/stores/getters.js @@ -208,7 +208,7 @@ export const canPushCodeStatus = (state, getters) => { PUSH_RULE_REJECT_UNSIGNED_COMMITS ]; - if (rejectUnsignedCommits) { + if (window.gon?.features?.rejectUnsignedCommitsByGitlab && rejectUnsignedCommits) { return { isAllowed: false, message: MSG_CANNOT_PUSH_UNSIGNED, diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb index 1e254606b58..4c7a91ee602 100644 --- a/app/controllers/ide_controller.rb +++ b/app/controllers/ide_controller.rb @@ -10,6 +10,7 @@ class IdeController < ApplicationController before_action do push_frontend_feature_flag(:build_service_proxy) push_frontend_feature_flag(:schema_linting) + push_frontend_feature_flag(:reject_unsigned_commits_by_gitlab, default_enabled: :yaml) define_index_vars end diff --git a/app/finders/merge_requests/by_approvals_finder.rb b/app/finders/merge_requests/by_approvals_finder.rb index e6ab1467f06..94f13468327 100644 --- a/app/finders/merge_requests/by_approvals_finder.rb +++ b/app/finders/merge_requests/by_approvals_finder.rb @@ -60,14 +60,14 @@ module MergeRequests ids.first.to_s.downcase == label || usernames.map(&:downcase).include?(label) end - # Merge Requests without any approval + # Merge requests without any approval # # @param [ActiveRecord::Relation] items def without_approvals(items) items.without_approvals end - # Merge Requests with any number of approvals + # Merge requests with any number of approvals # # @param [ActiveRecord::Relation] items the activerecord relation def with_any_approvals(items) @@ -76,14 +76,14 @@ module MergeRequests ]) end - # Merge Requests approved by given usernames + # Merge requests approved by given usernames # # @param [ActiveRecord::Relation] items the activerecord relation def find_approved_by_names(items) items.approved_by_users_with_usernames(*usernames) end - # Merge Requests approved by given user IDs + # Merge requests approved by given user IDs # # @param [ActiveRecord::Relation] items the activerecord relation def find_approved_by_ids(items) diff --git a/app/graphql/types/merge_request_state_enum.rb b/app/graphql/types/merge_request_state_enum.rb index a2d7bd0306c..bcf18b836de 100644 --- a/app/graphql/types/merge_request_state_enum.rb +++ b/app/graphql/types/merge_request_state_enum.rb @@ -5,6 +5,6 @@ module Types graphql_name 'MergeRequestState' description 'State of a GitLab merge request' - value 'merged', description: "Merge Request has been merged." + value 'merged', description: "Merge request has been merged." end end diff --git a/app/graphql/types/user_type.rb b/app/graphql/types/user_type.rb index 372bbeb4143..3d7db80ae11 100644 --- a/app/graphql/types/user_type.rb +++ b/app/graphql/types/user_type.rb @@ -83,13 +83,13 @@ module Types # Merge request field: MRs can be authored, assigned, or assigned-for-review: field :authored_merge_requests, resolver: Resolvers::AuthoredMergeRequestsResolver, - description: 'Merge Requests authored by the user.' + description: 'Merge requests authored by the user.' field :assigned_merge_requests, resolver: Resolvers::AssignedMergeRequestsResolver, - description: 'Merge Requests assigned to the user.' + description: 'Merge requests assigned to the user.' field :review_requested_merge_requests, resolver: Resolvers::ReviewRequestedMergeRequestsResolver, - description: 'Merge Requests assigned to the user for review.' + description: 'Merge requests assigned to the user for review.' field :snippets, description: 'Snippets authored by the user.', diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index 8c10b54c483..a892d1a4314 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class BambooService < CiService + include ActionView::Helpers::UrlHelper include ReactiveService prop_accessor :bamboo_url, :build_key, :username, :password @@ -31,15 +32,16 @@ class BambooService < CiService end def title - s_('BambooService|Atlassian Bamboo CI') + s_('BambooService|Atlassian Bamboo') end def description - s_('BambooService|A continuous integration and build server') + s_('BambooService|Use the Atlassian Bamboo CI/CD server with GitLab.') end def help - s_('BambooService|You must set up automatic revision labeling and a repository trigger in Bamboo.') + docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/bamboo'), target: '_blank', rel: 'noopener noreferrer' + s_('BambooService|Use Atlassian Bamboo to run CI/CD pipelines. You must set up automatic revision labeling and a repository trigger in Bamboo. %{docs_link}').html_safe % { docs_link: docs_link.html_safe } end def self.to_param @@ -52,19 +54,21 @@ class BambooService < CiService type: 'text', name: 'bamboo_url', title: s_('BambooService|Bamboo URL'), - placeholder: s_('BambooService|Bamboo root URL like https://bamboo.example.com'), + placeholder: s_('https://bamboo.example.com'), + help: s_('BambooService|Bamboo service root URL.'), required: true }, { type: 'text', name: 'build_key', - placeholder: s_('BambooService|Bamboo build plan key like KEY'), + placeholder: s_('KEY'), + help: s_('BambooService|Bamboo build plan key.'), required: true }, { type: 'text', name: 'username', - placeholder: s_('BambooService|A user with API access, if applicable') + help: s_('BambooService|The user with API access to the Bamboo server.') }, { type: 'password', diff --git a/app/models/timelog.rb b/app/models/timelog.rb index f4debedb656..c1aa84cbbcd 100644 --- a/app/models/timelog.rb +++ b/app/models/timelog.rb @@ -31,9 +31,9 @@ class Timelog < ApplicationRecord def issuable_id_is_present if issue_id && merge_request_id - errors.add(:base, _('Only Issue ID or Merge Request ID is required')) + errors.add(:base, _('Only Issue ID or merge request ID is required')) elsif issuable.nil? - errors.add(:base, _('Issue or Merge Request ID is required')) + errors.add(:base, _('Issue or merge request ID is required')) end end diff --git a/app/presenters/dev_ops_report/metric_presenter.rb b/app/presenters/dev_ops_report/metric_presenter.rb index b31dfd25a87..46b580d1f2a 100644 --- a/app/presenters/dev_ops_report/metric_presenter.rb +++ b/app/presenters/dev_ops_report/metric_presenter.rb @@ -38,7 +38,7 @@ module DevOpsReport ), Card.new( metric: subject, - title: 'Merge Requests', + title: 'Merge requests', description: 'per active user', feature: 'merge_requests', blog: 'https://8thlight.com/blog/uncle-bob/2013/02/01/The-Humble-Craftsman.html', diff --git a/app/services/pages/migrate_from_legacy_storage_service.rb b/app/services/pages/migrate_from_legacy_storage_service.rb index 37e701ce5ba..d649505f27d 100644 --- a/app/services/pages/migrate_from_legacy_storage_service.rb +++ b/app/services/pages/migrate_from_legacy_storage_service.rb @@ -2,11 +2,12 @@ module Pages class MigrateFromLegacyStorageService - def initialize(logger, migration_threads:, batch_size:, ignore_invalid_entries:) + def initialize(logger, migration_threads:, batch_size:, ignore_invalid_entries:, mark_projects_as_not_deployed:) @logger = logger @migration_threads = migration_threads @batch_size = batch_size @ignore_invalid_entries = ignore_invalid_entries + @mark_projects_as_not_deployed = mark_projects_as_not_deployed @migrated = 0 @errored = 0 @@ -60,7 +61,9 @@ module Pages def migrate_project(project) result = nil time = Benchmark.realtime do - result = ::Pages::MigrateLegacyStorageToDeploymentService.new(project, ignore_invalid_entries: @ignore_invalid_entries).execute + result = ::Pages::MigrateLegacyStorageToDeploymentService.new(project, + ignore_invalid_entries: @ignore_invalid_entries, + mark_projects_as_not_deployed: @mark_projects_as_not_deployed).execute end if result[:status] == :success diff --git a/app/services/pages/migrate_legacy_storage_to_deployment_service.rb b/app/services/pages/migrate_legacy_storage_to_deployment_service.rb index 3bffed4caf6..95c7107eb62 100644 --- a/app/services/pages/migrate_legacy_storage_to_deployment_service.rb +++ b/app/services/pages/migrate_legacy_storage_to_deployment_service.rb @@ -9,9 +9,10 @@ module Pages attr_reader :project - def initialize(project, ignore_invalid_entries: false) + def initialize(project, ignore_invalid_entries: false, mark_projects_as_not_deployed: false) @project = project @ignore_invalid_entries = ignore_invalid_entries + @mark_projects_as_not_deployed = mark_projects_as_not_deployed end def execute @@ -36,10 +37,12 @@ module Pages archive_path = zip_result[:archive_path] unless archive_path + return error("Archive not created. Missing public directory in #{@project.pages_path}") unless @mark_projects_as_not_deployed + project.set_first_pages_deployment!(nil) return success( - message: "Archive not created. Missing public directory in #{project.pages_path} ? Marked project as not deployed") + message: "Archive not created. Missing public directory in #{project.pages_path}? Marked project as not deployed") end deployment = nil diff --git a/app/services/pages/zip_directory_service.rb b/app/services/pages/zip_directory_service.rb index 2f4995899a1..6cb79452e1b 100644 --- a/app/services/pages/zip_directory_service.rb +++ b/app/services/pages/zip_directory_service.rb @@ -18,13 +18,7 @@ module Pages end def execute - unless resolve_public_dir - if Feature.enabled?(:pages_migration_mark_as_not_deployed) - return success - end - - return error("Can not find valid public dir in #{@input_dir}") - end + return success unless resolve_public_dir output_file = File.join(real_dir, "@migrated.zip") # '@' to avoid any name collision with groups or projects diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml index f0f85e0618d..15864e18f7c 100644 --- a/app/views/groups/merge_requests.html.haml +++ b/app/views/groups/merge_requests.html.haml @@ -1,6 +1,6 @@ - @can_bulk_update = can?(current_user, :admin_merge_request, @group) && @group.feature_available?(:group_bulk_edit) -- page_title _("Merge Requests") +- page_title _("Merge requests") - if @merge_requests&.size == 0 = render 'shared/empty_states/merge_requests', project_select_button: true diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml index f99aea61c17..b200d2e30c9 100644 --- a/app/views/shared/milestones/_milestone.html.haml +++ b/app/views/shared/milestones/_milestone.html.haml @@ -44,7 +44,7 @@ = link_to pluralize(milestone.total_issues_count, _('Issue')), issues_path - if milestone.merge_requests_enabled? · - = link_to pluralize(milestone.merge_requests_visible_to_user(current_user).size, _('Merge Request')), merge_requests_path + = link_to pluralize(milestone.merge_requests_visible_to_user(current_user).size, _('Merge request')), merge_requests_path .float-lg-right.light #{milestone.percent_complete}% complete .col-sm-2 .milestone-actions.d-flex.justify-content-sm-start.justify-content-md-end diff --git a/app/views/shared/milestones/_tabs.html.haml b/app/views/shared/milestones/_tabs.html.haml index 33e634c3e7b..3524a1b17ea 100644 --- a/app/views/shared/milestones/_tabs.html.haml +++ b/app/views/shared/milestones/_tabs.html.haml @@ -11,7 +11,7 @@ - if milestone.merge_requests_enabled? %li.nav-item = link_to '#tab-merge-requests', class: 'nav-link', data: { toggle: 'tab', endpoint: milestone_tab_path(milestone, 'merge_requests', show_project_name: show_project_name) } do - = _('Merge Requests') + = _('Merge requests') %span.badge.badge-pill= milestone.merge_requests_visible_to_user(current_user).size %li.nav-item = link_to '#tab-participants', class: 'nav-link', data: { toggle: 'tab', endpoint: milestone_tab_path(milestone, 'participants') } do diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index a33cd7c3b53..ef1092b83fa 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -90,7 +90,7 @@ - if show_merge_request_count?(disabled: !merge_requests, compact_mode: compact_mode) = link_to project_merge_requests_path(project), class: "d-none d-xl-flex align-items-center icon-wrapper merge-requests has-tooltip", - title: _('Merge Requests'), data: { container: 'body', placement: 'top' } do + title: _('Merge requests'), data: { container: 'body', placement: 'top' } do = sprite_icon('git-merge', size: 14, css_class: 'gl-mr-2') = number_with_delimiter(project.open_merge_requests_count) - if show_issue_count?(disabled: !issues, compact_mode: compact_mode) diff --git a/app/workers/database/batched_background_migration_worker.rb b/app/workers/database/batched_background_migration_worker.rb index 6b1fdafd9fc..5af2f713787 100644 --- a/app/workers/database/batched_background_migration_worker.rb +++ b/app/workers/database/batched_background_migration_worker.rb @@ -8,6 +8,9 @@ module Database feature_category :database idempotent! + LEASE_TIMEOUT_MULTIPLIER = 3 + MINIMUM_LEASE_TIMEOUT = 10.minutes.freeze + def perform return unless Feature.enabled?(:execute_batched_migrations_on_schedule, type: :ops) && active_migration @@ -33,14 +36,19 @@ module Database Gitlab::Database::BackgroundMigration::BatchedMigrationRunner.new.run_migration_job(active_migration) end - def with_exclusive_lease(timeout) - lease = Gitlab::ExclusiveLease.new(lease_key, timeout: timeout * 2) + def with_exclusive_lease(interval) + timeout = max(interval * LEASE_TIMEOUT_MULTIPLIER, MINIMUM_LEASE_TIMEOUT) + lease = Gitlab::ExclusiveLease.new(lease_key, timeout: timeout) yield if lease.try_obtain ensure lease&.cancel end + def max(left, right) + left >= right ? left : right + end + def lease_key self.class.name.demodulize.underscore end diff --git a/changelogs/unreleased/295187-roll-out-pages_migration_mark_as_not_deployed-feature-flag.yml b/changelogs/unreleased/295187-roll-out-pages_migration_mark_as_not_deployed-feature-flag.yml new file mode 100644 index 00000000000..8f409944ee5 --- /dev/null +++ b/changelogs/unreleased/295187-roll-out-pages_migration_mark_as_not_deployed-feature-flag.yml @@ -0,0 +1,6 @@ +--- +title: Allow users to mark pages projects as not deployed during migration to zip + storage +merge_request: 55862 +author: +type: added diff --git a/changelogs/unreleased/new-epic-form-deprecated-button.yml b/changelogs/unreleased/new-epic-form-deprecated-button.yml new file mode 100644 index 00000000000..66ee2105a79 --- /dev/null +++ b/changelogs/unreleased/new-epic-form-deprecated-button.yml @@ -0,0 +1,5 @@ +--- +title: Replace deprecated button on new epic creation form +merge_request: 57874 +author: +type: changed diff --git a/changelogs/unreleased/ui-text-bamboo-ci.yml b/changelogs/unreleased/ui-text-bamboo-ci.yml new file mode 100644 index 00000000000..645eaf3fe1a --- /dev/null +++ b/changelogs/unreleased/ui-text-bamboo-ci.yml @@ -0,0 +1,5 @@ +--- +title: Update pot file +merge_request: 58392 +author: +type: other diff --git a/config/feature_flags/development/pages_migration_mark_as_not_deployed.yml b/config/feature_flags/development/reject_unsigned_commits_by_gitlab.yml similarity index 53% rename from config/feature_flags/development/pages_migration_mark_as_not_deployed.yml rename to config/feature_flags/development/reject_unsigned_commits_by_gitlab.yml index f8b28785a2d..93c0026d59d 100644 --- a/config/feature_flags/development/pages_migration_mark_as_not_deployed.yml +++ b/config/feature_flags/development/reject_unsigned_commits_by_gitlab.yml @@ -1,8 +1,8 @@ --- -name: pages_migration_mark_as_not_deployed -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49473 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/295187 -milestone: '13.8' +name: reject_unsigned_commits_by_gitlab +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/58453 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/326775 +milestone: '13.11' type: development -group: group::release -default_enabled: false +group: group::editor +default_enabled: true diff --git a/danger/pipeline/Dangerfile b/danger/pipeline/Dangerfile index c71fb86325d..5503094ba50 100644 --- a/danger/pipeline/Dangerfile +++ b/danger/pipeline/Dangerfile @@ -3,9 +3,9 @@ MESSAGE = <<~MESSAGE ## Pipeline Changes -This Merge Request contains changes to the pipeline configuration for the GitLab project. +This merge request contains changes to the pipeline configuration for the GitLab project. -Please consider the effect of the changes in this Merge Request on the following: +Please consider the effect of the changes in this merge request on the following: - Effects on different [pipeline types](https://docs.gitlab.com/ee/development/pipelines.html#pipelines-for-merge-requests) - Effects on non-canonical projects: - `gitlab-foss` diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 303b8c5b574..9c1e4a06b65 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -6543,8 +6543,8 @@ Representation of a GitLab user. | Field | Type | Description | | ----- | ---- | ----------- | -| `assignedMergeRequests` | [`MergeRequestConnection`](#mergerequestconnection) | Merge Requests assigned to the user. | -| `authoredMergeRequests` | [`MergeRequestConnection`](#mergerequestconnection) | Merge Requests authored by the user. | +| `assignedMergeRequests` | [`MergeRequestConnection`](#mergerequestconnection) | Merge requests assigned to the user. | +| `authoredMergeRequests` | [`MergeRequestConnection`](#mergerequestconnection) | Merge requests authored by the user. | | `avatarUrl` | [`String`](#string) | URL of the user's avatar. | | `bot` | [`Boolean!`](#boolean) | Indicates if the user is a bot. | | `callouts` | [`UserCalloutConnection`](#usercalloutconnection) | User callouts that belong to the user. | @@ -6556,7 +6556,7 @@ Representation of a GitLab user. | `name` | [`String!`](#string) | Human-readable name of the user. | | `projectMemberships` | [`ProjectMemberConnection`](#projectmemberconnection) | Project memberships of the user. | | `publicEmail` | [`String`](#string) | User's public email. | -| `reviewRequestedMergeRequests` | [`MergeRequestConnection`](#mergerequestconnection) | Merge Requests assigned to the user for review. | +| `reviewRequestedMergeRequests` | [`MergeRequestConnection`](#mergerequestconnection) | Merge requests assigned to the user for review. | | `snippets` | [`SnippetConnection`](#snippetconnection) | Snippets authored by the user. | | `starredProjects` | [`ProjectConnection`](#projectconnection) | Projects starred by the user. | | `state` | [`UserState!`](#userstate) | State of the user. | @@ -7833,7 +7833,7 @@ State of a GitLab merge request. | `all` | All available. | | `closed` | In closed state. | | `locked` | Discussion has been locked. | -| `merged` | Merge Request has been merged. | +| `merged` | Merge request has been merged. | | `opened` | In open state. | ### `MergeStrategyEnum` diff --git a/doc/api/vulnerabilities.md b/doc/api/vulnerabilities.md index 8296292c1f8..a7412ca97f1 100644 --- a/doc/api/vulnerabilities.md +++ b/doc/api/vulnerabilities.md @@ -15,9 +15,11 @@ This document now describes the new Vulnerabilities API that provides access to [Vulnerabilities](https://gitlab.com/groups/gitlab-org/-/epics/634). WARNING: -This API is in an alpha stage and considered unstable. +This API is in the process of being deprecated and considered unstable. The response payload may be subject to change or breakage -across GitLab releases. +across GitLab releases. Please use the +[GraphQL API](graphql/reference/index.md#vulnerabilities) +instead. Every API call to vulnerabilities must be [authenticated](README.md#authentication). diff --git a/doc/development/documentation/feature_flags.md b/doc/development/documentation/feature_flags.md index ca5c250ce69..7dd02c44afa 100644 --- a/doc/development/documentation/feature_flags.md +++ b/doc/development/documentation/feature_flags.md @@ -67,8 +67,9 @@ be enabled for a single project, and is not ready for production use: > - Not recommended for production use. > - To use in GitLab self-managed instances, ask a GitLab administrator to [enable it](#anchor-to-section). **(FREE SELF)** -WARNING: -This feature might not be available to you. Check the **version history** note above for details. +This in-development feature might not be available for your use. There can be +[risks when enabling features still in development](/user/feature_flags.md#risks-when-enabling-features-still-in-development). +Refer to this feature's version history for more details. (...Regular content goes here...) @@ -126,8 +127,9 @@ use: > - Recommended for production use. > - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#anchor-to-section). **(FREE SELF)** -WARNING: -This feature might not be available to you. Check the **version history** note above for details. +There can be +[risks when disabling released features](/user/feature_flags.md#risks-when-disabling-released-features). +Refer to this feature's version history for more details. (...Regular content goes here...) @@ -182,8 +184,9 @@ cannot be enabled for a single project, and is ready for production use: > - Recommended for production use. > - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#anchor-to-section). **(FREE SELF)** -WARNING: -This feature might not be available to you. Check the **version history** note above for details. +There can be +[risks when disabling released features](/user/feature_flags.md#risks-when-disabling-released-features). +Refer to this feature's version history for more details. (...Regular content goes here...) @@ -255,8 +258,9 @@ be enabled by project, and is ready for production use: > - Recommended for production use. > - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#anchor-to-section). **(FREE SELF)** -WARNING: -This feature might not be available to you. Check the **version history** note above for details. +There can be +[risks when disabling released features](/user/feature_flags.md#risks-when-disabling-released-features). +Refer to this feature's version history for more details. (...Regular content goes here...) diff --git a/doc/development/i18n/externalization.md b/doc/development/i18n/externalization.md index 12aef403da8..0f7b8078933 100644 --- a/doc/development/i18n/externalization.md +++ b/doc/development/i18n/externalization.md @@ -256,7 +256,7 @@ For example use `%{created_at}` in Ruby but `%{createdAt}` in JavaScript. Make s - In Vue: Use the [`GlSprintf`](https://gitlab-org.gitlab.io/gitlab-ui/?path=/docs/utilities-sprintf--sentence-with-link) component if: - + - you need to include child components in the translation string. - you need to include HTML in your translation string. - you are using `sprintf` and need to pass `false` as the third argument to @@ -699,7 +699,7 @@ bin/rake gettext:regenerate This command updates `locale/gitlab.pot` file with the newly externalized strings and remove any strings that aren't used anymore. You should check this -file in. Once the changes are on master, they are picked up by +file in. Once the changes are on the default branch, they are picked up by [CrowdIn](https://translate.gitlab.com) and be presented for translation. diff --git a/doc/development/i18n/merging_translations.md b/doc/development/i18n/merging_translations.md index e7d25942143..553820733e7 100644 --- a/doc/development/i18n/merging_translations.md +++ b/doc/development/i18n/merging_translations.md @@ -42,10 +42,11 @@ page](https://translate.gitlab.com/project/gitlab-ee/settings#integration). ## Merging translations -When all translations are found good and pipelines pass the -translations can be merged into the master branch. When merging the translations, -make sure to check the **Remove source branch** checkbox, so CrowdIn recreates the -`master-i18n` from master after the new translation was merged. +After all translations are determined to be appropriate and the pipelines pass, +you can merge the translations into the default branch. When merging translations, +be sure to select the **Remove source branch** check box, which causes CrowdIn +to recreate the `master-i18n` from the default branch after merging the new +translation. We are discussing [automating this entire process](https://gitlab.com/gitlab-org/gitlab/-/issues/19896). @@ -59,7 +60,7 @@ and delete the [`master-18n`](https://gitlab.com/gitlab-org/gitlab/-/branches/all?utf8=✓&search=master-i18n). This might be needed when the merge request contains failures that -have been fixed on master. +have been fixed on the default branch. ## Recreate the GitLab integration in CrowdIn diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md index 1842bf5e7f8..7289e66a045 100644 --- a/doc/development/testing_guide/frontend_testing.md +++ b/doc/development/testing_guide/frontend_testing.md @@ -104,6 +104,15 @@ describe('Component', () => { Remember that the performance of each test depends on the environment. +### Test-specific stylesheets + +To help facilitate RSpec integration tests we have two test-specific stylesheets. These can be used to do things like disable animations to improve test speed, or to make elements visible when they need to be targeted by Capybara click events: + +- `app/assets/stylesheets/disable_animations.scss` +- `app/assets/stylesheets/test_environment.scss` + +Because the test environment should match the production environment as much as possible, use these minimally and only add to them when necessary. + ## What and how to test Before jumping into more gritty details about Jest-specific workflows like mocks and spies, we should briefly cover what to test with Jest. diff --git a/doc/user/feature_flags.md b/doc/user/feature_flags.md index a6be4c69f81..5be28de4101 100644 --- a/doc/user/feature_flags.md +++ b/doc/user/feature_flags.md @@ -18,9 +18,9 @@ may be unavailable to you. In this case, you'll see a warning like this in the feature documentation: -WARNING: -This feature might not be available to you. Review the **version history** note -on this page for details. +This in-development feature might not be available for your use. There can be +[risks when enabling features still in development](#risks-when-enabling-features-still-in-development). +Refer to this feature's version history for more details. In the version history note, you'll find information on the state of the feature flag, including whether the feature is on ("enabled by default") or @@ -41,3 +41,17 @@ although changing a feature's default state isn't recommended. If you're a GitLab.com user and the feature is disabled, be aware that GitLab may be working on the feature for potential release in the future. + +## Risks when enabling features still in development + +Features that are disabled by default may change or be removed without notice in a future version of GitLab. + +Data corruption, stability degradation, or performance degradation might occur if +you enable a feature that's disabled by default. Problems caused by using a default +disabled feature aren't covered by GitLab support, unless you were directed by GitLab +to enable the feature. + +## Risks when disabling released features + +In most cases, the feature flag code is removed in a future version of GitLab. +If and when that occurs, from that point onward you can't keep the feature in a disabled state. diff --git a/doc/user/group/saml_sso/img/scim_name_identifier_mapping.png b/doc/user/group/saml_sso/img/scim_name_identifier_mapping.png deleted file mode 100644 index f9c63970f16822204d280d12b1222adc9c484783..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59409 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�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 diff --git a/doc/user/group/saml_sso/img/scim_provisioning_status.png b/doc/user/group/saml_sso/img/scim_provisioning_status.png deleted file mode 100644 index 41466ec927628ccddcfbee5aab4827d7b6d565f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7756 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 diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md index 6eac88a1475..7bf54aea60e 100644 --- a/doc/user/group/saml_sso/scim_setup.md +++ b/doc/user/group/saml_sso/scim_setup.md @@ -50,19 +50,13 @@ Once [Group Single Sign-On](index.md) has been configured, we can: The SAML application that was created during [Single sign-on](index.md) setup for [Azure](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/view-applications-portal) now needs to be set up for SCIM. -1. Check the configuration for your GitLab SAML app and ensure that **Name identifier value** (NameID) points to `user.objectid` or another unique identifier. This matches the `extern_uid` used on GitLab. - - ![Name identifier value mapping](img/scim_name_identifier_mapping.png) - 1. Set up automatic provisioning and administrative credentials by following the - [Provisioning users and groups to applications that support SCIM](https://docs.microsoft.com/en-us/azure/active-directory/app-provisioning/use-scim-to-provision-users-and-groups#provisioning-users-and-groups-to-applications-that-support-scim) section in Azure's SCIM setup documentation. + [Azure's SCIM setup documentation](https://docs.microsoft.com/en-us/azure/active-directory/app-provisioning/use-scim-to-provision-users-and-groups#provisioning-users-and-groups-to-applications-that-support-scim). During this configuration, note the following: - The `Tenant URL` and `secret token` are the ones retrieved in the [previous step](#gitlab-configuration). -- Should there be any problems with the availability of GitLab or similar - errors, the notification email set gets those. - It is recommended to set a notification email and check the **Send an email notification when a failure occurs** checkbox. - For mappings, we will only leave `Synchronize Azure Active Directory Users to AppName` enabled. @@ -70,42 +64,30 @@ You can then test the connection by clicking on **Test Connection**. If the conn #### Configure attribute mapping -1. Click on `Synchronize Azure Active Directory Users to AppName` to configure the attribute mapping. -1. Click **Delete** next to the `mail` mapping. -1. Map `userPrincipalName` to `emails[type eq "work"].value` and change its **Matching precedence** to `2`. -1. Map `mailNickname` to `userName`. -1. Determine how GitLab uniquely identifies users. +Follow [Azure documentation to configure the attribute mapping](https://docs.microsoft.com/en-us/azure/active-directory/app-provisioning/customize-application-attributes). - - Use `objectId` unless users already have SAML linked for your group. - - If you already have users with SAML linked then use the `Name ID` value from the [SAML configuration](#azure). Using a different value may cause duplicate users and prevent users from accessing the GitLab group. +The following table below provides an attribute mapping known to work with GitLab. If +your SAML configuration differs from [the recommended SAML settings](index.md#azure-setup-notes), +modify the corresponding `customappsso` settings accordingly. If a mapping is not listed in the +table, use the Azure defaults. -1. Create a new mapping: - 1. Click **Add New Mapping**. - 1. Set: - - **Source attribute** to the unique identifier determined above, typically `objectId`. - - **Target attribute** to `externalId`. - - **Match objects using this attribute** to `Yes`. - - **Matching precedence** to `1`. +| Azure Active Directory Attribute | customappsso Attribute | Matching precedence | +| -------------------------------- | ---------------------- | -------------------- | +| `objectId` | `externalId` | 1 | +| `userPrincipalName` | `emails[type eq "work"].value` | | +| `mailNickname` | `userName` | | -1. Click the `userPrincipalName` mapping and change **Match objects using this attribute** to `No`. - -1. Save your changes. For reference, you can view [an example configuration in the troubleshooting reference](../../../administration/troubleshooting/group_saml_scim.md#azure-active-directory). - - NOTE: - If you used a unique identifier **other than** `objectId`, be sure to map it to `externalId`. +For guidance, you can view [an example configuration in the troubleshooting reference](../../../administration/troubleshooting/group_saml_scim.md#azure-active-directory). 1. Below the mapping list click on **Show advanced options > Edit attribute list for AppName**. - 1. Ensure the `id` is the primary and required field, and `externalId` is also required. NOTE: `username` should neither be primary nor required as we don't support that field on GitLab SCIM yet. -1. Save all the screens and, in the **Provisioning** step, set - the `Provisioning Status` to `On`. - - ![Provisioning status toggle switch](img/scim_provisioning_status.png) +1. Save all changes. +1. In the **Provisioning** step, set the `Provisioning Status` to `On`. NOTE: You can control what is actually synced by selecting the `Scope`. For example, diff --git a/doc/user/project/import/perforce.md b/doc/user/project/import/perforce.md index 85c8a3020b9..8040eb07c93 100644 --- a/doc/user/project/import/perforce.md +++ b/doc/user/project/import/perforce.md @@ -15,33 +15,33 @@ include a centralized, proprietary version control system similar to Git. The following list illustrates the main differences between Perforce Helix and Git: -1. In general the biggest difference is that Perforce branching is heavyweight - compared to Git's lightweight branching. When you create a branch in Perforce, - it creates an integration record in their proprietary database for every file - in the branch, regardless how many were actually changed. Whereas Git was - implemented with a different architecture so that a single SHA acts as a pointer - to the state of the whole repository after the changes, making it very easy to branch. - This is what made feature branching workflows so easy to adopt with Git. -1. Also, context switching between branches is much easier in Git. If your manager - said 'You need to stop work on that new feature and fix this security - vulnerability' you can do so very easily in Git. -1. Having a complete copy of the project and its history on your local machine - means every transaction is very fast and Git provides that. You can branch/merge - and experiment in isolation, then clean up your mess before sharing your new - cool stuff with everyone. -1. Git also made code review simple because you could share your changes without - merging them to master, whereas Perforce had to implement a Shelving feature on - the server so others could review changes before merging. +- In general, the biggest difference is that Perforce branching is heavyweight + compared to Git's lightweight branching. When you create a branch in Perforce, + it creates an integration record in their proprietary database for every file + in the branch, regardless how many were actually changed. With Git, however, + a single SHA acts as a pointer to the state of the whole repository after the + changes, which can be helpful when adopting feature branching workflows. +- Context switching between branches is less complex in Git. If your manager + says, 'You need to stop work on that new feature and fix this security + vulnerability,' Git can help you do this. +- Having a complete copy of the project and its history on your local computer + means every transaction is very fast, and Git provides that. You can branch + or merge, and experiment in isolation, and then clean up before sharing your + changes with others. +- Git makes code review less complex, because you can share your changes without + merging them to the default branch. This is compared to Perforce, which had to + implement a Shelving feature on the server so others could review changes + before merging. ## Why migrate Perforce Helix can be difficult to manage both from a user and an administrator perspective. Migrating to Git/GitLab there is: -- **No licensing costs**, Git is GPL while Perforce Helix is proprietary. -- **Shorter learning curve**, Git has a big community and a vast number of +- **No licensing costs**: Git is GPL while Perforce Helix is proprietary. +- **Shorter learning curve**: Git has a big community and a vast number of tutorials to get you started. -- **Integration with modern tools**, migrating to Git and GitLab you can have +- **Integration with modern tools**: By migrating to Git and GitLab, you can have an open source end-to-end software development platform with built-in version control, issue tracking, code review, CI/CD, and more. diff --git a/doc/user/project/integrations/bamboo.md b/doc/user/project/integrations/bamboo.md index 3b012ab4430..1eb8a8c60e0 100644 --- a/doc/user/project/integrations/bamboo.md +++ b/doc/user/project/integrations/bamboo.md @@ -4,11 +4,11 @@ group: Ecosystem info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments --- -# Atlassian Bamboo CI Service **(FREE)** +# Atlassian Bamboo Service **(FREE)** GitLab provides integration with Atlassian Bamboo for continuous integration. When configured, pushes to a project trigger a build in Bamboo automatically. -Merge requests also display CI status showing whether the build is pending, +Merge requests also display CI/CD status showing whether the build is pending, failed, or completed successfully. It also provides a link to the Bamboo build page for more information. @@ -20,21 +20,21 @@ need to be configured in a Bamboo build plan before GitLab can integrate. ### Complete these steps in Bamboo -1. Navigate to a Bamboo build plan and choose 'Configure plan' from the 'Actions' +1. Navigate to a Bamboo build plan and choose **Configure plan** from the **Actions** dropdown. -1. Select the 'Triggers' tab. -1. Click 'Add trigger'. -1. Enter a description such as 'GitLab trigger' -1. Choose 'Repository triggers the build when changes are committed' -1. Check one or more repositories checkboxes -1. Enter the GitLab IP address in the 'Trigger IP addresses' box. This is a +1. Select the **Triggers** tab. +1. Click **Add trigger**. +1. Enter a description such as **GitLab trigger**. +1. Choose **Repository triggers the build when changes are committed**. +1. Select the checkbox for one or more repositories. +1. Enter the GitLab IP address in the **Trigger IP addresses** box. This is a list of IP addresses that are allowed to trigger Bamboo builds. 1. Save the trigger. 1. In the left pane, select a build stage. If you have multiple build stages you want to select the last stage that contains the Git checkout task. -1. Select the 'Miscellaneous' tab. -1. Under 'Pattern Match Labeling' put `${bamboo.repository.revision.number}` - in the 'Labels' box. +1. Select the **Miscellaneous** tab. +1. Under **Pattern Match Labeling** put `${bamboo.repository.revision.number}` + in the **Labels** box. 1. Save Bamboo is now ready to accept triggers from GitLab. Next, set up the Bamboo @@ -44,18 +44,18 @@ service in GitLab. 1. Navigate to the project you want to configure to trigger builds. 1. Navigate to the [Integrations page](overview.md#accessing-integrations) -1. Click 'Atlassian Bamboo CI' +1. Click **Atlassian Bamboo**. 1. Ensure that the **Active** toggle is enabled. 1. Enter the base URL of your Bamboo server. `https://bamboo.example.com` 1. Enter the build key from your Bamboo build plan. Build keys are typically made up from the Project Key and Plan Key that are set on project/plan creation and - separated with a dash (`-`), for example **PROJ-PLAN**. This is a short, all + separated with a dash (`-`), for example **PROJ-PLAN**. This is a short, all uppercase identifier that is unique. When viewing a plan in Bamboo, the build key is also shown in the browser URL, for example `https://bamboo.example.com/browse/PROJ-PLAN`. 1. If necessary, enter username and password for a Bamboo user that has access to trigger the build plan. Leave these fields blank if you do not require authentication. -1. Save or optionally click 'Test Settings'. Please note that 'Test Settings' +1. Save or optionally click **Test Settings**. Please note that **Test Settings** actually triggers a build in Bamboo. ## Troubleshooting @@ -63,7 +63,7 @@ service in GitLab. ### Builds not triggered If builds are not triggered, ensure you entered the right GitLab IP address in -Bamboo under 'Trigger IP addresses'. Also check [service hook logs](overview.md#troubleshooting-integrations) for request failures. +Bamboo under **Trigger IP addresses**. Also check [service hook logs](overview.md#troubleshooting-integrations) for request failures. ### Advanced Atlassian Bamboo features not available in GitLab UI diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb index 2448c2c2bb2..72a4230cfe1 100644 --- a/lib/banzai/filter/abstract_reference_filter.rb +++ b/lib/banzai/filter/abstract_reference_filter.rb @@ -2,7 +2,7 @@ module Banzai module Filter - # Issues, Merge Requests, Snippets, Commits and Commit Ranges share + # Issues, merge requests, Snippets, Commits and Commit Ranges share # similar functionality in reference filtering. class AbstractReferenceFilter < ReferenceFilter include CrossProjectReference diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab index 9eafa5ef008..74eb8634d58 100644 --- a/lib/support/nginx/gitlab +++ b/lib/support/nginx/gitlab @@ -7,8 +7,8 @@ ## CONTRIBUTING ## ################################## ## -## If you change this file in a Merge Request, please also create -## a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests +## If you change this file in a merge request, please also create +## a merge request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests ## ################################### ## configuration ## diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index ae5c88455e4..576c13d8d10 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -11,8 +11,8 @@ ## CONTRIBUTING ## ################################## ## -## If you change this file in a Merge Request, please also create -## a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests +## If you change this file in a merge request, please also create +## a merge request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests ## ################################### ## configuration ## diff --git a/lib/tasks/gitlab/pages.rake b/lib/tasks/gitlab/pages.rake index b598dab901d..606d1369e18 100644 --- a/lib/tasks/gitlab/pages.rake +++ b/lib/tasks/gitlab/pages.rake @@ -11,7 +11,8 @@ namespace :gitlab do result = ::Pages::MigrateFromLegacyStorageService.new(logger, migration_threads: migration_threads, batch_size: batch_size, - ignore_invalid_entries: ignore_invalid_entries).execute + ignore_invalid_entries: ignore_invalid_entries, + mark_projects_as_not_deployed: mark_projects_as_not_deployed).execute logger.info("A total of #{result[:migrated] + result[:errored]} projects were processed.") logger.info("- The #{result[:migrated]} projects migrated successfully") @@ -51,5 +52,11 @@ namespace :gitlab do ENV.fetch('PAGES_MIGRATION_IGNORE_INVALID_ENTRIES', 'false') ) end + + def mark_projects_as_not_deployed + Gitlab::Utils.to_boolean( + ENV.fetch('PAGES_MIGRATION_MARK_PROJECTS_AS_NOT_DEPLOYED', 'false') + ) + end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 307070e8b56..3fb567dbee6 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -4714,25 +4714,25 @@ msgstr "" msgid "Balsamiq file could not be loaded." msgstr "" -msgid "BambooService|A continuous integration and build server" -msgstr "" - -msgid "BambooService|A user with API access, if applicable" -msgstr "" - -msgid "BambooService|Atlassian Bamboo CI" +msgid "BambooService|Atlassian Bamboo" msgstr "" msgid "BambooService|Bamboo URL" msgstr "" -msgid "BambooService|Bamboo build plan key like KEY" +msgid "BambooService|Bamboo build plan key." msgstr "" -msgid "BambooService|Bamboo root URL like https://bamboo.example.com" +msgid "BambooService|Bamboo service root URL." msgstr "" -msgid "BambooService|You must set up automatic revision labeling and a repository trigger in Bamboo." +msgid "BambooService|The user with API access to the Bamboo server." +msgstr "" + +msgid "BambooService|Use Atlassian Bamboo to run CI/CD pipelines. You must set up automatic revision labeling and a repository trigger in Bamboo. %{docs_link}" +msgstr "" + +msgid "BambooService|Use the Atlassian Bamboo CI/CD server with GitLab." msgstr "" msgid "Based on" @@ -17282,7 +17282,7 @@ msgstr "" msgid "Issue label" msgstr "" -msgid "Issue or Merge Request ID is required" +msgid "Issue or merge request ID is required" msgstr "" msgid "Issue published on status page." @@ -17849,6 +17849,9 @@ msgstr "" msgid "K8s pod health" msgstr "" +msgid "KEY" +msgstr "" + msgid "Keep artifacts from most recent successful jobs" msgstr "" @@ -21776,7 +21779,7 @@ msgstr "" msgid "Only 1 appearances row can exist" msgstr "" -msgid "Only Issue ID or Merge Request ID is required" +msgid "Only Issue ID or merge request ID is required" msgstr "" msgid "Only Project Members" @@ -36284,6 +36287,9 @@ msgstr "" msgid "http://www.example.com" msgstr "" +msgid "https://bamboo.example.com" +msgstr "" + msgid "https://your-bitbucket-server" msgstr "" diff --git a/spec/features/dashboard/milestones_spec.rb b/spec/features/dashboard/milestones_spec.rb index 308432b7a1b..992ed2f2ce6 100644 --- a/spec/features/dashboard/milestones_spec.rb +++ b/spec/features/dashboard/milestones_spec.rb @@ -30,7 +30,7 @@ RSpec.describe 'Dashboard > Milestones' do expect(current_path).to eq dashboard_milestones_path expect(page).to have_content(milestone.title) expect(page).to have_content(group.name) - expect(first('.milestone')).to have_content('Merge Requests') + expect(first('.milestone')).to have_content('Merge requests') end describe 'new milestones dropdown', :js do diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb index 8d1008b98a6..1d9ac5ee1e9 100644 --- a/spec/features/groups/milestone_spec.rb +++ b/spec/features/groups/milestone_spec.rb @@ -133,7 +133,7 @@ RSpec.describe 'Group milestones' do href: project_issues_path(project, milestone_title: 'v1.0') ) expect(page).to have_link( - '0 Merge Requests', + '0 Merge requests', href: project_merge_requests_path(project, milestone_title: 'v1.0') ) expect(page).to have_link( @@ -145,7 +145,7 @@ RSpec.describe 'Group milestones' do href: issues_group_path(group, milestone_title: 'GL-113') ) expect(page).to have_link( - '0 Merge Requests', + '0 Merge requests', href: merge_requests_group_path(group, milestone_title: 'GL-113') ) end @@ -179,7 +179,7 @@ RSpec.describe 'Group milestones' do it 'renders the merge requests tab' do within('.js-milestone-tabs') do - click_link('Merge Requests') + click_link('Merge requests') end within('#tab-merge-requests') do diff --git a/spec/features/milestones/user_views_milestone_spec.rb b/spec/features/milestones/user_views_milestone_spec.rb index 9c19f842427..8674d59afdf 100644 --- a/spec/features/milestones/user_views_milestone_spec.rb +++ b/spec/features/milestones/user_views_milestone_spec.rb @@ -98,7 +98,7 @@ RSpec.describe "User views milestone" do visit(project_milestone_path(project, milestone)) within('.js-milestone-tabs') do - click_link('Merge Requests') + click_link('Merge requests') end wait_for_requests @@ -116,7 +116,7 @@ RSpec.describe "User views milestone" do visit(group_milestone_path(group, group_milestone)) within('.js-milestone-tabs') do - click_link('Merge Requests') + click_link('Merge requests') end expect(page.find('#tab-merge-requests')).to have_text(project.name) diff --git a/spec/features/milestones/user_views_milestones_spec.rb b/spec/features/milestones/user_views_milestones_spec.rb index f8b4b802a60..58439df92ba 100644 --- a/spec/features/milestones/user_views_milestones_spec.rb +++ b/spec/features/milestones/user_views_milestones_spec.rb @@ -18,7 +18,7 @@ RSpec.describe "User views milestones" do expect(page).to have_content(milestone.title) .and have_content(milestone.expires_at) .and have_content("Issues") - .and have_content("Merge Requests") + .and have_content("Merge requests") end context "with issues", :js do @@ -80,7 +80,6 @@ RSpec.describe "User views milestones with no MR" do expect(page).to have_content(milestone.title) .and have_content(milestone.expires_at) .and have_content("Issues") - .and have_no_content("Merge Requests") end it "opens milestone" do diff --git a/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb b/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb index e8aed495ba5..91db375be3a 100644 --- a/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb +++ b/spec/features/projects/services/user_activates_atlassian_bamboo_ci_spec.rb @@ -10,7 +10,7 @@ RSpec.describe 'User activates Atlassian Bamboo CI' do end it 'activates service', :js do - visit_project_integration('Atlassian Bamboo CI') + visit_project_integration('Atlassian Bamboo') fill_in('Bamboo URL', with: 'http://bamboo.example.com') fill_in('Build key', with: 'KEY') fill_in('Username', with: 'user') @@ -18,10 +18,10 @@ RSpec.describe 'User activates Atlassian Bamboo CI' do click_test_then_save_integration(expect_test_to_fail: false) - expect(page).to have_content('Atlassian Bamboo CI settings saved and active.') + expect(page).to have_content('Atlassian Bamboo settings saved and active.') # Password field should not be filled in. - click_link('Atlassian Bamboo CI') + click_link('Atlassian Bamboo') expect(find_field('Enter new password').value).to be_blank expect(page).to have_content('Leave blank to use your current password') diff --git a/spec/frontend/diffs/store/mutations_spec.js b/spec/frontend/diffs/store/mutations_spec.js index f03b37b5a06..eb31724ee45 100644 --- a/spec/frontend/diffs/store/mutations_spec.js +++ b/spec/frontend/diffs/store/mutations_spec.js @@ -7,15 +7,17 @@ import diffFileMockData from '../mock_data/diff_file'; describe('DiffsStoreMutations', () => { describe('SET_BASE_CONFIG', () => { - it('should set endpoint and project path', () => { + it.each` + prop | value + ${'endpoint'} | ${'/diffs/endpoint'} + ${'projectPath'} | ${'/root/project'} + ${'endpointUpdateUser'} | ${'/user/preferences'} + `('should set the $prop property into state', ({ prop, value }) => { const state = {}; - const endpoint = '/diffs/endpoint'; - const projectPath = '/root/project'; - mutations[types.SET_BASE_CONFIG](state, { endpoint, projectPath }); + mutations[types.SET_BASE_CONFIG](state, { [prop]: value }); - expect(state.endpoint).toEqual(endpoint); - expect(state.projectPath).toEqual(projectPath); + expect(state[prop]).toEqual(value); }); }); diff --git a/spec/frontend/ide/stores/getters_spec.js b/spec/frontend/ide/stores/getters_spec.js index 7dc900b0f82..06456cdb12a 100644 --- a/spec/frontend/ide/stores/getters_spec.js +++ b/spec/frontend/ide/stores/getters_spec.js @@ -24,12 +24,22 @@ const TEST_FORK_PATH = '/test/fork/path'; describe('IDE store getters', () => { let localState; let localStore; + let origGon; beforeEach(() => { + origGon = window.gon; + + // Feature flag is defaulted to on in prod + window.gon = { features: { rejectUnsignedCommitsByGitlab: true } }; + localStore = createStore(); localState = localStore.state; }); + afterEach(() => { + window.gon = origGon; + }); + describe('activeFile', () => { it('returns the current active file', () => { localState.openFiles.push(file()); @@ -500,9 +510,25 @@ describe('IDE store getters', () => { }, }, ], + [ + 'when can push code, but cannot push unsigned commits, with reject_unsigned_commits_by_gitlab feature off', + { + input: { + pushCode: true, + rejectUnsignedCommits: true, + features: { rejectUnsignedCommitsByGitlab: false }, + }, + output: { + isAllowed: true, + message: '', + messageShort: '', + }, + }, + ], ])('%s', (testName, { input, output }) => { - const { forkInfo, rejectUnsignedCommits, pushCode } = input; + const { forkInfo, rejectUnsignedCommits, pushCode, features = {} } = input; + Object.assign(window.gon.features, features); localState.links = { forkInfo }; localState.projects[TEST_PROJECT_ID] = { pushRules: { diff --git a/spec/services/pages/migrate_from_legacy_storage_service_spec.rb b/spec/services/pages/migrate_from_legacy_storage_service_spec.rb index b3328d540e7..2a275bab4cc 100644 --- a/spec/services/pages/migrate_from_legacy_storage_service_spec.rb +++ b/spec/services/pages/migrate_from_legacy_storage_service_spec.rb @@ -3,7 +3,9 @@ require 'spec_helper' RSpec.describe Pages::MigrateFromLegacyStorageService do - let(:service) { described_class.new(Rails.logger, migration_threads: 3, batch_size: 10, ignore_invalid_entries: false) } + let(:batch_size) { 10 } + let(:mark_projects_as_not_deployed) { false } + let(:service) { described_class.new(Rails.logger, migration_threads: 3, batch_size: batch_size, ignore_invalid_entries: false, mark_projects_as_not_deployed: mark_projects_as_not_deployed) } it 'does not try to migrate pages if pages are not deployed' do expect(::Pages::MigrateLegacyStorageToDeploymentService).not_to receive(:new) @@ -11,33 +13,35 @@ RSpec.describe Pages::MigrateFromLegacyStorageService do expect(service.execute).to eq(migrated: 0, errored: 0) end - it 'uses multiple threads' do - projects = create_list(:project, 20) - projects.each do |project| - project.mark_pages_as_deployed + context 'when there is work for multiple threads' do + let(:batch_size) { 2 } # override to force usage of multiple threads - FileUtils.mkdir_p File.join(project.pages_path, "public") - File.open(File.join(project.pages_path, "public/index.html"), "w") do |f| - f.write("Hello!") + it 'uses multiple threads' do + projects = create_list(:project, 20) + projects.each do |project| + project.mark_pages_as_deployed + + FileUtils.mkdir_p File.join(project.pages_path, "public") + File.open(File.join(project.pages_path, "public/index.html"), "w") do |f| + f.write("Hello!") + end end + + threads = Concurrent::Set.new + + expect(service).to receive(:migrate_project).exactly(20).times.and_wrap_original do |m, *args| + threads.add(Thread.current) + + # sleep to be 100% certain that once thread can't consume all the queue + # it works without it, but I want to avoid making this test flaky + sleep(0.01) + + m.call(*args) + end + + expect(service.execute).to eq(migrated: 20, errored: 0) + expect(threads.length).to eq(3) end - - service = described_class.new(Rails.logger, migration_threads: 3, batch_size: 2, ignore_invalid_entries: false) - - threads = Concurrent::Set.new - - expect(service).to receive(:migrate_project).exactly(20).times.and_wrap_original do |m, *args| - threads.add(Thread.current) - - # sleep to be 100% certain that once thread can't consume all the queue - # it works without it, but I want to avoid making this test flaky - sleep(0.01) - - m.call(*args) - end - - expect(service.execute).to eq(migrated: 20, errored: 0) - expect(threads.length).to eq(3) end context 'when pages are marked as deployed' do @@ -48,12 +52,24 @@ RSpec.describe Pages::MigrateFromLegacyStorageService do end context 'when pages directory does not exist' do - it 'counts project as migrated' do - expect_next_instance_of(::Pages::MigrateLegacyStorageToDeploymentService, project, ignore_invalid_entries: false) do |service| + context 'when mark_projects_as_not_deployed is set' do + let(:mark_projects_as_not_deployed) { true } + + it 'counts project as migrated' do + expect_next_instance_of(::Pages::MigrateLegacyStorageToDeploymentService, project, ignore_invalid_entries: false, mark_projects_as_not_deployed: true) do |service| + expect(service).to receive(:execute).and_call_original + end + + expect(service.execute).to eq(migrated: 1, errored: 0) + end + end + + it 'counts project as errored' do + expect_next_instance_of(::Pages::MigrateLegacyStorageToDeploymentService, project, ignore_invalid_entries: false, mark_projects_as_not_deployed: false) do |service| expect(service).to receive(:execute).and_call_original end - expect(service.execute).to eq(migrated: 1, errored: 0) + expect(service.execute).to eq(migrated: 0, errored: 1) end end @@ -66,7 +82,7 @@ RSpec.describe Pages::MigrateFromLegacyStorageService do end it 'migrates pages projects without deployments' do - expect_next_instance_of(::Pages::MigrateLegacyStorageToDeploymentService, project, ignore_invalid_entries: false) do |service| + expect_next_instance_of(::Pages::MigrateLegacyStorageToDeploymentService, project, ignore_invalid_entries: false, mark_projects_as_not_deployed: false) do |service| expect(service).to receive(:execute).and_call_original end diff --git a/spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb b/spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb index be9dd69ffd3..25f571a73d1 100644 --- a/spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb +++ b/spec/services/pages/migrate_legacy_storage_to_deployment_service_spec.rb @@ -11,47 +11,47 @@ RSpec.describe Pages::MigrateLegacyStorageToDeploymentService do expect(zip_service).to receive(:execute).and_call_original end - expect(described_class.new(project, ignore_invalid_entries: true).execute[:status]).to eq(:success) + expect(described_class.new(project, ignore_invalid_entries: true).execute[:status]).to eq(:error) end - it 'marks pages as not deployed if public directory is absent' do - project.mark_pages_as_deployed + context 'when mark_projects_as_not_deployed is passed' do + let(:service) { described_class.new(project, mark_projects_as_not_deployed: true) } - expect(project.pages_metadatum.reload.deployed).to eq(true) + it 'marks pages as not deployed if public directory is absent and invalid entries are ignored' do + project.mark_pages_as_deployed + expect(project.pages_metadatum.reload.deployed).to eq(true) - expect(service.execute).to( - eq(status: :success, - message: "Archive not created. Missing public directory in #{project.pages_path} ? Marked project as not deployed") - ) + expect(service.execute).to( + eq(status: :success, + message: "Archive not created. Missing public directory in #{project.pages_path}? Marked project as not deployed") + ) - expect(project.pages_metadatum.reload.deployed).to eq(false) + expect(project.pages_metadatum.reload.deployed).to eq(false) + end + + it 'does not mark pages as not deployed if public directory is absent but pages_deployment exists' do + deployment = create(:pages_deployment, project: project) + project.update_pages_deployment!(deployment) + project.mark_pages_as_deployed + expect(project.pages_metadatum.reload.deployed).to eq(true) + + expect(service.execute).to( + eq(status: :success, + message: "Archive not created. Missing public directory in #{project.pages_path}? Marked project as not deployed") + ) + + expect(project.pages_metadatum.reload.deployed).to eq(true) + end end - it 'does not mark pages as not deployed if public directory is absent but pages_deployment exists' do - deployment = create(:pages_deployment, project: project) - project.update_pages_deployment!(deployment) - project.mark_pages_as_deployed - - expect(project.pages_metadatum.reload.deployed).to eq(true) - - expect(service.execute).to( - eq(status: :success, - message: "Archive not created. Missing public directory in #{project.pages_path} ? Marked project as not deployed") - ) - - expect(project.pages_metadatum.reload.deployed).to eq(true) - end - - it 'does not mark pages as not deployed if public directory is absent but feature is disabled' do - stub_feature_flags(pages_migration_mark_as_not_deployed: false) - + it 'does not mark pages as not deployed if public directory is absent but invalid entries are not ignored' do project.mark_pages_as_deployed expect(project.pages_metadatum.reload.deployed).to eq(true) expect(service.execute).to( eq(status: :error, - message: "Can't create zip archive: Can not find valid public dir in #{project.pages_path}") + message: "Archive not created. Missing public directory in #{project.pages_path}") ) expect(project.pages_metadatum.reload.deployed).to eq(true) diff --git a/spec/services/pages/zip_directory_service_spec.rb b/spec/services/pages/zip_directory_service_spec.rb index a34583413d2..9cce90c6c0d 100644 --- a/spec/services/pages/zip_directory_service_spec.rb +++ b/spec/services/pages/zip_directory_service_spec.rb @@ -33,15 +33,6 @@ RSpec.describe Pages::ZipDirectoryService do expect(archive).to be_nil expect(entries_count).to be_nil end - - it 'returns error if pages_migration_mark_as_not_deployed is disabled' do - stub_feature_flags(pages_migration_mark_as_not_deployed: false) - - expect(status).to eq(:error) - expect(message).to eq("Can not find valid public dir in #{service_directory}") - expect(archive).to be_nil - expect(entries_count).to be_nil - end end context "when work direcotry doesn't exist" do diff --git a/spec/tasks/gitlab/pages_rake_spec.rb b/spec/tasks/gitlab/pages_rake_spec.rb index 08194f4d1c9..1c5a803441d 100644 --- a/spec/tasks/gitlab/pages_rake_spec.rb +++ b/spec/tasks/gitlab/pages_rake_spec.rb @@ -14,7 +14,8 @@ RSpec.describe 'gitlab:pages' do expect_next_instance_of(::Pages::MigrateFromLegacyStorageService, anything, migration_threads: 3, batch_size: 10, - ignore_invalid_entries: false) do |service| + ignore_invalid_entries: false, + mark_projects_as_not_deployed: false) do |service| expect(service).to receive(:execute).and_call_original end @@ -27,7 +28,8 @@ RSpec.describe 'gitlab:pages' do expect_next_instance_of(::Pages::MigrateFromLegacyStorageService, anything, migration_threads: 5, batch_size: 10, - ignore_invalid_entries: false) do |service| + ignore_invalid_entries: false, + mark_projects_as_not_deployed: false) do |service| expect(service).to receive(:execute).and_call_original end @@ -40,7 +42,8 @@ RSpec.describe 'gitlab:pages' do expect_next_instance_of(::Pages::MigrateFromLegacyStorageService, anything, migration_threads: 3, batch_size: 100, - ignore_invalid_entries: false) do |service| + ignore_invalid_entries: false, + mark_projects_as_not_deployed: false) do |service| expect(service).to receive(:execute).and_call_original end @@ -53,7 +56,22 @@ RSpec.describe 'gitlab:pages' do expect_next_instance_of(::Pages::MigrateFromLegacyStorageService, anything, migration_threads: 3, batch_size: 10, - ignore_invalid_entries: true) do |service| + ignore_invalid_entries: true, + mark_projects_as_not_deployed: false) do |service| + expect(service).to receive(:execute).and_call_original + end + + subject + end + + it 'uses PAGES_MIGRATION_MARK_PROJECTS_AS_NOT_DEPLOYED environment variable' do + stub_env('PAGES_MIGRATION_MARK_PROJECTS_AS_NOT_DEPLOYED', 'true') + + expect_next_instance_of(::Pages::MigrateFromLegacyStorageService, anything, + migration_threads: 3, + batch_size: 10, + ignore_invalid_entries: false, + mark_projects_as_not_deployed: true) do |service| expect(service).to receive(:execute).and_call_original end diff --git a/spec/workers/database/batched_background_migration_worker_spec.rb b/spec/workers/database/batched_background_migration_worker_spec.rb index d9e3ec352e2..e71debadcb8 100644 --- a/spec/workers/database/batched_background_migration_worker_spec.rb +++ b/spec/workers/database/batched_background_migration_worker_spec.rb @@ -36,8 +36,10 @@ RSpec.describe Database::BatchedBackgroundMigrationWorker, '#perform', :clean_gi end context 'when active migrations exist' do + let(:job_interval) { 5.minutes } + let(:lease_timeout) { 15.minutes } let(:lease_key) { 'batched_background_migration_worker' } - let(:migration) { build(:batched_background_migration, :active, interval: 2.minutes) } + let(:migration) { build(:batched_background_migration, :active, interval: job_interval) } before do allow(Gitlab::Database::BackgroundMigration::BatchedMigration).to receive(:active_migration) @@ -49,7 +51,7 @@ RSpec.describe Database::BatchedBackgroundMigrationWorker, '#perform', :clean_gi context 'when the reloaded migration is no longer active' do it 'does not run the migration' do - expect_to_obtain_exclusive_lease(lease_key, timeout: 4.minutes) + expect_to_obtain_exclusive_lease(lease_key, timeout: lease_timeout) expect(migration).to receive(:reload) expect(migration).to receive(:active?).and_return(false) @@ -62,7 +64,7 @@ RSpec.describe Database::BatchedBackgroundMigrationWorker, '#perform', :clean_gi context 'when the interval has not elapsed' do it 'does not run the migration' do - expect_to_obtain_exclusive_lease(lease_key, timeout: 4.minutes) + expect_to_obtain_exclusive_lease(lease_key, timeout: lease_timeout) expect(migration).to receive(:interval_elapsed?).and_return(false) @@ -74,7 +76,24 @@ RSpec.describe Database::BatchedBackgroundMigrationWorker, '#perform', :clean_gi context 'when the reloaded migration is still active and the interval has elapsed' do it 'runs the migration' do - expect_to_obtain_exclusive_lease(lease_key, timeout: 4.minutes) + expect_to_obtain_exclusive_lease(lease_key, timeout: lease_timeout) + + expect_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner) do |instance| + expect(instance).to receive(:run_migration_job).with(migration) + end + + expect(worker).to receive(:run_active_migration).and_call_original + + worker.perform + end + end + + context 'when the calculated timeout is less than the minimum allowed' do + let(:minimum_timeout) { described_class::MINIMUM_LEASE_TIMEOUT } + let(:job_interval) { 2.minutes } + + it 'sets the lease timeout to the minimum value' do + expect_to_obtain_exclusive_lease(lease_key, timeout: minimum_timeout) expect_next_instance_of(Gitlab::Database::BackgroundMigration::BatchedMigrationRunner) do |instance| expect(instance).to receive(:run_migration_job).with(migration) @@ -87,7 +106,7 @@ RSpec.describe Database::BatchedBackgroundMigrationWorker, '#perform', :clean_gi end it 'always cleans up the exclusive lease' do - lease = stub_exclusive_lease_taken(lease_key, timeout: 4.minutes) + lease = stub_exclusive_lease_taken(lease_key, timeout: lease_timeout) expect(lease).to receive(:try_obtain).and_return(true)