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