Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/CODEOWNERS499
-rw-r--r--app/controllers/search_controller.rb3
-rw-r--r--app/graphql/mutations/uploads/delete.rb37
-rw-r--r--app/graphql/types/mutation_type.rb1
-rw-r--r--app/graphql/types/upload_type.rb16
-rw-r--r--app/helpers/search_helper.rb36
-rw-r--r--app/policies/group_policy.rb2
-rw-r--r--app/policies/project_policy.rb2
-rw-r--r--app/policies/upload_policy.rb5
-rw-r--r--app/services/uploads/destroy_service.rb51
-rw-r--r--app/workers/post_receive.rb1
-rw-r--r--config/metrics/aggregates/code_review.yml12
-rw-r--r--config/metrics/counts_28d/20220727020440_i_code_review_merge_request_widget_code_quality_view_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220727020446_i_code_review_merge_request_widget_code_quality_full_report_clicked_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220727020452_i_code_review_merge_request_widget_code_quality_expand_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220727020457_i_code_review_merge_request_widget_code_quality_expand_success_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220727020503_i_code_review_merge_request_widget_code_quality_expand_warning_monthly.yml25
-rw-r--r--config/metrics/counts_28d/20220727020509_i_code_review_merge_request_widget_code_quality_expand_failed_monthly.yml25
-rw-r--r--config/metrics/counts_7d/20220727020407_i_code_review_merge_request_widget_code_quality_view_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220727020413_i_code_review_merge_request_widget_code_quality_full_report_clicked_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220727020419_i_code_review_merge_request_widget_code_quality_expand_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220727020424_i_code_review_merge_request_widget_code_quality_expand_success_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220727020429_i_code_review_merge_request_widget_code_quality_expand_warning_weekly.yml25
-rw-r--r--config/metrics/counts_7d/20220727020435_i_code_review_merge_request_widget_code_quality_expand_failed_weekly.yml25
-rw-r--r--config/metrics/counts_all/20220727004434_i_code_review_merge_request_widget_code_quality_count_view.yml24
-rw-r--r--config/metrics/counts_all/20220727004440_i_code_review_merge_request_widget_code_quality_count_full_report_clicked.yml24
-rw-r--r--config/metrics/counts_all/20220727004446_i_code_review_merge_request_widget_code_quality_count_expand.yml24
-rw-r--r--config/metrics/counts_all/20220727004451_i_code_review_merge_request_widget_code_quality_count_expand_success.yml24
-rw-r--r--config/metrics/counts_all/20220727004457_i_code_review_merge_request_widget_code_quality_count_expand_warning.yml24
-rw-r--r--config/metrics/counts_all/20220727004502_i_code_review_merge_request_widget_code_quality_count_expand_failed.yml24
-rw-r--r--doc/api/graphql/reference/index.md40
-rw-r--r--doc/update/index.md11
-rw-r--r--doc/user/group/manage.md2
-rw-r--r--lib/gitlab/usage_data_counters/known_events/code_review_events.yml25
-rw-r--r--lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb2
-rw-r--r--locale/gitlab.pot6
-rw-r--r--spec/controllers/search_controller_spec.rb7
-rw-r--r--spec/graphql/types/upload_type_spec.rb13
-rw-r--r--spec/policies/upload_policy_spec.rb76
-rw-r--r--spec/requests/api/graphql/mutations/uploads/delete_spec.rb74
-rw-r--r--spec/requests/api/users_spec.rb14
-rw-r--r--spec/services/ci/create_pipeline_service/rules_spec.rb1028
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb1013
-rw-r--r--spec/services/uploads/destroy_service_spec.rb103
-rw-r--r--spec/support/shared_contexts/policies/group_policy_shared_context.rb1
-rw-r--r--spec/support/shared_contexts/policies/project_policy_shared_context.rb1
-rw-r--r--spec/workers/post_receive_spec.rb6
-rw-r--r--tooling/config/CODEOWNERS.yml2
48 files changed, 2241 insertions, 1292 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index 2dc0e0df51a..7fcbd782723 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -804,249 +804,256 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
/doc/user/workspace/index.md @fneill
[Authentication and Authorization]
-/app/assets/javascripts/access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/authentication/ @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/ide/components/shared/tokened_input.vue @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/invite_members/components/members_token_select.vue @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/packages_and_registries/package_registry/components/list/tokens/ @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/pages/admin/impersonation_tokens/ @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/pages/groups/settings/access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/pages/ldap/ @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/pages/oauth/ @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/pages/omniauth_callbacks/ @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/pages/profiles/password_prompt/ @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/pages/profiles/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/pages/profiles/two_factor_auths/ @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/pages/projects/settings/access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/pipelines/components/pipelines_list/tokens/constants.js @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_branch_name_token.vue @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_source_token.vue @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_status_token.vue @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_tag_name_token.vue @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/projects/settings/topics/components/ @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/related_issues/components/issue_token.vue @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/runner/components/registration/registration_token.vue @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/runner/components/search_tokens/ @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/token_access/components/ @gitlab-org/manage/authentication-and-authorization
-/app/assets/javascripts/token_access/index.js @gitlab-org/manage/authentication-and-authorization
-/app/assets/stylesheets/page_bundles/profile_two_factor_auth.scss @gitlab-org/manage/authentication-and-authorization
-/app/controllers/admin/impersonation_tokens_controller.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/concerns/access_tokens_actions.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/concerns/authenticates_with_two_factor.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/concerns/enforces_admin_authentication.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/concerns/enforces_two_factor_authentication.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/concerns/oauth_applications.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/concerns/project_unauthorized.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/concerns/sessionless_authentication.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/concerns/snippet_authorizations.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/concerns/workhorse_authorization.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/groups/settings/access_tokens_controller.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/ldap/ @gitlab-org/manage/authentication-and-authorization
-/app/controllers/oauth/ @gitlab-org/manage/authentication-and-authorization
-/app/controllers/omniauth_callbacks_controller.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/passwords_controller.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/profiles/passwords_controller.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/profiles/personal_access_tokens_controller.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/profiles/two_factor_auths_controller.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/profiles/webauthn_registrations_controller.rb @gitlab-org/manage/authentication-and-authorization
-/app/controllers/projects/settings/access_tokens_controller.rb @gitlab-org/manage/authentication-and-authorization
-/app/finders/groups/projects_requiring_authorizations_refresh/ @gitlab-org/manage/authentication-and-authorization
-/app/finders/personal_access_tokens_finder.rb @gitlab-org/manage/authentication-and-authorization
-/app/helpers/access_tokens_helper.rb @gitlab-org/manage/authentication-and-authorization
-/app/helpers/auth_helper.rb @gitlab-org/manage/authentication-and-authorization
-/app/models/authentication_event.rb @gitlab-org/manage/authentication-and-authorization
-/app/models/concerns/admin_changed_password_notifier.rb @gitlab-org/manage/authentication-and-authorization
-/app/models/concerns/mirror_authentication.rb @gitlab-org/manage/authentication-and-authorization
-/app/models/concerns/select_for_project_authorization.rb @gitlab-org/manage/authentication-and-authorization
-/app/models/concerns/token_authenticatable.rb @gitlab-org/manage/authentication-and-authorization
-/app/models/concerns/token_authenticatable_strategies/ @gitlab-org/manage/authentication-and-authorization
-/app/models/oauth_access_grant.rb @gitlab-org/manage/authentication-and-authorization
-/app/models/oauth_access_token.rb @gitlab-org/manage/authentication-and-authorization
-/app/models/personal_access_token.rb @gitlab-org/manage/authentication-and-authorization
-/app/models/project_authorization.rb @gitlab-org/manage/authentication-and-authorization
-/app/models/token_with_iv.rb @gitlab-org/manage/authentication-and-authorization
-/app/models/webauthn_registration.rb @gitlab-org/manage/authentication-and-authorization
-/app/policies/personal_access_token_policy.rb @gitlab-org/manage/authentication-and-authorization
-/app/services/access_token_validation_service.rb @gitlab-org/manage/authentication-and-authorization
-/app/services/auth/ @gitlab-org/manage/authentication-and-authorization
-/app/services/authorized_project_update/ @gitlab-org/manage/authentication-and-authorization
-/app/services/chat_names/authorize_user_service.rb @gitlab-org/manage/authentication-and-authorization
-/app/services/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/app/services/projects/move_project_authorizations_service.rb @gitlab-org/manage/authentication-and-authorization
-/app/services/resource_access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/app/services/todos/destroy/unauthorized_features_service.rb @gitlab-org/manage/authentication-and-authorization
-/app/services/users/authorized_build_service.rb @gitlab-org/manage/authentication-and-authorization
-/app/services/users/authorized_create_service.rb @gitlab-org/manage/authentication-and-authorization
-/app/services/users/refresh_authorized_projects_service.rb @gitlab-org/manage/authentication-and-authorization
-/app/services/webauthn/ @gitlab-org/manage/authentication-and-authorization
-/app/validators/json_schemas/cluster_agent_authorization_configuration.json @gitlab-org/manage/authentication-and-authorization
-/app/views/admin/application_settings/_external_authorization_service_form.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/admin/impersonation_tokens/ @gitlab-org/manage/authentication-and-authorization
-/app/views/authentication/ @gitlab-org/manage/authentication-and-authorization
-/app/views/ci/token_access/ @gitlab-org/manage/authentication-and-authorization
-/app/views/dashboard/projects/_zero_authorized_projects.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/devise/mailer/password_change.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/devise/mailer/password_change.text.erb @gitlab-org/manage/authentication-and-authorization
-/app/views/devise/mailer/password_change_by_admin.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/devise/mailer/password_change_by_admin.text.erb @gitlab-org/manage/authentication-and-authorization
-/app/views/devise/mailer/reset_password_instructions.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/devise/mailer/reset_password_instructions.text.erb @gitlab-org/manage/authentication-and-authorization
-/app/views/devise/passwords/ @gitlab-org/manage/authentication-and-authorization
-/app/views/devise/shared/_omniauth_box.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/devise/shared/_signup_omniauth_provider_list.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/devise/shared/_signup_omniauth_providers.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/devise/shared/_signup_omniauth_providers_top.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/doorkeeper/authorizations/ @gitlab-org/manage/authentication-and-authorization
-/app/views/doorkeeper/authorized_applications/ @gitlab-org/manage/authentication-and-authorization
-/app/views/errors/omniauth_error.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/groups/settings/_resource_access_token_creation.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/groups/settings/_two_factor_auth.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/groups/settings/access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/app/views/layouts/oauth_error.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/notify/access_token_about_to_expire_email.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/notify/access_token_about_to_expire_email.text.erb @gitlab-org/manage/authentication-and-authorization
-/app/views/notify/access_token_created_email.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/notify/access_token_created_email.text.erb @gitlab-org/manage/authentication-and-authorization
-/app/views/notify/access_token_expired_email.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/notify/access_token_expired_email.text.erb @gitlab-org/manage/authentication-and-authorization
-/app/views/profiles/passwords/ @gitlab-org/manage/authentication-and-authorization
-/app/views/profiles/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/app/views/profiles/two_factor_auths/ @gitlab-org/manage/authentication-and-authorization
-/app/views/projects/mirrors/_authentication_method.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/projects/settings/access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/app/views/shared/_no_password.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/shared/_two_factor_auth_recovery_settings_check.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/shared/access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/app/views/shared/members/_two_factor_auth_badge.html.haml @gitlab-org/manage/authentication-and-authorization
-/app/views/shared/tokens/ @gitlab-org/manage/authentication-and-authorization
-/app/workers/authorized_keys_worker.rb @gitlab-org/manage/authentication-and-authorization
-/app/workers/authorized_project_update/ @gitlab-org/manage/authentication-and-authorization
-/app/workers/authorized_projects_worker.rb @gitlab-org/manage/authentication-and-authorization
-/app/workers/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/config/feature_flags/development/application_settings_tokens_optional_encryption.yml @gitlab-org/manage/authentication-and-authorization
-/config/feature_flags/development/async_only_project_authorizations_refresh.yml @gitlab-org/manage/authentication-and-authorization
-/config/feature_flags/development/enforce_auth_checks_on_uploads.yml @gitlab-org/manage/authentication-and-authorization
-/config/feature_flags/development/forti_authenticator.yml @gitlab-org/manage/authentication-and-authorization
-/config/feature_flags/development/forti_token_cloud.yml @gitlab-org/manage/authentication-and-authorization
-/config/feature_flags/development/groups_tokens_optional_encryption.yml @gitlab-org/manage/authentication-and-authorization
-/config/feature_flags/development/omniauth_login_minimal_scopes.yml @gitlab-org/manage/authentication-and-authorization
-/config/feature_flags/development/projects_tokens_optional_encryption.yml @gitlab-org/manage/authentication-and-authorization
-/config/feature_flags/development/refresh_authorizations_via_affected_projects_on_group_membership.yml @gitlab-org/manage/authentication-and-authorization
-/config/feature_flags/development/skip_group_share_unlink_auth_refresh.yml @gitlab-org/manage/authentication-and-authorization
-/config/feature_flags/development/specialized_worker_for_group_lock_update_auth_recalculation.yml @gitlab-org/manage/authentication-and-authorization
-/config/feature_flags/development/update_oauth_registration_flow.yml @gitlab-org/manage/authentication-and-authorization
-/config/feature_flags/development/webauthn.yml @gitlab-org/manage/authentication-and-authorization
-/config/feature_flags/ops/block_password_auth_for_saml_users.yml @gitlab-org/manage/authentication-and-authorization
-/config/initializers/01_secret_token.rb @gitlab-org/manage/authentication-and-authorization
-/config/initializers/devise_dynamic_password_length_validation.rb @gitlab-org/manage/authentication-and-authorization
-/config/initializers/devise_password_length.rb.example @gitlab-org/manage/authentication-and-authorization
-/config/initializers/gitlab_shell_secret_token.rb @gitlab-org/manage/authentication-and-authorization
-/config/initializers/omniauth.rb @gitlab-org/manage/authentication-and-authorization
-/config/initializers/rails_host_authorization.rb @gitlab-org/manage/authentication-and-authorization
-/config/initializers/rails_host_authorization_gitpod.rb @gitlab-org/manage/authentication-and-authorization
-/config/initializers/webauthn.rb @gitlab-org/manage/authentication-and-authorization
-/config/initializers_before_autoloader/100_patch_omniauth_oauth2.rb @gitlab-org/manage/authentication-and-authorization
-/config/initializers_before_autoloader/100_patch_omniauth_saml.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/assets/javascripts/access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/assets/javascripts/audit_events/components/tokens/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/assets/javascripts/audit_events/token_utils.js @gitlab-org/manage/authentication-and-authorization
-/ee/app/assets/javascripts/groups/settings/components/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/assets/javascripts/pages/groups/omniauth_callbacks/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/assets/javascripts/pipelines/components/pipelines_list/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/assets/javascripts/requirements/components/tokens/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/assets/javascripts/saml_providers/scim_token_service.js @gitlab-org/manage/authentication-and-authorization
-/ee/app/assets/javascripts/saml_sso/components/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_auth.vue @gitlab-org/manage/authentication-and-authorization
-/ee/app/controllers/concerns/ee/authenticates_with_two_factor.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/controllers/concerns/ee/enforces_two_factor_authentication.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/controllers/concerns/saml_authorization.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/controllers/ee/ldap/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/controllers/ee/omniauth_callbacks_controller.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/controllers/ee/passwords_controller.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/controllers/groups/omniauth_callbacks_controller.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/controllers/groups/scim_oauth_controller.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/controllers/oauth/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/controllers/omniauth_kerberos_spnego_controller.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/finders/auth/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/helpers/ee/access_tokens_helper.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/helpers/ee/auth_helper.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/helpers/ee/personal_access_tokens_helper.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/models/concerns/password_complexity.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/models/ee/personal_access_token.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/models/ee/project_authorization.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/models/scim_oauth_access_token.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/serializers/scim_oauth_access_token_entity.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/services/ee/auth/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/services/ee/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/services/ee/resource_access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/services/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/services/security/token_revocation_service.rb @gitlab-org/manage/authentication-and-authorization
-/ee/app/validators/password/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/views/admin/application_settings/_personal_access_token_expiration_policy.html.haml @gitlab-org/manage/authentication-and-authorization
-/ee/app/views/credentials_inventory_mailer/personal_access_token_revoked_email.html.haml @gitlab-org/manage/authentication-and-authorization
-/ee/app/views/credentials_inventory_mailer/personal_access_token_revoked_email.text.haml @gitlab-org/manage/authentication-and-authorization
-/ee/app/views/groups/_personal_access_token_expiration_policy.html.haml @gitlab-org/manage/authentication-and-authorization
-/ee/app/views/groups/sso/_authorize_pane.html.haml @gitlab-org/manage/authentication-and-authorization
-/ee/app/views/notify/policy_revoked_personal_access_tokens_email.html.haml @gitlab-org/manage/authentication-and-authorization
-/ee/app/views/notify/policy_revoked_personal_access_tokens_email.text.erb @gitlab-org/manage/authentication-and-authorization
-/ee/app/views/oauth/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/views/shared/credentials_inventory/_personal_access_tokens.html.haml @gitlab-org/manage/authentication-and-authorization
-/ee/app/views/shared/credentials_inventory/_project_access_tokens.html.haml @gitlab-org/manage/authentication-and-authorization
-/ee/app/views/shared/credentials_inventory/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/views/shared/credentials_inventory/project_access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/workers/auth/ @gitlab-org/manage/authentication-and-authorization
-/ee/app/workers/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization
-/ee/config/routes/oauth.rb @gitlab-org/manage/authentication-and-authorization
-/ee/lib/ee/gitlab/auth/ @gitlab-org/manage/authentication-and-authorization
-/ee/lib/ee/gitlab/omniauth_initializer.rb @gitlab-org/manage/authentication-and-authorization
-/ee/lib/gitlab/auth/ @gitlab-org/manage/authentication-and-authorization
-/ee/lib/gitlab/auth_logger.rb @gitlab-org/manage/authentication-and-authorization
-/ee/lib/gitlab/authority_analyzer.rb @gitlab-org/manage/authentication-and-authorization
-/ee/lib/gitlab/geo/oauth/ @gitlab-org/manage/authentication-and-authorization
-/ee/lib/gitlab/kerberos/ @gitlab-org/manage/authentication-and-authorization
-/ee/lib/omni_auth/ @gitlab-org/manage/authentication-and-authorization
-/ee/lib/system_check/geo/authorized_keys_check.rb @gitlab-org/manage/authentication-and-authorization
-/ee/lib/system_check/geo/authorized_keys_flag_check.rb @gitlab-org/manage/authentication-and-authorization
-/lib/api/entities/ci/reset_token_result.rb @gitlab-org/manage/authentication-and-authorization
-/lib/api/entities/impersonation_token.rb @gitlab-org/manage/authentication-and-authorization
-/lib/api/entities/impersonation_token_with_token.rb @gitlab-org/manage/authentication-and-authorization
-/lib/api/entities/personal_access_token.rb @gitlab-org/manage/authentication-and-authorization
-/lib/api/entities/personal_access_token_with_details.rb @gitlab-org/manage/authentication-and-authorization
-/lib/api/entities/personal_access_token_with_token.rb @gitlab-org/manage/authentication-and-authorization
-/lib/api/entities/resource_access_token.rb @gitlab-org/manage/authentication-and-authorization
-/lib/api/entities/resource_access_token_with_token.rb @gitlab-org/manage/authentication-and-authorization
-/lib/api/helpers/authentication.rb @gitlab-org/manage/authentication-and-authorization
-/lib/api/helpers/packages/basic_auth_helpers.rb @gitlab-org/manage/authentication-and-authorization
-/lib/api/personal_access_tokens.rb @gitlab-org/manage/authentication-and-authorization
-/lib/api/resource_access_tokens.rb @gitlab-org/manage/authentication-and-authorization
-/lib/api/support/token_with_expiration.rb @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/api_authentication/ @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/auth/ @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/auth.rb @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/auth_logger.rb @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/authorized_keys.rb @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/background_migration/encrypt_static_object_token.rb @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/background_migration/expire_o_auth_tokens.rb @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/background_migration/migrate_u2f_webauthn.rb @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/background_migration/update_users_where_two_factor_auth_required_from_group.rb @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/chat_name_token.rb @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/ci/pipeline/expression/token.rb @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/external_authorization/ @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/external_authorization.rb @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/graphql/authorize/ @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/jwt_authenticatable.rb @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/jwt_token.rb @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/lfs_token.rb @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/mail_room/ @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/omniauth_initializer.rb @gitlab-org/manage/authentication-and-authorization
-/lib/gitlab/project_authorizations.rb @gitlab-org/manage/authentication-and-authorization
-/lib/json_web_token/ @gitlab-org/manage/authentication-and-authorization
-/lib/omni_auth/ @gitlab-org/manage/authentication-and-authorization
-/lib/system_check/app/authorized_keys_permission_check.rb @gitlab-org/manage/authentication-and-authorization
-/lib/system_check/incoming_email/imap_authentication_check.rb @gitlab-org/manage/authentication-and-authorization
-/lib/tasks/gitlab/password.rake @gitlab-org/manage/authentication-and-authorization
-/lib/tasks/tokens.rake @gitlab-org/manage/authentication-and-authorization
+/app/assets/javascripts/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/authentication/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/ide/components/shared/tokened_input.vue @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/invite_members/components/members_token_select.vue @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/packages_and_registries/package_registry/components/list/tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/pages/admin/impersonation_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/pages/groups/settings/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/pages/ldap/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/pages/oauth/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/pages/omniauth_callbacks/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/pages/profiles/password_prompt/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/pages/profiles/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/pages/profiles/two_factor_auths/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/pages/projects/settings/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/pages/sessions/new/oauth_remember_me.js @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/pipelines/components/pipelines_list/tokens/constants.js @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_branch_name_token.vue @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_source_token.vue @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_status_token.vue @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/pipelines/components/pipelines_list/tokens/pipeline_tag_name_token.vue @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/projects/settings/topics/components/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/related_issues/components/issue_token.vue @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/runner/components/registration/registration_token.vue @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/runner/components/registration/registration_token_reset_dropdown_item.vue @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/runner/components/search_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/token_access/components/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/javascripts/token_access/index.js @gitlab-org/manage/authentication-and-authorization/approvers
+/app/assets/stylesheets/page_bundles/profile_two_factor_auth.scss @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/admin/impersonation_tokens_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/concerns/access_tokens_actions.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/concerns/authenticates_with_two_factor.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/concerns/enforces_admin_authentication.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/concerns/enforces_two_factor_authentication.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/concerns/oauth_applications.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/concerns/project_unauthorized.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/concerns/sessionless_authentication.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/concerns/snippet_authorizations.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/concerns/workhorse_authorization.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/groups/settings/access_tokens_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/ldap/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/oauth/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/omniauth_callbacks_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/passwords_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/profiles/passwords_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/profiles/personal_access_tokens_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/profiles/two_factor_auths_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/profiles/webauthn_registrations_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/controllers/projects/settings/access_tokens_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/finders/groups/projects_requiring_authorizations_refresh/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/finders/personal_access_tokens_finder.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/helpers/access_tokens_helper.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/helpers/auth_helper.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/models/authentication_event.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/models/concerns/admin_changed_password_notifier.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/models/concerns/mirror_authentication.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/models/concerns/select_for_project_authorization.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/models/concerns/token_authenticatable.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/models/concerns/token_authenticatable_strategies/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/models/oauth_access_grant.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/models/oauth_access_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/models/personal_access_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/models/project_authorization.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/models/token_with_iv.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/models/webauthn_registration.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/policies/personal_access_token_policy.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/services/access_token_validation_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/services/auth/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/services/authorized_project_update/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/services/chat_names/authorize_user_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/services/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/services/projects/move_project_authorizations_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/services/resource_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/services/todos/destroy/unauthorized_features_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/services/users/authorized_build_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/services/users/authorized_create_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/services/users/refresh_authorized_projects_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/services/webauthn/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/validators/json_schemas/cluster_agent_authorization_configuration.json @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/admin/application_settings/_external_authorization_service_form.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/admin/impersonation_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/authentication/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/ci/token_access/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/dashboard/projects/_zero_authorized_projects.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/devise/mailer/password_change.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/devise/mailer/password_change.text.erb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/devise/mailer/password_change_by_admin.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/devise/mailer/password_change_by_admin.text.erb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/devise/mailer/reset_password_instructions.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/devise/mailer/reset_password_instructions.text.erb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/devise/passwords/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/devise/shared/_omniauth_box.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/devise/shared/_signup_omniauth_provider_list.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/devise/shared/_signup_omniauth_providers.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/devise/shared/_signup_omniauth_providers_top.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/doorkeeper/authorizations/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/doorkeeper/authorized_applications/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/errors/omniauth_error.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/groups/settings/_resource_access_token_creation.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/groups/settings/_two_factor_auth.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/groups/settings/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/layouts/oauth_error.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/notify/access_token_about_to_expire_email.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/notify/access_token_about_to_expire_email.text.erb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/notify/access_token_created_email.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/notify/access_token_created_email.text.erb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/notify/access_token_expired_email.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/notify/access_token_expired_email.text.erb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/profiles/passwords/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/profiles/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/profiles/two_factor_auths/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/projects/mirrors/_authentication_method.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/projects/settings/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/shared/_no_password.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/shared/_two_factor_auth_recovery_settings_check.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/shared/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/shared/members/_two_factor_auth_badge.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/app/views/shared/tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/workers/authorized_keys_worker.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/workers/authorized_project_update/ @gitlab-org/manage/authentication-and-authorization/approvers
+/app/workers/authorized_projects_worker.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/app/workers/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/config/feature_flags/development/access_token_pagination.yml @gitlab-org/manage/authentication-and-authorization/approvers
+/config/feature_flags/development/application_settings_tokens_optional_encryption.yml @gitlab-org/manage/authentication-and-authorization/approvers
+/config/feature_flags/development/enforce_auth_checks_on_uploads.yml @gitlab-org/manage/authentication-and-authorization/approvers
+/config/feature_flags/development/forti_authenticator.yml @gitlab-org/manage/authentication-and-authorization/approvers
+/config/feature_flags/development/forti_token_cloud.yml @gitlab-org/manage/authentication-and-authorization/approvers
+/config/feature_flags/development/groups_tokens_optional_encryption.yml @gitlab-org/manage/authentication-and-authorization/approvers
+/config/feature_flags/development/pbkdf2_password_encryption.yml @gitlab-org/manage/authentication-and-authorization/approvers
+/config/feature_flags/development/pbkdf2_password_encryption_write.yml @gitlab-org/manage/authentication-and-authorization/approvers
+/config/feature_flags/development/projects_tokens_optional_encryption.yml @gitlab-org/manage/authentication-and-authorization/approvers
+/config/feature_flags/development/skip_group_share_unlink_auth_refresh.yml @gitlab-org/manage/authentication-and-authorization/approvers
+/config/feature_flags/development/specialized_worker_for_group_lock_update_auth_recalculation.yml @gitlab-org/manage/authentication-and-authorization/approvers
+/config/feature_flags/development/update_oauth_registration_flow.yml @gitlab-org/manage/authentication-and-authorization/approvers
+/config/feature_flags/development/webauthn.yml @gitlab-org/manage/authentication-and-authorization/approvers
+/config/feature_flags/ops/block_password_auth_for_saml_users.yml @gitlab-org/manage/authentication-and-authorization/approvers
+/config/initializers/01_secret_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/config/initializers/devise_dynamic_password_length_validation.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/config/initializers/devise_password_length.rb.example @gitlab-org/manage/authentication-and-authorization/approvers
+/config/initializers/gitlab_shell_secret_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/config/initializers/omniauth.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/config/initializers/rails_host_authorization.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/config/initializers/rails_host_authorization_gitpod.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/config/initializers/webauthn.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/config/initializers_before_autoloader/100_patch_omniauth_oauth2.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/config/initializers_before_autoloader/100_patch_omniauth_saml.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/assets/javascripts/access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/assets/javascripts/audit_events/components/tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/assets/javascripts/audit_events/token_utils.js @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/assets/javascripts/groups/settings/components/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/assets/javascripts/pages/admin/application_settings/general/components/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/assets/javascripts/pages/groups/omniauth_callbacks/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/assets/javascripts/pages/passwords/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/assets/javascripts/pages/profiles/passwords/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/assets/javascripts/password/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/assets/javascripts/pipelines/components/pipelines_list/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/assets/javascripts/requirements/components/tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/assets/javascripts/runner/components/search_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/assets/javascripts/saml_providers/scim_token_service.js @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/assets/javascripts/saml_sso/components/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_auth.vue @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/controllers/concerns/ee/authenticates_with_two_factor.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/controllers/concerns/ee/enforces_two_factor_authentication.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/controllers/concerns/saml_authorization.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/controllers/ee/ldap/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/controllers/ee/omniauth_callbacks_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/controllers/ee/passwords_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/controllers/groups/omniauth_callbacks_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/controllers/groups/scim_oauth_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/controllers/oauth/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/controllers/omniauth_kerberos_spnego_controller.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/finders/auth/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/helpers/ee/access_tokens_helper.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/helpers/ee/auth_helper.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/helpers/ee/personal_access_tokens_helper.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/models/concerns/password_complexity.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/models/ee/personal_access_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/models/ee/project_authorization.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/models/scim_oauth_access_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/serializers/scim_oauth_access_token_entity.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/services/ee/auth/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/services/ee/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/services/ee/resource_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/services/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/services/security/token_revocation_service.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/validators/password/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/views/admin/application_settings/_personal_access_token_expiration_policy.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/views/credentials_inventory_mailer/personal_access_token_revoked_email.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/views/credentials_inventory_mailer/personal_access_token_revoked_email.text.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/views/groups/_personal_access_token_expiration_policy.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/views/groups/sso/_authorize_pane.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/views/notify/policy_revoked_personal_access_tokens_email.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/views/notify/policy_revoked_personal_access_tokens_email.text.erb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/views/oauth/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/views/shared/_password_requirements_list.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/views/shared/credentials_inventory/_personal_access_tokens.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/views/shared/credentials_inventory/_project_access_tokens.html.haml @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/views/shared/credentials_inventory/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/views/shared/credentials_inventory/project_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/workers/auth/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/app/workers/personal_access_tokens/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/config/routes/oauth.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/lib/ee/gitlab/auth/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/lib/ee/gitlab/omniauth_initializer.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/lib/gitlab/auth/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/lib/gitlab/auth_logger.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/lib/gitlab/authority_analyzer.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/lib/gitlab/geo/oauth/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/lib/gitlab/kerberos/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/lib/omni_auth/ @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/lib/system_check/geo/authorized_keys_check.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/ee/lib/system_check/geo/authorized_keys_flag_check.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/api/entities/ci/reset_token_result.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/api/entities/impersonation_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/api/entities/impersonation_token_with_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/api/entities/personal_access_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/api/entities/personal_access_token_with_details.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/api/entities/personal_access_token_with_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/api/entities/resource_access_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/api/entities/resource_access_token_with_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/api/helpers/authentication.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/api/helpers/packages/basic_auth_helpers.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/api/personal_access_tokens.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/api/resource_access_tokens.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/api/support/token_with_expiration.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/api_authentication/ @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/auth/ @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/auth.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/auth_logger.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/authorized_keys.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/background_migration/encrypt_static_object_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/background_migration/expire_o_auth_tokens.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/background_migration/migrate_u2f_webauthn.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/background_migration/update_users_where_two_factor_auth_required_from_group.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/chat_name_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/ci/pipeline/expression/token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/external_authorization/ @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/external_authorization.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/grape_logging/loggers/token_logger.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/graphql/authorize/ @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/jwt_authenticatable.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/jwt_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/lfs_token.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/mail_room/ @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/omniauth_initializer.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/gitlab/project_authorizations.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/json_web_token/ @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/omni_auth/ @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/system_check/app/authorized_keys_permission_check.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/system_check/incoming_email/imap_authentication_check.rb @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/tasks/gitlab/password.rake @gitlab-org/manage/authentication-and-authorization/approvers
+/lib/tasks/tokens.rake @gitlab-org/manage/authentication-and-authorization/approvers
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 908da71a8e4..5a655c92e46 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -83,8 +83,9 @@ class SearchController < ApplicationController
@project = search_service.project
@ref = params[:project_ref] if params[:project_ref].present?
+ @filter = params[:filter]
- render json: search_autocomplete_opts(term).to_json
+ render json: search_autocomplete_opts(term, filter: @filter).to_json
end
def opensearch
diff --git a/app/graphql/mutations/uploads/delete.rb b/app/graphql/mutations/uploads/delete.rb
new file mode 100644
index 00000000000..e2fb967cd2c
--- /dev/null
+++ b/app/graphql/mutations/uploads/delete.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Mutations
+ module Uploads
+ class Delete < BaseMutation
+ graphql_name 'UploadDelete'
+ description 'Deletes an upload.'
+
+ include Mutations::ResolvesResourceParent
+
+ authorize :destroy_upload
+
+ argument :secret, GraphQL::Types::String,
+ required: true,
+ description: 'Secret part of upload path.'
+
+ argument :filename, GraphQL::Types::String,
+ required: true,
+ description: 'Upload filename.'
+
+ field :upload, Types::UploadType,
+ null: true,
+ description: 'Deleted upload.'
+
+ def resolve(args)
+ parent = authorized_resource_parent_find!(args)
+
+ result = ::Uploads::DestroyService.new(parent, current_user).execute(args[:secret], args[:filename])
+
+ {
+ upload: result[:status] == :success ? result[:upload] : nil,
+ errors: Array(result[:message])
+ }
+ end
+ end
+ end
+end
diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb
index 3907b096c2c..dc9eb369dc8 100644
--- a/app/graphql/types/mutation_type.rb
+++ b/app/graphql/types/mutation_type.rb
@@ -151,6 +151,7 @@ module Types
mount_mutation Mutations::SavedReplies::Update
mount_mutation Mutations::Pages::MarkOnboardingComplete
mount_mutation Mutations::SavedReplies::Destroy
+ mount_mutation Mutations::Uploads::Delete
end
end
diff --git a/app/graphql/types/upload_type.rb b/app/graphql/types/upload_type.rb
new file mode 100644
index 00000000000..68792fa526f
--- /dev/null
+++ b/app/graphql/types/upload_type.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Types
+ class UploadType < BaseObject
+ graphql_name 'FileUpload'
+
+ authorize :read_upload
+
+ field :id, Types::GlobalIDType[::Upload], null: false,
+ description: 'Global ID of the upload.'
+ field :path, GraphQL::Types::String, null: false,
+ description: 'Path of the upload.'
+ field :size, GraphQL::Types::Int, null: false,
+ description: 'Size of the upload in bytes.'
+ end
+end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index ecbcaec27bc..6e2e0a86446 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -14,28 +14,42 @@ module SearchHelper
:project_ids
].freeze
- def search_autocomplete_opts(term)
+ def search_autocomplete_opts(term, filter: nil)
return unless current_user
- resources_results = [
- recent_items_autocomplete(term),
+ results = case filter&.to_sym
+ when :search
+ resource_results(term)
+ when :generic
+ [
+ generic_results(term),
+ recent_items_autocomplete(term)
+ ]
+ else
+ [
+ generic_results(term),
+ resource_results(term),
+ recent_items_autocomplete(term)
+ ]
+ end
+
+ results.flatten { |item| item[:label] }
+ end
+
+ def resource_results(term)
+ [
groups_autocomplete(term),
projects_autocomplete(term),
issue_autocomplete(term)
].flatten
+ end
+ def generic_results(term)
search_pattern = Regexp.new(Regexp.escape(term), "i")
generic_results = project_autocomplete + default_autocomplete + help_autocomplete
generic_results.concat(default_autocomplete_admin) if current_user.admin?
- generic_results.select! { |result| result[:label] =~ search_pattern }
-
- [
- resources_results,
- generic_results
- ].flatten do |item|
- item[:label]
- end
+ generic_results.select { |result| result[:label] =~ search_pattern }
end
def recent_items_autocomplete(term)
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index 50b6f4bbe15..0ee759a6ad6 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -181,6 +181,8 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy
enable :create_jira_connect_subscription
enable :maintainer_access
enable :maintain_namespace
+ enable :read_upload
+ enable :destroy_upload
end
rule { owner }.policy do
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 70e8542c44a..9d121715d2c 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -498,6 +498,8 @@ class ProjectPolicy < BasePolicy
enable :admin_project_google_cloud
enable :admin_secure_files
enable :read_web_hooks
+ enable :read_upload
+ enable :destroy_upload
end
rule { public_project & metrics_dashboard_allowed }.policy do
diff --git a/app/policies/upload_policy.rb b/app/policies/upload_policy.rb
new file mode 100644
index 00000000000..c7fde5d9df4
--- /dev/null
+++ b/app/policies/upload_policy.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class UploadPolicy < BasePolicy # rubocop:disable Gitlab/NamespacedClass
+ delegate { @subject.model }
+end
diff --git a/app/services/uploads/destroy_service.rb b/app/services/uploads/destroy_service.rb
new file mode 100644
index 00000000000..1f0d99ff7bb
--- /dev/null
+++ b/app/services/uploads/destroy_service.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+module Uploads
+ class DestroyService < BaseService
+ attr_accessor :model, :current_user
+
+ def initialize(model, user = nil)
+ @model = model
+ @current_user = user
+ end
+
+ def execute(secret, filename)
+ upload = find_upload(secret, filename)
+
+ unless current_user && upload && current_user.can?(:destroy_upload, upload)
+ return error(_("The resource that you are attempting to access does not "\
+ "exist or you don't have permission to perform this action."))
+ end
+
+ if upload.destroy
+ success(upload: upload)
+ else
+ error(_('Upload could not be deleted.'))
+ end
+ end
+
+ private
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def find_upload(secret, filename)
+ uploader = uploader_class.new(model, secret: secret)
+ upload_paths = uploader.upload_paths(filename)
+
+ Upload.find_by(model: model, uploader: uploader_class.to_s, path: upload_paths)
+ rescue FileUploader::InvalidSecret
+ nil
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def uploader_class
+ case model
+ when Group
+ NamespaceFileUploader
+ when Project
+ FileUploader
+ else
+ raise ArgumentError, "unknown uploader for #{model.class.name}"
+ end
+ end
+ end
+end
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index 68a0934e2b7..329ccfc6362 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -85,6 +85,7 @@ class PostReceive
replicate_snippet_changes(snippet)
expire_caches(post_received, snippet.repository)
+ snippet.touch
Snippets::UpdateStatisticsService.new(snippet).execute
end
diff --git a/config/metrics/aggregates/code_review.yml b/config/metrics/aggregates/code_review.yml
index 64f7825dce0..15ef2c06a01 100644
--- a/config/metrics/aggregates/code_review.yml
+++ b/config/metrics/aggregates/code_review.yml
@@ -92,6 +92,12 @@
- 'i_code_review_merge_request_widget_accessibility_expand_success'
- 'i_code_review_merge_request_widget_accessibility_expand_warning'
- 'i_code_review_merge_request_widget_accessibility_expand_failed'
+ - 'i_code_review_merge_request_widget_code_quality_view'
+ - 'i_code_review_merge_request_widget_code_quality_full_report_clicked'
+ - 'i_code_review_merge_request_widget_code_quality_expand'
+ - 'i_code_review_merge_request_widget_code_quality_expand_success'
+ - 'i_code_review_merge_request_widget_code_quality_expand_warning'
+ - 'i_code_review_merge_request_widget_code_quality_expand_failed'
- name: code_review_category_monthly_active_users
operator: OR
source: redis
@@ -172,6 +178,12 @@
- 'i_code_review_merge_request_widget_accessibility_expand_success'
- 'i_code_review_merge_request_widget_accessibility_expand_warning'
- 'i_code_review_merge_request_widget_accessibility_expand_failed'
+ - 'i_code_review_merge_request_widget_code_quality_view'
+ - 'i_code_review_merge_request_widget_code_quality_full_report_clicked'
+ - 'i_code_review_merge_request_widget_code_quality_expand'
+ - 'i_code_review_merge_request_widget_code_quality_expand_success'
+ - 'i_code_review_merge_request_widget_code_quality_expand_warning'
+ - 'i_code_review_merge_request_widget_code_quality_expand_failed'
- name: code_review_extension_category_monthly_active_users
operator: OR
source: redis
diff --git a/config/metrics/counts_28d/20220727020440_i_code_review_merge_request_widget_code_quality_view_monthly.yml b/config/metrics/counts_28d/20220727020440_i_code_review_merge_request_widget_code_quality_view_monthly.yml
new file mode 100644
index 00000000000..a73f6a919ea
--- /dev/null
+++ b/config/metrics/counts_28d/20220727020440_i_code_review_merge_request_widget_code_quality_view_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_code_quality_view_monthly
+description: The count of unique users (monthly) who were able to see the Code Quality widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_view
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20220727020446_i_code_review_merge_request_widget_code_quality_full_report_clicked_monthly.yml b/config/metrics/counts_28d/20220727020446_i_code_review_merge_request_widget_code_quality_full_report_clicked_monthly.yml
new file mode 100644
index 00000000000..3fbd759deab
--- /dev/null
+++ b/config/metrics/counts_28d/20220727020446_i_code_review_merge_request_widget_code_quality_full_report_clicked_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_code_quality_full_report_clicked_monthly
+description: The count of unique users (monthly) who clicked the Full Report button on the Code Quality widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_full_report_clicked
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20220727020452_i_code_review_merge_request_widget_code_quality_expand_monthly.yml b/config/metrics/counts_28d/20220727020452_i_code_review_merge_request_widget_code_quality_expand_monthly.yml
new file mode 100644
index 00000000000..3b4686c28cc
--- /dev/null
+++ b/config/metrics/counts_28d/20220727020452_i_code_review_merge_request_widget_code_quality_expand_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_code_quality_expand_monthly
+description: The count of unique users (monthly) who expanded the Code Quality widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_expand
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20220727020457_i_code_review_merge_request_widget_code_quality_expand_success_monthly.yml b/config/metrics/counts_28d/20220727020457_i_code_review_merge_request_widget_code_quality_expand_success_monthly.yml
new file mode 100644
index 00000000000..e36a74550d4
--- /dev/null
+++ b/config/metrics/counts_28d/20220727020457_i_code_review_merge_request_widget_code_quality_expand_success_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_code_quality_expand_success_monthly
+description: The count of unique users (monthly) who expanded the Code Quality widget extension while it is in its Success state
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_expand_success
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20220727020503_i_code_review_merge_request_widget_code_quality_expand_warning_monthly.yml b/config/metrics/counts_28d/20220727020503_i_code_review_merge_request_widget_code_quality_expand_warning_monthly.yml
new file mode 100644
index 00000000000..181c06ec06f
--- /dev/null
+++ b/config/metrics/counts_28d/20220727020503_i_code_review_merge_request_widget_code_quality_expand_warning_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_code_quality_expand_warning_monthly
+description: The count of unique users (monthly) who expanded the Code Quality widget extension while it is in its Warning state
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_expand_warning
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_28d/20220727020509_i_code_review_merge_request_widget_code_quality_expand_failed_monthly.yml b/config/metrics/counts_28d/20220727020509_i_code_review_merge_request_widget_code_quality_expand_failed_monthly.yml
new file mode 100644
index 00000000000..5e4e6221142
--- /dev/null
+++ b/config/metrics/counts_28d/20220727020509_i_code_review_merge_request_widget_code_quality_expand_failed_monthly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_code_quality_expand_failed_monthly
+description: The count of unique users (monthly) who expanded the Code Quality widget extension while it is in its Failed state
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: 28d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_expand_failed
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20220727020407_i_code_review_merge_request_widget_code_quality_view_weekly.yml b/config/metrics/counts_7d/20220727020407_i_code_review_merge_request_widget_code_quality_view_weekly.yml
new file mode 100644
index 00000000000..029103609cd
--- /dev/null
+++ b/config/metrics/counts_7d/20220727020407_i_code_review_merge_request_widget_code_quality_view_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_code_quality_view_weekly
+description: The count of unique users (weekly) who were able to see the Code Quality widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_view
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20220727020413_i_code_review_merge_request_widget_code_quality_full_report_clicked_weekly.yml b/config/metrics/counts_7d/20220727020413_i_code_review_merge_request_widget_code_quality_full_report_clicked_weekly.yml
new file mode 100644
index 00000000000..d6e9d88d395
--- /dev/null
+++ b/config/metrics/counts_7d/20220727020413_i_code_review_merge_request_widget_code_quality_full_report_clicked_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_code_quality_full_report_clicked_weekly
+description: The count of unique users (weekly) who clicked the Full Report button on the Code Quality widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_full_report_clicked
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20220727020419_i_code_review_merge_request_widget_code_quality_expand_weekly.yml b/config/metrics/counts_7d/20220727020419_i_code_review_merge_request_widget_code_quality_expand_weekly.yml
new file mode 100644
index 00000000000..7d35f90ff4d
--- /dev/null
+++ b/config/metrics/counts_7d/20220727020419_i_code_review_merge_request_widget_code_quality_expand_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_code_quality_expand_weekly
+description: The count of unique users (weekly) who expanded the Code Quality widget extension
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_expand
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20220727020424_i_code_review_merge_request_widget_code_quality_expand_success_weekly.yml b/config/metrics/counts_7d/20220727020424_i_code_review_merge_request_widget_code_quality_expand_success_weekly.yml
new file mode 100644
index 00000000000..a0c94702a5f
--- /dev/null
+++ b/config/metrics/counts_7d/20220727020424_i_code_review_merge_request_widget_code_quality_expand_success_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_code_quality_expand_success_weekly
+description: The count of unique users (weekly) who expanded the Code Quality widget extension while it is in its Success state
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_expand_success
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20220727020429_i_code_review_merge_request_widget_code_quality_expand_warning_weekly.yml b/config/metrics/counts_7d/20220727020429_i_code_review_merge_request_widget_code_quality_expand_warning_weekly.yml
new file mode 100644
index 00000000000..c88c435eaf2
--- /dev/null
+++ b/config/metrics/counts_7d/20220727020429_i_code_review_merge_request_widget_code_quality_expand_warning_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_code_quality_expand_warning_weekly
+description: The count of unique users (weekly) who expanded the Code Quality widget extension while it is in its Warning state
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_expand_warning
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_7d/20220727020435_i_code_review_merge_request_widget_code_quality_expand_failed_weekly.yml b/config/metrics/counts_7d/20220727020435_i_code_review_merge_request_widget_code_quality_expand_failed_weekly.yml
new file mode 100644
index 00000000000..4ef43c77c5d
--- /dev/null
+++ b/config/metrics/counts_7d/20220727020435_i_code_review_merge_request_widget_code_quality_expand_failed_weekly.yml
@@ -0,0 +1,25 @@
+---
+key_path: redis_hll_counters.code_review.i_code_review_merge_request_widget_code_quality_expand_failed_weekly
+description: The count of unique users (weekly) who expanded the Code Quality widget extension while it is in its Failed state
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: 7d
+data_source: redis_hll
+data_category: optional
+instrumentation_class: RedisHLLMetric
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_expand_failed
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20220727004434_i_code_review_merge_request_widget_code_quality_count_view.yml b/config/metrics/counts_all/20220727004434_i_code_review_merge_request_widget_code_quality_count_view.yml
new file mode 100644
index 00000000000..1687d346e34
--- /dev/null
+++ b/config/metrics/counts_all/20220727004434_i_code_review_merge_request_widget_code_quality_count_view.yml
@@ -0,0 +1,24 @@
+---
+key_path: counts.i_code_review_merge_request_widget_code_quality_count_view
+description: Total number of times the Code Quality widget extension was viewed (rendered to the screen)
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: all
+data_source: redis
+data_category: optional
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_count_view
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20220727004440_i_code_review_merge_request_widget_code_quality_count_full_report_clicked.yml b/config/metrics/counts_all/20220727004440_i_code_review_merge_request_widget_code_quality_count_full_report_clicked.yml
new file mode 100644
index 00000000000..21ff87b8e39
--- /dev/null
+++ b/config/metrics/counts_all/20220727004440_i_code_review_merge_request_widget_code_quality_count_full_report_clicked.yml
@@ -0,0 +1,24 @@
+---
+key_path: counts.i_code_review_merge_request_widget_code_quality_count_full_report_clicked
+description: Total number of times the Code Quality widget extension Full Report button was clicked
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: all
+data_source: redis
+data_category: optional
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_count_full_report_clicked
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20220727004446_i_code_review_merge_request_widget_code_quality_count_expand.yml b/config/metrics/counts_all/20220727004446_i_code_review_merge_request_widget_code_quality_count_expand.yml
new file mode 100644
index 00000000000..49123b462d9
--- /dev/null
+++ b/config/metrics/counts_all/20220727004446_i_code_review_merge_request_widget_code_quality_count_expand.yml
@@ -0,0 +1,24 @@
+---
+key_path: counts.i_code_review_merge_request_widget_code_quality_count_expand
+description: Total number of times the Code Quality widget extension was expanded (in any state)
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: all
+data_source: redis
+data_category: optional
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_count_expand
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20220727004451_i_code_review_merge_request_widget_code_quality_count_expand_success.yml b/config/metrics/counts_all/20220727004451_i_code_review_merge_request_widget_code_quality_count_expand_success.yml
new file mode 100644
index 00000000000..8b349e04d21
--- /dev/null
+++ b/config/metrics/counts_all/20220727004451_i_code_review_merge_request_widget_code_quality_count_expand_success.yml
@@ -0,0 +1,24 @@
+---
+key_path: counts.i_code_review_merge_request_widget_code_quality_count_expand_success
+description: Total number of times the Code Quality widget extension was expanded (while in its Success state)
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: all
+data_source: redis
+data_category: optional
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_count_expand_success
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20220727004457_i_code_review_merge_request_widget_code_quality_count_expand_warning.yml b/config/metrics/counts_all/20220727004457_i_code_review_merge_request_widget_code_quality_count_expand_warning.yml
new file mode 100644
index 00000000000..e94c6999711
--- /dev/null
+++ b/config/metrics/counts_all/20220727004457_i_code_review_merge_request_widget_code_quality_count_expand_warning.yml
@@ -0,0 +1,24 @@
+---
+key_path: counts.i_code_review_merge_request_widget_code_quality_count_expand_warning
+description: Total number of times the Code Quality widget extension was expanded (while in its Warning state)
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: all
+data_source: redis
+data_category: optional
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_count_expand_warning
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/config/metrics/counts_all/20220727004502_i_code_review_merge_request_widget_code_quality_count_expand_failed.yml b/config/metrics/counts_all/20220727004502_i_code_review_merge_request_widget_code_quality_count_expand_failed.yml
new file mode 100644
index 00000000000..5e49393afff
--- /dev/null
+++ b/config/metrics/counts_all/20220727004502_i_code_review_merge_request_widget_code_quality_count_expand_failed.yml
@@ -0,0 +1,24 @@
+---
+key_path: counts.i_code_review_merge_request_widget_code_quality_count_expand_failed
+description: Total number of times the Code Quality widget extension was expanded (while in its Failed state)
+product_section: dev
+product_stage: create
+product_group: code_review
+product_category: code_review
+value_type: number
+status: active
+milestone: "15.3"
+introduced_by_url: "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93333"
+time_frame: all
+data_source: redis
+data_category: optional
+options:
+ events:
+ - i_code_review_merge_request_widget_code_quality_count_expand_failed
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index e04ef88b277..7ee604207a0 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -5385,6 +5385,30 @@ Input type: `UpdateSnippetInput`
| <a id="mutationupdatesnippeterrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
| <a id="mutationupdatesnippetsnippet"></a>`snippet` | [`Snippet`](#snippet) | Snippet after mutation. |
+### `Mutation.uploadDelete`
+
+Deletes an upload.
+
+Input type: `UploadDeleteInput`
+
+#### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationuploaddeleteclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationuploaddeletefilename"></a>`filename` | [`String!`](#string) | Upload filename. |
+| <a id="mutationuploaddeletegrouppath"></a>`groupPath` | [`ID`](#id) | Full path of the group with which the resource is associated. |
+| <a id="mutationuploaddeleteprojectpath"></a>`projectPath` | [`ID`](#id) | Full path of the project with which the resource is associated. |
+| <a id="mutationuploaddeletesecret"></a>`secret` | [`String!`](#string) | Secret part of upload path. |
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="mutationuploaddeleteclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
+| <a id="mutationuploaddeleteerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
+| <a id="mutationuploaddeleteupload"></a>`upload` | [`FileUpload`](#fileupload) | Deleted upload. |
+
### `Mutation.userCalloutCreate`
Input type: `UserCalloutCreateInput`
@@ -11845,6 +11869,16 @@ Represents an external issue.
| <a id="externalissueupdatedat"></a>`updatedAt` | [`Time`](#time) | Timestamp of when the issue was updated. |
| <a id="externalissueweburl"></a>`webUrl` | [`String`](#string) | URL to the issue in the external tracker. |
+### `FileUpload`
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="fileuploadid"></a>`id` | [`UploadID!`](#uploadid) | Global ID of the upload. |
+| <a id="fileuploadpath"></a>`path` | [`String!`](#string) | Path of the upload. |
+| <a id="fileuploadsize"></a>`size` | [`Int!`](#int) | Size of the upload in bytes. |
+
### `GeoNode`
#### Fields
@@ -21247,6 +21281,12 @@ A regexp containing patterns sourced from user input.
### `Upload`
+### `UploadID`
+
+A `UploadID` is a global ID. It is encoded as a string.
+
+An example `UploadID` is: `"gid://gitlab/Upload/1"`.
+
### `UserID`
A `UserID` is a global ID. It is encoded as a string.
diff --git a/doc/update/index.md b/doc/update/index.md
index 6c594053b4a..6515a9301e4 100644
--- a/doc/update/index.md
+++ b/doc/update/index.md
@@ -12,11 +12,6 @@ GitLab version is, if you're upgrading to a major version, and so on.
Make sure to read the whole page as it contains information related to every upgrade method.
-NOTE:
-Upgrade GitLab to the latest available patch release, for example `13.8.8` rather than `13.8.0`.
-This includes [versions you must stop at on the upgrade path](#upgrade-paths) as there may
-be fixes for issues relating to the upgrade process.
-
The [maintenance policy documentation](../policy/maintenance.md)
has additional information about upgrading, including:
@@ -388,6 +383,12 @@ accordingly, while also consulting the
`8.11.Z` -> `8.12.0` -> `8.17.7` -> `9.5.10` -> `10.8.7` -> [`11.11.8`](#1200) -> `12.0.12` -> [`12.1.17`](#1210) -> [`12.10.14`](#12100) -> `13.0.14` -> [`13.1.11`](#1310) -> [`13.8.8`](#1388) -> [`13.12.15`](#13120) -> [`14.0.12`](#1400) -> [`14.3.6`](#1430) -> [`14.9.5`](#1490) -> [`14.10.Z`](#14100) -> [`15.0.Z`](#1500) -> [latest `15.Y.Z`](https://gitlab.com/gitlab-org/gitlab/-/releases)
+NOTE:
+When not explicitly specified, upgrade GitLab to the latest available patch
+release rather than the first patch release, for example `13.8.8` instead of `13.8.0`.
+This includes versions you must stop at on the upgrade path as there may
+be fixes for issues relating to the upgrade process.
+
The following table, while not exhaustive, shows some examples of the supported
upgrade paths.
Additional steps between the mentioned versions are possible. We list the minimally necessary steps only.
diff --git a/doc/user/group/manage.md b/doc/user/group/manage.md
index 453b9b976af..c54d58931dc 100644
--- a/doc/user/group/manage.md
+++ b/doc/user/group/manage.md
@@ -8,8 +8,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
Use Groups to manage one or more related projects at the same time.
-For instructions on how to view, create, and manage groups, see [Groups](index.md).
-
## Create a group
To create a group:
diff --git a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
index d97d22ef8d3..7adb45bc9fa 100644
--- a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
+++ b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml
@@ -350,3 +350,28 @@
redis_slot: code_review
category: code_review
aggregation: weekly
+## Code Quality
+- name: i_code_review_merge_request_widget_code_quality_view
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+- name: i_code_review_merge_request_widget_code_quality_full_report_clicked
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+- name: i_code_review_merge_request_widget_code_quality_expand
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+- name: i_code_review_merge_request_widget_code_quality_expand_success
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+- name: i_code_review_merge_request_widget_code_quality_expand_warning
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
+- name: i_code_review_merge_request_widget_code_quality_expand_failed
+ redis_slot: code_review
+ category: code_review
+ aggregation: weekly
diff --git a/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb b/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb
index 6a0731ba5ab..e5634203932 100644
--- a/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb
+++ b/lib/gitlab/usage_data_counters/merge_request_widget_extension_counter.rb
@@ -5,7 +5,7 @@ module Gitlab
class MergeRequestWidgetExtensionCounter < BaseCounter
KNOWN_EVENTS = %w[view full_report_clicked expand expand_success expand_warning expand_failed].freeze
PREFIX = 'i_code_review_merge_request_widget'
- WIDGETS = %w[accessibility test_summary].freeze
+ WIDGETS = %w[accessibility code_quality test_summary].freeze
class << self
private
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index d07818c19f5..0f3b5a747e3 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -39185,6 +39185,9 @@ msgstr ""
msgid "The repository must be accessible over %{code_open}http://%{code_close}, %{code_open}https://%{code_close}, %{code_open}ssh://%{code_close} or %{code_open}git://%{code_close}."
msgstr ""
+msgid "The resource that you are attempting to access does not exist or you don't have permission to perform this action."
+msgstr ""
+
msgid "The same shared runner executes code from multiple projects, unless you configure autoscaling with %{link} set to 1 (which it is on GitLab.com)."
msgstr ""
@@ -41700,6 +41703,9 @@ msgstr ""
msgid "Upload a private key for your certificate"
msgstr ""
+msgid "Upload could not be deleted."
+msgstr ""
+
msgid "Upload file"
msgstr ""
diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb
index 6922b0f3831..e03a8b7d321 100644
--- a/spec/controllers/search_controller_spec.rb
+++ b/spec/controllers/search_controller_spec.rb
@@ -393,6 +393,13 @@ RSpec.describe SearchController do
get(:autocomplete, params: { term: 'foo@bar.com', scope: 'users' })
end
end
+
+ it 'can be filtered with params[:filter]' do
+ get :autocomplete, params: { term: 'setting', filter: 'generic' }
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response.count).to eq(1)
+ expect(json_response.first['label']).to match(/User settings/)
+ end
end
describe '#append_info_to_payload' do
diff --git a/spec/graphql/types/upload_type_spec.rb b/spec/graphql/types/upload_type_spec.rb
new file mode 100644
index 00000000000..2b959fbf105
--- /dev/null
+++ b/spec/graphql/types/upload_type_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GitlabSchema.types['FileUpload'] do
+ it { expect(described_class).to require_graphql_authorizations(:read_upload) }
+
+ it 'has the expected fields' do
+ expected_fields = %w[id size path]
+
+ expect(described_class).to include_graphql_fields(*expected_fields)
+ end
+end
diff --git a/spec/policies/upload_policy_spec.rb b/spec/policies/upload_policy_spec.rb
new file mode 100644
index 00000000000..1169df0b300
--- /dev/null
+++ b/spec/policies/upload_policy_spec.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe UploadPolicy do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:guest) { create(:user).tap { |user| group.add_guest(user) } }
+ let_it_be(:developer) { create(:user).tap { |user| group.add_developer(user) } }
+ let_it_be(:maintainer) { create(:user).tap { |user| group.add_maintainer(user) } }
+ let_it_be(:owner) { create(:user).tap { |user| group.add_owner(user) } }
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:non_member_user) { create(:user) }
+
+ let(:upload_permissions) { [:read_upload, :destroy_upload] }
+
+ shared_examples_for 'uploads policy' do
+ subject { described_class.new(current_user, upload) }
+
+ context 'when user is guest' do
+ let(:current_user) { guest }
+
+ it { is_expected.to be_disallowed(*upload_permissions) }
+ end
+
+ context 'when user is developer' do
+ let(:current_user) { developer }
+
+ it { is_expected.to be_disallowed(*upload_permissions) }
+ end
+
+ context 'when user is maintainer' do
+ let(:current_user) { maintainer }
+
+ it { is_expected.to be_allowed(*upload_permissions) }
+ end
+
+ context 'when user is owner' do
+ let(:current_user) { owner }
+
+ it { is_expected.to be_allowed(*upload_permissions) }
+ end
+
+ context 'when user is admin' do
+ let(:current_user) { admin }
+
+ it { is_expected.to be_disallowed(*upload_permissions) }
+
+ context 'with admin mode', :enable_admin_mode do
+ it { is_expected.to be_allowed(*upload_permissions) }
+ end
+ end
+ end
+
+ describe 'destroy_upload' do
+ context 'when deleting project upload' do
+ let_it_be(:upload) { create(:upload, model: project) }
+
+ it_behaves_like 'uploads policy'
+ end
+
+ context 'when deleting group upload' do
+ let_it_be(:upload) { create(:upload, model: group) }
+
+ it_behaves_like 'uploads policy'
+ end
+
+ context 'when deleting upload associated with other model' do
+ let_it_be(:upload) { create(:upload, model: maintainer) }
+
+ subject { described_class.new(maintainer, upload) }
+
+ it { is_expected.to be_disallowed(*upload_permissions) }
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/mutations/uploads/delete_spec.rb b/spec/requests/api/graphql/mutations/uploads/delete_spec.rb
new file mode 100644
index 00000000000..f44bf179397
--- /dev/null
+++ b/spec/requests/api/graphql/mutations/uploads/delete_spec.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'Delete an upload' do
+ include GraphqlHelpers
+
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, group: group) }
+ let_it_be(:developer) { create(:user).tap { |user| group.add_developer(user) } }
+ let_it_be(:maintainer) { create(:user).tap { |user| group.add_maintainer(user) } }
+
+ let(:extra_params) { {} }
+ let(:params) { { filename: File.basename(upload.path), secret: upload.secret }.merge(extra_params) }
+ let(:mutation) { graphql_mutation(:uploadDelete, params) }
+ let(:mutation_response) { graphql_mutation_response(:upload_delete) }
+
+ shared_examples_for 'upload deletion' do
+ context 'when the user is not allowed to delete uploads' do
+ let(:current_user) { developer }
+
+ it_behaves_like 'a mutation that returns a top-level access error'
+ end
+
+ context 'when the user is anonymous' do
+ let(:current_user) { nil }
+
+ it_behaves_like 'a mutation that returns a top-level access error'
+ end
+
+ context 'when user has permissions to delete uploads' do
+ let(:current_user) { maintainer }
+
+ it 'deletes the upload' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['upload']).to include('id' => upload.to_global_id.to_s)
+ expect(mutation_response['errors']).to be_empty
+ end
+
+ context 'when upload does not exist' do
+ let(:params) { { filename: 'invalid', secret: upload.secret }.merge(extra_params) }
+
+ it 'returns an error' do
+ post_graphql_mutation(mutation, current_user: current_user)
+
+ expect(response).to have_gitlab_http_status(:success)
+ expect(mutation_response['upload']).to be_nil
+ expect(mutation_response['errors']).to match_array([
+ "The resource that you are attempting to access does not "\
+ "exist or you don't have permission to perform this action."
+ ])
+ end
+ end
+ end
+ end
+
+ context 'when deleting project upload' do
+ let_it_be_with_reload(:upload) { create(:upload, :issuable_upload, model: project) }
+
+ let(:extra_params) { { project_path: project.full_path } }
+
+ it_behaves_like 'upload deletion'
+ end
+
+ context 'when deleting group upload' do
+ let_it_be_with_reload(:upload) { create(:upload, :namespace_upload, model: group) }
+
+ let(:extra_params) { { group_path: group.full_path } }
+
+ it_behaves_like 'upload deletion'
+ end
+end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 68d5fad8ff4..1c8b74558ae 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -1184,7 +1184,7 @@ RSpec.describe API::Users do
post api('/users', admin),
params: {
email: 'invalid email',
- password: 'password',
+ password: User.random_password,
name: 'test'
}
expect(response).to have_gitlab_http_status(:bad_request)
@@ -1250,7 +1250,7 @@ RSpec.describe API::Users do
post api('/users', admin),
params: {
email: 'test@example.com',
- password: 'password',
+ password: User.random_password,
username: 'test',
name: 'foo'
}
@@ -1262,7 +1262,7 @@ RSpec.describe API::Users do
params: {
name: 'foo',
email: 'test@example.com',
- password: 'password',
+ password: User.random_password,
username: 'foo'
}
end.to change { User.count }.by(0)
@@ -1276,7 +1276,7 @@ RSpec.describe API::Users do
params: {
name: 'foo',
email: 'foo@example.com',
- password: 'password',
+ password: User.random_password,
username: 'test'
}
end.to change { User.count }.by(0)
@@ -1290,7 +1290,7 @@ RSpec.describe API::Users do
params: {
name: 'foo',
email: 'foo@example.com',
- password: 'password',
+ password: User.random_password,
username: 'TEST'
}
end.to change { User.count }.by(0)
@@ -1635,8 +1635,8 @@ RSpec.describe API::Users do
context "with existing user" do
before do
- post api("/users", admin), params: { email: 'test@example.com', password: 'password', username: 'test', name: 'test' }
- post api("/users", admin), params: { email: 'foo@bar.com', password: 'password', username: 'john', name: 'john' }
+ post api("/users", admin), params: { email: 'test@example.com', password: User.random_password, username: 'test', name: 'test' }
+ post api("/users", admin), params: { email: 'foo@bar.com', password: User.random_password, username: 'john', name: 'john' }
@user = User.all.last
end
diff --git a/spec/services/ci/create_pipeline_service/rules_spec.rb b/spec/services/ci/create_pipeline_service/rules_spec.rb
index d0ce1c5aba8..88cec0cde9a 100644
--- a/spec/services/ci/create_pipeline_service/rules_spec.rb
+++ b/spec/services/ci/create_pipeline_service/rules_spec.rb
@@ -7,10 +7,38 @@ RSpec.describe Ci::CreatePipelineService do
let(:ref) { 'refs/heads/master' }
let(:source) { :push }
let(:service) { described_class.new(project, user, { ref: ref }) }
- let(:pipeline) { service.execute(source).payload }
+ let(:response) { execute_service }
+ let(:pipeline) { response.payload }
let(:build_names) { pipeline.builds.pluck(:name) }
+ def execute_service(before: '00000000', variables_attributes: nil)
+ params = { ref: ref, before: before, after: project.commit(ref).sha, variables_attributes: variables_attributes }
+
+ described_class
+ .new(project, user, params)
+ .execute(source) do |pipeline|
+ yield(pipeline) if block_given?
+ end
+ end
+
context 'job:rules' do
+ let(:regular_job) { find_job('regular-job') }
+ let(:rules_job) { find_job('rules-job') }
+ let(:delayed_job) { find_job('delayed-job') }
+
+ def find_job(name)
+ pipeline.builds.find_by(name: name)
+ end
+
+ shared_examples 'rules jobs are excluded' do
+ it 'only persists the job without rules' do
+ expect(pipeline).to be_persisted
+ expect(regular_job).to be_persisted
+ expect(rules_job).to be_nil
+ expect(delayed_job).to be_nil
+ end
+ end
+
before do
stub_ci_pipeline_yaml_file(config)
allow_next_instance_of(Ci::BuildScheduleWorker) do |instance|
@@ -95,10 +123,6 @@ RSpec.describe Ci::CreatePipelineService do
end
context 'with allow_failure and exit_codes', :aggregate_failures do
- def find_job(name)
- pipeline.builds.find_by(name: name)
- end
-
let(:config) do
<<-EOY
job-1:
@@ -280,6 +304,773 @@ RSpec.describe Ci::CreatePipelineService do
end
end
end
+
+ context 'with simple if: clauses' do
+ let(:config) do
+ <<-EOY
+ regular-job:
+ script: 'echo Hello, World!'
+
+ master-job:
+ script: "echo hello world, $CI_COMMIT_REF_NAME"
+ rules:
+ - if: $CI_COMMIT_REF_NAME == "nonexistant-branch"
+ when: never
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ when: manual
+
+ negligible-job:
+ script: "exit 1"
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ allow_failure: true
+
+ delayed-job:
+ script: "echo See you later, World!"
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ when: delayed
+ start_in: 1 hour
+
+ never-job:
+ script: "echo Goodbye, World!"
+ rules:
+ - if: $CI_COMMIT_REF_NAME
+ when: never
+ EOY
+ end
+
+ context 'with matches' do
+ it 'creates a pipeline with the vanilla and manual jobs' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly(
+ 'regular-job', 'delayed-job', 'master-job', 'negligible-job'
+ )
+ end
+
+ it 'assigns job:when values to the builds' do
+ expect(find_job('regular-job').when).to eq('on_success')
+ expect(find_job('master-job').when).to eq('manual')
+ expect(find_job('negligible-job').when).to eq('on_success')
+ expect(find_job('delayed-job').when).to eq('delayed')
+ end
+
+ it 'assigns job:allow_failure values to the builds' do
+ expect(find_job('regular-job').allow_failure).to eq(false)
+ expect(find_job('master-job').allow_failure).to eq(false)
+ expect(find_job('negligible-job').allow_failure).to eq(true)
+ expect(find_job('delayed-job').allow_failure).to eq(false)
+ end
+
+ it 'assigns start_in for delayed jobs' do
+ expect(delayed_job.options[:start_in]).to eq('1 hour')
+ end
+ end
+
+ context 'with no matches' do
+ let(:ref) { 'refs/heads/feature' }
+
+ it_behaves_like 'rules jobs are excluded'
+ end
+ end
+
+ context 'with complex if: clauses' do
+ let(:config) do
+ <<-EOY
+ regular-job:
+ script: 'echo Hello, World!'
+ rules:
+ - if: $VAR == 'present' && $OTHER || $CI_COMMIT_REF_NAME
+ when: manual
+ allow_failure: true
+ EOY
+ end
+
+ it 'matches the first rule' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly('regular-job')
+ expect(regular_job.when).to eq('manual')
+ expect(regular_job.allow_failure).to eq(true)
+ end
+ end
+ end
+
+ context 'changes:' do
+ let(:config) do
+ <<-EOY
+ regular-job:
+ script: 'echo Hello, World!'
+
+ rules-job:
+ script: "echo hello world, $CI_COMMIT_REF_NAME"
+ rules:
+ - changes:
+ - README.md
+ when: manual
+ - changes:
+ - app.rb
+ when: on_success
+
+ delayed-job:
+ script: "echo See you later, World!"
+ rules:
+ - changes:
+ - README.md
+ when: delayed
+ start_in: 4 hours
+
+ negligible-job:
+ script: "can be failed sometimes"
+ rules:
+ - changes:
+ - README.md
+ allow_failure: true
+
+ README:
+ script: "I use variables for changes!"
+ rules:
+ - changes:
+ - $CI_JOB_NAME*
+
+ changes-paths:
+ script: "I am using a new syntax!"
+ rules:
+ - changes:
+ paths: [README.md]
+ EOY
+ end
+
+ context 'and matches' do
+ before do
+ allow_next_instance_of(Ci::Pipeline) do |pipeline|
+ allow(pipeline).to receive(:modified_paths).and_return(%w[README.md])
+ end
+ end
+
+ it 'creates five jobs' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly(
+ 'regular-job', 'rules-job', 'delayed-job', 'negligible-job', 'README', 'changes-paths'
+ )
+ end
+
+ it 'sets when: for all jobs' do
+ expect(regular_job.when).to eq('on_success')
+ expect(rules_job.when).to eq('manual')
+ expect(delayed_job.when).to eq('delayed')
+ expect(delayed_job.options[:start_in]).to eq('4 hours')
+ end
+
+ it 'sets allow_failure: for negligible job' do
+ expect(find_job('negligible-job').allow_failure).to eq(true)
+ end
+ end
+
+ context 'and matches the second rule' do
+ before do
+ allow_next_instance_of(Ci::Pipeline) do |pipeline|
+ allow(pipeline).to receive(:modified_paths).and_return(%w[app.rb])
+ end
+ end
+
+ it 'includes both jobs' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly('regular-job', 'rules-job')
+ end
+
+ it 'sets when: for the created rules job based on the second clause' do
+ expect(regular_job.when).to eq('on_success')
+ expect(rules_job.when).to eq('on_success')
+ end
+ end
+
+ context 'and does not match' do
+ before do
+ allow_next_instance_of(Ci::Pipeline) do |pipeline|
+ allow(pipeline).to receive(:modified_paths).and_return(%w[useless_script.rb])
+ end
+ end
+
+ it_behaves_like 'rules jobs are excluded'
+
+ it 'sets when: for the created job' do
+ expect(regular_job.when).to eq('on_success')
+ end
+ end
+
+ context 'with paths and compare_to' do
+ let_it_be(:project) { create(:project, :empty_repo) }
+ let_it_be(:user) { project.first_owner }
+
+ before_all do
+ project.repository.add_branch(user, 'feature_1', 'master')
+
+ project.repository.create_file(
+ user, 'file1.txt', 'file 1', message: 'Create file1.txt', branch_name: 'feature_1'
+ )
+
+ project.repository.add_branch(user, 'feature_2', 'feature_1')
+
+ project.repository.create_file(
+ user, 'file2.txt', 'file 2', message: 'Create file2.txt', branch_name: 'feature_2'
+ )
+ end
+
+ let(:changed_file) { 'file2.txt' }
+ let(:ref) { 'feature_2' }
+
+ let(:response) { execute_service(before: nil) }
+
+ context 'for jobs rules' do
+ let(:config) do
+ <<-EOY
+ job1:
+ script: exit 0
+ rules:
+ - changes:
+ paths: [#{changed_file}]
+ compare_to: #{compare_to}
+
+ job2:
+ script: exit 0
+ EOY
+ end
+
+ context 'when there is no such compare_to ref' do
+ let(:compare_to) { 'invalid-branch' }
+
+ it 'returns an error' do
+ expect(pipeline.errors.full_messages).to eq([
+ 'Failed to parse rule for job1: rules:changes:compare_to is not a valid ref'
+ ])
+ end
+
+ context 'when the FF ci_rules_changes_compare is not enabled' do
+ before do
+ stub_feature_flags(ci_rules_changes_compare: false)
+ end
+
+ it 'ignores compare_to and changes is always true' do
+ expect(build_names).to contain_exactly('job1', 'job2')
+ end
+ end
+ end
+
+ context 'when the compare_to ref exists' do
+ let(:compare_to) { 'feature_1'}
+
+ context 'when the rule matches' do
+ it 'creates job1 and job2' do
+ expect(build_names).to contain_exactly('job1', 'job2')
+ end
+
+ context 'when the FF ci_rules_changes_compare is not enabled' do
+ before do
+ stub_feature_flags(ci_rules_changes_compare: false)
+ end
+
+ it 'ignores compare_to and changes is always true' do
+ expect(build_names).to contain_exactly('job1', 'job2')
+ end
+ end
+ end
+
+ context 'when the rule does not match' do
+ let(:changed_file) { 'file1.txt' }
+
+ it 'does not create job1' do
+ expect(build_names).to contain_exactly('job2')
+ end
+
+ context 'when the FF ci_rules_changes_compare is not enabled' do
+ before do
+ stub_feature_flags(ci_rules_changes_compare: false)
+ end
+
+ it 'ignores compare_to and changes is always true' do
+ expect(build_names).to contain_exactly('job1', 'job2')
+ end
+ end
+ end
+ end
+ end
+
+ context 'for workflow rules' do
+ let(:config) do
+ <<-EOY
+ workflow:
+ rules:
+ - changes:
+ paths: [#{changed_file}]
+ compare_to: #{compare_to}
+
+ job1:
+ script: exit 0
+ EOY
+ end
+
+ let(:compare_to) { 'feature_1'}
+
+ context 'when the rule matches' do
+ it 'creates job1' do
+ expect(pipeline).to be_created_successfully
+ expect(build_names).to contain_exactly('job1')
+ end
+
+ context 'when the FF ci_rules_changes_compare is not enabled' do
+ before do
+ stub_feature_flags(ci_rules_changes_compare: false)
+ end
+
+ it 'ignores compare_to and changes is always true' do
+ expect(pipeline).to be_created_successfully
+ expect(build_names).to contain_exactly('job1')
+ end
+ end
+ end
+
+ context 'when the rule does not match' do
+ let(:changed_file) { 'file1.txt' }
+
+ it 'does not create job1' do
+ expect(pipeline).not_to be_created_successfully
+ expect(build_names).to be_empty
+ end
+ end
+ end
+ end
+ end
+
+ context 'mixed if: and changes: rules' do
+ let(:config) do
+ <<-EOY
+ regular-job:
+ script: 'echo Hello, World!'
+
+ rules-job:
+ script: "echo hello world, $CI_COMMIT_REF_NAME"
+ allow_failure: true
+ rules:
+ - changes:
+ - README.md
+ when: manual
+ - if: $CI_COMMIT_REF_NAME == "master"
+ when: on_success
+ allow_failure: false
+
+ delayed-job:
+ script: "echo See you later, World!"
+ rules:
+ - changes:
+ - README.md
+ when: delayed
+ start_in: 4 hours
+ allow_failure: true
+ - if: $CI_COMMIT_REF_NAME == "master"
+ when: delayed
+ start_in: 1 hour
+ EOY
+ end
+
+ context 'and changes: matches before if' do
+ before do
+ allow_next_instance_of(Ci::Pipeline) do |pipeline|
+ allow(pipeline).to receive(:modified_paths).and_return(%w[README.md])
+ end
+ end
+
+ it 'creates two jobs' do
+ expect(pipeline).to be_persisted
+ expect(build_names)
+ .to contain_exactly('regular-job', 'rules-job', 'delayed-job')
+ end
+
+ it 'sets when: for all jobs' do
+ expect(regular_job.when).to eq('on_success')
+ expect(rules_job.when).to eq('manual')
+ expect(delayed_job.when).to eq('delayed')
+ expect(delayed_job.options[:start_in]).to eq('4 hours')
+ end
+
+ it 'sets allow_failure: for all jobs' do
+ expect(regular_job.allow_failure).to eq(false)
+ expect(rules_job.allow_failure).to eq(true)
+ expect(delayed_job.allow_failure).to eq(true)
+ end
+ end
+
+ context 'and if: matches after changes' do
+ it 'includes both jobs' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly('regular-job', 'rules-job', 'delayed-job')
+ end
+
+ it 'sets when: for the created rules job based on the second clause' do
+ expect(regular_job.when).to eq('on_success')
+ expect(rules_job.when).to eq('on_success')
+ expect(delayed_job.when).to eq('delayed')
+ expect(delayed_job.options[:start_in]).to eq('1 hour')
+ end
+ end
+
+ context 'and does not match' do
+ let(:ref) { 'refs/heads/wip' }
+
+ it_behaves_like 'rules jobs are excluded'
+
+ it 'sets when: for the created job' do
+ expect(regular_job.when).to eq('on_success')
+ end
+ end
+ end
+
+ context 'mixed if: and changes: clauses' do
+ let(:config) do
+ <<-EOY
+ regular-job:
+ script: 'echo Hello, World!'
+
+ rules-job:
+ script: "echo hello world, $CI_COMMIT_REF_NAME"
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ changes: [README.md]
+ when: on_success
+ allow_failure: true
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ changes: [app.rb]
+ when: manual
+ EOY
+ end
+
+ context 'with if matches and changes matches' do
+ before do
+ allow_next_instance_of(Ci::Pipeline) do |pipeline|
+ allow(pipeline).to receive(:modified_paths).and_return(%w[app.rb])
+ end
+ end
+
+ it 'persists all jobs' do
+ expect(pipeline).to be_persisted
+ expect(regular_job).to be_persisted
+ expect(rules_job).to be_persisted
+ expect(rules_job.when).to eq('manual')
+ expect(rules_job.allow_failure).to eq(false)
+ end
+ end
+
+ context 'with if matches and no change matches' do
+ it_behaves_like 'rules jobs are excluded'
+ end
+
+ context 'with change matches and no if matches' do
+ let(:ref) { 'refs/heads/feature' }
+
+ before do
+ allow_next_instance_of(Ci::Pipeline) do |pipeline|
+ allow(pipeline).to receive(:modified_paths).and_return(%w[README.md])
+ end
+ end
+
+ it_behaves_like 'rules jobs are excluded'
+ end
+
+ context 'and no matches' do
+ let(:ref) { 'refs/heads/feature' }
+
+ it_behaves_like 'rules jobs are excluded'
+ end
+ end
+
+ context 'complex if: allow_failure usages' do
+ let(:config) do
+ <<-EOY
+ job-1:
+ script: "exit 1"
+ allow_failure: true
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ allow_failure: false
+
+ job-2:
+ script: "exit 1"
+ allow_failure: true
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
+ allow_failure: false
+
+ job-3:
+ script: "exit 1"
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
+ allow_failure: true
+
+ job-4:
+ script: "exit 1"
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ allow_failure: false
+
+ job-5:
+ script: "exit 1"
+ allow_failure: false
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ allow_failure: true
+
+ job-6:
+ script: "exit 1"
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
+ allow_failure: false
+ - allow_failure: true
+ EOY
+ end
+
+ it 'creates a pipeline' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly('job-1', 'job-4', 'job-5', 'job-6')
+ end
+
+ it 'assigns job:allow_failure values to the builds' do
+ expect(find_job('job-1').allow_failure).to eq(false)
+ expect(find_job('job-4').allow_failure).to eq(false)
+ expect(find_job('job-5').allow_failure).to eq(true)
+ expect(find_job('job-6').allow_failure).to eq(true)
+ end
+ end
+
+ context 'complex if: allow_failure & when usages' do
+ let(:config) do
+ <<-EOY
+ job-1:
+ script: "exit 1"
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ when: manual
+
+ job-2:
+ script: "exit 1"
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ when: manual
+ allow_failure: true
+
+ job-3:
+ script: "exit 1"
+ allow_failure: true
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ when: manual
+
+ job-4:
+ script: "exit 1"
+ allow_failure: true
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ when: manual
+ allow_failure: false
+
+ job-5:
+ script: "exit 1"
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
+ when: manual
+ allow_failure: false
+ - when: always
+ allow_failure: true
+
+ job-6:
+ script: "exit 1"
+ allow_failure: false
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ when: manual
+
+ job-7:
+ script: "exit 1"
+ allow_failure: false
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
+ when: manual
+ - when: :on_failure
+ allow_failure: true
+ EOY
+ end
+
+ it 'creates a pipeline' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly(
+ 'job-1', 'job-2', 'job-3', 'job-4', 'job-5', 'job-6', 'job-7'
+ )
+ end
+
+ it 'assigns job:allow_failure values to the builds' do
+ expect(find_job('job-1').allow_failure).to eq(false)
+ expect(find_job('job-2').allow_failure).to eq(true)
+ expect(find_job('job-3').allow_failure).to eq(true)
+ expect(find_job('job-4').allow_failure).to eq(false)
+ expect(find_job('job-5').allow_failure).to eq(true)
+ expect(find_job('job-6').allow_failure).to eq(false)
+ expect(find_job('job-7').allow_failure).to eq(true)
+ end
+
+ it 'assigns job:when values to the builds' do
+ expect(find_job('job-1').when).to eq('manual')
+ expect(find_job('job-2').when).to eq('manual')
+ expect(find_job('job-3').when).to eq('manual')
+ expect(find_job('job-4').when).to eq('manual')
+ expect(find_job('job-5').when).to eq('always')
+ expect(find_job('job-6').when).to eq('manual')
+ expect(find_job('job-7').when).to eq('on_failure')
+ end
+ end
+
+ context 'deploy freeze period `if:` clause' do
+ # '0 23 * * 5' == "At 23:00 on Friday."", '0 7 * * 1' == "At 07:00 on Monday.""
+ let!(:freeze_period) { create(:ci_freeze_period, project: project, freeze_start: '0 23 * * 5', freeze_end: '0 7 * * 1') }
+
+ context 'with 2 jobs' do
+ let(:config) do
+ <<-EOY
+ stages:
+ - test
+ - deploy
+
+ test-job:
+ script:
+ - echo 'running TEST stage'
+
+ deploy-job:
+ stage: deploy
+ script:
+ - echo 'running DEPLOY stage'
+ rules:
+ - if: $CI_DEPLOY_FREEZE == null
+ EOY
+ end
+
+ context 'when outside freeze period' do
+ it 'creates two jobs' do
+ Timecop.freeze(2020, 4, 10, 22, 59) do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly('test-job', 'deploy-job')
+ end
+ end
+ end
+
+ context 'when inside freeze period' do
+ it 'creates one job' do
+ Timecop.freeze(2020, 4, 10, 23, 1) do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly('test-job')
+ end
+ end
+ end
+ end
+
+ context 'with 1 job' do
+ let(:config) do
+ <<-EOY
+ stages:
+ - deploy
+
+ deploy-job:
+ stage: deploy
+ script:
+ - echo 'running DEPLOY stage'
+ rules:
+ - if: $CI_DEPLOY_FREEZE == null
+ EOY
+ end
+
+ context 'when outside freeze period' do
+ it 'creates two jobs' do
+ Timecop.freeze(2020, 4, 10, 22, 59) do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly('deploy-job')
+ end
+ end
+ end
+
+ context 'when inside freeze period' do
+ it 'does not create the pipeline', :aggregate_failures do
+ Timecop.freeze(2020, 4, 10, 23, 1) do
+ expect(response).to be_error
+ expect(pipeline).not_to be_persisted
+ end
+ end
+ end
+ end
+ end
+
+ context 'with when:manual' do
+ let(:config) do
+ <<-EOY
+ job-with-rules:
+ script: 'echo hey'
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+
+ job-when-with-rules:
+ script: 'echo hey'
+ when: manual
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+
+ job-when-with-rules-when:
+ script: 'echo hey'
+ when: manual
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ when: on_success
+
+ job-with-rules-when:
+ script: 'echo hey'
+ rules:
+ - if: $CI_COMMIT_REF_NAME =~ /master/
+ when: manual
+
+ job-without-rules:
+ script: 'echo this is a job with NO rules'
+ EOY
+ end
+
+ let(:job_with_rules) { find_job('job-with-rules') }
+ let(:job_when_with_rules) { find_job('job-when-with-rules') }
+ let(:job_when_with_rules_when) { find_job('job-when-with-rules-when') }
+ let(:job_with_rules_when) { find_job('job-with-rules-when') }
+ let(:job_without_rules) { find_job('job-without-rules') }
+
+ context 'when matching the rules' do
+ let(:ref) { 'refs/heads/master' }
+
+ it 'adds the job-with-rules with a when:manual' do
+ expect(job_with_rules).to be_persisted
+ expect(job_when_with_rules).to be_persisted
+ expect(job_when_with_rules_when).to be_persisted
+ expect(job_with_rules_when).to be_persisted
+ expect(job_without_rules).to be_persisted
+
+ expect(job_with_rules.when).to eq('on_success')
+ expect(job_when_with_rules.when).to eq('manual')
+ expect(job_when_with_rules_when.when).to eq('on_success')
+ expect(job_with_rules_when.when).to eq('manual')
+ expect(job_without_rules.when).to eq('on_success')
+ end
+ end
+
+ context 'when there is no match to the rule' do
+ let(:ref) { 'refs/heads/wip' }
+
+ it 'does not add job_with_rules' do
+ expect(job_with_rules).to be_nil
+ expect(job_when_with_rules).to be_nil
+ expect(job_when_with_rules_when).to be_nil
+ expect(job_with_rules_when).to be_nil
+ expect(job_without_rules).to be_persisted
+ end
+ end
end
end
@@ -447,5 +1238,232 @@ RSpec.describe Ci::CreatePipelineService do
end
end
end
+
+ context 'with persisted variables' do
+ let(:config) do
+ <<-EOY
+ workflow:
+ rules:
+ - if: $CI_COMMIT_REF_NAME == "master"
+
+ regular-job:
+ script: 'echo Hello, World!'
+ EOY
+ end
+
+ context 'with matches' do
+ it 'creates a pipeline' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly('regular-job')
+ end
+ end
+
+ context 'with no matches' do
+ let(:ref) { 'refs/heads/feature' }
+
+ it 'does not create a pipeline', :aggregate_failures do
+ expect(response).to be_error
+ expect(pipeline).not_to be_persisted
+ end
+ end
+ end
+
+ context 'with pipeline variables' do
+ let(:pipeline) do
+ execute_service(variables_attributes: variables_attributes).payload
+ end
+
+ let(:config) do
+ <<-EOY
+ workflow:
+ rules:
+ - if: $SOME_VARIABLE
+
+ regular-job:
+ script: 'echo Hello, World!'
+ EOY
+ end
+
+ context 'with matches' do
+ let(:variables_attributes) do
+ [{ key: 'SOME_VARIABLE', secret_value: 'SOME_VAR' }]
+ end
+
+ it 'creates a pipeline' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly('regular-job')
+ end
+ end
+
+ context 'with no matches' do
+ let(:variables_attributes) { {} }
+
+ it 'does not create a pipeline', :aggregate_failures do
+ expect(response).to be_error
+ expect(pipeline).not_to be_persisted
+ end
+ end
+ end
+
+ context 'with trigger variables' do
+ let(:pipeline) do
+ execute_service do |pipeline|
+ pipeline.variables.build(variables)
+ end.payload
+ end
+
+ let(:config) do
+ <<-EOY
+ workflow:
+ rules:
+ - if: $SOME_VARIABLE
+
+ regular-job:
+ script: 'echo Hello, World!'
+ EOY
+ end
+
+ context 'with matches' do
+ let(:variables) do
+ [{ key: 'SOME_VARIABLE', secret_value: 'SOME_VAR' }]
+ end
+
+ it 'creates a pipeline' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly('regular-job')
+ end
+
+ context 'when a job requires the same variable' do
+ let(:config) do
+ <<-EOY
+ workflow:
+ rules:
+ - if: $SOME_VARIABLE
+
+ build:
+ stage: build
+ script: 'echo build'
+ rules:
+ - if: $SOME_VARIABLE
+
+ test1:
+ stage: test
+ script: 'echo test1'
+ needs: [build]
+
+ test2:
+ stage: test
+ script: 'echo test2'
+ EOY
+ end
+
+ it 'creates a pipeline' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly('build', 'test1', 'test2')
+ end
+ end
+ end
+
+ context 'with no matches' do
+ let(:variables) { {} }
+
+ it 'does not create a pipeline', :aggregate_failures do
+ expect(response).to be_error
+ expect(pipeline).not_to be_persisted
+ end
+
+ context 'when a job requires the same variable' do
+ let(:config) do
+ <<-EOY
+ workflow:
+ rules:
+ - if: $SOME_VARIABLE
+
+ build:
+ stage: build
+ script: 'echo build'
+ rules:
+ - if: $SOME_VARIABLE
+
+ test1:
+ stage: test
+ script: 'echo test1'
+ needs: [build]
+
+ test2:
+ stage: test
+ script: 'echo test2'
+ EOY
+ end
+
+ it 'does not create a pipeline', :aggregate_failures do
+ expect(response).to be_error
+ expect(pipeline).not_to be_persisted
+ end
+ end
+ end
+ end
+
+ context 'changes' do
+ shared_examples 'comparing file changes with workflow rules' do
+ context 'when matches' do
+ before do
+ allow_next_instance_of(Ci::Pipeline) do |pipeline|
+ allow(pipeline).to receive(:modified_paths).and_return(%w[file1.md])
+ end
+ end
+
+ it 'creates the pipeline with a job' do
+ expect(pipeline).to be_persisted
+ expect(build_names).to contain_exactly('job')
+ end
+ end
+
+ context 'when does not match' do
+ before do
+ allow_next_instance_of(Ci::Pipeline) do |pipeline|
+ allow(pipeline).to receive(:modified_paths).and_return(%w[unknown])
+ end
+ end
+
+ it 'creates the pipeline with a job' do
+ expect(pipeline.errors.full_messages).to eq(['Pipeline filtered out by workflow rules.'])
+ expect(response).to be_error
+ expect(pipeline).not_to be_persisted
+ end
+ end
+ end
+
+ context 'changes is an array' do
+ let(:config) do
+ <<-EOY
+ workflow:
+ rules:
+ - changes: [file1.md]
+
+ job:
+ script: exit 0
+ EOY
+ end
+
+ it_behaves_like 'comparing file changes with workflow rules'
+ end
+
+ context 'changes:paths is an array' do
+ let(:config) do
+ <<-EOY
+ workflow:
+ rules:
+ - changes:
+ paths: [file1.md]
+
+ job:
+ script: exit 0
+ EOY
+ end
+
+ it_behaves_like 'comparing file changes with workflow rules'
+ end
+ end
end
end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index 6f89e10da7f..a9442b0dc68 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -1866,1019 +1866,6 @@ RSpec.describe Ci::CreatePipelineService do
end
end
end
-
- context 'when rules are used' do
- let(:ref_name) { 'refs/heads/master' }
- let(:response) { execute_service }
- let(:pipeline) { response.payload }
- let(:build_names) { pipeline.builds.pluck(:name) }
- let(:regular_job) { find_job('regular-job') }
- let(:rules_job) { find_job('rules-job') }
- let(:delayed_job) { find_job('delayed-job') }
-
- context 'with when:manual' do
- let(:config) do
- <<-EOY
- job-with-rules:
- script: 'echo hey'
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /master/
-
- job-when-with-rules:
- script: 'echo hey'
- when: manual
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /master/
-
- job-when-with-rules-when:
- script: 'echo hey'
- when: manual
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /master/
- when: on_success
-
- job-with-rules-when:
- script: 'echo hey'
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /master/
- when: manual
-
- job-without-rules:
- script: 'echo this is a job with NO rules'
- EOY
- end
-
- let(:job_with_rules) { find_job('job-with-rules') }
- let(:job_when_with_rules) { find_job('job-when-with-rules') }
- let(:job_when_with_rules_when) { find_job('job-when-with-rules-when') }
- let(:job_with_rules_when) { find_job('job-with-rules-when') }
- let(:job_without_rules) { find_job('job-without-rules') }
-
- context 'when matching the rules' do
- let(:ref_name) { 'refs/heads/master' }
-
- it 'adds the job-with-rules with a when:manual' do
- expect(job_with_rules).to be_persisted
- expect(job_when_with_rules).to be_persisted
- expect(job_when_with_rules_when).to be_persisted
- expect(job_with_rules_when).to be_persisted
- expect(job_without_rules).to be_persisted
-
- expect(job_with_rules.when).to eq('on_success')
- expect(job_when_with_rules.when).to eq('manual')
- expect(job_when_with_rules_when.when).to eq('on_success')
- expect(job_with_rules_when.when).to eq('manual')
- expect(job_without_rules.when).to eq('on_success')
- end
- end
-
- context 'when there is no match to the rule' do
- let(:ref_name) { 'refs/heads/wip' }
-
- it 'does not add job_with_rules' do
- expect(job_with_rules).to be_nil
- expect(job_when_with_rules).to be_nil
- expect(job_when_with_rules_when).to be_nil
- expect(job_with_rules_when).to be_nil
- expect(job_without_rules).to be_persisted
- end
- end
- end
-
- shared_examples 'rules jobs are excluded' do
- it 'only persists the job without rules' do
- expect(pipeline).to be_persisted
- expect(regular_job).to be_persisted
- expect(rules_job).to be_nil
- expect(delayed_job).to be_nil
- end
- end
-
- def find_job(name)
- pipeline.builds.find_by(name: name)
- end
-
- before do
- stub_ci_pipeline_yaml_file(config)
- allow_any_instance_of(Ci::BuildScheduleWorker).to receive(:perform).and_return(true)
- end
-
- context 'with simple if: clauses' do
- let(:config) do
- <<-EOY
- regular-job:
- script: 'echo Hello, World!'
-
- master-job:
- script: "echo hello world, $CI_COMMIT_REF_NAME"
- rules:
- - if: $CI_COMMIT_REF_NAME == "nonexistant-branch"
- when: never
- - if: $CI_COMMIT_REF_NAME =~ /master/
- when: manual
-
- negligible-job:
- script: "exit 1"
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /master/
- allow_failure: true
-
- delayed-job:
- script: "echo See you later, World!"
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /master/
- when: delayed
- start_in: 1 hour
-
- never-job:
- script: "echo Goodbye, World!"
- rules:
- - if: $CI_COMMIT_REF_NAME
- when: never
- EOY
- end
-
- context 'with matches' do
- it 'creates a pipeline with the vanilla and manual jobs' do
- expect(pipeline).to be_persisted
- expect(build_names).to contain_exactly(
- 'regular-job', 'delayed-job', 'master-job', 'negligible-job'
- )
- end
-
- it 'assigns job:when values to the builds' do
- expect(find_job('regular-job').when).to eq('on_success')
- expect(find_job('master-job').when).to eq('manual')
- expect(find_job('negligible-job').when).to eq('on_success')
- expect(find_job('delayed-job').when).to eq('delayed')
- end
-
- it 'assigns job:allow_failure values to the builds' do
- expect(find_job('regular-job').allow_failure).to eq(false)
- expect(find_job('master-job').allow_failure).to eq(false)
- expect(find_job('negligible-job').allow_failure).to eq(true)
- expect(find_job('delayed-job').allow_failure).to eq(false)
- end
-
- it 'assigns start_in for delayed jobs' do
- expect(delayed_job.options[:start_in]).to eq('1 hour')
- end
- end
-
- context 'with no matches' do
- let(:ref_name) { 'refs/heads/feature' }
-
- it_behaves_like 'rules jobs are excluded'
- end
- end
-
- context 'with complex if: clauses' do
- let(:config) do
- <<-EOY
- regular-job:
- script: 'echo Hello, World!'
- rules:
- - if: $VAR == 'present' && $OTHER || $CI_COMMIT_REF_NAME
- when: manual
- allow_failure: true
- EOY
- end
-
- it 'matches the first rule' do
- expect(pipeline).to be_persisted
- expect(build_names).to contain_exactly('regular-job')
- expect(regular_job.when).to eq('manual')
- expect(regular_job.allow_failure).to eq(true)
- end
- end
-
- context 'with changes:' do
- let(:config) do
- <<-EOY
- regular-job:
- script: 'echo Hello, World!'
-
- rules-job:
- script: "echo hello world, $CI_COMMIT_REF_NAME"
- rules:
- - changes:
- - README.md
- when: manual
- - changes:
- - app.rb
- when: on_success
-
- delayed-job:
- script: "echo See you later, World!"
- rules:
- - changes:
- - README.md
- when: delayed
- start_in: 4 hours
-
- negligible-job:
- script: "can be failed sometimes"
- rules:
- - changes:
- - README.md
- allow_failure: true
-
- README:
- script: "I use variables for changes!"
- rules:
- - changes:
- - $CI_JOB_NAME*
-
- changes-paths:
- script: "I am using a new syntax!"
- rules:
- - changes:
- paths: [README.md]
- EOY
- end
-
- context 'and matches' do
- before do
- allow_any_instance_of(Ci::Pipeline)
- .to receive(:modified_paths).and_return(%w[README.md])
- end
-
- it 'creates five jobs' do
- expect(pipeline).to be_persisted
- expect(build_names).to contain_exactly(
- 'regular-job', 'rules-job', 'delayed-job', 'negligible-job', 'README', 'changes-paths'
- )
- end
-
- it 'sets when: for all jobs' do
- expect(regular_job.when).to eq('on_success')
- expect(rules_job.when).to eq('manual')
- expect(delayed_job.when).to eq('delayed')
- expect(delayed_job.options[:start_in]).to eq('4 hours')
- end
-
- it 'sets allow_failure: for negligible job' do
- expect(find_job('negligible-job').allow_failure).to eq(true)
- end
- end
-
- context 'and matches the second rule' do
- before do
- allow_any_instance_of(Ci::Pipeline)
- .to receive(:modified_paths).and_return(%w[app.rb])
- end
-
- it 'includes both jobs' do
- expect(pipeline).to be_persisted
- expect(build_names).to contain_exactly('regular-job', 'rules-job')
- end
-
- it 'sets when: for the created rules job based on the second clause' do
- expect(regular_job.when).to eq('on_success')
- expect(rules_job.when).to eq('on_success')
- end
- end
-
- context 'and does not match' do
- before do
- allow_any_instance_of(Ci::Pipeline)
- .to receive(:modified_paths).and_return(%w[useless_script.rb])
- end
-
- it_behaves_like 'rules jobs are excluded'
-
- it 'sets when: for the created job' do
- expect(regular_job.when).to eq('on_success')
- end
- end
- end
-
- context 'with changes: paths and compare_to' do
- before_all do
- project.repository.add_branch(user, 'feature_1', 'master')
-
- project.repository.create_file(
- user, 'file1.txt', 'file 1', message: 'Create file1.txt', branch_name: 'feature_1'
- )
-
- project.repository.add_branch(user, 'feature_2', 'feature_1')
-
- project.repository.create_file(
- user, 'file2.txt', 'file 2', message: 'Create file2.txt', branch_name: 'feature_2'
- )
- end
-
- let(:changed_file) { 'file2.txt' }
- let(:ref_name) { 'feature_2' }
-
- let(:response) { execute_service(ref: ref_name, before: nil, after: project.commit(ref_name).sha) }
-
- context 'for jobs rules' do
- let(:config) do
- <<-EOY
- job1:
- script: exit 0
- rules:
- - changes:
- paths: [#{changed_file}]
- compare_to: #{compare_to}
-
- job2:
- script: exit 0
- EOY
- end
-
- context 'when there is no such compare_to ref' do
- let(:compare_to) { 'invalid-branch' }
-
- it 'returns an error' do
- expect(pipeline.errors.full_messages).to eq([
- 'Failed to parse rule for job1: rules:changes:compare_to is not a valid ref'
- ])
- end
-
- context 'when the FF ci_rules_changes_compare is not enabled' do
- before do
- stub_feature_flags(ci_rules_changes_compare: false)
- end
-
- it 'ignores compare_to and changes is always true' do
- expect(build_names).to contain_exactly('job1', 'job2')
- end
- end
- end
-
- context 'when the compare_to ref exists' do
- let(:compare_to) { 'feature_1'}
-
- context 'when the rule matches' do
- it 'creates job1 and job2' do
- expect(build_names).to contain_exactly('job1', 'job2')
- end
-
- context 'when the FF ci_rules_changes_compare is not enabled' do
- before do
- stub_feature_flags(ci_rules_changes_compare: false)
- end
-
- it 'ignores compare_to and changes is always true' do
- expect(build_names).to contain_exactly('job1', 'job2')
- end
- end
- end
-
- context 'when the rule does not match' do
- let(:changed_file) { 'file1.txt' }
-
- it 'does not create job1' do
- expect(build_names).to contain_exactly('job2')
- end
-
- context 'when the FF ci_rules_changes_compare is not enabled' do
- before do
- stub_feature_flags(ci_rules_changes_compare: false)
- end
-
- it 'ignores compare_to and changes is always true' do
- expect(build_names).to contain_exactly('job1', 'job2')
- end
- end
- end
- end
- end
-
- context 'for workflow rules' do
- let(:config) do
- <<-EOY
- workflow:
- rules:
- - changes:
- paths: [#{changed_file}]
- compare_to: #{compare_to}
-
- job1:
- script: exit 0
- EOY
- end
-
- let(:compare_to) { 'feature_1'}
-
- context 'when the rule matches' do
- it 'creates job1' do
- expect(pipeline).to be_created_successfully
- expect(build_names).to contain_exactly('job1')
- end
-
- context 'when the FF ci_rules_changes_compare is not enabled' do
- before do
- stub_feature_flags(ci_rules_changes_compare: false)
- end
-
- it 'ignores compare_to and changes is always true' do
- expect(pipeline).to be_created_successfully
- expect(build_names).to contain_exactly('job1')
- end
- end
- end
-
- context 'when the rule does not match' do
- let(:changed_file) { 'file1.txt' }
-
- it 'does not create job1' do
- expect(pipeline).not_to be_created_successfully
- expect(build_names).to be_empty
- end
- end
- end
- end
-
- context 'with mixed if: and changes: rules' do
- let(:config) do
- <<-EOY
- regular-job:
- script: 'echo Hello, World!'
-
- rules-job:
- script: "echo hello world, $CI_COMMIT_REF_NAME"
- allow_failure: true
- rules:
- - changes:
- - README.md
- when: manual
- - if: $CI_COMMIT_REF_NAME == "master"
- when: on_success
- allow_failure: false
-
- delayed-job:
- script: "echo See you later, World!"
- rules:
- - changes:
- - README.md
- when: delayed
- start_in: 4 hours
- allow_failure: true
- - if: $CI_COMMIT_REF_NAME == "master"
- when: delayed
- start_in: 1 hour
- EOY
- end
-
- context 'and changes: matches before if' do
- before do
- allow_any_instance_of(Ci::Pipeline)
- .to receive(:modified_paths).and_return(%w[README.md])
- end
-
- it 'creates two jobs' do
- expect(pipeline).to be_persisted
- expect(build_names)
- .to contain_exactly('regular-job', 'rules-job', 'delayed-job')
- end
-
- it 'sets when: for all jobs' do
- expect(regular_job.when).to eq('on_success')
- expect(rules_job.when).to eq('manual')
- expect(delayed_job.when).to eq('delayed')
- expect(delayed_job.options[:start_in]).to eq('4 hours')
- end
-
- it 'sets allow_failure: for all jobs' do
- expect(regular_job.allow_failure).to eq(false)
- expect(rules_job.allow_failure).to eq(true)
- expect(delayed_job.allow_failure).to eq(true)
- end
- end
-
- context 'and if: matches after changes' do
- it 'includes both jobs' do
- expect(pipeline).to be_persisted
- expect(build_names).to contain_exactly('regular-job', 'rules-job', 'delayed-job')
- end
-
- it 'sets when: for the created rules job based on the second clause' do
- expect(regular_job.when).to eq('on_success')
- expect(rules_job.when).to eq('on_success')
- expect(delayed_job.when).to eq('delayed')
- expect(delayed_job.options[:start_in]).to eq('1 hour')
- end
- end
-
- context 'and does not match' do
- let(:ref_name) { 'refs/heads/wip' }
-
- it_behaves_like 'rules jobs are excluded'
-
- it 'sets when: for the created job' do
- expect(regular_job.when).to eq('on_success')
- end
- end
- end
-
- context 'with mixed if: and changes: clauses' do
- let(:config) do
- <<-EOY
- regular-job:
- script: 'echo Hello, World!'
-
- rules-job:
- script: "echo hello world, $CI_COMMIT_REF_NAME"
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /master/
- changes: [README.md]
- when: on_success
- allow_failure: true
- - if: $CI_COMMIT_REF_NAME =~ /master/
- changes: [app.rb]
- when: manual
- EOY
- end
-
- context 'with if matches and changes matches' do
- before do
- allow_any_instance_of(Ci::Pipeline)
- .to receive(:modified_paths).and_return(%w[app.rb])
- end
-
- it 'persists all jobs' do
- expect(pipeline).to be_persisted
- expect(regular_job).to be_persisted
- expect(rules_job).to be_persisted
- expect(rules_job.when).to eq('manual')
- expect(rules_job.allow_failure).to eq(false)
- end
- end
-
- context 'with if matches and no change matches' do
- it_behaves_like 'rules jobs are excluded'
- end
-
- context 'with change matches and no if matches' do
- let(:ref_name) { 'refs/heads/feature' }
-
- before do
- allow_any_instance_of(Ci::Pipeline)
- .to receive(:modified_paths).and_return(%w[README.md])
- end
-
- it_behaves_like 'rules jobs are excluded'
- end
-
- context 'and no matches' do
- let(:ref_name) { 'refs/heads/feature' }
-
- it_behaves_like 'rules jobs are excluded'
- end
- end
-
- context 'with complex if: allow_failure usages' do
- let(:config) do
- <<-EOY
- job-1:
- script: "exit 1"
- allow_failure: true
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /master/
- allow_failure: false
-
- job-2:
- script: "exit 1"
- allow_failure: true
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
- allow_failure: false
-
- job-3:
- script: "exit 1"
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
- allow_failure: true
-
- job-4:
- script: "exit 1"
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /master/
- allow_failure: false
-
- job-5:
- script: "exit 1"
- allow_failure: false
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /master/
- allow_failure: true
-
- job-6:
- script: "exit 1"
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
- allow_failure: false
- - allow_failure: true
- EOY
- end
-
- it 'creates a pipeline' do
- expect(pipeline).to be_persisted
- expect(build_names).to contain_exactly('job-1', 'job-4', 'job-5', 'job-6')
- end
-
- it 'assigns job:allow_failure values to the builds' do
- expect(find_job('job-1').allow_failure).to eq(false)
- expect(find_job('job-4').allow_failure).to eq(false)
- expect(find_job('job-5').allow_failure).to eq(true)
- expect(find_job('job-6').allow_failure).to eq(true)
- end
- end
-
- context 'with complex if: allow_failure & when usages' do
- let(:config) do
- <<-EOY
- job-1:
- script: "exit 1"
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /master/
- when: manual
-
- job-2:
- script: "exit 1"
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /master/
- when: manual
- allow_failure: true
-
- job-3:
- script: "exit 1"
- allow_failure: true
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /master/
- when: manual
-
- job-4:
- script: "exit 1"
- allow_failure: true
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /master/
- when: manual
- allow_failure: false
-
- job-5:
- script: "exit 1"
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
- when: manual
- allow_failure: false
- - when: always
- allow_failure: true
-
- job-6:
- script: "exit 1"
- allow_failure: false
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /master/
- when: manual
-
- job-7:
- script: "exit 1"
- allow_failure: false
- rules:
- - if: $CI_COMMIT_REF_NAME =~ /nonexistant-branch/
- when: manual
- - when: :on_failure
- allow_failure: true
- EOY
- end
-
- it 'creates a pipeline' do
- expect(pipeline).to be_persisted
- expect(build_names).to contain_exactly(
- 'job-1', 'job-2', 'job-3', 'job-4', 'job-5', 'job-6', 'job-7'
- )
- end
-
- it 'assigns job:allow_failure values to the builds' do
- expect(find_job('job-1').allow_failure).to eq(false)
- expect(find_job('job-2').allow_failure).to eq(true)
- expect(find_job('job-3').allow_failure).to eq(true)
- expect(find_job('job-4').allow_failure).to eq(false)
- expect(find_job('job-5').allow_failure).to eq(true)
- expect(find_job('job-6').allow_failure).to eq(false)
- expect(find_job('job-7').allow_failure).to eq(true)
- end
-
- it 'assigns job:when values to the builds' do
- expect(find_job('job-1').when).to eq('manual')
- expect(find_job('job-2').when).to eq('manual')
- expect(find_job('job-3').when).to eq('manual')
- expect(find_job('job-4').when).to eq('manual')
- expect(find_job('job-5').when).to eq('always')
- expect(find_job('job-6').when).to eq('manual')
- expect(find_job('job-7').when).to eq('on_failure')
- end
- end
-
- context 'with deploy freeze period `if:` clause' do
- # '0 23 * * 5' == "At 23:00 on Friday."", '0 7 * * 1' == "At 07:00 on Monday.""
- let!(:freeze_period) { create(:ci_freeze_period, project: project, freeze_start: '0 23 * * 5', freeze_end: '0 7 * * 1') }
-
- context 'with 2 jobs' do
- let(:config) do
- <<-EOY
- stages:
- - test
- - deploy
-
- test-job:
- script:
- - echo 'running TEST stage'
-
- deploy-job:
- stage: deploy
- script:
- - echo 'running DEPLOY stage'
- rules:
- - if: $CI_DEPLOY_FREEZE == null
- EOY
- end
-
- context 'when outside freeze period' do
- it 'creates two jobs' do
- Timecop.freeze(2020, 4, 10, 22, 59) do
- expect(pipeline).to be_persisted
- expect(build_names).to contain_exactly('test-job', 'deploy-job')
- end
- end
- end
-
- context 'when inside freeze period' do
- it 'creates one job' do
- Timecop.freeze(2020, 4, 10, 23, 1) do
- expect(pipeline).to be_persisted
- expect(build_names).to contain_exactly('test-job')
- end
- end
- end
- end
-
- context 'with 1 job' do
- let(:config) do
- <<-EOY
- stages:
- - deploy
-
- deploy-job:
- stage: deploy
- script:
- - echo 'running DEPLOY stage'
- rules:
- - if: $CI_DEPLOY_FREEZE == null
- EOY
- end
-
- context 'when outside freeze period' do
- it 'creates two jobs' do
- Timecop.freeze(2020, 4, 10, 22, 59) do
- expect(pipeline).to be_persisted
- expect(build_names).to contain_exactly('deploy-job')
- end
- end
- end
-
- context 'when inside freeze period' do
- it 'does not create the pipeline', :aggregate_failures do
- Timecop.freeze(2020, 4, 10, 23, 1) do
- expect(response).to be_error
- expect(pipeline).not_to be_persisted
- end
- end
- end
- end
- end
-
- context 'with workflow rules with persisted variables' do
- let(:config) do
- <<-EOY
- workflow:
- rules:
- - if: $CI_COMMIT_REF_NAME == "master"
-
- regular-job:
- script: 'echo Hello, World!'
- EOY
- end
-
- context 'with matches' do
- it 'creates a pipeline' do
- expect(pipeline).to be_persisted
- expect(build_names).to contain_exactly('regular-job')
- end
- end
-
- context 'with no matches' do
- let(:ref_name) { 'refs/heads/feature' }
-
- it 'does not create a pipeline', :aggregate_failures do
- expect(response).to be_error
- expect(pipeline).not_to be_persisted
- end
- end
- end
-
- context 'with workflow rules with pipeline variables' do
- let(:pipeline) do
- execute_service(variables_attributes: variables_attributes).payload
- end
-
- let(:config) do
- <<-EOY
- workflow:
- rules:
- - if: $SOME_VARIABLE
-
- regular-job:
- script: 'echo Hello, World!'
- EOY
- end
-
- context 'with matches' do
- let(:variables_attributes) do
- [{ key: 'SOME_VARIABLE', secret_value: 'SOME_VAR' }]
- end
-
- it 'creates a pipeline' do
- expect(pipeline).to be_persisted
- expect(build_names).to contain_exactly('regular-job')
- end
- end
-
- context 'with no matches' do
- let(:variables_attributes) { {} }
-
- it 'does not create a pipeline', :aggregate_failures do
- expect(response).to be_error
- expect(pipeline).not_to be_persisted
- end
- end
- end
-
- context 'with workflow rules with trigger variables' do
- let(:pipeline) do
- execute_service do |pipeline|
- pipeline.variables.build(variables)
- end.payload
- end
-
- let(:config) do
- <<-EOY
- workflow:
- rules:
- - if: $SOME_VARIABLE
-
- regular-job:
- script: 'echo Hello, World!'
- EOY
- end
-
- context 'with matches' do
- let(:variables) do
- [{ key: 'SOME_VARIABLE', secret_value: 'SOME_VAR' }]
- end
-
- it 'creates a pipeline' do
- expect(pipeline).to be_persisted
- expect(build_names).to contain_exactly('regular-job')
- end
-
- context 'when a job requires the same variable' do
- let(:config) do
- <<-EOY
- workflow:
- rules:
- - if: $SOME_VARIABLE
-
- build:
- stage: build
- script: 'echo build'
- rules:
- - if: $SOME_VARIABLE
-
- test1:
- stage: test
- script: 'echo test1'
- needs: [build]
-
- test2:
- stage: test
- script: 'echo test2'
- EOY
- end
-
- it 'creates a pipeline' do
- expect(pipeline).to be_persisted
- expect(build_names).to contain_exactly('build', 'test1', 'test2')
- end
- end
- end
-
- context 'with no matches' do
- let(:variables) { {} }
-
- it 'does not create a pipeline', :aggregate_failures do
- expect(response).to be_error
- expect(pipeline).not_to be_persisted
- end
-
- context 'when a job requires the same variable' do
- let(:config) do
- <<-EOY
- workflow:
- rules:
- - if: $SOME_VARIABLE
-
- build:
- stage: build
- script: 'echo build'
- rules:
- - if: $SOME_VARIABLE
-
- test1:
- stage: test
- script: 'echo test1'
- needs: [build]
-
- test2:
- stage: test
- script: 'echo test2'
- EOY
- end
-
- it 'does not create a pipeline', :aggregate_failures do
- expect(response).to be_error
- expect(pipeline).not_to be_persisted
- end
- end
- end
- end
-
- context 'with workflow rules changes' do
- shared_examples 'comparing file changes with workflow rules' do
- context 'when matches' do
- before do
- allow_next_instance_of(Ci::Pipeline) do |pipeline|
- allow(pipeline).to receive(:modified_paths).and_return(%w[file1.md])
- end
- end
-
- it 'creates the pipeline with a job' do
- expect(pipeline).to be_persisted
- expect(build_names).to contain_exactly('job')
- end
- end
-
- context 'when does not match' do
- before do
- allow_next_instance_of(Ci::Pipeline) do |pipeline|
- allow(pipeline).to receive(:modified_paths).and_return(%w[unknown])
- end
- end
-
- it 'creates the pipeline with a job' do
- expect(pipeline.errors.full_messages).to eq(['Pipeline filtered out by workflow rules.'])
- expect(response).to be_error
- expect(pipeline).not_to be_persisted
- end
- end
- end
-
- context 'changes is an array' do
- let(:config) do
- <<-EOY
- workflow:
- rules:
- - changes: [file1.md]
-
- job:
- script: exit 0
- EOY
- end
-
- it_behaves_like 'comparing file changes with workflow rules'
- end
-
- context 'changes:paths is an array' do
- let(:config) do
- <<-EOY
- workflow:
- rules:
- - changes:
- paths: [file1.md]
-
- job:
- script: exit 0
- EOY
- end
-
- it_behaves_like 'comparing file changes with workflow rules'
- end
- end
- end
end
describe '#execute!' do
diff --git a/spec/services/uploads/destroy_service_spec.rb b/spec/services/uploads/destroy_service_spec.rb
new file mode 100644
index 00000000000..bb58da231b6
--- /dev/null
+++ b/spec/services/uploads/destroy_service_spec.rb
@@ -0,0 +1,103 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Uploads::DestroyService do
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { create(:user) }
+ let_it_be_with_reload(:upload) { create(:upload, :issuable_upload, model: project) }
+
+ let(:filename) { File.basename(upload.path) }
+ let(:secret) { upload.secret }
+ let(:model) { project }
+ let(:service) { described_class.new(model, user) }
+
+ describe '#execute' do
+ subject { service.execute(secret, filename) }
+
+ shared_examples_for 'upload not found' do
+ it 'does not delete any upload' do
+ expect { subject }.not_to change { Upload.count }
+ end
+
+ it 'returns an error' do
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:message]).to eq("The resource that you are attempting to access does not "\
+ "exist or you don't have permission to perform this action.")
+ end
+ end
+
+ context 'when user is nil' do
+ let(:user) { nil }
+
+ it_behaves_like 'upload not found'
+ end
+
+ context 'when user cannot destroy upload' do
+ before do
+ project.add_developer(user)
+ end
+
+ it_behaves_like 'upload not found'
+ end
+
+ context 'when user can destroy upload' do
+ before do
+ project.add_maintainer(user)
+ end
+
+ it 'deletes the upload' do
+ expect { subject }.to change { Upload.count }.by(-1)
+ end
+
+ it 'returns success response' do
+ expect(subject[:status]).to eq(:success)
+ expect(subject[:upload]).to eq(upload)
+ end
+
+ context 'when upload is not found' do
+ let(:filename) { 'not existing filename' }
+
+ it_behaves_like 'upload not found'
+ end
+
+ context 'when upload secret is not found' do
+ let(:secret) { 'aaaaaaaaaa' }
+
+ it_behaves_like 'upload not found'
+ end
+
+ context 'when upload secret has invalid format' do
+ let(:secret) { 'invalid' }
+
+ it_behaves_like 'upload not found'
+ end
+
+ context 'when unknown model is used' do
+ let(:model) { user }
+
+ it 'raises an error' do
+ expect { subject }.to raise_exception(ArgumentError)
+ end
+ end
+
+ context 'when upload belongs to other model' do
+ let_it_be(:upload) { create(:upload, :namespace_upload) }
+
+ it_behaves_like 'upload not found'
+ end
+
+ context 'when upload destroy fails' do
+ before do
+ allow(service).to receive(:find_upload).and_return(upload)
+ allow(upload).to receive(:destroy).and_return(false)
+ end
+
+ it 'returns error' do
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:message]).to eq('Upload could not be deleted.')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_contexts/policies/group_policy_shared_context.rb b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
index eec6e92c5fe..893d3702407 100644
--- a/spec/support/shared_contexts/policies/group_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/group_policy_shared_context.rb
@@ -56,6 +56,7 @@ RSpec.shared_context 'GroupPolicy context' do
admin_package
create_projects
create_cluster update_cluster admin_cluster add_cluster
+ destroy_upload
]
end
diff --git a/spec/support/shared_contexts/policies/project_policy_shared_context.rb b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
index 789b385c435..1d4731d9b39 100644
--- a/spec/support/shared_contexts/policies/project_policy_shared_context.rb
+++ b/spec/support/shared_contexts/policies/project_policy_shared_context.rb
@@ -62,6 +62,7 @@ RSpec.shared_context 'ProjectPolicy context' do
admin_project admin_project_member admin_snippet admin_terraform_state
admin_wiki create_deploy_token destroy_deploy_token
push_to_delete_protected_branch read_deploy_token update_snippet
+ destroy_upload
]
end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index 4ddb793516f..d632ca39e44 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -452,6 +452,12 @@ RSpec.describe PostReceive do
perform
end
+ it 'updates the snippet model updated_at' do
+ expect(snippet).to receive(:touch)
+
+ perform
+ end
+
it 'updates snippet statistics' do
expect(Snippets::UpdateStatisticsService).to receive(:new).with(snippet).and_call_original
diff --git a/tooling/config/CODEOWNERS.yml b/tooling/config/CODEOWNERS.yml
index a8ae90437e2..18f3abfc97b 100644
--- a/tooling/config/CODEOWNERS.yml
+++ b/tooling/config/CODEOWNERS.yml
@@ -3,7 +3,7 @@
# And paste the contents into .gitlab/CODEOWNERS
'[Authentication and Authorization]':
- '@gitlab-org/manage/authentication-and-authorization':
+ '@gitlab-org/manage/authentication-and-authorization/approvers':
allow:
keywords:
- 'password'