diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index d31a0edac58..a4a932c7dd0 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -1289,7 +1289,7 @@
rules:
- if: '$DAST_DISABLED || $GITLAB_FEATURES !~ /\bdast\b/'
when: never
- - <<: *if-default-branch-schedule-nightly
+ - <<: *if-dot-com-ee-nightly-schedule
allow_failure: true
.reports:rules:package_hunter-yarn:
diff --git a/.gitlab/issue_templates/Geo Replicate a new Git repository type.md b/.gitlab/issue_templates/Geo Replicate a new Git repository type.md
index 73233644d37..476ee14a632 100644
--- a/.gitlab/issue_templates/Geo Replicate a new Git repository type.md
+++ b/.gitlab/issue_templates/Geo Replicate a new Git repository type.md
@@ -826,5 +826,7 @@ Individual Cool Widget replication and verification data should now be available
feature_flag: :geo_cool_widget_replication # REMOVE THIS LINE
```
+- [ ] Run `bundle exec rake gitlab:graphql:compile_docs` after the step above to regenerate the GraphQL docs.
+
- [ ] Add a row for Cool Widgets to the `Data types` table in [Geo data types support](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/administration/geo/replication/datatypes.md#data-types)
- [ ] Add a row for Cool Widgets to the `Limitations on replication/verification` table in [Geo data types support](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/administration/geo/replication/datatypes.md#limitations-on-replicationverification). If the row already exists, then update it to show that Replication and Verification is released in the current version.
diff --git a/.gitlab/issue_templates/Geo Replicate a new blob type.md b/.gitlab/issue_templates/Geo Replicate a new blob type.md
index cc5a606d68b..aef983f6495 100644
--- a/.gitlab/issue_templates/Geo Replicate a new blob type.md
+++ b/.gitlab/issue_templates/Geo Replicate a new blob type.md
@@ -794,5 +794,7 @@ Individual Cool Widget replication and verification data should now be available
feature_flag: :geo_cool_widget_replication # REMOVE THIS LINE
```
+- [ ] Run `bundle exec rake gitlab:graphql:compile_docs` after the step above to regenerate the GraphQL docs.
+
- [ ] Add a row for Cool Widgets to the `Data types` table in [Geo data types support](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/administration/geo/replication/datatypes.md#data-types)
- [ ] Add a row for Cool Widgets to the `Limitations on replication/verification` table in [Geo data types support](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/administration/geo/replication/datatypes.md#limitations-on-replicationverification). If the row already exists, then update it to show that Replication and Verification is released in the current version.
diff --git a/GITLAB_KAS_VERSION b/GITLAB_KAS_VERSION
index 6dfe8b1298c..f84a4218283 100644
--- a/GITLAB_KAS_VERSION
+++ b/GITLAB_KAS_VERSION
@@ -1 +1 @@
-14.3.1
+14.3.2
diff --git a/app/assets/javascripts/issues_list/components/issuable.vue b/app/assets/javascripts/issues_list/components/issuable.vue
index 60b01a6d37f..6dc7460b037 100644
--- a/app/assets/javascripts/issues_list/components/issuable.vue
+++ b/app/assets/javascripts/issues_list/components/issuable.vue
@@ -315,7 +315,7 @@ export default {
{{ referencePath }}
diff --git a/app/controllers/admin/applications_controller.rb b/app/controllers/admin/applications_controller.rb
index 449aa90b0e6..ce7d64336c8 100644
--- a/app/controllers/admin/applications_controller.rb
+++ b/app/controllers/admin/applications_controller.rb
@@ -18,7 +18,10 @@ class Admin::ApplicationsController < Admin::ApplicationController
end
def new
- @application = Doorkeeper::Application.new
+ # Default access tokens to expire. This preserves backward compatibility
+ # with existing applications. This will be removed in 15.0.
+ # Removal issue: https://gitlab.com/gitlab-org/gitlab/-/issues/340848
+ @application = Doorkeeper::Application.new(expire_access_tokens: true)
end
def edit
@@ -55,10 +58,13 @@ class Admin::ApplicationsController < Admin::ApplicationController
@application = ApplicationsFinder.new(id: params[:id]).execute
end
- # Only allow a trusted parameter "white list" through.
+ def permitted_params
+ super << :trusted
+ end
+
def application_params
- params
- .require(:doorkeeper_application)
- .permit(:name, :redirect_uri, :trusted, :scopes, :confidential)
+ super.tap do |params|
+ params[:owner] = nil
+ end
end
end
diff --git a/app/controllers/admin/runner_projects_controller.rb b/app/controllers/admin/runner_projects_controller.rb
index 3b408de5f01..fdf681de9ef 100644
--- a/app/controllers/admin/runner_projects_controller.rb
+++ b/app/controllers/admin/runner_projects_controller.rb
@@ -9,7 +9,7 @@ class Admin::RunnerProjectsController < Admin::ApplicationController
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
if @runner.assign_to(@project, current_user)
- redirect_to admin_runner_path(@runner)
+ redirect_to admin_runner_path(@runner), notice: s_('Runners|Runner assigned to project.')
else
redirect_to admin_runner_path(@runner), alert: 'Failed adding runner to project'
end
@@ -20,7 +20,7 @@ class Admin::RunnerProjectsController < Admin::ApplicationController
runner = rp.runner
rp.destroy
- redirect_to admin_runner_path(runner), status: :found
+ redirect_to admin_runner_path(runner), status: :found, notice: s_('Runners|Runner unassigned from project.')
end
private
diff --git a/app/controllers/concerns/oauth_applications.rb b/app/controllers/concerns/oauth_applications.rb
index d97e22df472..d2c746db12d 100644
--- a/app/controllers/concerns/oauth_applications.rb
+++ b/app/controllers/concerns/oauth_applications.rb
@@ -18,4 +18,14 @@ module OauthApplications
def load_scopes
@scopes ||= Doorkeeper.configuration.scopes
end
+
+ def permitted_params
+ %i{name redirect_uri scopes confidential expire_access_tokens}
+ end
+
+ def application_params
+ params
+ .require(:doorkeeper_application)
+ .permit(*permitted_params)
+ end
end
diff --git a/app/controllers/groups/settings/applications_controller.rb b/app/controllers/groups/settings/applications_controller.rb
index cefb5425867..f05a96d7810 100644
--- a/app/controllers/groups/settings/applications_controller.rb
+++ b/app/controllers/groups/settings/applications_controller.rb
@@ -54,8 +54,10 @@ module Groups
# https://gitlab.com/gitlab-org/gitlab/-/issues/324187
@applications = @group.oauth_applications.limit(100)
- # Don't overwrite a value possibly set by `create`
- @application ||= Doorkeeper::Application.new
+ # Default access tokens to expire. This preserves backward compatibility
+ # with existing applications. This will be removed in 15.0.
+ # Removal issue: https://gitlab.com/gitlab-org/gitlab/-/issues/340848
+ @application ||= Doorkeeper::Application.new(expire_access_tokens: true)
end
def set_application
@@ -63,12 +65,9 @@ module Groups
end
def application_params
- params
- .require(:doorkeeper_application)
- .permit(:name, :redirect_uri, :scopes, :confidential)
- .tap do |params|
- params[:owner] = @group
- end
+ super.tap do |params|
+ params[:owner] = @group
+ end
end
end
end
diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb
index 8158db282fb..81f188256ba 100644
--- a/app/controllers/oauth/applications_controller.rb
+++ b/app/controllers/oauth/applications_controller.rb
@@ -25,7 +25,7 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
end
def create
- @application = Applications::CreateService.new(current_user, create_application_params).execute(request)
+ @application = Applications::CreateService.new(current_user, application_params).execute(request)
if @application.persisted?
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
@@ -51,8 +51,10 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
@authorized_anonymous_tokens = @authorized_tokens.reject(&:application)
@authorized_apps = @authorized_tokens.map(&:application).uniq.reject(&:nil?)
- # Don't overwrite a value possibly set by `create`
- @application ||= Doorkeeper::Application.new
+ # Default access tokens to expire. This preserves backward compatibility
+ # with existing applications. This will be removed in 15.0.
+ # Removal issue: https://gitlab.com/gitlab-org/gitlab/-/issues/340848
+ @application ||= Doorkeeper::Application.new(expire_access_tokens: true)
end
# Override Doorkeeper to scope to the current user
@@ -64,8 +66,8 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
render "errors/not_found", layout: "errors", status: :not_found
end
- def create_application_params
- application_params.tap do |params|
+ def application_params
+ super.tap do |params|
params[:owner] = current_user
end
end
diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb
index 5da81045e02..39db7618db0 100644
--- a/app/controllers/projects/runner_projects_controller.rb
+++ b/app/controllers/projects/runner_projects_controller.rb
@@ -15,7 +15,7 @@ class Projects::RunnerProjectsController < Projects::ApplicationController
path = project_runners_path(project)
if @runner.assign_to(project, current_user)
- redirect_to path
+ redirect_to path, notice: s_('Runners|Runner assigned to project.')
else
assign_to_messages = @runner.errors.messages[:assign_to]
alert = assign_to_messages&.join(',') || 'Failed adding runner to project'
@@ -28,6 +28,6 @@ class Projects::RunnerProjectsController < Projects::ApplicationController
runner_project = project.runner_projects.find(params[:id])
runner_project.destroy
- redirect_to project_runners_path(project), status: :found
+ redirect_to project_runners_path(project), status: :found, notice: s_('Runners|Runner unassigned from project.')
end
end
diff --git a/app/models/concerns/enums/ci/commit_status.rb b/app/models/concerns/enums/ci/commit_status.rb
index 16dec5fb081..7f46e44697e 100644
--- a/app/models/concerns/enums/ci/commit_status.rb
+++ b/app/models/concerns/enums/ci/commit_status.rb
@@ -26,6 +26,7 @@ module Enums
pipeline_loop_detected: 17,
no_matching_runner: 18, # not used anymore, but cannot be deleted because of old data
trace_size_exceeded: 19,
+ builds_disabled: 20,
insufficient_bridge_permissions: 1_001,
downstream_bridge_project_not_found: 1_002,
invalid_bridge_trigger: 1_003,
diff --git a/app/presenters/commit_status_presenter.rb b/app/presenters/commit_status_presenter.rb
index 5f5bbf13f92..3c39470b730 100644
--- a/app/presenters/commit_status_presenter.rb
+++ b/app/presenters/commit_status_presenter.rb
@@ -27,7 +27,8 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
user_blocked: 'The user who created this job is blocked',
ci_quota_exceeded: 'No more CI minutes available',
no_matching_runner: 'No matching runner available',
- trace_size_exceeded: 'The job log size limit was reached'
+ trace_size_exceeded: 'The job log size limit was reached',
+ builds_disabled: 'The CI/CD is disabled for this project'
}.freeze
private_constant :CALLOUT_FAILURE_MESSAGES
diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index f9b51c63165..c46ddd22558 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -271,6 +271,15 @@ module Ci
missing_dependency_failure: -> (build, _) { !build.has_valid_build_dependencies? },
runner_unsupported: -> (build, params) { !build.supported_runner?(params.dig(:info, :features)) },
archived_failure: -> (build, _) { build.archived? }
+ }.merge(builds_enabled_checks)
+ end
+
+ def builds_enabled_checks
+ return {} unless ::Feature.enabled?(:ci_queueing_builds_enabled_checks, runner, default_enabled: :yaml)
+
+ {
+ project_deleted: -> (build, _) { build.project.pending_delete? },
+ builds_disabled: -> (build, _) { !build.project.builds_enabled? }
}
end
end
diff --git a/app/services/packages/composer/version_parser_service.rb b/app/services/packages/composer/version_parser_service.rb
index 36275d1b680..516c306d2a5 100644
--- a/app/services/packages/composer/version_parser_service.rb
+++ b/app/services/packages/composer/version_parser_service.rb
@@ -12,18 +12,19 @@ module Packages
if @tag_name.present?
@tag_name.delete_prefix('v')
elsif @branch_name.present?
- branch_sufix_or_prefix(@branch_name.match(Gitlab::Regex.composer_package_version_regex))
+ branch_suffix_or_prefix(@branch_name.match(Gitlab::Regex.composer_package_version_regex))
end
end
private
- def branch_sufix_or_prefix(match)
+ def branch_suffix_or_prefix(match)
if match
- if match.captures[1] == '.x'
- match.captures[0] + '-dev'
+ captures = match.captures.reject(&:blank?)
+ if captures[-1] == '.x'
+ captures[0] + '-dev'
else
- match.captures[0] + '.x-dev'
+ captures[0] + '.x-dev'
end
else
"dev-#{@branch_name}"
diff --git a/app/views/admin/applications/_form.html.haml b/app/views/admin/applications/_form.html.haml
index 74eda21d5bd..a1990ad5750 100644
--- a/app/views/admin/applications/_form.html.haml
+++ b/app/views/admin/applications/_form.html.haml
@@ -33,6 +33,14 @@
%span.form-text.text-muted
= _('The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential.')
+ = content_tag :div, class: 'form-group row' do
+ .col-sm-2.col-form-label.pt-0
+ = f.label :expire_access_tokens
+ .col-sm-10
+ = f.check_box :expire_access_tokens
+ %span.form-text.text-muted
+ = _('Access tokens expire after 2 hours. A refresh token may be used at any time to generate a new access token. Non-expiring access tokens are deprecated. Clear this setting to enable backward compatibility.')
+
.form-group.row
.col-sm-2.col-form-label.pt-0
= f.label :scopes
diff --git a/app/views/shared/doorkeeper/applications/_form.html.haml b/app/views/shared/doorkeeper/applications/_form.html.haml
index 91a32b55542..180c658dbdc 100644
--- a/app/views/shared/doorkeeper/applications/_form.html.haml
+++ b/app/views/shared/doorkeeper/applications/_form.html.haml
@@ -18,6 +18,12 @@
%span.form-text.text-muted
= _('The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential.')
+ .form-group.form-check
+ = f.check_box :expire_access_tokens, class: 'form-check-input'
+ = f.label :expire_access_tokens, class: 'label-bold form-check-label'
+ %span.form-text.text-muted
+ = _('Access tokens expire after 2 hours. A refresh token may be used at any time to generate a new access token. Non-expiring access tokens are deprecated. Clear this setting to enable backward compatibility.')
+
.form-group
= f.label :scopes, class: 'label-bold'
= render 'shared/tokens/scopes_form', prefix: 'doorkeeper_application', token: @application, scopes: @scopes
diff --git a/config/feature_flags/development/ci_queueing_builds_enabled_checks.yml b/config/feature_flags/development/ci_queueing_builds_enabled_checks.yml
new file mode 100644
index 00000000000..effaf78cef2
--- /dev/null
+++ b/config/feature_flags/development/ci_queueing_builds_enabled_checks.yml
@@ -0,0 +1,8 @@
+---
+name: ci_queueing_builds_enabled_checks
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70581
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341131
+milestone: '14.4'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb
index 4533779339a..477d419576a 100644
--- a/config/initializers/doorkeeper.rb
+++ b/config/initializers/doorkeeper.rb
@@ -38,8 +38,11 @@ Doorkeeper.configure do
# authorization_code_expires_in 10.minutes
# Access token expiration time (default 2 hours).
- # If you want to disable expiration, set this to nil.
- access_token_expires_in nil
+ # Until 15.0, applications can opt-out of expiring tokens.
+ # Removal issue: https://gitlab.com/gitlab-org/gitlab/-/issues/340848
+ custom_access_token_expires_in do |context|
+ context.client&.expire_access_tokens ? 2.hours : Float::INFINITY
+ end
# Reuse access token for the same resource owner within an application (disabled by default)
# Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383
diff --git a/config/metrics/counts_28d/20210916080405_promoted_issues.yml b/config/metrics/counts_28d/20210916080405_promoted_issues.yml
new file mode 100644
index 00000000000..106c3a61289
--- /dev/null
+++ b/config/metrics/counts_28d/20210916080405_promoted_issues.yml
@@ -0,0 +1,23 @@
+---
+key_path: counts_monthly.promoted_issues
+name: count_promoted_issues
+description: Count of issues promoted to epics
+product_section: growth
+product_stage: growth
+product_group: group::product intelligence
+product_category: collection
+value_type: number
+status: active
+milestone: "14.3"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70485
+time_frame: 28d
+data_source: database
+data_category: optional
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/db/migrate/20210825193448_add_iteration_cadence_id_to_issue_boards.rb b/db/migrate/20210825193448_add_iteration_cadence_id_to_issue_boards.rb
new file mode 100644
index 00000000000..cbe7d08894e
--- /dev/null
+++ b/db/migrate/20210825193448_add_iteration_cadence_id_to_issue_boards.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddIterationCadenceIdToIssueBoards < Gitlab::Database::Migration[1.0]
+ enable_lock_retries!
+
+ def change
+ add_column :boards, :iteration_cadence_id, :bigint
+ end
+end
diff --git a/db/migrate/20210902184334_add_expire_access_tokens_to_doorkeeper_application.rb b/db/migrate/20210902184334_add_expire_access_tokens_to_doorkeeper_application.rb
new file mode 100644
index 00000000000..4638637331d
--- /dev/null
+++ b/db/migrate/20210902184334_add_expire_access_tokens_to_doorkeeper_application.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddExpireAccessTokensToDoorkeeperApplication < Gitlab::Database::Migration[1.0]
+ def change
+ add_column :oauth_applications, :expire_access_tokens, :boolean, default: false, null: false
+ end
+end
diff --git a/db/post_migrate/20210825193548_add_fk_to_iteration_cadence_id_on_boards.rb b/db/post_migrate/20210825193548_add_fk_to_iteration_cadence_id_on_boards.rb
new file mode 100644
index 00000000000..eb879d9bc7a
--- /dev/null
+++ b/db/post_migrate/20210825193548_add_fk_to_iteration_cadence_id_on_boards.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddFkToIterationCadenceIdOnBoards < Gitlab::Database::Migration[1.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_boards_on_iteration_cadence_id'
+
+ def up
+ add_concurrent_index :boards, :iteration_cadence_id, name: INDEX_NAME
+ add_concurrent_foreign_key :boards, :iterations_cadences, column: :iteration_cadence_id
+ end
+
+ def down
+ with_lock_retries do
+ remove_foreign_key_if_exists :boards, column: :iteration_cadence_id
+ end
+ remove_concurrent_index_by_name :boards, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20210825193652_backfill_cadence_id_for_boards_scoped_to_iteration.rb b/db/post_migrate/20210825193652_backfill_cadence_id_for_boards_scoped_to_iteration.rb
new file mode 100644
index 00000000000..f350fbe3d12
--- /dev/null
+++ b/db/post_migrate/20210825193652_backfill_cadence_id_for_boards_scoped_to_iteration.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+class BackfillCadenceIdForBoardsScopedToIteration < Gitlab::Database::Migration[1.0]
+ disable_ddl_transaction!
+
+ BATCH_SIZE = 1000
+ DELAY = 2.minutes.to_i
+ MIGRATION = 'BackfillIterationCadenceIdForBoards'
+
+ class MigrationBoard < ApplicationRecord
+ include EachBatch
+
+ self.table_name = 'boards'
+ end
+
+ def up
+ schedule_backfill_group_boards
+ schedule_backfill_project_boards
+ end
+
+ def down
+ MigrationBoard.where.not(iteration_cadence_id: nil).each_batch(of: BATCH_SIZE) do |batch, index|
+ range = batch.pluck(Arel.sql('MIN(id)'), Arel.sql('MAX(id)')).first
+ delay = index * DELAY
+
+ migrate_in(delay, MIGRATION, ['none', 'down', *range])
+ end
+ end
+
+ private
+
+ def schedule_backfill_project_boards
+ MigrationBoard.where(iteration_id: -4).where.not(project_id: nil).where(iteration_cadence_id: nil).each_batch(of: BATCH_SIZE) do |batch, index|
+ range = batch.pluck(Arel.sql('MIN(id)'), Arel.sql('MAX(id)')).first
+ delay = index * DELAY
+
+ migrate_in(delay, MIGRATION, ['project', 'up', *range])
+ end
+ end
+
+ def schedule_backfill_group_boards
+ MigrationBoard.where(iteration_id: -4).where.not(group_id: nil).where(iteration_cadence_id: nil).each_batch(of: BATCH_SIZE) do |batch, index|
+ range = batch.pluck(Arel.sql('MIN(id)'), Arel.sql('MAX(id)')).first
+ delay = index * DELAY
+
+ migrate_in(delay, MIGRATION, ['group', 'up', *range])
+ end
+ end
+end
diff --git a/db/schema_migrations/20210825193448 b/db/schema_migrations/20210825193448
new file mode 100644
index 00000000000..b62b45b61ae
--- /dev/null
+++ b/db/schema_migrations/20210825193448
@@ -0,0 +1 @@
+d9c7cc7721b28cbd442bf40255ecfbd20d0abf4cd31631c150ebdc05c76062be
\ No newline at end of file
diff --git a/db/schema_migrations/20210825193548 b/db/schema_migrations/20210825193548
new file mode 100644
index 00000000000..0255e6719ef
--- /dev/null
+++ b/db/schema_migrations/20210825193548
@@ -0,0 +1 @@
+b97b77aef61db2e51106ac090f5511a67fa85be8f3741f618fe03c8c03ecd88c
\ No newline at end of file
diff --git a/db/schema_migrations/20210825193652 b/db/schema_migrations/20210825193652
new file mode 100644
index 00000000000..0ecca0962dc
--- /dev/null
+++ b/db/schema_migrations/20210825193652
@@ -0,0 +1 @@
+fd7aef11635bc4c5d6b9346dbed90f6c114da7b7a33744083e8610f3850e4736
\ No newline at end of file
diff --git a/db/schema_migrations/20210902184334 b/db/schema_migrations/20210902184334
new file mode 100644
index 00000000000..f35699b357e
--- /dev/null
+++ b/db/schema_migrations/20210902184334
@@ -0,0 +1 @@
+508b8d4608d28b2a12cf429262c3dd336e130013a41e51ff6c95027ece1540e5
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index de719052be4..d7e00112d73 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -10894,7 +10894,8 @@ CREATE TABLE boards (
weight integer,
hide_backlog_list boolean DEFAULT false NOT NULL,
hide_closed_list boolean DEFAULT false NOT NULL,
- iteration_id bigint
+ iteration_id bigint,
+ iteration_cadence_id bigint
);
CREATE TABLE boards_epic_board_labels (
@@ -16385,7 +16386,8 @@ CREATE TABLE oauth_applications (
owner_id integer,
owner_type character varying,
trusted boolean DEFAULT false NOT NULL,
- confidential boolean DEFAULT true NOT NULL
+ confidential boolean DEFAULT true NOT NULL,
+ expire_access_tokens boolean DEFAULT false NOT NULL
);
CREATE SEQUENCE oauth_applications_id_seq
@@ -24405,6 +24407,8 @@ CREATE INDEX index_boards_epic_user_preferences_on_user_id ON boards_epic_user_p
CREATE INDEX index_boards_on_group_id ON boards USING btree (group_id);
+CREATE INDEX index_boards_on_iteration_cadence_id ON boards USING btree (iteration_cadence_id);
+
CREATE INDEX index_boards_on_iteration_id ON boards USING btree (iteration_id);
CREATE INDEX index_boards_on_milestone_id ON boards USING btree (milestone_id);
@@ -27911,6 +27915,9 @@ ALTER TABLE ONLY alert_management_alerts
ALTER TABLE ONLY identities
ADD CONSTRAINT fk_aade90f0fc FOREIGN KEY (saml_provider_id) REFERENCES saml_providers(id) ON DELETE CASCADE;
+ALTER TABLE ONLY boards
+ ADD CONSTRAINT fk_ab0a250ff6 FOREIGN KEY (iteration_cadence_id) REFERENCES iterations_cadences(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY dep_ci_build_trace_sections
ADD CONSTRAINT fk_ab7c104e26 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index af5fb66a2e6..b5722933bea 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -1075,6 +1075,7 @@ Input type: `CreateBoardInput`
| `groupPath` | [`ID`](#id) | Full path of the group with which the resource is associated. |
| `hideBacklogList` | [`Boolean`](#boolean) | Whether or not backlog list is hidden. |
| `hideClosedList` | [`Boolean`](#boolean) | Whether or not closed list is hidden. |
+| `iterationCadenceId` | [`IterationsCadenceID`](#iterationscadenceid) | ID of iteration cadence to be assigned to the board. |
| `iterationId` | [`IterationID`](#iterationid) | ID of iteration to be assigned to the board. |
| `labelIds` | [`[LabelID!]`](#labelid) | IDs of labels to be added to the board. |
| `labels` | [`[String!]`](#string) | Labels of the issue. |
@@ -4122,6 +4123,7 @@ Input type: `UpdateBoardInput`
| `hideBacklogList` | [`Boolean`](#boolean) | Whether or not backlog list is hidden. |
| `hideClosedList` | [`Boolean`](#boolean) | Whether or not closed list is hidden. |
| `id` | [`BoardID!`](#boardid) | Board global ID. |
+| `iterationCadenceId` | [`IterationsCadenceID`](#iterationscadenceid) | ID of iteration cadence to be assigned to the board. |
| `iterationId` | [`IterationID`](#iterationid) | ID of iteration to be assigned to the board. |
| `labelIds` | [`[LabelID!]`](#labelid) | IDs of labels to be added to the board. |
| `labels` | [`[String!]`](#string) | Labels of the issue. |
@@ -7897,6 +7899,7 @@ Represents a project or group issue board.
| `hideClosedList` | [`Boolean`](#boolean) | Whether or not closed list is hidden. |
| `id` | [`ID!`](#id) | ID (global ID) of the board. |
| `iteration` | [`Iteration`](#iteration) | Board iteration. |
+| `iterationCadence` | [`IterationCadence`](#iterationcadence) | Board iteration cadence. |
| `labels` | [`LabelConnection`](#labelconnection) | Labels of the board. (see [Connections](#connections)) |
| `milestone` | [`Milestone`](#milestone) | Board milestone. |
| `name` | [`String`](#string) | Name of the board. |
diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md
index ff1ea3a2bca..25561764bd6 100644
--- a/doc/development/contributing/merge_request_workflow.md
+++ b/doc/development/contributing/merge_request_workflow.md
@@ -223,7 +223,7 @@ the contribution acceptance criteria below:
## Definition of done
-If you contribute to GitLab please know that changes involve more than just
+If you contribute to GitLab, please know that changes involve more than just
code. We use the following [definition of done](https://www.agilealliance.org/glossary/definition-of-done).
To reach the definition of done, the merge request must create no regressions and meet all these criteria:
@@ -231,7 +231,7 @@ To reach the definition of done, the merge request must create no regressions an
- Verified as working for self-managed instances.
If a regression occurs, we prefer you revert the change. We break the definition of done into two phases: [MR Merge](#mr-merge) and [Production use](#production-use).
-Your contribution is not *done* until you have made sure it meets all of these
+Your contribution is *incomplete* until you have made sure it meets all of these
requirements.
### MR Merge
@@ -246,18 +246,24 @@ requirements.
1. [Secure coding guidelines](https://gitlab.com/gitlab-com/gl-security/security-guidelines) have been followed.
1. [Documented](../documentation/index.md) in the `/doc` directory.
1. [Changelog entry added](../changelog.md), if necessary.
-1. Reviewed by relevant reviewers and all concerns are addressed for Availability, Regressions, Security. Documentation reviews should take place as soon as possible, but they should not block a merge request.
+1. Reviewed by relevant reviewers, and all concerns are addressed for Availability, Regressions, and Security. Documentation reviews should take place as soon as possible, but they should not block a merge request.
+1. The [MR acceptance checklist](../code_review.md#acceptance-checklist) has been checked as confirmed in the MR.
1. Create an issue in the [infrastructure issue tracker](https://gitlab.com/gitlab-com/gl-infra/infrastructure/-/issues) to inform the Infrastructure department when your contribution is changing default settings or introduces a new setting, if relevant.
1. [Black-box tests/end-to-end tests](../testing_guide/testing_levels.md#black-box-tests-at-the-system-level-aka-end-to-end-tests)
added if required. Please contact [the quality team](https://about.gitlab.com/handbook/engineering/quality/#teams)
with any questions.
+1. The change is tested in a review app where possible and if appropriate.
1. The new feature does not degrade the user experience of the product.
-1. An agreed upon rollout plan.
+1. The change is evaluated to [limit the impact of far-reaching work](https://about.gitlab.com/handbook/engineering/development/#reducing-the-impact-of-far-reaching-work).
+1. An agreed-upon rollout plan.
1. Merged by a project maintainer.
### Production use
+1. Confirmed to be working in staging before implementing the change in production, where possible.
1. Confirmed to be working in the production with no new [Sentry](https://about.gitlab.com/handbook/engineering/#sentry) errors after the contribution is deployed.
+1. Confirmed that the rollout plan has been completed.
+1. If there is a performance risk in the change, I have analyzed the performance of the system before and after the change.
1. *If the merge request uses feature flags, per-project or per-group enablement, and a staged rollout:*
- Confirmed to be working on GitLab projects.
- Confirmed to be working at each stage for all projects added.
diff --git a/lib/gitlab/background_migration/backfill_iteration_cadence_id_for_boards.rb b/lib/gitlab/background_migration/backfill_iteration_cadence_id_for_boards.rb
new file mode 100644
index 00000000000..67f4690868e
--- /dev/null
+++ b/lib/gitlab/background_migration/backfill_iteration_cadence_id_for_boards.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # rubocop: disable Style/Documentation
+ class BackfillIterationCadenceIdForBoards
+ def perform(*args)
+ end
+ end
+ end
+end
+
+Gitlab::BackgroundMigration::BackfillIterationCadenceIdForBoards.prepend_mod_with('Gitlab::BackgroundMigration::BackfillIterationCadenceIdForBoards')
diff --git a/lib/gitlab/ci/status/build/failed.rb b/lib/gitlab/ci/status/build/failed.rb
index dbbb9a01dab..ee210e51232 100644
--- a/lib/gitlab/ci/status/build/failed.rb
+++ b/lib/gitlab/ci/status/build/failed.rb
@@ -32,7 +32,8 @@ module Gitlab
user_blocked: 'pipeline user was blocked',
ci_quota_exceeded: 'no more CI minutes available',
no_matching_runner: 'no matching runner available',
- trace_size_exceeded: 'log size limit exceeded'
+ trace_size_exceeded: 'log size limit exceeded',
+ builds_disabled: 'project builds are disabled'
}.freeze
private_constant :REASONS
diff --git a/lib/gitlab/database/migration_helpers/v2.rb b/lib/gitlab/database/migration_helpers/v2.rb
index cabc18faddd..0e7f6075196 100644
--- a/lib/gitlab/database/migration_helpers/v2.rb
+++ b/lib/gitlab/database/migration_helpers/v2.rb
@@ -70,7 +70,9 @@ module Gitlab
# If the lock was not acquired within the retry period, a last attempt is made without using +lock_timeout+.
#
# In order to retry the block, the method wraps the block into a transaction.
- # Note it cannot be used inside an already open transaction and will raise an error in that case.
+ #
+ # When called inside an open transaction it will execute the block directly if lock retries are enabled
+ # with `enable_lock_retries!` at migration level, otherwise it will raise an error.
#
# ==== Examples
# # Invoking without parameters
@@ -101,14 +103,19 @@ module Gitlab
# * +env+ - [Hash] custom environment hash, see the example with `DISABLE_LOCK_RETRIES`
def with_lock_retries(*args, **kwargs, &block)
if transaction_open?
- raise <<~EOF
+ if enable_lock_retries?
+ Gitlab::AppLogger.warn 'Lock retries already enabled, executing the block directly'
+ yield
+ else
+ raise <<~EOF
#{__callee__} can not be run inside an already open transaction
Use migration-level lock retries instead, see https://docs.gitlab.com/ee/development/migration_style_guide.html#retry-mechanism-when-acquiring-database-locks
- EOF
+ EOF
+ end
+ else
+ super(*args, **kwargs.merge(allow_savepoints: false), &block)
end
-
- super(*args, **kwargs.merge(allow_savepoints: false), &block)
end
# Renames a column without requiring downtime.
diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml
index 95ae6c10e72..fe0974d27a6 100644
--- a/lib/gitlab/import_export/project/import_export.yml
+++ b/lib/gitlab/import_export/project/import_export.yml
@@ -400,6 +400,7 @@ excluded_attributes:
boards:
- :milestone_id
- :iteration_id
+ - :iteration_cadence_id
lists:
- :board_id
- :label_id
diff --git a/lib/gitlab/rack_attack.rb b/lib/gitlab/rack_attack.rb
index 64c2faf7d50..3f4c0fa45aa 100644
--- a/lib/gitlab/rack_attack.rb
+++ b/lib/gitlab/rack_attack.rb
@@ -82,9 +82,21 @@ module Gitlab
end
def self.configure_throttles(rack_attack)
- throttle_or_track(rack_attack, 'throttle_unauthenticated_api', Gitlab::Throttle.unauthenticated_api_options) do |req|
- if req.throttle_unauthenticated_api?
- req.ip
+ # Each of these settings follows the same pattern of specifying separate
+ # authenticated and unauthenticated rates via settings
+ Gitlab::Throttle::REGULAR_THROTTLES.each do |throttle|
+ unauthenticated_options = Gitlab::Throttle.options(throttle, authenticated: false)
+ throttle_or_track(rack_attack, "throttle_unauthenticated_#{throttle}", unauthenticated_options) do |req|
+ if req.throttle?(throttle, authenticated: false)
+ req.ip
+ end
+ end
+
+ authenticated_options = Gitlab::Throttle.options(throttle, authenticated: true)
+ throttle_or_track(rack_attack, "throttle_authenticated_#{throttle}", authenticated_options) do |req|
+ if req.throttle?(throttle, authenticated: true)
+ req.throttled_user_id([:api])
+ end
end
end
@@ -94,12 +106,6 @@ module Gitlab
end
end
- throttle_or_track(rack_attack, 'throttle_authenticated_api', Gitlab::Throttle.authenticated_api_options) do |req|
- if req.throttle_authenticated_api?
- req.throttled_user_id([:api])
- end
- end
-
# Product analytics feature is in experimental stage.
# At this point we want to limit amount of events registered
# per application (aid stands for application id).
@@ -133,36 +139,12 @@ module Gitlab
end
end
- throttle_or_track(rack_attack, 'throttle_unauthenticated_packages_api', Gitlab::Throttle.unauthenticated_packages_api_options) do |req|
- if req.throttle_unauthenticated_packages_api?
- req.ip
- end
- end
-
- throttle_or_track(rack_attack, 'throttle_authenticated_packages_api', Gitlab::Throttle.authenticated_packages_api_options) do |req|
- if req.throttle_authenticated_packages_api?
- req.throttled_user_id([:api])
- end
- end
-
throttle_or_track(rack_attack, 'throttle_authenticated_git_lfs', Gitlab::Throttle.throttle_authenticated_git_lfs_options) do |req|
if req.throttle_authenticated_git_lfs?
req.throttled_user_id([:api])
end
end
- throttle_or_track(rack_attack, 'throttle_unauthenticated_files_api', Gitlab::Throttle.unauthenticated_files_api_options) do |req|
- if req.throttle_unauthenticated_files_api?
- req.ip
- end
- end
-
- throttle_or_track(rack_attack, 'throttle_authenticated_files_api', Gitlab::Throttle.authenticated_files_api_options) do |req|
- if req.throttle_authenticated_files_api?
- req.throttled_user_id([:api])
- end
- end
-
rack_attack.safelist('throttle_bypass_header') do |req|
Gitlab::Throttle.bypass_header.present? &&
req.get_header(Gitlab::Throttle.bypass_header) == '1'
diff --git a/lib/gitlab/rack_attack/request.rb b/lib/gitlab/rack_attack/request.rb
index 9deba0c7ca1..099174842d0 100644
--- a/lib/gitlab/rack_attack/request.rb
+++ b/lib/gitlab/rack_attack/request.rb
@@ -60,6 +60,12 @@ module Gitlab
path =~ protected_paths_regex
end
+ def throttle?(throttle, authenticated:)
+ fragment = Gitlab::Throttle.throttle_fragment!(throttle, authenticated: authenticated)
+
+ __send__("#{fragment}?") # rubocop:disable GitlabSecurity/PublicSend
+ end
+
def throttle_unauthenticated_api?
api_request? &&
!should_be_skipped? &&
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index 698a417283e..a88ef5fe73e 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -21,7 +21,8 @@ module Gitlab
end
def composer_package_version_regex
- @composer_package_version_regex ||= %r{^v?(\d+(\.(\d+|x))*(-.+)?)}.freeze
+ # see https://github.com/composer/semver/blob/31f3ea725711245195f62e54ffa402d8ef2fdba9/src/VersionParser.php#L215
+ @composer_package_version_regex ||= %r{\Av?((\d++)(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?(\.(?:\d++|[xX*]))?)?\z}.freeze
end
def composer_dev_version_regex
diff --git a/lib/gitlab/throttle.rb b/lib/gitlab/throttle.rb
index f57ae67f6ae..622dc7d9ed0 100644
--- a/lib/gitlab/throttle.rb
+++ b/lib/gitlab/throttle.rb
@@ -4,6 +4,11 @@ module Gitlab
class Throttle
DEFAULT_RATE_LIMITING_RESPONSE_TEXT = 'Retry later'
+ # Each of these settings follows the same pattern of specifying separate
+ # authenticated and unauthenticated rates via settings. New throttles should
+ # ideally be regular as well.
+ REGULAR_THROTTLES = [:api, :packages_api, :files_api].freeze
+
def self.settings
Gitlab::CurrentSettings.current_application_settings
end
@@ -24,28 +29,38 @@ module Gitlab
"HTTP_#{env_value.upcase.tr('-', '_')}"
end
- def self.unauthenticated_api_options
- limit_proc = proc { |req| settings.throttle_unauthenticated_api_requests_per_period }
- period_proc = proc { |req| settings.throttle_unauthenticated_api_period_in_seconds.seconds }
- { limit: limit_proc, period: period_proc }
+ class << self
+ def options(throttle, authenticated:)
+ fragment = throttle_fragment!(throttle, authenticated: authenticated)
+
+ # rubocop:disable GitlabSecurity/PublicSend
+ limit_proc = proc { |req| settings.public_send("#{fragment}_requests_per_period") }
+ period_proc = proc { |req| settings.public_send("#{fragment}_period_in_seconds").seconds }
+ # rubocop:enable GitlabSecurity/PublicSend
+
+ { limit: limit_proc, period: period_proc }
+ end
+
+ def throttle_fragment!(throttle, authenticated:)
+ raise("Unknown throttle: #{throttle}") unless REGULAR_THROTTLES.include?(throttle)
+
+ "throttle_#{'un' unless authenticated}authenticated_#{throttle}"
+ end
end
def self.unauthenticated_web_options
# TODO: Columns will be renamed in https://gitlab.com/gitlab-org/gitlab/-/issues/340031
+ # Once this is done, web can be made into a regular throttle
limit_proc = proc { |req| settings.throttle_unauthenticated_requests_per_period }
period_proc = proc { |req| settings.throttle_unauthenticated_period_in_seconds.seconds }
- { limit: limit_proc, period: period_proc }
- end
- def self.authenticated_api_options
- limit_proc = proc { |req| settings.throttle_authenticated_api_requests_per_period }
- period_proc = proc { |req| settings.throttle_authenticated_api_period_in_seconds.seconds }
{ limit: limit_proc, period: period_proc }
end
def self.authenticated_web_options
limit_proc = proc { |req| settings.throttle_authenticated_web_requests_per_period }
period_proc = proc { |req| settings.throttle_authenticated_web_period_in_seconds.seconds }
+
{ limit: limit_proc, period: period_proc }
end
@@ -56,20 +71,6 @@ module Gitlab
{ limit: limit_proc, period: period_proc }
end
- def self.unauthenticated_packages_api_options
- limit_proc = proc { |req| settings.throttle_unauthenticated_packages_api_requests_per_period }
- period_proc = proc { |req| settings.throttle_unauthenticated_packages_api_period_in_seconds.seconds }
-
- { limit: limit_proc, period: period_proc }
- end
-
- def self.authenticated_packages_api_options
- limit_proc = proc { |req| settings.throttle_authenticated_packages_api_requests_per_period }
- period_proc = proc { |req| settings.throttle_authenticated_packages_api_period_in_seconds.seconds }
-
- { limit: limit_proc, period: period_proc }
- end
-
def self.throttle_authenticated_git_lfs_options
limit_proc = proc { |req| settings.throttle_authenticated_git_lfs_requests_per_period }
period_proc = proc { |req| settings.throttle_authenticated_git_lfs_period_in_seconds.seconds }
@@ -77,20 +78,6 @@ module Gitlab
{ limit: limit_proc, period: period_proc }
end
- def self.unauthenticated_files_api_options
- limit_proc = proc { |req| settings.throttle_unauthenticated_files_api_requests_per_period }
- period_proc = proc { |req| settings.throttle_unauthenticated_files_api_period_in_seconds.seconds }
-
- { limit: limit_proc, period: period_proc }
- end
-
- def self.authenticated_files_api_options
- limit_proc = proc { |req| settings.throttle_authenticated_files_api_requests_per_period }
- period_proc = proc { |req| settings.throttle_authenticated_files_api_period_in_seconds.seconds }
-
- { limit: limit_proc, period: period_proc }
- end
-
def self.rate_limiting_response_text
(settings.rate_limiting_response_text.presence || DEFAULT_RATE_LIMITING_RESPONSE_TEXT) + "\n"
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index e9091b9bba4..acb88e08098 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1741,6 +1741,9 @@ msgstr ""
msgid "Access to '%{classification_label}' not allowed"
msgstr ""
+msgid "Access tokens expire after 2 hours. A refresh token may be used at any time to generate a new access token. Non-expiring access tokens are deprecated. Clear this setting to enable backward compatibility."
+msgstr ""
+
msgid "AccessDropdown|Deploy Keys"
msgstr ""
@@ -29142,6 +29145,9 @@ msgstr ""
msgid "Runners|Runner #%{runner_id}"
msgstr ""
+msgid "Runners|Runner assigned to project."
+msgstr ""
+
msgid "Runners|Runner is offline, last contact was %{runner_contact} ago"
msgstr ""
@@ -29154,6 +29160,9 @@ msgstr ""
msgid "Runners|Runner registration"
msgstr ""
+msgid "Runners|Runner unassigned from project."
+msgstr ""
+
msgid "Runners|Runners"
msgstr ""
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb
index 58ad6a83865..79cc91acc79 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb
@@ -24,6 +24,7 @@ module QA
Resource::MergeRequest.fabricate_via_browser_ui! do |merge_request|
merge_request.project = project
merge_request.title = merge_request_title
+ merge_request.assignee = 'me'
merge_request.description = merge_request_description
end
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index 54c07985a21..8053be89ffc 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -356,6 +356,7 @@ RSpec.describe "Admin Runners" do
assigned_project = page.find('[data-testid="assigned-projects"]')
+ expect(page).to have_content('Runner assigned to project.')
expect(assigned_project).to have_content(@project2.path)
end
end
@@ -399,13 +400,14 @@ RSpec.describe "Admin Runners" do
visit admin_runner_path(runner)
end
- it 'enables specific runner for project' do
+ it 'removed specific runner from project' do
within '[data-testid="assigned-projects"]' do
click_on 'Disable'
end
new_runner_project = page.find('[data-testid="unassigned-projects"]')
+ expect(page).to have_content('Runner unassigned from project.')
expect(new_runner_project).to have_content(@project1.path)
end
end
diff --git a/spec/lib/gitlab/database/migration_helpers/v2_spec.rb b/spec/lib/gitlab/database/migration_helpers/v2_spec.rb
index 11f5f2895d3..854e97ef897 100644
--- a/spec/lib/gitlab/database/migration_helpers/v2_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers/v2_spec.rb
@@ -293,5 +293,33 @@ RSpec.describe Gitlab::Database::MigrationHelpers::V2 do
model.with_lock_retries(env: env, logger: in_memory_logger) { }
end
+
+ context 'when in transaction' do
+ before do
+ allow(model).to receive(:transaction_open?).and_return(true)
+ end
+
+ context 'when lock retries are enabled' do
+ before do
+ allow(model).to receive(:enable_lock_retries?).and_return(true)
+ end
+
+ it 'does not use Gitlab::Database::WithLockRetries and executes the provided block directly' do
+ expect(Gitlab::Database::WithLockRetries).not_to receive(:new)
+
+ expect(model.with_lock_retries(env: env, logger: in_memory_logger) { :block_result }).to eq(:block_result)
+ end
+ end
+
+ context 'when lock retries are not enabled' do
+ before do
+ allow(model).to receive(:enable_lock_retries?).and_return(false)
+ end
+
+ it 'raises an error' do
+ expect { model.with_lock_retries(env: env, logger: in_memory_logger) { } }.to raise_error /can not be run inside an already open transaction/
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index ecd4e91afe6..614aa55c3c5 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -683,6 +683,7 @@ boards:
- destroyable_lists
- milestone
- iteration
+- iteration_cadence
- board_labels
- board_assignee
- assignee
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 10162ade48b..a9efa32f986 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -762,6 +762,7 @@ Board:
- group_id
- milestone_id
- iteration_id
+- iteration_cadence_id
- weight
- name
- hide_backlog_list
diff --git a/spec/lib/gitlab/rack_attack_spec.rb b/spec/lib/gitlab/rack_attack_spec.rb
index d71c7cc1aa5..8f03905e08d 100644
--- a/spec/lib/gitlab/rack_attack_spec.rb
+++ b/spec/lib/gitlab/rack_attack_spec.rb
@@ -10,19 +10,19 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
let(:throttles) do
{
- throttle_unauthenticated_api: Gitlab::Throttle.unauthenticated_api_options,
+ throttle_unauthenticated_api: Gitlab::Throttle.options(:api, authenticated: false),
+ throttle_authenticated_api: Gitlab::Throttle.options(:api, authenticated: true),
throttle_unauthenticated_web: Gitlab::Throttle.unauthenticated_web_options,
- throttle_authenticated_api: Gitlab::Throttle.authenticated_api_options,
- throttle_product_analytics_collector: { limit: 100, period: 60 },
throttle_authenticated_web: Gitlab::Throttle.authenticated_web_options,
+ throttle_product_analytics_collector: { limit: 100, period: 60 },
throttle_unauthenticated_protected_paths: Gitlab::Throttle.protected_paths_options,
throttle_authenticated_protected_paths_api: Gitlab::Throttle.protected_paths_options,
throttle_authenticated_protected_paths_web: Gitlab::Throttle.protected_paths_options,
- throttle_unauthenticated_packages_api: Gitlab::Throttle.unauthenticated_packages_api_options,
- throttle_authenticated_packages_api: Gitlab::Throttle.authenticated_packages_api_options,
+ throttle_unauthenticated_packages_api: Gitlab::Throttle.options(:packages_api, authenticated: false),
+ throttle_authenticated_packages_api: Gitlab::Throttle.options(:packages_api, authenticated: true),
throttle_authenticated_git_lfs: Gitlab::Throttle.throttle_authenticated_git_lfs_options,
- throttle_unauthenticated_files_api: Gitlab::Throttle.unauthenticated_files_api_options,
- throttle_authenticated_files_api: Gitlab::Throttle.authenticated_files_api_options
+ throttle_unauthenticated_files_api: Gitlab::Throttle.options(:files_api, authenticated: false),
+ throttle_authenticated_files_api: Gitlab::Throttle.options(:files_api, authenticated: true)
}
end
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index c1c97e87a4c..f1b4e50b1eb 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -924,4 +924,25 @@ RSpec.describe Gitlab::Regex do
it { is_expected.not_to match('/api/v4/groups/1234/packages/debian/dists/stable/Release.gpg') }
it { is_expected.not_to match('/api/v4/groups/1234/packages/debian/pool/compon/a/pkg/file.name') }
end
+
+ describe '.composer_package_version_regex' do
+ subject { described_class.composer_package_version_regex }
+
+ it { is_expected.to match('v1.2.3') }
+ it { is_expected.to match('v1.2.x') }
+ it { is_expected.to match('v1.2.X') }
+ it { is_expected.to match('1.2.3') }
+ it { is_expected.to match('1') }
+ it { is_expected.to match('v1') }
+ it { is_expected.to match('1.2') }
+ it { is_expected.to match('v1.2') }
+ it { is_expected.not_to match('1.2.3-beta') }
+ it { is_expected.not_to match('1.2.x-beta') }
+ it { is_expected.not_to match('1.2.X-beta') }
+ it { is_expected.not_to match('1.2.3-alpha.3') }
+ it { is_expected.not_to match('1./2.3') }
+ it { is_expected.not_to match('v1./2.3') }
+ it { is_expected.not_to match('../../../../../1.2.3') }
+ it { is_expected.not_to match('%2e%2e%2f1.2.3') }
+ end
end
diff --git a/spec/migrations/backfill_cadence_id_for_boards_scoped_to_iteration_spec.rb b/spec/migrations/backfill_cadence_id_for_boards_scoped_to_iteration_spec.rb
new file mode 100644
index 00000000000..1a64de8d0db
--- /dev/null
+++ b/spec/migrations/backfill_cadence_id_for_boards_scoped_to_iteration_spec.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+# require Rails.root.join('db', 'post_migrate', '20210825193652_backfill_candence_id_for_boards_scoped_to_iteration.rb')
+
+RSpec.describe BackfillCadenceIdForBoardsScopedToIteration, :migration do
+ let(:projects) { table(:projects) }
+ let(:namespaces) { table(:namespaces) }
+ let(:iterations_cadences) { table(:iterations_cadences) }
+ let(:boards) { table(:boards) }
+
+ let!(:group) { namespaces.create!(name: 'group1', path: 'group1', type: 'Group') }
+ let!(:cadence) { iterations_cadences.create!(title: 'group cadence', group_id: group.id, start_date: Time.current) }
+ let!(:project) { projects.create!(name: 'gitlab1', path: 'gitlab1', namespace_id: group.id, visibility_level: 0) }
+ let!(:project_board1) { boards.create!(name: 'Project Dev1', project_id: project.id) }
+ let!(:project_board2) { boards.create!(name: 'Project Dev2', project_id: project.id, iteration_id: -4) }
+ let!(:project_board3) { boards.create!(name: 'Project Dev3', project_id: project.id, iteration_id: -4) }
+ let!(:project_board4) { boards.create!(name: 'Project Dev4', project_id: project.id, iteration_id: -4) }
+
+ let!(:group_board1) { boards.create!(name: 'Group Dev1', group_id: group.id) }
+ let!(:group_board2) { boards.create!(name: 'Group Dev2', group_id: group.id, iteration_id: -4) }
+ let!(:group_board3) { boards.create!(name: 'Group Dev3', group_id: group.id, iteration_id: -4) }
+ let!(:group_board4) { boards.create!(name: 'Group Dev4', group_id: group.id, iteration_id: -4) }
+
+ describe '#up' do
+ it 'schedules background migrations' do
+ Sidekiq::Testing.fake! do
+ freeze_time do
+ described_class.new.up
+
+ migration = described_class::MIGRATION
+
+ expect(migration).to be_scheduled_delayed_migration(2.minutes, 'group', 'up', group_board2.id, group_board4.id)
+ expect(migration).to be_scheduled_delayed_migration(2.minutes, 'project', 'up', project_board2.id, project_board4.id)
+ expect(BackgroundMigrationWorker.jobs.size).to eq 2
+ end
+ end
+ end
+
+ context 'in batches' do
+ before do
+ stub_const('BackfillCadenceIdForBoardsScopedToIteration::BATCH_SIZE', 2)
+ end
+
+ it 'schedules background migrations' do
+ Sidekiq::Testing.fake! do
+ freeze_time do
+ described_class.new.up
+
+ migration = described_class::MIGRATION
+
+ expect(migration).to be_scheduled_delayed_migration(2.minutes, 'group', 'up', group_board2.id, group_board3.id)
+ expect(migration).to be_scheduled_delayed_migration(4.minutes, 'group', 'up', group_board4.id, group_board4.id)
+ expect(migration).to be_scheduled_delayed_migration(2.minutes, 'project', 'up', project_board2.id, project_board3.id)
+ expect(migration).to be_scheduled_delayed_migration(4.minutes, 'project', 'up', project_board4.id, project_board4.id)
+ expect(BackgroundMigrationWorker.jobs.size).to eq 4
+ end
+ end
+ end
+ end
+ end
+
+ describe '#down' do
+ let!(:project_board1) { boards.create!(name: 'Project Dev1', project_id: project.id) }
+ let!(:project_board2) { boards.create!(name: 'Project Dev2', project_id: project.id, iteration_cadence_id: cadence.id) }
+ let!(:project_board3) { boards.create!(name: 'Project Dev3', project_id: project.id, iteration_id: -4, iteration_cadence_id: cadence.id) }
+ let!(:project_board4) { boards.create!(name: 'Project Dev4', project_id: project.id, iteration_id: -4, iteration_cadence_id: cadence.id) }
+
+ let!(:group_board1) { boards.create!(name: 'Group Dev1', group_id: group.id) }
+ let!(:group_board2) { boards.create!(name: 'Group Dev2', group_id: group.id, iteration_cadence_id: cadence.id) }
+ let!(:group_board3) { boards.create!(name: 'Group Dev3', group_id: group.id, iteration_id: -4, iteration_cadence_id: cadence.id) }
+ let!(:group_board4) { boards.create!(name: 'Group Dev4', group_id: group.id, iteration_id: -4, iteration_cadence_id: cadence.id) }
+
+ it 'schedules background migrations' do
+ Sidekiq::Testing.fake! do
+ freeze_time do
+ described_class.new.down
+
+ migration = described_class::MIGRATION
+
+ expect(migration).to be_scheduled_delayed_migration(2.minutes, 'none', 'down', project_board2.id, group_board4.id)
+ expect(BackgroundMigrationWorker.jobs.size).to eq 1
+ end
+ end
+ end
+
+ context 'in batches' do
+ before do
+ stub_const('BackfillCadenceIdForBoardsScopedToIteration::BATCH_SIZE', 2)
+ end
+
+ it 'schedules background migrations' do
+ Sidekiq::Testing.fake! do
+ freeze_time do
+ described_class.new.down
+
+ migration = described_class::MIGRATION
+
+ expect(migration).to be_scheduled_delayed_migration(2.minutes, 'none', 'down', project_board2.id, project_board3.id)
+ expect(migration).to be_scheduled_delayed_migration(4.minutes, 'none', 'down', project_board4.id, group_board2.id)
+ expect(migration).to be_scheduled_delayed_migration(6.minutes, 'none', 'down', group_board3.id, group_board4.id)
+ expect(BackgroundMigrationWorker.jobs.size).to eq 3
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/oauth_tokens_spec.rb b/spec/requests/oauth_tokens_spec.rb
index 6d944bbc783..fdcc76f42cc 100644
--- a/spec/requests/oauth_tokens_spec.rb
+++ b/spec/requests/oauth_tokens_spec.rb
@@ -55,5 +55,29 @@ RSpec.describe 'OAuth Tokens requests' do
expect(json_response['access_token']).not_to be_nil
end
+
+ context 'when the application is configured to use expiring tokens' do
+ before do
+ application.update!(expire_access_tokens: true)
+ end
+
+ it 'generates an access token with an expiration' do
+ request_access_token(user)
+
+ expect(json_response['expires_in']).not_to be_nil
+ end
+ end
+
+ context 'when the application is configured not to use expiring tokens' do
+ before do
+ application.update!(expire_access_tokens: false)
+ end
+
+ it 'generates an access token without an expiration' do
+ request_access_token(user)
+
+ expect(json_response.key?('expires_in')).to eq(false)
+ end
+ end
end
end
diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb
index a8820f5f2c7..73ff15ec393 100644
--- a/spec/services/ci/register_job_service_spec.rb
+++ b/spec/services/ci/register_job_service_spec.rb
@@ -87,12 +87,30 @@ module Ci
end
context 'for specific runner' do
- before do
- stub_feature_flags(ci_pending_builds_project_runners_decoupling: false)
+ context 'with FF disabled' do
+ before do
+ stub_feature_flags(
+ ci_pending_builds_project_runners_decoupling: false,
+ ci_queueing_builds_enabled_checks: false)
+ end
+
+ it 'does not pick a build' do
+ expect(execute(specific_runner)).to be_nil
+ end
end
- it 'does not pick a build' do
- expect(execute(specific_runner)).to be_nil
+ context 'with FF enabled' do
+ before do
+ stub_feature_flags(
+ ci_pending_builds_project_runners_decoupling: true,
+ ci_queueing_builds_enabled_checks: true)
+ end
+
+ it 'does not pick a build' do
+ expect(execute(specific_runner)).to be_nil
+ expect(pending_job.reload).to be_failed
+ expect(pending_job.queuing_entry).to be_nil
+ end
end
end
end
@@ -246,13 +264,31 @@ module Ci
end
context 'and uses project runner' do
- before do
- stub_feature_flags(ci_pending_builds_project_runners_decoupling: false)
- end
-
let(:build) { execute(specific_runner) }
- it { expect(build).to be_nil }
+ context 'with FF disabled' do
+ before do
+ stub_feature_flags(
+ ci_pending_builds_project_runners_decoupling: false,
+ ci_queueing_builds_enabled_checks: false)
+ end
+
+ it { expect(build).to be_nil }
+ end
+
+ context 'with FF enabled' do
+ before do
+ stub_feature_flags(
+ ci_pending_builds_project_runners_decoupling: true,
+ ci_queueing_builds_enabled_checks: true)
+ end
+
+ it 'does not pick a build' do
+ expect(build).to be_nil
+ expect(pending_job.reload).to be_failed
+ expect(pending_job.queuing_entry).to be_nil
+ end
+ end
end
end
diff --git a/spec/services/packages/composer/version_parser_service_spec.rb b/spec/services/packages/composer/version_parser_service_spec.rb
index 1a2f653c042..69253ff934e 100644
--- a/spec/services/packages/composer/version_parser_service_spec.rb
+++ b/spec/services/packages/composer/version_parser_service_spec.rb
@@ -12,6 +12,7 @@ RSpec.describe Packages::Composer::VersionParserService do
where(:tagname, :branchname, :expected_version) do
nil | 'master' | 'dev-master'
nil | 'my-feature' | 'dev-my-feature'
+ nil | '12-feature' | 'dev-12-feature'
nil | 'v1' | '1.x-dev'
nil | 'v1.x' | '1.x-dev'
nil | 'v1.7.x' | '1.7.x-dev'
diff --git a/spec/support/shared_examples/features/manage_applications_shared_examples.rb b/spec/support/shared_examples/features/manage_applications_shared_examples.rb
index 38bb87eaed2..0161899cb76 100644
--- a/spec/support/shared_examples/features/manage_applications_shared_examples.rb
+++ b/spec/support/shared_examples/features/manage_applications_shared_examples.rb
@@ -9,9 +9,11 @@ RSpec.shared_examples 'manage applications' do
visit new_application_path
expect(page).to have_content 'Add new application'
+ expect(find('#doorkeeper_application_expire_access_tokens')).to be_checked
fill_in :doorkeeper_application_name, with: application_name
fill_in :doorkeeper_application_redirect_uri, with: application_redirect_uri
+ uncheck :doorkeeper_application_expire_access_tokens
check :doorkeeper_application_scopes_read_user
click_on 'Save application'
@@ -22,6 +24,8 @@ RSpec.shared_examples 'manage applications' do
click_on 'Edit'
+ expect(find('#doorkeeper_application_expire_access_tokens')).not_to be_checked
+
application_name_changed = "#{application_name} changed"
fill_in :doorkeeper_application_name, with: application_name_changed