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--.rubocop_todo/layout/line_length.yml128
-rw-r--r--.rubocop_todo/style/format_string.yml16
-rw-r--r--app/assets/javascripts/jobs/components/job/manual_variables_form.vue16
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/extensions/code_quality/index.js4
-rw-r--r--app/assets/javascripts/vue_shared/components/runner_instructions/constants.js18
-rw-r--r--app/assets/javascripts/vue_shared/components/runner_instructions/instructions/runner_cli_instructions.vue169
-rw-r--r--app/assets/javascripts/vue_shared/components/runner_instructions/instructions/runner_docker_instructions.vue35
-rw-r--r--app/assets/javascripts/vue_shared/components/runner_instructions/instructions/runner_kubernetes_instructions.vue35
-rw-r--r--app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions_modal.vue199
-rw-r--r--app/controllers/admin/application_settings_controller.rb6
-rw-r--r--app/controllers/admin/groups_controller.rb4
-rw-r--r--app/controllers/admin/impersonation_tokens_controller.rb4
-rw-r--r--app/controllers/admin/projects_controller.rb2
-rw-r--r--app/controllers/admin/spam_logs_controller.rb2
-rw-r--r--app/controllers/admin/topics_controller.rb6
-rw-r--r--app/controllers/admin/users_controller.rb8
-rw-r--r--app/controllers/concerns/access_tokens_actions.rb4
-rw-r--r--app/controllers/concerns/confirm_email_warning.rb11
-rw-r--r--app/controllers/concerns/enforces_two_factor_authentication.rb5
-rw-r--r--app/controllers/concerns/integrations/actions.rb4
-rw-r--r--app/controllers/concerns/membership_actions.rb7
-rw-r--r--app/controllers/concerns/redirects_for_missing_path_on_tree.rb2
-rw-r--r--app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb2
-rw-r--r--app/controllers/concerns/verifies_with_email.rb6
-rw-r--r--app/controllers/groups/settings/ci_cd_controller.rb4
-rw-r--r--app/helpers/version_check_helper.rb2
-rw-r--r--config/feature_flags/development/critical_security_alert.yml8
-rw-r--r--doc/administration/geo/replication/troubleshooting.md29
-rw-r--r--doc/administration/reference_architectures/10k_users.md2
-rw-r--r--doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md2
-rw-r--r--doc/development/elasticsearch.md16
-rw-r--r--doc/install/installation.md24
-rw-r--r--doc/raketasks/backup_gitlab.md2
-rw-r--r--doc/user/ssh.md3
-rw-r--r--scripts/rspec_helpers.sh46
-rw-r--r--spec/factories/personal_access_tokens.rb6
-rw-r--r--spec/frontend/jobs/components/job/manual_variables_form_spec.js21
-rw-r--r--spec/frontend/vue_merge_request_widget/extentions/code_quality/index_spec.js29
-rw-r--r--spec/frontend/vue_merge_request_widget/extentions/code_quality/mock_data.js25
-rw-r--r--spec/frontend/vue_shared/components/runner_instructions/instructions/__snapshots__/runner_docker_instructions_spec.js.snap3
-rw-r--r--spec/frontend/vue_shared/components/runner_instructions/instructions/__snapshots__/runner_kubernetes_instructions_spec.js.snap3
-rw-r--r--spec/frontend/vue_shared/components/runner_instructions/instructions/runner_cli_instructions_spec.js169
-rw-r--r--spec/frontend/vue_shared/components/runner_instructions/instructions/runner_docker_instructions_spec.js28
-rw-r--r--spec/frontend/vue_shared/components/runner_instructions/instructions/runner_kubernetes_instructions_spec.js28
-rw-r--r--spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js191
-rw-r--r--spec/helpers/version_check_helper_spec.rb21
-rw-r--r--spec/migrations/20210804150320_create_base_work_item_types_spec.rb3
-rw-r--r--spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb2
-rw-r--r--spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb2
-rw-r--r--spec/migrations/20221018050323_add_objective_and_keyresult_to_work_item_types_spec.rb2
-rw-r--r--spec/support/helpers/api_helpers.rb9
-rw-r--r--spec/tooling/danger/specs_spec.rb33
-rw-r--r--tooling/danger/specs.rb31
53 files changed, 870 insertions, 567 deletions
diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml
index c47e47c74e9..4d67bb9448c 100644
--- a/.rubocop_todo/layout/line_length.yml
+++ b/.rubocop_todo/layout/line_length.yml
@@ -17,7 +17,6 @@ Layout/LineLength:
- 'app/controllers/concerns/analytics/cycle_analytics/stage_actions.rb'
- 'app/controllers/concerns/clientside_preview_csp.rb'
- 'app/controllers/concerns/confirm_email_warning.rb'
- - 'app/controllers/concerns/cycle_analytics_params.rb'
- 'app/controllers/concerns/integrations/actions.rb'
- 'app/controllers/concerns/issuable_actions.rb'
- 'app/controllers/concerns/issuable_collections.rb'
@@ -328,7 +327,6 @@ Layout/LineLength:
- 'app/models/concerns/enums/vulnerability.rb'
- 'app/models/concerns/fast_destroy_all.rb'
- 'app/models/concerns/group_descendant.rb'
- - 'app/models/concerns/has_user_type.rb'
- 'app/models/concerns/id_in_ordered.rb'
- 'app/models/concerns/ignorable_columns.rb'
- 'app/models/concerns/iid_routes.rb'
@@ -464,7 +462,6 @@ Layout/LineLength:
- 'app/models/wiki_page.rb'
- 'app/policies/base_policy.rb'
- 'app/policies/global_policy.rb'
- - 'app/policies/group_member_policy.rb'
- 'app/policies/group_policy.rb'
- 'app/policies/project_policy.rb'
- 'app/presenters/blob_presenter.rb'
@@ -519,12 +516,7 @@ Layout/LineLength:
- 'app/services/ci/runners/unregister_runner_service.rb'
- 'app/services/clusters/agent_tokens/create_service.rb'
- 'app/services/clusters/agents/delete_service.rb'
- - 'app/services/clusters/applications/check_progress_service.rb'
- - 'app/services/clusters/aws/finalize_creation_service.rb'
- - 'app/services/clusters/aws/verify_provision_status_service.rb'
- 'app/services/clusters/build_kubernetes_namespace_service.rb'
- - 'app/services/clusters/gcp/finalize_creation_service.rb'
- - 'app/services/clusters/gcp/verify_provision_status_service.rb'
- 'app/services/clusters/integrations/create_service.rb'
- 'app/services/clusters/integrations/prometheus_health_check_service.rb'
- 'app/services/clusters/kubernetes/create_or_update_service_account_service.rb'
@@ -716,7 +708,6 @@ Layout/LineLength:
- 'app/workers/merge_request_mergeability_check_worker.rb'
- 'app/workers/object_storage/migrate_uploads_worker.rb'
- 'app/workers/packages/maven/metadata/sync_worker.rb'
- - 'app/workers/personal_access_tokens/expired_notification_worker.rb'
- 'app/workers/pipeline_metrics_worker.rb'
- 'app/workers/repository_fork_worker.rb'
- 'app/workers/repository_import_worker.rb'
@@ -765,49 +756,6 @@ Layout/LineLength:
- 'danger/roulette/Dangerfile'
- 'danger/vue_shared_documentation/Dangerfile'
- 'danger/z_metadata/Dangerfile'
- - 'db/migrate/20210302103851_add_deployed_deployment_id_index_to_project_pages_metadata.rb'
- - 'db/migrate/20210302155904_remove_index_for_security_orchestration_policy.rb'
- - 'db/migrate/20210302160544_add_index_to_security_orchestration_policy.rb'
- - 'db/migrate/20210305031822_create_dast_site_profile_variables.rb'
- - 'db/migrate/20210305182855_create_ci_unit_test_failures.rb'
- - 'db/migrate/20210313045845_add_verification_indexes_to_snippet_repositories.rb'
- - 'db/migrate/20210316171009_create_packages_helm_file_metadata.rb'
- - 'db/migrate/20210317035357_create_dast_profiles_pipelines.rb'
- - 'db/migrate/20210317123054_add_throttle_package_registry_columns.rb'
- - 'db/migrate/20210323131543_add_external_approval_rule_foreign_key_to_status_check_responses.rb'
- - 'db/migrate/20210325152011_add_verification_indexes_to_ci_pipeline_artifacts.rb'
- - 'db/migrate/20210326190903_create_vulnerability_finding_evidences.rb'
- - 'db/migrate/20210409084242_create_index_on_notes_for_cherry_picked_merge_requests.rb'
- - 'db/migrate/20210412111213_create_security_orchestration_policy_rule_schedule.rb'
- - 'db/migrate/20210414133310_add_bulk_import_export_uploads_table.rb'
- - 'db/migrate/20210415142700_add_url_limit_to_pipeline_validation.rb'
- - 'db/migrate/20210415172516_create_vulnerability_finding_evidence_requests.rb'
- - 'db/migrate/20210416172516_create_vulnerability_finding_evidence_responses.rb'
- - 'db/migrate/20210420173030_add_verification_indexes_to_terraform_state_versions.rb'
- - 'db/migrate/20210420210642_recreate_index_for_project_deployments_with_environment_id_and_date_at.rb'
- - 'db/migrate/20210422142647_add_project_id_next_run_at_index_to_container_expiration_policies.rb'
- - 'db/migrate/20210422195929_create_elastic_reindexing_slices.rb'
- - 'db/migrate/20210423054022_create_dast_site_profiles_pipelines.rb'
- - 'db/migrate/20210423054537_add_dast_site_profile_id_fk_to_dast_site_profiles_pipelines.rb'
- - 'db/migrate/20210423054846_add_ci_pipeline_id_fk_to_dast_site_profiles_pipelines.rb'
- - 'db/migrate/20210423171304_re_order_fk_source_project_id_in_merge_requests.rb'
- - 'db/migrate/20210427062807_add_index_to_batched_migration_jobs_status.rb'
- - 'db/migrate/20210427094931_add_execution_order_index_to_batched_background_migration_jobs.rb'
- - 'db/migrate/20210429032320_add_escalation_rules.rb'
- - 'db/migrate/20210505170152_add_verification_indexes_to_merge_request_diff_details_table.rb'
- - 'db/migrate/20210506150833_create_vulnerability_finding_evidence_headers.rb'
- - 'db/migrate/20210511104929_add_epic_board_recent_visits_table.rb'
- - 'db/migrate/20210511165250_add_foreign_key_to_lfs_objects_projects.rb'
- - 'db/migrate/20210512120122_add_pending_builds_table.rb'
- - 'db/migrate/20210521073920_drop_devops_adoption_namespace_uniqueness.rb'
- - 'db/migrate/20210526181821_add_foreign_key_for_latest_pipeline_id_to_ci_pipelines.rb'
- - 'db/migrate/20210527194558_create_ci_job_token_project_scope_links.rb'
- - 'db/migrate/20210529164247_change_iterations_title_uniqueness_index.rb'
- - 'db/migrate/20210601123341_add_running_builds_table.rb'
- - 'db/migrate/20210601125410_add_runners_created_at_index.rb'
- - 'db/migrate/20210601132134_remove_partial_index_for_hashed_storage_migration.rb'
- - 'db/migrate/20210601133459_replace_runners_contacted_at_index.rb'
- - 'db/migrate/20210602122233_add_runners_description_index.rb'
- 'db/migrate/20210604032738_create_dast_site_profiles_builds.rb'
- 'db/migrate/20210604034354_add_dast_site_profile_id_fk_to_dast_site_profiles_builds.rb'
- 'db/migrate/20210604051330_create_dast_scanner_profiles_builds.rb'
@@ -911,16 +859,6 @@ Layout/LineLength:
- 'db/migrate/20220310101118_update_holder_name_limit.rb'
- 'db/migrate/20220314184209_add_group_fk_to_protected_environment_approval_rules.rb'
- 'db/migrate/20220314204009_add_approval_rule_fk_to_deployment_approvals.rb'
- - 'db/post_migrate/20210328214434_remove_temporary_index_from_vulnerabilities_table.rb'
- - 'db/post_migrate/20210401131948_move_container_registry_enabled_to_project_features2.rb'
- - 'db/post_migrate/20210402005225_add_source_and_level_index_on_notification_settings.rb'
- - 'db/post_migrate/20210407150240_confirm_support_bot_user.rb'
- - 'db/post_migrate/20210415155043_move_container_registry_enabled_to_project_features3.rb'
- - 'db/post_migrate/20210430121542_backfill_ci_build_trace_sections_for_bigint_conversion.rb'
- - 'db/post_migrate/20210505092746_create_partial_covering_index_for_pending_builds.rb'
- - 'db/post_migrate/20210513163904_cleanup_move_container_registry_enabled_to_project_feature.rb'
- - 'db/post_migrate/20210514063252_schedule_cleanup_orphaned_lfs_objects_projects.rb'
- - 'db/post_migrate/20210526160133_remove_segment_selections_table.rb'
- 'db/post_migrate/20210606143426_add_index_for_container_registry_access_level.rb'
- 'db/post_migrate/20210611080951_fix_missing_traversal_ids.rb'
- 'db/post_migrate/20210615234935_fix_batched_migrations_old_format_job_arguments.rb'
@@ -968,7 +906,6 @@ Layout/LineLength:
- 'db/post_migrate/20211112113300_remove_ci_pipeline_chat_data_fk_on_chat_names.rb'
- 'db/post_migrate/20211118194239_drop_invalid_remediations.rb'
- 'db/post_migrate/20211201101541_drop_clusters_applications_runners_ci_runners_fk.rb'
- - 'db/post_migrate/20211206162601_cleanup_after_add_primary_email_to_emails_if_user_confirmed.rb'
- 'db/post_migrate/20211207173510_remove_extra_finding_evidence_tables_foreign_keys.rb'
- 'db/post_migrate/20211207173511_remove_extra_finding_evidence_tables.rb'
- 'db/post_migrate/20211209103048_backfill_project_namespaces_for_group.rb'
@@ -1174,7 +1111,6 @@ Layout/LineLength:
- 'ee/app/helpers/billing_plans_helper.rb'
- 'ee/app/helpers/ee/application_helper.rb'
- 'ee/app/helpers/ee/button_helper.rb'
- - 'ee/app/helpers/ee/environments_helper.rb'
- 'ee/app/helpers/ee/feature_flags_helper.rb'
- 'ee/app/helpers/ee/geo_helper.rb'
- 'ee/app/helpers/ee/groups/analytics/cycle_analytics_helper.rb'
@@ -1428,7 +1364,6 @@ Layout/LineLength:
- 'ee/app/services/merge_trains/create_pipeline_service.rb'
- 'ee/app/services/merge_trains/refresh_merge_request_service.rb'
- 'ee/app/services/personal_access_tokens/rotation_verifier_service.rb'
- - 'ee/app/services/projects/licenses/create_policy_service.rb'
- 'ee/app/services/projects/mark_for_deletion_service.rb'
- 'ee/app/services/projects/update_mirror_service.rb'
- 'ee/app/services/resource_events/change_weight_service.rb'
@@ -1535,9 +1470,7 @@ Layout/LineLength:
- 'ee/lib/api/project_push_rule.rb'
- 'ee/lib/api/protected_environments.rb'
- 'ee/lib/api/resource_iteration_events.rb'
- - 'ee/lib/api/scim.rb'
- 'ee/lib/api/status_checks.rb'
- - 'ee/lib/api/vulnerability_findings.rb'
- 'ee/lib/api/vulnerability_issue_links.rb'
- 'ee/lib/ee/api/deployments.rb'
- 'ee/lib/ee/api/entities/application_setting.rb'
@@ -1589,7 +1522,6 @@ Layout/LineLength:
- 'ee/lib/ee/gitlab/ci/pipeline/chain/create_cross_database_associations.rb'
- 'ee/lib/ee/gitlab/ci/pipeline/chain/validate/after_config.rb'
- 'ee/lib/ee/gitlab/ci/pipeline/chain/validate/security_orchestration_policy.rb'
- - 'ee/lib/ee/gitlab/ci/reports/security/reports.rb'
- 'ee/lib/ee/gitlab/ci/status/build/manual.rb'
- 'ee/lib/ee/gitlab/git_access.rb'
- 'ee/lib/ee/gitlab/import_export/after_export_strategies/custom_template_export_import_strategy.rb'
@@ -1600,7 +1532,6 @@ Layout/LineLength:
- 'ee/lib/ee/gitlab/quick_actions/issue_actions.rb'
- 'ee/lib/ee/gitlab/rack_attack.rb'
- 'ee/lib/ee/gitlab/repository_size_checker.rb'
- - 'ee/lib/ee/gitlab/scim/deprovision_service.rb'
- 'ee/lib/ee/gitlab/usage_data.rb'
- 'ee/lib/ee/sidebars/groups/panel.rb'
- 'ee/lib/ee/sidebars/projects/menus/security_compliance_menu.rb'
@@ -1857,7 +1788,6 @@ Layout/LineLength:
- 'ee/spec/finders/productivity_analytics_finder_spec.rb'
- 'ee/spec/finders/projects/integrations/jira/by_ids_finder_spec.rb'
- 'ee/spec/finders/projects/integrations/jira/issues_finder_spec.rb'
- - 'ee/spec/finders/requirements_management/requirements_finder_spec.rb'
- 'ee/spec/finders/security/findings_finder_spec.rb'
- 'ee/spec/finders/security/pipeline_vulnerabilities_finder_spec.rb'
- 'ee/spec/finders/security/training_providers/base_url_finder_spec.rb'
@@ -2038,14 +1968,12 @@ Layout/LineLength:
- 'ee/spec/lib/ee/gitlab/elastic/helper_spec.rb'
- 'ee/spec/lib/ee/gitlab/email/handler/service_desk_handler_spec.rb'
- 'ee/spec/lib/ee/gitlab/etag_caching/router/rails_spec.rb'
- - 'ee/spec/lib/ee/gitlab/git_access_snippet_spec.rb'
- 'ee/spec/lib/ee/gitlab/gon_helper_spec.rb'
- 'ee/spec/lib/ee/gitlab/group_search_results_spec.rb'
- 'ee/spec/lib/ee/gitlab/import_export/project/tree_restorer_spec.rb'
- 'ee/spec/lib/ee/gitlab/import_export/wiki_repo_saver_spec.rb'
- 'ee/spec/lib/ee/gitlab/repo_path_spec.rb'
- 'ee/spec/lib/ee/gitlab/repository_size_checker_spec.rb'
- - 'ee/spec/lib/ee/gitlab/scim/deprovision_service_spec.rb'
- 'ee/spec/lib/ee/gitlab/security/scan_configuration_spec.rb'
- 'ee/spec/lib/ee/gitlab/url_builder_spec.rb'
- 'ee/spec/lib/ee/gitlab/usage/service_ping/payload_keys_processor_spec.rb'
@@ -2264,7 +2192,6 @@ Layout/LineLength:
- 'ee/spec/models/ee/project_authorization_spec.rb'
- 'ee/spec/models/ee/protected_branch_spec.rb'
- 'ee/spec/models/ee/service_desk_setting_spec.rb'
- - 'ee/spec/models/ee/user_highest_role_spec.rb'
- 'ee/spec/models/ee/user_spec.rb'
- 'ee/spec/models/ee/vulnerability_spec.rb'
- 'ee/spec/models/elastic/migration_record_spec.rb'
@@ -2424,7 +2351,6 @@ Layout/LineLength:
- 'ee/spec/requests/api/graphql/project/incident_management/escalation_policies_spec.rb'
- 'ee/spec/requests/api/graphql/project/incident_management/escalation_policy/rules_spec.rb'
- 'ee/spec/requests/api/graphql/project/incident_management/oncall_shifts_spec.rb'
- - 'ee/spec/requests/api/graphql/project/issues_spec.rb'
- 'ee/spec/requests/api/graphql/project/pipeline/security_report_summary_spec.rb'
- 'ee/spec/requests/api/graphql/project/requirements_management/requirements_spec.rb'
- 'ee/spec/requests/api/graphql/project/vulnerability_severities_count_spec.rb'
@@ -2460,7 +2386,6 @@ Layout/LineLength:
- 'ee/spec/requests/api/related_epic_links_spec.rb'
- 'ee/spec/requests/api/releases_spec.rb'
- 'ee/spec/requests/api/resource_iteration_events_spec.rb'
- - 'ee/spec/requests/api/scim_spec.rb'
- 'ee/spec/requests/api/search_spec.rb'
- 'ee/spec/requests/api/settings_spec.rb'
- 'ee/spec/requests/api/status_checks_spec.rb'
@@ -2585,7 +2510,6 @@ Layout/LineLength:
- 'ee/spec/services/ee/issues/move_service_spec.rb'
- 'ee/spec/services/ee/issues/update_service_spec.rb'
- 'ee/spec/services/ee/members/destroy_service_spec.rb'
- - 'ee/spec/services/ee/merge_requests/after_create_service_spec.rb'
- 'ee/spec/services/ee/merge_requests/create_from_vulnerability_data_service_spec.rb'
- 'ee/spec/services/ee/merge_requests/post_merge_service_spec.rb'
- 'ee/spec/services/ee/merge_requests/refresh_service_spec.rb'
@@ -2828,7 +2752,6 @@ Layout/LineLength:
- 'ee/spec/workers/geo/destroy_worker_spec.rb'
- 'ee/spec/workers/geo/project_sync_worker_spec.rb'
- 'ee/spec/workers/geo/prune_event_log_worker_spec.rb'
- - 'ee/spec/workers/geo/registry_sync_worker_spec.rb'
- 'ee/spec/workers/geo/repositories_clean_up_worker_spec.rb'
- 'ee/spec/workers/geo/repository_shard_sync_worker_spec.rb'
- 'ee/spec/workers/geo/repository_verification/primary/shard_worker_spec.rb'
@@ -3035,7 +2958,6 @@ Layout/LineLength:
- 'lib/gitlab/auth/o_auth/user.rb'
- 'lib/gitlab/auth/saml/auth_hash.rb'
- 'lib/gitlab/auth/user_access_denied_reason.rb'
- - 'lib/gitlab/background_migration/add_primary_email_to_emails_if_user_confirmed.rb'
- 'lib/gitlab/background_migration/backfill_issue_search_data.rb'
- 'lib/gitlab/background_migration/backfill_iteration_cadence_id_for_boards.rb'
- 'lib/gitlab/background_migration/backfill_snippet_repositories.rb'
@@ -3084,7 +3006,6 @@ Layout/LineLength:
- 'lib/gitlab/ci/config/entry/trigger.rb'
- 'lib/gitlab/ci/config/external/file/project.rb'
- 'lib/gitlab/ci/config/external/file/remote.rb'
- - 'lib/gitlab/ci/config/external/mapper.rb'
- 'lib/gitlab/ci/parsers/coverage/cobertura.rb'
- 'lib/gitlab/ci/parsers/coverage/sax_document.rb'
- 'lib/gitlab/ci/parsers/security/common.rb'
@@ -3099,7 +3020,6 @@ Layout/LineLength:
- 'lib/gitlab/ci/pipeline/seed/build.rb'
- 'lib/gitlab/ci/reports/codequality_reports.rb'
- 'lib/gitlab/ci/reports/security/finding.rb'
- - 'lib/gitlab/ci/reports/security/reports.rb'
- 'lib/gitlab/ci/reports/test_case.rb'
- 'lib/gitlab/ci/reports/test_suite.rb'
- 'lib/gitlab/ci/reports/test_suite_comparer.rb'
@@ -3307,7 +3227,6 @@ Layout/LineLength:
- 'lib/gitlab/quick_actions/relate_actions.rb'
- 'lib/gitlab/rack_attack.rb'
- 'lib/gitlab/redis/wrapper.rb'
- - 'lib/gitlab/reference_extractor.rb'
- 'lib/gitlab/regex.rb'
- 'lib/gitlab/relative_positioning/item_context.rb'
- 'lib/gitlab/repository_size_error_message.rb'
@@ -3424,15 +3343,9 @@ Layout/LineLength:
- 'qa/qa/resource/protected_branch.rb'
- 'qa/qa/resource/registry_repository.rb'
- 'qa/qa/resource/repository/push.rb'
- - 'qa/qa/resource/reusable.rb'
- - 'qa/qa/resource/reusable_collection.rb'
- - 'qa/qa/resource/reusable_group.rb'
- - 'qa/qa/resource/reusable_project.rb'
- - 'qa/qa/resource/runner.rb'
- 'qa/qa/resource/snippet.rb'
- 'qa/qa/resource/wiki/group_page.rb'
- 'qa/qa/runtime/api/repository_storage_moves.rb'
- - 'qa/qa/runtime/application_settings.rb'
- 'qa/qa/runtime/env.rb'
- 'qa/qa/runtime/feature.rb'
- 'qa/qa/runtime/fixtures.rb'
@@ -3547,9 +3460,7 @@ Layout/LineLength:
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/license/cloud_activation_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/license/license_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/free_trial_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/purchase_ci_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/purchase_storage_spec.rb'
- - 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/upgrade_group_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/11_fulfillment/purchase/user_registration_billing_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/13_secure/enable_scanning_from_configuration_spec.rb'
- 'qa/qa/specs/features/ee/browser_ui/13_secure/license_compliance_spec.rb'
@@ -3630,7 +3541,6 @@ Layout/LineLength:
- 'scripts/changed-feature-flags'
- 'scripts/failed_tests.rb'
- 'scripts/flaky_examples/prune-old-flaky-examples'
- - 'scripts/lib/gitlab.rb'
- 'scripts/lint_templates_bash.rb'
- 'scripts/no-dir-check'
- 'scripts/perf/query_limiting_report.rb'
@@ -3744,7 +3654,6 @@ Layout/LineLength:
- 'spec/db/schema_spec.rb'
- 'spec/deprecation_toolkit_env.rb'
- 'spec/experiments/concerns/project_commit_count_spec.rb'
- - 'spec/factories/ci/builds.rb'
- 'spec/factories/ci/job_artifacts.rb'
- 'spec/factories/ci/pipelines.rb'
- 'spec/factories/ci/reports/codequality_degradations.rb'
@@ -3840,7 +3749,6 @@ Layout/LineLength:
- 'spec/features/markdown/gitlab_flavored_markdown_spec.rb'
- 'spec/features/markdown/metrics_spec.rb'
- 'spec/features/merge_request/batch_comments_spec.rb'
- - 'spec/features/merge_request/maintainer_edits_fork_spec.rb'
- 'spec/features/merge_request/user_accepts_merge_request_spec.rb'
- 'spec/features/merge_request/user_allows_commits_from_memebers_who_can_merge_spec.rb'
- 'spec/features/merge_request/user_assigns_themselves_spec.rb'
@@ -3961,7 +3869,6 @@ Layout/LineLength:
- 'spec/features/security/project/snippet/public_access_spec.rb'
- 'spec/features/signed_commits_spec.rb'
- 'spec/features/snippets/embedded_snippet_spec.rb'
- - 'spec/features/snippets/spam_snippets_spec.rb'
- 'spec/features/snippets/user_edits_snippet_spec.rb'
- 'spec/features/task_lists_spec.rb'
- 'spec/features/unsubscribe_links_spec.rb'
@@ -4172,7 +4079,6 @@ Layout/LineLength:
- 'spec/helpers/users_helper_spec.rb'
- 'spec/helpers/visibility_level_helper_spec.rb'
- 'spec/helpers/webpack_helper_spec.rb'
- - 'spec/helpers/wiki_helper_spec.rb'
- 'spec/helpers/wiki_page_version_helper_spec.rb'
- 'spec/initializers/00_rails_disable_joins_spec.rb'
- 'spec/initializers/6_validations_spec.rb'
@@ -4300,7 +4206,6 @@ Layout/LineLength:
- 'spec/lib/gitlab/auth/user_access_denied_reason_spec.rb'
- 'spec/lib/gitlab/auth_spec.rb'
- 'spec/lib/gitlab/authorized_keys_spec.rb'
- - 'spec/lib/gitlab/background_migration/add_primary_email_to_emails_if_user_confirmed_spec.rb'
- 'spec/lib/gitlab/background_migration/backfill_issue_search_data_spec.rb'
- 'spec/lib/gitlab/background_migration/backfill_member_namespace_for_group_members_spec.rb'
- 'spec/lib/gitlab/background_migration/backfill_project_repositories_spec.rb'
@@ -4384,7 +4289,6 @@ Layout/LineLength:
- 'spec/lib/gitlab/ci/pipeline_object_hierarchy_spec.rb'
- 'spec/lib/gitlab/ci/reports/codequality_mr_diff_spec.rb'
- 'spec/lib/gitlab/ci/reports/security/flag_spec.rb'
- - 'spec/lib/gitlab/ci/reports/security/reports_spec.rb'
- 'spec/lib/gitlab/ci/reports/security/scanner_spec.rb'
- 'spec/lib/gitlab/ci/reports/security/vulnerability_reports_comparer_spec.rb'
- 'spec/lib/gitlab/ci/runner_upgrade_check_spec.rb'
@@ -4550,7 +4454,6 @@ Layout/LineLength:
- 'spec/lib/gitlab/import_export/base/relation_object_saver_spec.rb'
- 'spec/lib/gitlab/import_export/command_line_util_spec.rb'
- 'spec/lib/gitlab/import_export/fast_hash_serializer_spec.rb'
- - 'spec/lib/gitlab/import_export/group/legacy_tree_restorer_spec.rb'
- 'spec/lib/gitlab/import_export/import_failure_service_spec.rb'
- 'spec/lib/gitlab/import_export/importer_spec.rb'
- 'spec/lib/gitlab/import_export/json/ndjson_reader_spec.rb'
@@ -4658,7 +4561,6 @@ Layout/LineLength:
- 'spec/lib/gitlab/search_results_spec.rb'
- 'spec/lib/gitlab/serializer/pagination_spec.rb'
- 'spec/lib/gitlab/serverless/service_spec.rb'
- - 'spec/lib/gitlab/shell_spec.rb'
- 'spec/lib/gitlab/sidekiq_config/worker_router_spec.rb'
- 'spec/lib/gitlab/sidekiq_daemon/memory_killer_spec.rb'
- 'spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb'
@@ -4750,10 +4652,6 @@ Layout/LineLength:
- 'spec/mailers/emails/releases_spec.rb'
- 'spec/mailers/emails/service_desk_spec.rb'
- 'spec/mailers/notify_spec.rb'
- - 'spec/migrations/20210423160427_schedule_drop_invalid_vulnerabilities_spec.rb'
- - 'spec/migrations/20210511142748_schedule_drop_invalid_vulnerabilities2_spec.rb'
- - 'spec/migrations/20210514063252_schedule_cleanup_orphaned_lfs_objects_projects_spec.rb'
- - 'spec/migrations/20210601073400_fix_total_stage_in_vsa_spec.rb'
- 'spec/migrations/20210610153556_delete_legacy_operations_feature_flags_spec.rb'
- 'spec/migrations/2021061716138_cascade_delete_freeze_periods_spec.rb'
- 'spec/migrations/20210713042000_fix_ci_sources_pipelines_index_names_spec.rb'
@@ -4784,19 +4682,11 @@ Layout/LineLength:
- 'spec/migrations/add_upvotes_count_index_to_issues_spec.rb'
- 'spec/migrations/backfill_all_project_namespaces_spec.rb'
- 'spec/migrations/backfill_cadence_id_for_boards_scoped_to_iteration_spec.rb'
- - 'spec/migrations/backfill_clusters_integration_prometheus_enabled_spec.rb'
- - 'spec/migrations/backfill_escalation_policies_for_oncall_schedules_spec.rb'
- - 'spec/migrations/backfill_nuget_temporary_packages_to_processing_status_spec.rb'
- 'spec/migrations/backfill_project_namespaces_for_group_spec.rb'
- - 'spec/migrations/cleanup_after_add_primary_email_to_emails_if_user_confirmed_spec.rb'
- 'spec/migrations/cleanup_after_fixing_issue_when_admin_changed_primary_email_spec.rb'
- - 'spec/migrations/cleanup_move_container_registry_enabled_to_project_feature_spec.rb'
- - 'spec/migrations/confirm_support_bot_user_spec.rb'
- - 'spec/migrations/delete_security_findings_without_uuid_spec.rb'
- 'spec/migrations/finalize_project_namespaces_backfill_spec.rb'
- 'spec/migrations/fix_and_backfill_project_namespaces_for_projects_with_duplicate_name_spec.rb'
- 'spec/migrations/fix_batched_migrations_old_format_job_arguments_spec.rb'
- - 'spec/migrations/populate_dismissal_information_for_vulnerabilities_spec.rb'
- 'spec/migrations/re_schedule_latest_pipeline_id_population_with_all_security_related_artifact_types_spec.rb'
- 'spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_features_spec.rb'
- 'spec/migrations/recreate_index_security_ci_builds_on_name_and_id_parser_with_new_features_spec.rb'
@@ -4805,11 +4695,8 @@ Layout/LineLength:
- 'spec/migrations/rename_services_to_integrations_spec.rb'
- 'spec/migrations/replace_external_wiki_triggers_spec.rb'
- 'spec/migrations/reset_severity_levels_to_new_default_spec.rb'
- - 'spec/migrations/schedule_add_primary_email_to_emails_if_user_confirmed_spec.rb'
- 'spec/migrations/schedule_recalculate_vulnerability_finding_signatures_for_findings_spec.rb'
- 'spec/migrations/schedule_update_timelogs_null_spent_at_spec.rb'
- - 'spec/migrations/schedule_update_timelogs_project_id_spec.rb'
- - 'spec/migrations/schedule_update_users_where_two_factor_auth_required_from_group_spec.rb'
- 'spec/migrations/update_invalid_member_states_spec.rb'
- 'spec/models/active_session_spec.rb'
- 'spec/models/acts_as_taggable_on/tag_spec.rb'
@@ -4832,12 +4719,9 @@ Layout/LineLength:
- 'spec/models/ci/build_spec.rb'
- 'spec/models/ci/build_trace_chunk_spec.rb'
- 'spec/models/ci/daily_build_group_report_result_spec.rb'
- - 'spec/models/ci/freeze_period_status_spec.rb'
- 'spec/models/ci/group_variable_spec.rb'
- 'spec/models/ci/instance_variable_spec.rb'
- 'spec/models/ci/job_artifact_spec.rb'
- - 'spec/models/ci/job_token/scope_spec.rb'
- - 'spec/models/ci/pipeline_schedule_spec.rb'
- 'spec/models/ci/pipeline_spec.rb'
- 'spec/models/ci/processable_spec.rb'
- 'spec/models/ci/ref_spec.rb'
@@ -4859,7 +4743,6 @@ Layout/LineLength:
- 'spec/models/concerns/clusters/agents/authorization_config_scopes_spec.rb'
- 'spec/models/concerns/deployment_platform_spec.rb'
- 'spec/models/concerns/group_descendant_spec.rb'
- - 'spec/models/concerns/has_user_type_spec.rb'
- 'spec/models/concerns/id_in_ordered_spec.rb'
- 'spec/models/concerns/ignorable_columns_spec.rb'
- 'spec/models/concerns/integrations/has_data_fields_spec.rb'
@@ -5123,7 +5006,6 @@ Layout/LineLength:
- 'spec/requests/api/graphql/project/container_repositories_spec.rb'
- 'spec/requests/api/graphql/project/issue/designs/designs_spec.rb'
- 'spec/requests/api/graphql/project/jira_import_spec.rb'
- - 'spec/requests/api/graphql/project/jobs_spec.rb'
- 'spec/requests/api/graphql/project/milestones_spec.rb'
- 'spec/requests/api/graphql/project/pipeline_spec.rb'
- 'spec/requests/api/graphql/project/project_statistics_spec.rb'
@@ -5169,7 +5051,6 @@ Layout/LineLength:
- 'spec/requests/api/npm_project_packages_spec.rb'
- 'spec/requests/api/nuget_group_packages_spec.rb'
- 'spec/requests/api/nuget_project_packages_spec.rb'
- - 'spec/requests/api/oauth_tokens_spec.rb'
- 'spec/requests/api/pages/internal_access_spec.rb'
- 'spec/requests/api/pages/private_access_spec.rb'
- 'spec/requests/api/pages/public_access_spec.rb'
@@ -5220,10 +5101,8 @@ Layout/LineLength:
- 'spec/requests/projects/incident_management/pagerduty_incidents_spec.rb'
- 'spec/requests/projects/issue_links_controller_spec.rb'
- 'spec/requests/projects/issues/discussions_spec.rb'
- - 'spec/requests/projects/issues_controller_spec.rb'
- 'spec/requests/projects/merge_requests/content_spec.rb'
- 'spec/requests/projects/merge_requests/context_commit_diffs_spec.rb'
- - 'spec/requests/projects/merge_requests_controller_spec.rb'
- 'spec/requests/projects/merge_requests_discussions_spec.rb'
- 'spec/requests/projects/merge_requests_spec.rb'
- 'spec/requests/projects/metrics/dashboards/builder_spec.rb'
@@ -5315,8 +5194,6 @@ Layout/LineLength:
- 'spec/services/ci/test_failure_history_service_spec.rb'
- 'spec/services/ci/unlock_artifacts_service_spec.rb'
- 'spec/services/ci/update_pending_build_service_spec.rb'
- - 'spec/services/clusters/aws/fetch_credentials_service_spec.rb'
- - 'spec/services/clusters/aws/provision_service_spec.rb'
- 'spec/services/clusters/create_service_spec.rb'
- 'spec/services/clusters/integrations/prometheus_health_check_service_spec.rb'
- 'spec/services/clusters/kubernetes/create_or_update_namespace_service_spec.rb'
@@ -5496,8 +5373,6 @@ Layout/LineLength:
- 'spec/services/projects/import_export/export_service_spec.rb'
- 'spec/services/projects/import_service_spec.rb'
- 'spec/services/projects/lfs_pointers/lfs_download_service_spec.rb'
- - 'spec/services/projects/lfs_pointers/lfs_import_service_spec.rb'
- - 'spec/services/projects/lfs_pointers/lfs_object_download_list_service_spec.rb'
- 'spec/services/projects/operations/update_service_spec.rb'
- 'spec/services/projects/overwrite_project_service_spec.rb'
- 'spec/services/projects/transfer_service_spec.rb'
@@ -5617,7 +5492,6 @@ Layout/LineLength:
- 'spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb'
- 'spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb'
- 'spec/support/shared_examples/features/2fa_shared_examples.rb'
- - 'spec/support/shared_examples/features/container_registry_shared_examples.rb'
- 'spec/support/shared_examples/features/discussion_comments_shared_example.rb'
- 'spec/support/shared_examples/features/editable_merge_request_shared_examples.rb'
- 'spec/support/shared_examples/features/error_tracking_shared_example.rb'
@@ -5743,7 +5617,6 @@ Layout/LineLength:
- 'spec/support/shared_examples/uploaders/upload_type_shared_examples.rb'
- 'spec/support/shared_examples/views/registration_features_prompt_shared_examples.rb'
- 'spec/support/shared_examples/workers/concerns/dependency_proxy/cleanup_worker_shared_examples.rb'
- - 'spec/support/shared_examples/workers/concerns/git_garbage_collect_methods_shared_examples.rb'
- 'spec/support/shared_examples/workers/gitlab/jira_import/jira_import_workers_shared_examples.rb'
- 'spec/support/shared_examples/workers/project_export_shared_examples.rb'
- 'spec/support_specs/database/prevent_cross_joins_spec.rb'
@@ -5821,7 +5694,6 @@ Layout/LineLength:
- 'spec/views/shared/milestones/_issuable.html.haml_spec.rb'
- 'spec/views/shared/projects/_project.html.haml_spec.rb'
- 'spec/views/shared/snippets/_snippet.html.haml_spec.rb'
- - 'spec/views/shared/ssh_keys/_key_details.html.haml_spec.rb'
- 'spec/views/shared/wikis/_sidebar.html.haml_spec.rb'
- 'spec/workers/analytics/usage_trends/counter_job_worker_spec.rb'
- 'spec/workers/authorized_project_update/project_recalculate_per_user_worker_spec.rb'
diff --git a/.rubocop_todo/style/format_string.yml b/.rubocop_todo/style/format_string.yml
index c1ba754edca..827a6a66998 100644
--- a/.rubocop_todo/style/format_string.yml
+++ b/.rubocop_todo/style/format_string.yml
@@ -3,22 +3,6 @@
Style/FormatString:
Exclude:
- 'app/components/diffs/overflow_warning_component.rb'
- - 'app/controllers/admin/application_settings_controller.rb'
- - 'app/controllers/admin/groups_controller.rb'
- - 'app/controllers/admin/impersonation_tokens_controller.rb'
- - 'app/controllers/admin/projects_controller.rb'
- - 'app/controllers/admin/spam_logs_controller.rb'
- - 'app/controllers/admin/topics_controller.rb'
- - 'app/controllers/admin/users_controller.rb'
- - 'app/controllers/concerns/access_tokens_actions.rb'
- - 'app/controllers/concerns/confirm_email_warning.rb'
- - 'app/controllers/concerns/enforces_two_factor_authentication.rb'
- - 'app/controllers/concerns/integrations/actions.rb'
- - 'app/controllers/concerns/membership_actions.rb'
- - 'app/controllers/concerns/redirects_for_missing_path_on_tree.rb'
- - 'app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb'
- - 'app/controllers/concerns/verifies_with_email.rb'
- - 'app/controllers/groups/settings/ci_cd_controller.rb'
- 'app/controllers/import/bitbucket_server_controller.rb'
- 'app/controllers/import/bulk_imports_controller.rb'
- 'app/controllers/import/fogbugz_controller.rb'
diff --git a/app/assets/javascripts/jobs/components/job/manual_variables_form.vue b/app/assets/javascripts/jobs/components/job/manual_variables_form.vue
index d7bbd6daed2..734d3ca0d49 100644
--- a/app/assets/javascripts/jobs/components/job/manual_variables_form.vue
+++ b/app/assets/javascripts/jobs/components/job/manual_variables_form.vue
@@ -68,6 +68,7 @@ export default {
required: true,
},
},
+ clearBtnSharedClasses: ['gl-flex-grow-0 gl-flex-basis-0'],
inputTypes: {
key: 'key',
value: 'value',
@@ -229,16 +230,23 @@ export default {
v-gl-tooltip
:aria-label="$options.i18n.clearInputs"
:title="$options.i18n.clearInputs"
- class="gl-flex-grow-0 gl-flex-basis-0"
+ :class="$options.clearBtnSharedClasses"
category="tertiary"
variant="danger"
icon="clear"
data-testid="delete-variable-btn"
@click="deleteVariable(variable.id)"
/>
-
- <!-- delete variable button placeholder to not break flex layout -->
- <div v-else class="gl-w-7 gl-mr-3" data-testid="delete-variable-btn-placeholder"></div>
+ <!-- Placeholder button to keep the layout fixed -->
+ <gl-button
+ v-else
+ class="gl-opacity-0 gl-pointer-events-none"
+ :class="$options.clearBtnSharedClasses"
+ data-testid="delete-variable-btn-placeholder"
+ category="tertiary"
+ variant="danger"
+ icon="clear"
+ />
</div>
<div class="gl-text-center gl-mt-5">
diff --git a/app/assets/javascripts/vue_merge_request_widget/extensions/code_quality/index.js b/app/assets/javascripts/vue_merge_request_widget/extensions/code_quality/index.js
index 23f83b8d6cc..4f9bba1e0cb 100644
--- a/app/assets/javascripts/vue_merge_request_widget/extensions/code_quality/index.js
+++ b/app/assets/javascripts/vue_merge_request_widget/extensions/code_quality/index.js
@@ -38,8 +38,10 @@ export default {
statusIcon() {
if (this.collapsedData.newErrors.length >= 1) {
return EXTENSION_ICONS.warning;
+ } else if (this.collapsedData.resolvedErrors.length >= 1) {
+ return EXTENSION_ICONS.success;
}
- return EXTENSION_ICONS.success;
+ return EXTENSION_ICONS.neutral;
},
},
methods: {
diff --git a/app/assets/javascripts/vue_shared/components/runner_instructions/constants.js b/app/assets/javascripts/vue_shared/components/runner_instructions/constants.js
index c97e191b630..ac83cb78bc0 100644
--- a/app/assets/javascripts/vue_shared/components/runner_instructions/constants.js
+++ b/app/assets/javascripts/vue_shared/components/runner_instructions/constants.js
@@ -1,18 +1,4 @@
-import { s__ } from '~/locale';
-
export const REGISTRATION_TOKEN_PLACEHOLDER = '$REGISTRATION_TOKEN';
-export const INSTRUCTIONS_PLATFORMS_WITHOUT_ARCHITECTURES = {
- docker: {
- instructions: s__(
- 'Runners|To install Runner in a container follow the instructions described in the GitLab documentation',
- ),
- link: 'https://docs.gitlab.com/runner/install/docker.html',
- },
- kubernetes: {
- instructions: s__(
- 'Runners|To install Runner in Kubernetes follow the instructions described in the GitLab documentation.',
- ),
- link: 'https://docs.gitlab.com/runner/install/kubernetes.html',
- },
-};
+export const PLATFORM_DOCKER = 'docker';
+export const PLATFORM_KUBERNETES = 'kubernetes';
diff --git a/app/assets/javascripts/vue_shared/components/runner_instructions/instructions/runner_cli_instructions.vue b/app/assets/javascripts/vue_shared/components/runner_instructions/instructions/runner_cli_instructions.vue
new file mode 100644
index 00000000000..36e608a068b
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/runner_instructions/instructions/runner_cli_instructions.vue
@@ -0,0 +1,169 @@
+<script>
+import { GlButton, GlDropdown, GlDropdownItem, GlLoadingIcon } from '@gitlab/ui';
+import { s__ } from '~/locale';
+import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
+import { REGISTRATION_TOKEN_PLACEHOLDER } from '../constants';
+import getRunnerSetupInstructionsQuery from '../graphql/get_runner_setup.query.graphql';
+
+export default {
+ components: {
+ GlButton,
+ GlDropdown,
+ GlDropdownItem,
+ GlLoadingIcon,
+ ModalCopyButton,
+ },
+ props: {
+ platform: {
+ type: Object,
+ required: false,
+ default: null,
+ },
+ registrationToken: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+ data() {
+ return {
+ selectedArchitecture: this.platform?.architectures[0] || null,
+ instructions: null,
+ };
+ },
+ apollo: {
+ instructions: {
+ query: getRunnerSetupInstructionsQuery,
+ skip() {
+ return !this.platform || !this.selectedArchitecture;
+ },
+ variables() {
+ return {
+ platform: this.platform.name,
+ architecture: this.selectedArchitecture.name,
+ };
+ },
+ update(data) {
+ return data?.runnerSetup;
+ },
+ error() {
+ this.$emit('error');
+ },
+ },
+ },
+ computed: {
+ architectures() {
+ return this.platform?.architectures || [];
+ },
+ binaryUrl() {
+ return this.selectedArchitecture?.downloadLocation;
+ },
+ registerInstructionsWithToken() {
+ const { registerInstructions } = this.instructions || {};
+
+ if (this.registrationToken) {
+ return registerInstructions?.replace(
+ REGISTRATION_TOKEN_PLACEHOLDER,
+ this.registrationToken,
+ );
+ }
+ return registerInstructions;
+ },
+ },
+ watch: {
+ platform() {
+ // reset selection if architecture is not in this list
+ const arch = this.architectures.find(({ name }) => name === this.selectedArchitecture.name);
+ if (!arch) {
+ this.selectArchitecture(this.architectures[0]);
+ }
+ },
+ },
+ methods: {
+ selectArchitecture(architecture) {
+ this.selectedArchitecture = architecture;
+ },
+ onClose() {
+ this.$emit('close');
+ },
+ },
+ i18n: {
+ architecture: s__('Runners|Architecture'),
+ downloadInstallBinary: s__('Runners|Download and install binary'),
+ downloadLatestBinary: s__('Runners|Download latest binary'),
+ registerRunnerCommand: s__('Runners|Command to register runner'),
+ copyInstructions: s__('Runners|Copy instructions'),
+ },
+};
+</script>
+
+<template>
+ <div>
+ <h5>
+ {{ $options.i18n.architecture }}
+ <gl-loading-icon v-if="$apollo.loading" size="sm" inline />
+ </h5>
+
+ <gl-dropdown class="gl-mb-3" :text="selectedArchitecture.name">
+ <gl-dropdown-item
+ v-for="architecture in architectures"
+ :key="architecture.name"
+ is-check-item
+ :is-checked="selectedArchitecture.name === architecture.name"
+ data-testid="architecture-dropdown-item"
+ @click="selectArchitecture(architecture)"
+ >
+ {{ architecture.name }}
+ </gl-dropdown-item>
+ </gl-dropdown>
+ <div class="gl-sm-display-flex gl-align-items-center gl-mb-3">
+ <h5>{{ $options.i18n.downloadInstallBinary }}</h5>
+ <gl-button
+ v-if="binaryUrl"
+ class="gl-ml-auto"
+ :href="binaryUrl"
+ download
+ icon="download"
+ data-testid="binary-download-button"
+ >
+ {{ $options.i18n.downloadLatestBinary }}
+ </gl-button>
+ </div>
+
+ <template v-if="instructions">
+ <div class="gl-display-flex">
+ <pre
+ class="gl-bg-gray gl-flex-grow-1 gl-white-space-pre-line"
+ data-testid="binary-instructions"
+ >{{ instructions.installInstructions }}</pre
+ >
+ <modal-copy-button
+ :title="$options.i18n.copyInstructions"
+ :text="instructions.installInstructions"
+ :modal-id="$options.modalId"
+ css-classes="gl-align-self-start gl-ml-2 gl-mt-2"
+ category="tertiary"
+ />
+ </div>
+ <h5 class="gl-mb-3">{{ $options.i18n.registerRunnerCommand }}</h5>
+ <div class="gl-display-flex">
+ <pre
+ class="gl-bg-gray gl-flex-grow-1 gl-white-space-pre-line"
+ data-testid="register-command"
+ >{{ registerInstructionsWithToken }}</pre
+ >
+ <modal-copy-button
+ :title="$options.i18n.copyInstructions"
+ :text="registerInstructionsWithToken"
+ :modal-id="$options.modalId"
+ css-classes="gl-align-self-start gl-ml-2 gl-mt-2"
+ category="tertiary"
+ />
+ </div>
+ </template>
+
+ <footer class="gl-display-flex gl-justify-content-end gl-pt-3">
+ <gl-button @click="onClose()">{{ __('Close') }}</gl-button>
+ </footer>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/runner_instructions/instructions/runner_docker_instructions.vue b/app/assets/javascripts/vue_shared/components/runner_instructions/instructions/runner_docker_instructions.vue
new file mode 100644
index 00000000000..ff7e803af2a
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/runner_instructions/instructions/runner_docker_instructions.vue
@@ -0,0 +1,35 @@
+<script>
+import { GlButton, GlIcon } from '@gitlab/ui';
+import { s__ } from '~/locale';
+
+export default {
+ components: {
+ GlButton,
+ GlIcon,
+ },
+ methods: {
+ onClose() {
+ this.$emit('close');
+ },
+ },
+ I18N_INSTRUCTIONS_TEXT: s__(
+ 'Runners|To install Runner in a container follow the instructions described in the GitLab documentation',
+ ),
+ I18N_VIEW_INSTRUCTIONS: s__('Runners|View installation instructions'),
+ HELP_URL: 'https://docs.gitlab.com/runner/install/docker.html',
+};
+</script>
+<template>
+ <div>
+ <p>
+ {{ $options.I18N_INSTRUCTIONS_TEXT }}
+ </p>
+ <gl-button :href="$options.HELP_URL">
+ <gl-icon name="external-link" />
+ {{ $options.I18N_VIEW_INSTRUCTIONS }}
+ </gl-button>
+ <footer class="gl-display-flex gl-justify-content-end gl-pt-3">
+ <gl-button @click="onClose()">{{ __('Close') }}</gl-button>
+ </footer>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/runner_instructions/instructions/runner_kubernetes_instructions.vue b/app/assets/javascripts/vue_shared/components/runner_instructions/instructions/runner_kubernetes_instructions.vue
new file mode 100644
index 00000000000..ee41dab0cec
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/runner_instructions/instructions/runner_kubernetes_instructions.vue
@@ -0,0 +1,35 @@
+<script>
+import { GlButton, GlIcon } from '@gitlab/ui';
+import { s__ } from '~/locale';
+
+export default {
+ components: {
+ GlButton,
+ GlIcon,
+ },
+ methods: {
+ onClose() {
+ this.$emit('close');
+ },
+ },
+ I18N_INSTRUCTIONS_TEXT: s__(
+ 'Runners|To install Runner in Kubernetes follow the instructions described in the GitLab documentation.',
+ ),
+ I18N_VIEW_INSTRUCTIONS: s__('Runners|View installation instructions'),
+ HELP_URL: 'https://docs.gitlab.com/runner/install/kubernetes.html',
+};
+</script>
+<template>
+ <div>
+ <p>
+ {{ $options.I18N_INSTRUCTIONS_TEXT }}
+ </p>
+ <gl-button :href="$options.HELP_URL">
+ <gl-icon name="external-link" />
+ {{ $options.I18N_VIEW_INSTRUCTIONS }}
+ </gl-button>
+ <footer class="gl-display-flex gl-justify-content-end gl-pt-3">
+ <gl-button @click="onClose()">{{ __('Close') }}</gl-button>
+ </footer>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions_modal.vue b/app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions_modal.vue
index f41654c0090..729fe9c462c 100644
--- a/app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions_modal.vue
@@ -12,15 +12,13 @@ import {
GlResizeObserverDirective,
} from '@gitlab/ui';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
-import { isEmpty } from 'lodash';
import { __, s__ } from '~/locale';
-import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
-import {
- INSTRUCTIONS_PLATFORMS_WITHOUT_ARCHITECTURES,
- REGISTRATION_TOKEN_PLACEHOLDER,
-} from './constants';
import getRunnerPlatformsQuery from './graphql/get_runner_platforms.query.graphql';
-import getRunnerSetupInstructionsQuery from './graphql/get_runner_setup.query.graphql';
+import { PLATFORM_DOCKER, PLATFORM_KUBERNETES } from './constants';
+
+import RunnerCliInstructions from './instructions/runner_cli_instructions.vue';
+import RunnerDockerInstructions from './instructions/runner_docker_instructions.vue';
+import RunnerKubernetesInstructions from './instructions/runner_kubernetes_instructions.vue';
export default {
components: {
@@ -33,7 +31,7 @@ export default {
GlIcon,
GlLoadingIcon,
GlSkeletonLoader,
- ModalCopyButton,
+ RunnerDockerInstructions,
},
directives: {
GlResizeObserver: GlResizeObserverDirective,
@@ -74,27 +72,13 @@ export default {
);
},
result() {
- // If it is set and available, select the defaultSelectedPlatform.
+ // If found, select the defaultSelectedPlatform.
// Otherwise, select the first available platform
- this.selectPlatform(this.defaultPlatformName || this.platforms?.[0].name);
- },
- error() {
- this.toggleAlert(true);
- },
- },
- instructions: {
- query: getRunnerSetupInstructionsQuery,
- skip() {
- return !this.shown || !this.selectedPlatform;
- },
- variables() {
- return {
- platform: this.selectedPlatform,
- architecture: this.selectedArchitecture || '',
- };
- },
- update(data) {
- return data?.runnerSetup;
+ const platform =
+ this.platforms?.find(({ name }) => this.defaultPlatformName === name) ||
+ this.platforms?.[0];
+
+ this.selectPlatform(platform);
},
error() {
this.toggleAlert(true);
@@ -106,39 +90,23 @@ export default {
shown: false,
platforms: [],
selectedPlatform: null,
- selectedArchitecture: null,
showAlert: false,
- instructions: {},
platformsButtonGroupVertical: false,
};
},
computed: {
- instructionsEmpty() {
- return isEmpty(this.instructions);
- },
- architectures() {
- return this.platforms.find(({ name }) => name === this.selectedPlatform)?.architectures || [];
- },
- binaryUrl() {
- return this.architectures.find(({ name }) => name === this.selectedArchitecture)
- ?.downloadLocation;
- },
- instructionsWithoutArchitecture() {
- return INSTRUCTIONS_PLATFORMS_WITHOUT_ARCHITECTURES[this.selectedPlatform]?.instructions;
- },
- runnerInstallationLink() {
- return INSTRUCTIONS_PLATFORMS_WITHOUT_ARCHITECTURES[this.selectedPlatform]?.link;
- },
- registerInstructionsWithToken() {
- const { registerInstructions } = this.instructions || {};
-
- if (this.registrationToken) {
- return registerInstructions?.replace(
- REGISTRATION_TOKEN_PLACEHOLDER,
- this.registrationToken,
- );
+ instructionsComponent() {
+ if (this.selectedPlatform?.architectures?.length) {
+ return RunnerCliInstructions;
+ }
+ switch (this.selectedPlatform?.name) {
+ case PLATFORM_DOCKER:
+ return RunnerDockerInstructions;
+ case PLATFORM_KUBERNETES:
+ return RunnerKubernetesInstructions;
+ default:
+ return null;
}
- return registerInstructions;
},
},
updated() {
@@ -149,6 +117,12 @@ export default {
show() {
this.$refs.modal.show();
},
+ close() {
+ this.$refs.modal.close();
+ },
+ onClose() {
+ this.close();
+ },
onShown() {
this.shown = true;
this.refocusSelectedPlatformButton();
@@ -159,21 +133,13 @@ export default {
// get focused when setting a `defaultPlatformName`.
// This method refocuses the expected button.
// See more about this auto-focus: https://bootstrap-vue.org/docs/components/modal#auto-focus-on-open
- this.$refs[this.selectedPlatform]?.[0].$el.focus();
+ this.$refs[this.selectedPlatform?.name]?.[0].$el.focus();
},
- selectPlatform(platformName) {
- this.selectedPlatform = platformName;
-
- // Update architecture when platform changes
- const arch = this.architectures.find(({ name }) => name === this.selectedArchitecture);
- if (arch) {
- this.selectArchitecture(arch.name);
- } else {
- this.selectArchitecture(this.architectures[0]?.name);
- }
+ selectPlatform(platform) {
+ this.selectedPlatform = platform;
},
- selectArchitecture(architecture) {
- this.selectedArchitecture = architecture;
+ isPlatformSelected(platform) {
+ return this.selectedPlatform.name === platform.name;
},
toggleAlert(state) {
this.showAlert = state;
@@ -189,17 +155,9 @@ export default {
i18n: {
environment: __('Environment'),
installARunner: s__('Runners|Install a runner'),
- architecture: s__('Runners|Architecture'),
downloadInstallBinary: s__('Runners|Download and install binary'),
downloadLatestBinary: s__('Runners|Download latest binary'),
- registerRunnerCommand: s__('Runners|Command to register runner'),
fetchError: s__('Runners|An error has occurred fetching instructions'),
- copyInstructions: s__('Runners|Copy instructions'),
- viewInstallationInstructions: s__('Runners|View installation instructions'),
- },
- closeButton: {
- text: __('Close'),
- attributes: [{ variant: 'default' }],
},
};
</script>
@@ -208,8 +166,8 @@ export default {
ref="modal"
:modal-id="modalId"
:title="$options.i18n.installARunner"
- :action-secondary="$options.closeButton"
v-bind="$attrs"
+ hide-footer
v-on="$listeners"
@shown="onShown"
>
@@ -234,88 +192,23 @@ export default {
v-for="platform in platforms"
:key="platform.name"
:ref="platform.name"
- :selected="selectedPlatform === platform.name"
- @click="selectPlatform(platform.name)"
+ :selected="isPlatformSelected(platform)"
+ @click="selectPlatform(platform)"
>
{{ platform.humanReadableName }}
</gl-button>
</gl-button-group>
</div>
</template>
- <template v-if="architectures.length">
- <template v-if="selectedPlatform">
- <h5>
- {{ $options.i18n.architecture }}
- <gl-loading-icon v-if="$apollo.loading" size="sm" inline />
- </h5>
-
- <gl-dropdown class="gl-mb-3" :text="selectedArchitecture">
- <gl-dropdown-item
- v-for="architecture in architectures"
- :key="architecture.name"
- is-check-item
- :is-checked="selectedArchitecture === architecture.name"
- data-testid="architecture-dropdown-item"
- @click="selectArchitecture(architecture.name)"
- >
- {{ architecture.name }}
- </gl-dropdown-item>
- </gl-dropdown>
- <div class="gl-sm-display-flex gl-align-items-center gl-mb-3">
- <h5>{{ $options.i18n.downloadInstallBinary }}</h5>
- <gl-button
- v-if="binaryUrl"
- class="gl-ml-auto"
- :href="binaryUrl"
- download
- icon="download"
- data-testid="binary-download-button"
- >
- {{ $options.i18n.downloadLatestBinary }}
- </gl-button>
- </div>
- </template>
- <template v-if="!instructionsEmpty">
- <div class="gl-display-flex">
- <pre
- class="gl-bg-gray gl-flex-grow-1 gl-white-space-pre-line"
- data-testid="binary-instructions"
- >{{ instructions.installInstructions }}</pre
- >
- <modal-copy-button
- :title="$options.i18n.copyInstructions"
- :text="instructions.installInstructions"
- :modal-id="$options.modalId"
- css-classes="gl-align-self-start gl-ml-2 gl-mt-2"
- category="tertiary"
- />
- </div>
- <h5 class="gl-mb-3">{{ $options.i18n.registerRunnerCommand }}</h5>
- <div class="gl-display-flex">
- <pre
- class="gl-bg-gray gl-flex-grow-1 gl-white-space-pre-line"
- data-testid="register-command"
- >{{ registerInstructionsWithToken }}</pre
- >
- <modal-copy-button
- :title="$options.i18n.copyInstructions"
- :text="registerInstructionsWithToken"
- :modal-id="$options.modalId"
- css-classes="gl-align-self-start gl-ml-2 gl-mt-2"
- category="tertiary"
- />
- </div>
- </template>
- </template>
- <template v-else>
- <div>
- <p>{{ instructionsWithoutArchitecture }}</p>
- <gl-button :href="runnerInstallationLink">
- <gl-icon name="external-link" />
- {{ $options.i18n.viewInstallationInstructions }}
- </gl-button>
- </div>
- </template>
+ <keep-alive>
+ <component
+ :is="instructionsComponent"
+ :registration-token="registrationToken"
+ :platform="selectedPlatform"
+ @close="onClose"
+ @error="toggleAlert(true)"
+ />
+ </keep-alive>
</gl-modal>
</template>
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index b8c1bc266f7..ade58ca0970 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -137,8 +137,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
unless job_id.length <= PARAM_JOB_ID_MAX_SIZE
return render status: :bad_request, json: {
- message: _('Parameter "job_id" cannot exceed length of %{job_id_max_size}' %
- { job_id_max_size: PARAM_JOB_ID_MAX_SIZE })
+ message: format(_('Parameter "job_id" cannot exceed length of %{job_id_max_size}'), job_id_max_size: PARAM_JOB_ID_MAX_SIZE)
}
end
@@ -174,8 +173,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
unless job_id.length <= PARAM_JOB_ID_MAX_SIZE
return render status: :bad_request, json: {
- message: _('Parameter "job_id" cannot exceed length of %{job_id_max_size}' %
- { job_id_max_size: PARAM_JOB_ID_MAX_SIZE })
+ message: format(_('Parameter "job_id" cannot exceed length of %{job_id_max_size}'), job_id_max_size: PARAM_JOB_ID_MAX_SIZE)
}
end
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 8005babe19e..e3a33bafb62 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -41,7 +41,7 @@ class Admin::GroupsController < Admin::ApplicationController
@group = ::Groups::CreateService.new(current_user, group_params).execute
if @group.persisted?
- redirect_to [:admin, @group], notice: _('Group %{group_name} was successfully created.') % { group_name: @group.name }
+ redirect_to [:admin, @group], notice: format(_('Group %{group_name} was successfully created.'), group_name: @group.name)
else
render "new"
end
@@ -66,7 +66,7 @@ class Admin::GroupsController < Admin::ApplicationController
redirect_to admin_groups_path,
status: :found,
- alert: _('Group %{group_name} was scheduled for deletion.') % { group_name: @group.name }
+ alert: format(_('Group %{group_name} was scheduled for deletion.'), group_name: @group.name)
end
private
diff --git a/app/controllers/admin/impersonation_tokens_controller.rb b/app/controllers/admin/impersonation_tokens_controller.rb
index 9d884478e98..ddc555add5c 100644
--- a/app/controllers/admin/impersonation_tokens_controller.rb
+++ b/app/controllers/admin/impersonation_tokens_controller.rb
@@ -25,9 +25,9 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController
@impersonation_token = finder.find(params[:id])
if @impersonation_token.revoke!
- flash[:notice] = _("Revoked impersonation token %{token_name}!") % { token_name: @impersonation_token.name }
+ flash[:notice] = format(_("Revoked impersonation token %{token_name}!"), token_name: @impersonation_token.name)
else
- flash[:alert] = _("Could not revoke impersonation token %{token_name}.") % { token_name: @impersonation_token.name }
+ flash[:alert] = format(_("Could not revoke impersonation token %{token_name}."), token_name: @impersonation_token.name)
end
redirect_to admin_user_impersonation_tokens_path
diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb
index 9e841487508..5d37bd27302 100644
--- a/app/controllers/admin/projects_controller.rb
+++ b/app/controllers/admin/projects_controller.rb
@@ -43,7 +43,7 @@ class Admin::ProjectsController < Admin::ApplicationController
def destroy
::Projects::DestroyService.new(@project, current_user, {}).async_execute
- flash[:notice] = _("Project '%{project_name}' is in the process of being deleted.") % { project_name: @project.full_name }
+ flash[:notice] = format(_("Project '%{project_name}' is in the process of being deleted."), project_name: @project.full_name)
redirect_to admin_projects_path, status: :found
rescue Projects::DestroyService::DestroyError => e
diff --git a/app/controllers/admin/spam_logs_controller.rb b/app/controllers/admin/spam_logs_controller.rb
index 180f4634136..984ae736697 100644
--- a/app/controllers/admin/spam_logs_controller.rb
+++ b/app/controllers/admin/spam_logs_controller.rb
@@ -16,7 +16,7 @@ class Admin::SpamLogsController < Admin::ApplicationController
spam_log.remove_user(deleted_by: current_user)
redirect_to admin_spam_logs_path,
status: :found,
- notice: _('User %{username} was successfully removed.') % { username: spam_log.user.username }
+ notice: format(_('User %{username} was successfully removed.'), username: spam_log.user.username)
else
spam_log.destroy
head :ok
diff --git a/app/controllers/admin/topics_controller.rb b/app/controllers/admin/topics_controller.rb
index e97ead12f71..345a778772d 100644
--- a/app/controllers/admin/topics_controller.rb
+++ b/app/controllers/admin/topics_controller.rb
@@ -23,7 +23,7 @@ class Admin::TopicsController < Admin::ApplicationController
@topic = Projects::Topic.new(topic_params)
if @topic.save
- redirect_to edit_admin_topic_path(@topic), notice: _('Topic %{topic_name} was successfully created.') % { topic_name: @topic.name }
+ redirect_to edit_admin_topic_path(@topic), notice: format(_('Topic %{topic_name} was successfully created.'), topic_name: @topic.name)
else
render "new"
end
@@ -42,7 +42,7 @@ class Admin::TopicsController < Admin::ApplicationController
redirect_to admin_topics_path,
status: :found,
- notice: _('Topic %{topic_name} was successfully removed.') % { topic_name: @topic.title_or_name }
+ notice: format(_('Topic %{topic_name} was successfully removed.'), topic_name: @topic.title_or_name)
end
def merge
@@ -53,7 +53,7 @@ class Admin::TopicsController < Admin::ApplicationController
return render status: :bad_request, json: { type: :alert, message: response.message } if response.error?
message = _('Topic %{source_topic} was successfully merged into topic %{target_topic}.')
- flash[:toast] = message % { source_topic: source_topic.name, target_topic: target_topic.name }
+ flash[:toast] = format(message, source_topic: source_topic.name, target_topic: target_topic.name)
redirect_to admin_topics_path, status: :found
end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index abefbe28f35..b191a5f967a 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -57,7 +57,7 @@ class Admin::UsersController < Admin::ApplicationController
log_impersonation_event
- flash[:alert] = _("You are now impersonating %{username}") % { username: user.username }
+ flash[:alert] = format(_("You are now impersonating %{username}"), username: user.username)
redirect_to root_path
else
@@ -81,7 +81,7 @@ class Admin::UsersController < Admin::ApplicationController
result = Users::RejectService.new(current_user).execute(user)
if result[:status] == :success
- redirect_back_or_admin_user(notice: _("You've rejected %{user}" % { user: user.name }))
+ redirect_back_or_admin_user(notice: format(_("You've rejected %{user}"), user: user.name))
else
redirect_back_or_admin_user(alert: result[:message])
end
@@ -105,7 +105,7 @@ class Admin::UsersController < Admin::ApplicationController
return redirect_back_or_admin_user(notice: _("Internal users cannot be deactivated")) if user.internal?
unless user.can_be_deactivated?
- return redirect_back_or_admin_user(notice: _("The user you are trying to deactivate has been active in the past %{minimum_inactive_days} days and cannot be deactivated") % { minimum_inactive_days: Gitlab::CurrentSettings.deactivate_dormant_users_period })
+ return redirect_back_or_admin_user(notice: format(_("The user you are trying to deactivate has been active in the past %{minimum_inactive_days} days and cannot be deactivated"), minimum_inactive_days: Gitlab::CurrentSettings.deactivate_dormant_users_period))
end
user.deactivate
@@ -378,7 +378,7 @@ class Admin::UsersController < Admin::ApplicationController
end
def log_impersonation_event
- Gitlab::AppLogger.info(_("User %{current_user_username} has started impersonating %{username}") % { current_user_username: current_user.username, username: user.username })
+ Gitlab::AppLogger.info(format(_("User %{current_user_username} has started impersonating %{username}"), current_user_username: current_user.username, username: user.username))
end
def can_impersonate_user
diff --git a/app/controllers/concerns/access_tokens_actions.rb b/app/controllers/concerns/access_tokens_actions.rb
index fdb08c6572f..6a84c436aae 100644
--- a/app/controllers/concerns/access_tokens_actions.rb
+++ b/app/controllers/concerns/access_tokens_actions.rb
@@ -43,9 +43,9 @@ module AccessTokensActions
revoked_response = ResourceAccessTokens::RevokeService.new(current_user, resource, @resource_access_token).execute
if revoked_response.success?
- flash[:notice] = _("Revoked access token %{access_token_name}!") % { access_token_name: @resource_access_token.name }
+ flash[:notice] = format(_("Revoked access token %{access_token_name}!"), access_token_name: @resource_access_token.name)
else
- flash[:alert] = _("Could not revoke access token %{access_token_name}.") % { access_token_name: @resource_access_token.name }
+ flash[:alert] = format(_("Could not revoke access token %{access_token_name}."), access_token_name: @resource_access_token.name)
end
redirect_to resource_access_tokens_path
diff --git a/app/controllers/concerns/confirm_email_warning.rb b/app/controllers/concerns/confirm_email_warning.rb
index 32e1a46e580..ec5140bf223 100644
--- a/app/controllers/concerns/confirm_email_warning.rb
+++ b/app/controllers/concerns/confirm_email_warning.rb
@@ -19,10 +19,17 @@ module ConfirmEmailWarning
email = current_user.unconfirmed_email || current_user.email
- flash.now[:warning] = _("Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}.").html_safe % {
+ flash.now[:warning] = format(
+ confirm_warning_message,
email: email,
resend_link: view_context.link_to(_('Resend it'), user_confirmation_path(user: { email: email }), method: :post),
update_link: view_context.link_to(_('Update it'), profile_path)
- }
+ ).html_safe
+ end
+
+ private
+
+ def confirm_warning_message
+ _("Please check your email (%{email}) to verify that you own this address and unlock the power of CI/CD. Didn't receive it? %{resend_link}. Wrong email address? %{update_link}.")
end
end
diff --git a/app/controllers/concerns/enforces_two_factor_authentication.rb b/app/controllers/concerns/enforces_two_factor_authentication.rb
index c8de041d5bd..cdef1a45a27 100644
--- a/app/controllers/concerns/enforces_two_factor_authentication.rb
+++ b/app/controllers/concerns/enforces_two_factor_authentication.rb
@@ -25,8 +25,9 @@ module EnforcesTwoFactorAuthentication
case self
when GraphqlController
render_error(
- _("Authentication error: enable 2FA in your profile settings to continue using GitLab: %{mfa_help_page}") %
- { mfa_help_page: mfa_help_page_url },
+ format(
+ _("Authentication error: enable 2FA in your profile settings to continue using GitLab: %{mfa_help_page}"),
+ mfa_help_page: mfa_help_page_url),
status: :unauthorized
)
else
diff --git a/app/controllers/concerns/integrations/actions.rb b/app/controllers/concerns/integrations/actions.rb
index e0a12555e11..7bebafae0fd 100644
--- a/app/controllers/concerns/integrations/actions.rb
+++ b/app/controllers/concerns/integrations/actions.rb
@@ -57,9 +57,9 @@ module Integrations::Actions
def success_message
if integration.active?
- s_('Integrations|%{integration} settings saved and active.') % { integration: integration.title }
+ format(s_('Integrations|%{integration} settings saved and active.'), integration: integration.title)
else
- s_('Integrations|%{integration} settings saved, but not active.') % { integration: integration.title }
+ format(s_('Integrations|%{integration} settings saved, but not active.'), integration: integration.title)
end
end
diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb
index a8af5cf7c6b..7c6e449b509 100644
--- a/app/controllers/concerns/membership_actions.rb
+++ b/app/controllers/concerns/membership_actions.rb
@@ -66,8 +66,7 @@ module MembershipActions
notice: _('Your request for access has been queued for review.')
else
redirect_to polymorphic_path(membershipable),
- alert: _("Your request for access could not be processed: %{error_message}") %
- { error_message: access_requester.errors.full_messages.to_sentence }
+ alert: format(_("Your request for access could not be processed: %{error_message}"), error_message: access_requester.errors.full_messages.to_sentence)
end
end
@@ -87,9 +86,9 @@ module MembershipActions
notice =
if member.request?
- _("Your access request to the %{source_type} has been withdrawn.") % { source_type: source_type }
+ format(_("Your access request to the %{source_type} has been withdrawn."), source_type: source_type)
else
- _("You left the \"%{membershipable_human_name}\" %{source_type}.") % { membershipable_human_name: membershipable.human_name, source_type: source_type }
+ format(_("You left the \"%{membershipable_human_name}\" %{source_type}."), membershipable_human_name: membershipable.human_name, source_type: source_type)
end
respond_to do |format|
diff --git a/app/controllers/concerns/redirects_for_missing_path_on_tree.rb b/app/controllers/concerns/redirects_for_missing_path_on_tree.rb
index 085afbf3975..92574dfade9 100644
--- a/app/controllers/concerns/redirects_for_missing_path_on_tree.rb
+++ b/app/controllers/concerns/redirects_for_missing_path_on_tree.rb
@@ -8,7 +8,7 @@ module RedirectsForMissingPathOnTree
private
def missing_path_on_ref(path, ref)
- _('"%{path}" did not exist on "%{ref}"') % { path: truncate_path(path), ref: ref }
+ format(_('"%{path}" did not exist on "%{ref}"'), path: truncate_path(path), ref: ref)
end
def truncate_path(path)
diff --git a/app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb b/app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb
index 044519004b2..6ba079ee658 100644
--- a/app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb
+++ b/app/controllers/concerns/spammable_actions/akismet_mark_as_spam_action.rb
@@ -9,7 +9,7 @@ module SpammableActions::AkismetMarkAsSpamAction
def mark_as_spam
if Spam::AkismetMarkAsSpamService.new(target: spammable).execute
- redirect_to spammable_path, notice: _("%{spammable_titlecase} was submitted to Akismet successfully.") % { spammable_titlecase: spammable.spammable_entity_type.titlecase }
+ redirect_to spammable_path, notice: format(_("%{spammable_titlecase} was submitted to Akismet successfully."), spammable_titlecase: spammable.spammable_entity_type.titlecase)
else
redirect_to spammable_path, alert: _('Error with Akismet. Please check the logs for more info.')
end
diff --git a/app/controllers/concerns/verifies_with_email.rb b/app/controllers/concerns/verifies_with_email.rb
index 3cada24a81a..82388090350 100644
--- a/app/controllers/concerns/verifies_with_email.rb
+++ b/app/controllers/concerns/verifies_with_email.rb
@@ -105,8 +105,10 @@ module VerifiesWithEmail
end
def render_sign_in_rate_limited
- message = s_('IdentityVerification|Maximum login attempts exceeded. '\
- 'Wait %{interval} and try again.') % { interval: user_sign_in_interval }
+ message = format(
+ s_('IdentityVerification|Maximum login attempts exceeded. Wait %{interval} and try again.'),
+ interval: user_sign_in_interval
+ )
redirect_to new_user_session_path, alert: message
end
diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb
index 1dfa8cdf133..78e3ffa4af9 100644
--- a/app/controllers/groups/settings/ci_cd_controller.rb
+++ b/app/controllers/groups/settings/ci_cd_controller.rb
@@ -23,7 +23,7 @@ module Groups
if update_group_service.execute
flash[:notice] = s_('GroupSettings|Pipeline settings was updated for the group')
else
- flash[:alert] = s_("GroupSettings|There was a problem updating the pipeline settings: %{error_messages}." % { error_messages: group.errors.full_messages })
+ flash[:alert] = format(s_("GroupSettings|There was a problem updating the pipeline settings: %{error_messages}."), error_messages: group.errors.full_messages)
end
redirect_to group_settings_ci_cd_path
@@ -33,7 +33,7 @@ module Groups
if auto_devops_service.execute
flash[:notice] = s_('GroupSettings|Auto DevOps pipeline was updated for the group')
else
- flash[:alert] = s_("GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}." % { error_messages: group.errors.full_messages })
+ flash[:alert] = format(s_("GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."), error_messages: group.errors.full_messages)
end
redirect_to group_settings_ci_cd_path
diff --git a/app/helpers/version_check_helper.rb b/app/helpers/version_check_helper.rb
index 0bb92dfd118..4bd89a3d4e2 100644
--- a/app/helpers/version_check_helper.rb
+++ b/app/helpers/version_check_helper.rb
@@ -18,7 +18,7 @@ module VersionCheckHelper
strong_memoize_attr :gitlab_version_check
def show_security_patch_upgrade_alert?
- return false unless show_version_check? && gitlab_version_check
+ return false unless Feature.enabled?(:critical_security_alert) && show_version_check? && gitlab_version_check
gitlab_version_check['severity'] === SECURITY_ALERT_SEVERITY
end
diff --git a/config/feature_flags/development/critical_security_alert.yml b/config/feature_flags/development/critical_security_alert.yml
new file mode 100644
index 00000000000..6bb5100efc8
--- /dev/null
+++ b/config/feature_flags/development/critical_security_alert.yml
@@ -0,0 +1,8 @@
+---
+name: critical_security_alert
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108732
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/387719
+milestone: '15.8'
+type: development
+group: group::distribution
+default_enabled: false
diff --git a/doc/administration/geo/replication/troubleshooting.md b/doc/administration/geo/replication/troubleshooting.md
index 4c9ad1c8bc5..2f2759d7339 100644
--- a/doc/administration/geo/replication/troubleshooting.md
+++ b/doc/administration/geo/replication/troubleshooting.md
@@ -1317,6 +1317,18 @@ registry = Geo::PackageFileRegistry.find(registry_id)
registry.replicator.send(:download)
```
+#### Find registry records of blobs that failed to sync
+
+```ruby
+Geo::PackageFileRegistry.failed
+```
+
+#### Find registry records of blobs that are missing on the primary site
+
+```ruby
+Geo::PackageFileRegistry.where(last_sync_failure: 'The file is missing on the Geo primary site')
+```
+
#### Verify package files on the secondary manually
This iterates over all package files on the secondary, looking at the
@@ -1344,7 +1356,7 @@ status.keys.each {|key| puts "#{key} count: #{status[key].count}"}
status
```
-### Reverify all uploads (or any SSF data type which is verified)
+#### Reverify all uploads (or any SSF data type which is verified)
1. SSH into a GitLab Rails node in the primary Geo site.
1. Open [Rails console](../../../administration/operations/rails_console.md#starting-a-rails-console-session).
@@ -1400,21 +1412,6 @@ registry = Geo::SnippetRepositoryRegistry.find(registry_id)
registry.replicator.send(:sync_repository)
```
-### Find failed artifacts
-
-[Start a Rails console session](../../../administration/operations/rails_console.md#starting-a-rails-console-session)
-to run the following commands:
-
-```ruby
-Geo::JobArtifactRegistry.failed
-```
-
-#### Find `ID` of synced artifacts that are missing on primary
-
-```ruby
-Geo::JobArtifactRegistry.synced.missing_on_primary.pluck(:artifact_id)
-```
-
### Project or project wiki repositories
#### Find repository verification failures
diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md
index 2485f44d173..bb50f66aff0 100644
--- a/doc/administration/reference_architectures/10k_users.md
+++ b/doc/administration/reference_architectures/10k_users.md
@@ -1855,7 +1855,7 @@ Updates to example must be made at:
# Set number of Sidekiq queue processes to the same number as available CPUs
sidekiq['queue_groups'] = ['*'] * 4
- # Set number of Sidekiq threads per queue process to the recommend number of 20
+ # Set number of Sidekiq threads per queue process to the recommended number of 20
sidekiq['max_concurrency'] = 20
# Monitoring
diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
index 7c5feb24e15..0cd34ca16df 100644
--- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
+++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
@@ -74,7 +74,7 @@ Moved to [Geo replication troubleshooting](../geo/replication/troubleshooting.md
### Artifacts
-Moved to [Geo replication troubleshooting](../geo/replication/troubleshooting.md#find-failed-artifacts).
+Moved to [Geo replication troubleshooting](../geo/replication/troubleshooting.md#find-registry-records-of-blobs-that-failed-to-sync).
### Repository verification failures
diff --git a/doc/development/elasticsearch.md b/doc/development/elasticsearch.md
index 08c2865a5a6..961a47e005b 100644
--- a/doc/development/elasticsearch.md
+++ b/doc/development/elasticsearch.md
@@ -234,8 +234,6 @@ Backfills a specific field in an index. In most cases, the mapping for the field
Requires the `index_name` and `field_name` methods.
-<details><summary>Example</summary>
-
```ruby
class MigrationName < Elastic::Migration
include Elastic::MigrationBackfillHelper
@@ -252,16 +250,12 @@ class MigrationName < Elastic::Migration
end
```
-</details>
-
#### `Elastic::MigrationUpdateMappingsHelper`
Updates a mapping in an index by calling `put_mapping` with the mapping specified.
Requires the `index_name` and `new_mappings` methods.
-<details><summary>Example</summary>
-
```ruby
class MigrationName < Elastic::Migration
include Elastic::MigrationUpdateMappingsHelper
@@ -282,28 +276,20 @@ class MigrationName < Elastic::Migration
end
```
-</details>
-
#### `Elastic::MigrationObsolete`
Marks a migration as obsolete when it's no longer required.
-<details><summary>Example</summary>
-
```ruby
class MigrationName < Elastic::Migration
include Elastic::MigrationObsolete
end
```
-</details>
-
#### `Elastic::MigrationHelper`
Contains methods you can use when a migration doesn't fit the previous examples.
-<details><summary>Example</summary>
-
```ruby
class MigrationName < Elastic::Migration
include Elastic::MigrationHelper
@@ -318,8 +304,6 @@ class MigrationName < Elastic::Migration
end
```
-</details>
-
### Migration options supported by the `Elastic::MigrationWorker`
[`Elastic::MigrationWorker`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/ee/app/workers/elastic/migration_worker.rb) supports the following migration options:
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 543a377b8ee..68c2b663566 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -344,6 +344,12 @@ In GitLab 12.1 and later, only PostgreSQL is supported. In GitLab 14.0 and later
sudo -u postgres psql -d template1 -c "CREATE EXTENSION IF NOT EXISTS btree_gist;"
```
+1. Create the `plpgsql` extension:
+
+ ```shell
+ sudo -u postgres psql -d template1 -c "CREATE EXTENSION IF NOT EXISTS plpgsql;"
+ ```
+
1. Create the GitLab production database and grant all privileges on the database:
```shell
@@ -392,6 +398,24 @@ In GitLab 12.1 and later, only PostgreSQL is supported. In GitLab 14.0 and later
(1 row)
```
+1. Check if the `plpgsql` extension is enabled:
+
+ ```sql
+ SELECT true AS enabled
+ FROM pg_available_extensions
+ WHERE name = 'plpgsql'
+ AND installed_version IS NOT NULL;
+ ```
+
+ If the extension is enabled this produces the following output:
+
+ ```plaintext
+ enabled
+ ---------
+ t
+ (1 row)
+ ```
+
1. Quit the database session:
```shell
diff --git a/doc/raketasks/backup_gitlab.md b/doc/raketasks/backup_gitlab.md
index aea61e36037..350cf110878 100644
--- a/doc/raketasks/backup_gitlab.md
+++ b/doc/raketasks/backup_gitlab.md
@@ -892,7 +892,7 @@ When troubleshooting backup problems, however, replace `CRON=1` with `--trace` t
## Limit backup lifetime for local files (prune old backups)
WARNING:
-The process described in this section don't work if you used a [custom filename](#backup-filename)
+The process described in this section doesn't work if you used a [custom filename](#backup-filename)
for your backups.
To prevent regular backups from using all your disk space, you may want to set a limited lifetime
diff --git a/doc/user/ssh.md b/doc/user/ssh.md
index 1d82d301e9a..b71b120d246 100644
--- a/doc/user/ssh.md
+++ b/doc/user/ssh.md
@@ -278,7 +278,8 @@ To learn more about using 1Password with SSH keys, see [1Password's documentatio
## Add an SSH key to your GitLab account
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/271239) in GitLab 15.4, default expiration date suggested in UI.
+> - Suggested default expiration date for keys [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/271239) in GitLab 15.4.
+> - Usage types for SSH keys [added](https://gitlab.com/gitlab-org/gitlab/-/issues/383046) in GitLab 15.7.
To use SSH with GitLab, copy your public key to your GitLab account:
diff --git a/scripts/rspec_helpers.sh b/scripts/rspec_helpers.sh
index 923b633fcc9..646f9821a60 100644
--- a/scripts/rspec_helpers.sh
+++ b/scripts/rspec_helpers.sh
@@ -105,9 +105,13 @@ function rspec_simple_job() {
export NO_KNAPSACK="1"
local rspec_cmd="bin/rspec $(rspec_args "${1}" "${2}")"
+ local rspec_run_status=0
+ local rspec_log="${CI_PROJECT_DIR}/tmp/rspec.log"
echoinfo "Running RSpec command: ${rspec_cmd}"
- eval "${rspec_cmd}"
+ eval "${rspec_cmd}" | tee "${rspec_log}" || rspec_run_status=$?
+
+ handle_retry_rspec_in_new_process $rspec_run_status $rspec_log
}
function rspec_db_library_code() {
@@ -136,6 +140,29 @@ function debug_rspec_variables() {
echoinfo "CRYSTALBALL: ${CRYSTALBALL}"
}
+function handle_retry_rspec_in_new_process() {
+ local rspec_run_status="${1}"
+ local rspec_log="${2}"
+
+ # Experiment to retry failed examples in a new RSpec process: https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/1148
+ if [[ $rspec_run_status -ne 0 ]]; then
+ if [[ "${RETRY_FAILED_TESTS_IN_NEW_PROCESS}" == "true" ]]; then
+ if grep -q "error occurred outside of examples" "${rspec_log}"; then
+ echoerr "Not retrying failing examples since there were errors happening outside of the RSpec examples!"
+ else
+ retry_failed_rspec_examples
+ rspec_run_status=$?
+ fi
+ else
+ echoerr "Not retrying failing examples since \$RETRY_FAILED_TESTS_IN_NEW_PROCESS != 'true'!"
+ fi
+ else
+ echosuccess "No examples to retry, congrats!"
+ fi
+
+ exit $rspec_run_status
+}
+
function rspec_paralellized_job() {
read -ra job_name <<< "${CI_JOB_NAME}"
local test_tool="${job_name[0]}"
@@ -146,6 +173,7 @@ function rspec_paralellized_job() {
local rspec_flaky_folder_path="$(dirname "${FLAKY_RSPEC_SUITE_REPORT_PATH}")/"
local knapsack_folder_path="$(dirname "${KNAPSACK_RSPEC_SUITE_REPORT_PATH}")/"
local rspec_run_status=0
+ local rspec_log="${CI_PROJECT_DIR}/tmp/rspec.log"
if [[ "${test_tool}" =~ "-ee" ]]; then
spec_folder_prefixes="'ee/'"
@@ -197,24 +225,14 @@ function rspec_paralellized_job() {
debug_rspec_variables
if [[ -n "${RSPEC_TESTS_MAPPING_ENABLED}" ]]; then
- tooling/bin/parallel_rspec --rspec_args "$(rspec_args "${rspec_opts}")" --filter "${RSPEC_TESTS_FILTER_FILE}" || rspec_run_status=$?
+ tooling/bin/parallel_rspec --rspec_args "$(rspec_args "${rspec_opts}")" --filter "${RSPEC_TESTS_FILTER_FILE}" | tee "${rspec_log}" || rspec_run_status=$?
else
- tooling/bin/parallel_rspec --rspec_args "$(rspec_args "${rspec_opts}")" || rspec_run_status=$?
+ tooling/bin/parallel_rspec --rspec_args "$(rspec_args "${rspec_opts}")" | tee "${rspec_log}" || rspec_run_status=$?
fi
echoinfo "RSpec exited with ${rspec_run_status}."
- # Experiment to retry failed examples in a new RSpec process: https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/1148
- if [[ $rspec_run_status -ne 0 ]]; then
- if [[ "${RETRY_FAILED_TESTS_IN_NEW_PROCESS}" == "true" ]]; then
- retry_failed_rspec_examples
- rspec_run_status=$?
- fi
- else
- echosuccess "No examples to retry, congrats!"
- fi
-
- exit $rspec_run_status
+ handle_retry_rspec_in_new_process $rspec_run_status $rspec_log
}
function retry_failed_rspec_examples() {
diff --git a/spec/factories/personal_access_tokens.rb b/spec/factories/personal_access_tokens.rb
index 9625fdc195d..a140011941f 100644
--- a/spec/factories/personal_access_tokens.rb
+++ b/spec/factories/personal_access_tokens.rb
@@ -27,6 +27,12 @@ FactoryBot.define do
token_digest { nil }
end
+ trait :admin_mode do
+ before(:create) do |personal_access_token|
+ personal_access_token.scopes.append(Gitlab::Auth::ADMIN_MODE_SCOPE) if personal_access_token.user.admin?
+ end
+ end
+
trait :no_prefix do
after(:build) { |personal_access_token| personal_access_token.set_token(Devise.friendly_token) }
end
diff --git a/spec/frontend/jobs/components/job/manual_variables_form_spec.js b/spec/frontend/jobs/components/job/manual_variables_form_spec.js
index 45a1e9dca76..3040570df19 100644
--- a/spec/frontend/jobs/components/job/manual_variables_form_spec.js
+++ b/spec/frontend/jobs/components/job/manual_variables_form_spec.js
@@ -212,9 +212,30 @@ describe('Manual Variables Form', () => {
expect(findDeleteVarBtn().exists()).toBe(true);
});
+ });
+
+ describe('variable delete button placeholder', () => {
+ beforeEach(async () => {
+ getJobQueryResponse.mockResolvedValue(mockJobResponse);
+ await createComponentWithApollo();
+ });
it('delete variable button placeholder should only exist when a user cannot remove', async () => {
expect(findDeleteVarBtnPlaceholder().exists()).toBe(true);
});
+
+ it('does not show the placeholder button', () => {
+ expect(findDeleteVarBtnPlaceholder().classes('gl-opacity-0')).toBe(true);
+ });
+
+ it('placeholder button will not delete the row on click', async () => {
+ expect(findAllCiVariableKeys()).toHaveLength(1);
+ expect(findDeleteVarBtnPlaceholder().exists()).toBe(true);
+
+ await findDeleteVarBtnPlaceholder().trigger('click');
+
+ expect(findAllCiVariableKeys()).toHaveLength(1);
+ expect(findDeleteVarBtnPlaceholder().exists()).toBe(true);
+ });
});
});
diff --git a/spec/frontend/vue_merge_request_widget/extentions/code_quality/index_spec.js b/spec/frontend/vue_merge_request_widget/extentions/code_quality/index_spec.js
index 9e004f98715..67b327217ef 100644
--- a/spec/frontend/vue_merge_request_widget/extentions/code_quality/index_spec.js
+++ b/spec/frontend/vue_merge_request_widget/extentions/code_quality/index_spec.js
@@ -18,6 +18,7 @@ import {
} from '~/vue_merge_request_widget/extensions/code_quality/constants';
import {
codeQualityResponseNewErrors,
+ codeQualityResponseResolvedErrors,
codeQualityResponseResolvedAndNewErrors,
codeQualityResponseNoErrors,
} from './mock_data';
@@ -37,6 +38,9 @@ describe('Code Quality extension', () => {
const findToggleCollapsedButton = () => wrapper.findByTestId('toggle-button');
const findAllExtensionListItems = () => wrapper.findAllByTestId('extension-list-item');
const isCollapsable = () => wrapper.findByTestId('toggle-button').exists();
+ const getNeutralIcon = () => wrapper.findByTestId('status-neutral-icon').exists();
+ const getAlertIcon = () => wrapper.findByTestId('status-alert-icon').exists();
+ const getSuccessIcon = () => wrapper.findByTestId('status-success-icon').exists();
const createComponent = () => {
wrapper = mountExtended(extensionsContainer, {
@@ -90,7 +94,7 @@ describe('Code Quality extension', () => {
expect(isCollapsable()).toBe(false);
});
- it('displays correct single Report', async () => {
+ it('displays new Errors finding', async () => {
mockApi(HTTP_STATUS_OK, codeQualityResponseNewErrors);
createComponent();
@@ -104,8 +108,29 @@ describe('Code Quality extension', () => {
.replace(/%{strong_start}/g, '')
.replace(/%{strong_end}/g, ''),
);
+ expect(isCollapsable()).toBe(true);
+ expect(getAlertIcon()).toBe(true);
+ });
+
+ it('displays resolved Errors finding', async () => {
+ mockApi(HTTP_STATUS_OK, codeQualityResponseResolvedErrors);
+
+ createComponent();
+ await waitForPromises();
+ expect(wrapper.text()).toBe(
+ i18n
+ .singularCopy(
+ i18n.findings(
+ codeQualityResponseResolvedErrors.resolved_errors,
+ codeQualityPrefixes.fixed,
+ ),
+ )
+ .replace(/%{strong_start}/g, '')
+ .replace(/%{strong_end}/g, ''),
+ );
expect(isCollapsable()).toBe(true);
+ expect(getSuccessIcon()).toBe(true);
});
it('displays quality improvement and degradation', async () => {
@@ -131,6 +156,7 @@ describe('Code Quality extension', () => {
.replace(/%{strong_end}/g, ''),
);
expect(isCollapsable()).toBe(true);
+ expect(getAlertIcon()).toBe(true);
});
it('displays no detected errors', async () => {
@@ -142,6 +168,7 @@ describe('Code Quality extension', () => {
expect(wrapper.text()).toBe(i18n.noChanges);
expect(isCollapsable()).toBe(false);
+ expect(getNeutralIcon()).toBe(true);
});
});
diff --git a/spec/frontend/vue_merge_request_widget/extentions/code_quality/mock_data.js b/spec/frontend/vue_merge_request_widget/extentions/code_quality/mock_data.js
index 093913075b8..cb23b730a93 100644
--- a/spec/frontend/vue_merge_request_widget/extentions/code_quality/mock_data.js
+++ b/spec/frontend/vue_merge_request_widget/extentions/code_quality/mock_data.js
@@ -23,6 +23,31 @@ export const codeQualityResponseNewErrors = {
},
};
+export const codeQualityResponseResolvedErrors = {
+ status: 'success',
+ new_errors: [],
+ resolved_errors: [
+ {
+ description: "Parsing error: 'return' outside of function",
+ severity: 'minor',
+ file_path: 'index.js',
+ line: 12,
+ },
+ {
+ description: 'TODO found',
+ severity: 'minor',
+ file_path: '.gitlab-ci.yml',
+ line: 73,
+ },
+ ],
+ existing_errors: [],
+ summary: {
+ total: 12235,
+ resolved: 0,
+ errored: 12235,
+ },
+};
+
export const codeQualityResponseResolvedAndNewErrors = {
status: 'failed',
new_errors: [
diff --git a/spec/frontend/vue_shared/components/runner_instructions/instructions/__snapshots__/runner_docker_instructions_spec.js.snap b/spec/frontend/vue_shared/components/runner_instructions/instructions/__snapshots__/runner_docker_instructions_spec.js.snap
new file mode 100644
index 00000000000..d14f66df8a1
--- /dev/null
+++ b/spec/frontend/vue_shared/components/runner_instructions/instructions/__snapshots__/runner_docker_instructions_spec.js.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`RunnerDockerInstructions renders contents 1`] = `"To install Runner in a container follow the instructions described in the GitLab documentation View installation instructions Close"`;
diff --git a/spec/frontend/vue_shared/components/runner_instructions/instructions/__snapshots__/runner_kubernetes_instructions_spec.js.snap b/spec/frontend/vue_shared/components/runner_instructions/instructions/__snapshots__/runner_kubernetes_instructions_spec.js.snap
new file mode 100644
index 00000000000..1172bf07dff
--- /dev/null
+++ b/spec/frontend/vue_shared/components/runner_instructions/instructions/__snapshots__/runner_kubernetes_instructions_spec.js.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`RunnerKubernetesInstructions renders contents 1`] = `"To install Runner in Kubernetes follow the instructions described in the GitLab documentation. View installation instructions Close"`;
diff --git a/spec/frontend/vue_shared/components/runner_instructions/instructions/runner_cli_instructions_spec.js b/spec/frontend/vue_shared/components/runner_instructions/instructions/runner_cli_instructions_spec.js
new file mode 100644
index 00000000000..f9d700fe67f
--- /dev/null
+++ b/spec/frontend/vue_shared/components/runner_instructions/instructions/runner_cli_instructions_spec.js
@@ -0,0 +1,169 @@
+import { GlAlert, GlLoadingIcon } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import getRunnerSetupInstructionsQuery from '~/vue_shared/components/runner_instructions/graphql/get_runner_setup.query.graphql';
+import RunnerCliInstructions from '~/vue_shared/components/runner_instructions/instructions/runner_cli_instructions.vue';
+
+import { mockRunnerPlatforms, mockInstructions, mockInstructionsWindows } from '../mock_data';
+
+Vue.use(VueApollo);
+
+jest.mock('@gitlab/ui/dist/utils');
+
+const mockPlatforms = mockRunnerPlatforms.data.runnerPlatforms.nodes.map(
+ ({ name, humanReadableName, architectures }) => ({
+ name,
+ humanReadableName,
+ architectures: architectures?.nodes || [],
+ }),
+);
+
+const [mockPlatform, mockPlatform2] = mockPlatforms;
+const mockArchitectures = mockPlatform.architectures;
+
+describe('RunnerCliInstructions component', () => {
+ let wrapper;
+ let fakeApollo;
+ let runnerSetupInstructionsHandler;
+
+ const findGlLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
+ const findAlert = () => wrapper.findComponent(GlAlert);
+ const findArchitectureDropdownItems = () => wrapper.findAllByTestId('architecture-dropdown-item');
+ const findBinaryDownloadButton = () => wrapper.findByTestId('binary-download-button');
+ const findBinaryInstructions = () => wrapper.findByTestId('binary-instructions');
+ const findRegisterCommand = () => wrapper.findByTestId('register-command');
+
+ const createComponent = ({ props, ...options } = {}) => {
+ const requestHandlers = [[getRunnerSetupInstructionsQuery, runnerSetupInstructionsHandler]];
+
+ fakeApollo = createMockApollo(requestHandlers);
+
+ wrapper = extendedWrapper(
+ shallowMount(RunnerCliInstructions, {
+ propsData: {
+ platform: mockPlatform,
+ registrationToken: 'MY_TOKEN',
+ ...props,
+ },
+ apolloProvider: fakeApollo,
+ ...options,
+ }),
+ );
+ };
+
+ beforeEach(() => {
+ runnerSetupInstructionsHandler = jest.fn().mockResolvedValue(mockInstructions);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when the instructions are shown', () => {
+ beforeEach(async () => {
+ createComponent();
+ await waitForPromises();
+ });
+
+ it('should not show alert', async () => {
+ expect(findAlert().exists()).toBe(false);
+ });
+
+ it('should contain a number of dropdown items for the architecture options', () => {
+ expect(findArchitectureDropdownItems()).toHaveLength(
+ mockRunnerPlatforms.data.runnerPlatforms.nodes[0].architectures.nodes.length,
+ );
+ });
+
+ describe('should display instructions', () => {
+ const { installInstructions } = mockInstructions.data.runnerSetup;
+
+ it('runner instructions are requested', () => {
+ expect(runnerSetupInstructionsHandler).toHaveBeenCalledWith({
+ platform: 'linux',
+ architecture: 'amd64',
+ });
+ });
+
+ it('binary instructions are shown', async () => {
+ const instructions = findBinaryInstructions().text();
+
+ expect(instructions).toBe(installInstructions.trim());
+ });
+
+ it('register command is shown with a replaced token', async () => {
+ const command = findRegisterCommand().text();
+
+ expect(command).toBe(
+ 'sudo gitlab-runner register --url http://localhost/ --registration-token MY_TOKEN',
+ );
+ });
+
+ it('architecture download link is shown', () => {
+ expect(findBinaryDownloadButton().attributes('href')).toBe(
+ mockArchitectures[0].downloadLocation,
+ );
+ });
+ });
+
+ describe('after another platform and architecture are selected', () => {
+ beforeEach(async () => {
+ runnerSetupInstructionsHandler.mockResolvedValue(mockInstructionsWindows);
+
+ findArchitectureDropdownItems().at(1).vm.$emit('click');
+
+ wrapper.setProps({ platform: mockPlatform2 });
+ await waitForPromises();
+ });
+
+ it('runner instructions are requested', () => {
+ expect(runnerSetupInstructionsHandler).toHaveBeenLastCalledWith({
+ platform: mockPlatform2.name,
+ architecture: mockPlatform2.architectures[0].name,
+ });
+ });
+ });
+ });
+
+ describe('when a register token is not known', () => {
+ beforeEach(async () => {
+ createComponent({ props: { registrationToken: undefined } });
+ await waitForPromises();
+ });
+
+ it('register command is shown without a defined registration token', () => {
+ const instructions = findRegisterCommand().text();
+
+ expect(instructions).toBe(mockInstructions.data.runnerSetup.registerInstructions);
+ });
+ });
+
+ describe('when apollo is loading', () => {
+ it('should show a loading icon', async () => {
+ createComponent();
+
+ expect(findGlLoadingIcon().exists()).toBe(true);
+
+ await waitForPromises();
+
+ expect(findGlLoadingIcon().exists()).toBe(false);
+ });
+ });
+
+ describe('when instructions cannot be loaded', () => {
+ beforeEach(async () => {
+ runnerSetupInstructionsHandler.mockRejectedValue();
+
+ createComponent();
+ await waitForPromises();
+ });
+
+ it('should show alert', () => {
+ expect(wrapper.emitted()).toEqual({ error: [[]] });
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/runner_instructions/instructions/runner_docker_instructions_spec.js b/spec/frontend/vue_shared/components/runner_instructions/instructions/runner_docker_instructions_spec.js
new file mode 100644
index 00000000000..2922d261b24
--- /dev/null
+++ b/spec/frontend/vue_shared/components/runner_instructions/instructions/runner_docker_instructions_spec.js
@@ -0,0 +1,28 @@
+import { shallowMount } from '@vue/test-utils';
+
+import { GlButton } from '@gitlab/ui';
+import RunnerDockerInstructions from '~/vue_shared/components/runner_instructions/instructions/runner_docker_instructions.vue';
+
+describe('RunnerDockerInstructions', () => {
+ let wrapper;
+
+ const createComponent = () => {
+ wrapper = shallowMount(RunnerDockerInstructions, {});
+ };
+
+ const findButton = () => wrapper.findComponent(GlButton);
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders contents', () => {
+ expect(wrapper.text().replace(/\s+/g, ' ')).toMatchSnapshot();
+ });
+
+ it('renders link', () => {
+ expect(findButton().attributes('href')).toBe(
+ 'https://docs.gitlab.com/runner/install/docker.html',
+ );
+ });
+});
diff --git a/spec/frontend/vue_shared/components/runner_instructions/instructions/runner_kubernetes_instructions_spec.js b/spec/frontend/vue_shared/components/runner_instructions/instructions/runner_kubernetes_instructions_spec.js
new file mode 100644
index 00000000000..0bfcc0e3d86
--- /dev/null
+++ b/spec/frontend/vue_shared/components/runner_instructions/instructions/runner_kubernetes_instructions_spec.js
@@ -0,0 +1,28 @@
+import { shallowMount } from '@vue/test-utils';
+
+import { GlButton } from '@gitlab/ui';
+import RunnerKubernetesInstructions from '~/vue_shared/components/runner_instructions/instructions/runner_kubernetes_instructions.vue';
+
+describe('RunnerKubernetesInstructions', () => {
+ let wrapper;
+
+ const createComponent = () => {
+ wrapper = shallowMount(RunnerKubernetesInstructions, {});
+ };
+
+ const findButton = () => wrapper.findComponent(GlButton);
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('renders contents', () => {
+ expect(wrapper.text().replace(/\s+/g, ' ')).toMatchSnapshot();
+ });
+
+ it('renders link', () => {
+ expect(findButton().attributes('href')).toBe(
+ 'https://docs.gitlab.com/runner/install/kubernetes.html',
+ );
+ });
+});
diff --git a/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js b/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js
index 4e7ece4c930..19f2dd137ff 100644
--- a/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js
+++ b/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js
@@ -1,4 +1,4 @@
-import { GlAlert, GlModal, GlButton, GlLoadingIcon, GlSkeletonLoader } from '@gitlab/ui';
+import { GlAlert, GlModal, GlButton, GlSkeletonLoader } from '@gitlab/ui';
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
import { shallowMount } from '@vue/test-utils';
import Vue, { nextTick } from 'vue';
@@ -7,10 +7,12 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import getRunnerPlatformsQuery from '~/vue_shared/components/runner_instructions/graphql/get_runner_platforms.query.graphql';
-import getRunnerSetupInstructionsQuery from '~/vue_shared/components/runner_instructions/graphql/get_runner_setup.query.graphql';
import RunnerInstructionsModal from '~/vue_shared/components/runner_instructions/runner_instructions_modal.vue';
+import RunnerCliInstructions from '~/vue_shared/components/runner_instructions/instructions/runner_cli_instructions.vue';
+import RunnerDockerInstructions from '~/vue_shared/components/runner_instructions/instructions/runner_docker_instructions.vue';
+import RunnerKubernetesInstructions from '~/vue_shared/components/runner_instructions/instructions/runner_kubernetes_instructions.vue';
-import { mockRunnerPlatforms, mockInstructions, mockInstructionsWindows } from './mock_data';
+import { mockRunnerPlatforms } from './mock_data';
Vue.use(VueApollo);
@@ -36,24 +38,16 @@ describe('RunnerInstructionsModal component', () => {
let wrapper;
let fakeApollo;
let runnerPlatformsHandler;
- let runnerSetupInstructionsHandler;
const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
- const findGlLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findAlert = () => wrapper.findComponent(GlAlert);
const findModal = () => wrapper.findComponent(GlModal);
const findPlatformButtonGroup = () => wrapper.findByTestId('platform-buttons');
const findPlatformButtons = () => findPlatformButtonGroup().findAllComponents(GlButton);
- const findArchitectureDropdownItems = () => wrapper.findAllByTestId('architecture-dropdown-item');
- const findBinaryDownloadButton = () => wrapper.findByTestId('binary-download-button');
- const findBinaryInstructions = () => wrapper.findByTestId('binary-instructions');
- const findRegisterCommand = () => wrapper.findByTestId('register-command');
+ const findRunnerCliInstructions = () => wrapper.findComponent(RunnerCliInstructions);
const createComponent = ({ props, shown = true, ...options } = {}) => {
- const requestHandlers = [
- [getRunnerPlatformsQuery, runnerPlatformsHandler],
- [getRunnerSetupInstructionsQuery, runnerSetupInstructionsHandler],
- ];
+ const requestHandlers = [[getRunnerPlatformsQuery, runnerPlatformsHandler]];
fakeApollo = createMockApollo(requestHandlers);
@@ -77,7 +71,6 @@ describe('RunnerInstructionsModal component', () => {
beforeEach(() => {
runnerPlatformsHandler = jest.fn().mockResolvedValue(mockRunnerPlatforms);
- runnerSetupInstructionsHandler = jest.fn().mockResolvedValue(mockInstructions);
});
afterEach(() => {
@@ -102,87 +95,12 @@ describe('RunnerInstructionsModal component', () => {
expect(buttons).toHaveLength(mockRunnerPlatforms.data.runnerPlatforms.nodes.length);
});
- it('should contain a number of dropdown items for the architecture options', () => {
- expect(findArchitectureDropdownItems()).toHaveLength(
- mockRunnerPlatforms.data.runnerPlatforms.nodes[0].architectures.nodes.length,
- );
- });
-
- describe('should display default instructions', () => {
- const { installInstructions } = mockInstructions.data.runnerSetup;
-
- it('runner instructions are requested', () => {
- expect(runnerSetupInstructionsHandler).toHaveBeenCalledWith({
- platform: 'linux',
- architecture: 'amd64',
- });
- });
-
- it('binary instructions are shown', async () => {
- const instructions = findBinaryInstructions().text();
-
- expect(instructions).toBe(installInstructions.trim());
- });
-
- it('register command is shown with a replaced token', async () => {
- const command = findRegisterCommand().text();
-
- expect(command).toBe(
- 'sudo gitlab-runner register --url http://localhost/ --registration-token MY_TOKEN',
- );
- });
- });
-
- describe('after a platform and architecture are selected', () => {
- const windowsIndex = 2;
- const { installInstructions } = mockInstructionsWindows.data.runnerSetup;
-
- beforeEach(async () => {
- runnerSetupInstructionsHandler.mockResolvedValue(mockInstructionsWindows);
-
- findPlatformButtons().at(windowsIndex).vm.$emit('click');
- await waitForPromises();
- });
+ it('should display architecture options', () => {
+ const { architectures } = findRunnerCliInstructions().props('platform');
- it('runner instructions are requested', () => {
- expect(runnerSetupInstructionsHandler).toHaveBeenLastCalledWith({
- platform: 'windows',
- architecture: 'amd64',
- });
- });
-
- it('architecture download link is updated', () => {
- const architectures =
- mockRunnerPlatforms.data.runnerPlatforms.nodes[windowsIndex].architectures.nodes;
-
- expect(findBinaryDownloadButton().attributes('href')).toBe(
- architectures[0].downloadLocation,
- );
- });
-
- it('other binary instructions are shown', () => {
- const instructions = findBinaryInstructions().text();
-
- expect(instructions).toBe(installInstructions.trim());
- });
-
- it('register command is shown', () => {
- const command = findRegisterCommand().text();
-
- expect(command).toBe(
- './gitlab-runner.exe register --url http://localhost/ --registration-token MY_TOKEN',
- );
- });
-
- it('runner instructions are requested with another architecture', async () => {
- findArchitectureDropdownItems().at(1).vm.$emit('click');
- await waitForPromises();
-
- expect(runnerSetupInstructionsHandler).toHaveBeenLastCalledWith({
- platform: 'windows',
- architecture: '386',
- });
- });
+ expect(architectures).toEqual(
+ mockRunnerPlatforms.data.runnerPlatforms.nodes[0].architectures.nodes,
+ );
});
describe('when the modal resizes', () => {
@@ -202,16 +120,14 @@ describe('RunnerInstructionsModal component', () => {
});
});
- describe('when a register token is not known', () => {
+ describe.each([null, 'DEFINED'])('when registration token is %p', (token) => {
beforeEach(async () => {
- createComponent({ props: { registrationToken: undefined } });
+ createComponent({ props: { registrationToken: token } });
await waitForPromises();
});
it('register command is shown without a defined registration token', () => {
- const instructions = findRegisterCommand().text();
-
- expect(instructions).toBe(mockInstructions.data.runnerSetup.registerInstructions);
+ expect(findRunnerCliInstructions().props('registrationToken')).toBe(token);
});
});
@@ -221,21 +137,33 @@ describe('RunnerInstructionsModal component', () => {
await waitForPromises();
});
- it('runner instructions for the default selected platform are requested', () => {
- expect(runnerSetupInstructionsHandler).toHaveBeenLastCalledWith({
- platform: 'osx',
- architecture: 'amd64',
- });
+ it('should preselect', () => {
+ const selected = findPlatformButtons()
+ .filter((btn) => btn.props('selected'))
+ .at(0);
+
+ expect(selected.text()).toBe('macOS');
});
- it('sets the focus on the default selected platform', () => {
- const findOsxPlatformButton = () => wrapper.findComponent({ ref: 'osx' });
+ it('runner instructions for the default selected platform are requested', () => {
+ const { name } = findRunnerCliInstructions().props('platform');
- findOsxPlatformButton().element.focus = jest.fn();
+ expect(name).toBe('osx');
+ });
+ });
- findModal().vm.$emit('shown');
+ describe.each`
+ platform | component
+ ${'docker'} | ${RunnerDockerInstructions}
+ ${'kubernetes'} | ${RunnerKubernetesInstructions}
+ `('with platform "$platform"', ({ platform, component }) => {
+ beforeEach(async () => {
+ createComponent({ props: { defaultPlatformName: platform } });
+ await waitForPromises();
+ });
- expect(findOsxPlatformButton().element.focus).toHaveBeenCalled();
+ it(`runner instructions for ${platform} are shown`, () => {
+ expect(wrapper.findComponent(component).exists()).toBe(true);
});
});
@@ -247,7 +175,6 @@ describe('RunnerInstructionsModal component', () => {
it('does not fetch instructions', () => {
expect(runnerPlatformsHandler).not.toHaveBeenCalled();
- expect(runnerSetupInstructionsHandler).not.toHaveBeenCalled();
});
});
@@ -255,43 +182,41 @@ describe('RunnerInstructionsModal component', () => {
it('should show a skeleton loader', async () => {
createComponent();
await nextTick();
- await nextTick();
expect(findSkeletonLoader().exists()).toBe(true);
- expect(findGlLoadingIcon().exists()).toBe(false);
-
- // wait on fetch of both `platforms` and `instructions`
- await nextTick();
- await nextTick();
-
- expect(findGlLoadingIcon().exists()).toBe(true);
});
it('once loaded, should not show a loading state', async () => {
createComponent();
-
await waitForPromises();
expect(findSkeletonLoader().exists()).toBe(false);
- expect(findGlLoadingIcon().exists()).toBe(false);
});
});
- describe('when instructions cannot be loaded', () => {
- beforeEach(async () => {
- runnerSetupInstructionsHandler.mockRejectedValue();
+ describe('errors', () => {
+ it('should show an alert when platforms cannot be loaded', async () => {
+ runnerPlatformsHandler.mockRejectedValue();
createComponent();
await waitForPromises();
- });
- it('should show alert', () => {
expect(findAlert().exists()).toBe(true);
});
- it('should not show instructions', () => {
- expect(findBinaryInstructions().exists()).toBe(false);
- expect(findRegisterCommand().exists()).toBe(false);
+ it('should show alert when instructions cannot be loaded', async () => {
+ createComponent();
+ await waitForPromises();
+
+ findRunnerCliInstructions().vm.$emit('error');
+ await waitForPromises();
+
+ expect(findAlert().exists()).toBe(true);
+
+ findAlert().vm.$emit('dismiss');
+ await nextTick();
+
+ expect(findAlert().exists()).toBe(false);
});
});
@@ -308,14 +233,16 @@ describe('RunnerInstructionsModal component', () => {
describe('show()', () => {
let mockShow;
+ let mockClose;
beforeEach(() => {
mockShow = jest.fn();
+ mockClose = jest.fn();
createComponent({
shown: false,
stubs: {
- GlModal: getGlModalStub({ show: mockShow }),
+ GlModal: getGlModalStub({ show: mockShow, close: mockClose }),
},
});
});
@@ -325,6 +252,12 @@ describe('RunnerInstructionsModal component', () => {
expect(mockShow).toHaveBeenCalledTimes(1);
});
+
+ it('delegates close()', () => {
+ wrapper.vm.close();
+
+ expect(mockClose).toHaveBeenCalledTimes(1);
+ });
});
});
});
diff --git a/spec/helpers/version_check_helper_spec.rb b/spec/helpers/version_check_helper_spec.rb
index 2bb85e7b6b8..c76eb08820a 100644
--- a/spec/helpers/version_check_helper_spec.rb
+++ b/spec/helpers/version_check_helper_spec.rb
@@ -49,19 +49,26 @@ RSpec.describe VersionCheckHelper do
describe '#show_security_patch_upgrade_alert?' do
describe 'return conditions' do
- where(:show_version_check, :gitlab_version_check, :result) do
+ where(:feature_enabled, :show_version_check, :gitlab_version_check, :result) do
[
- [false, nil, false],
- [false, { "severity" => "success" }, false],
- [false, { "severity" => "danger" }, false],
- [true, nil, false],
- [true, { "severity" => "success" }, false],
- [true, { "severity" => "danger" }, true]
+ [false, false, nil, false],
+ [false, false, { "severity" => "success" }, false],
+ [false, false, { "severity" => "danger" }, false],
+ [false, true, nil, false],
+ [false, true, { "severity" => "success" }, false],
+ [false, true, { "severity" => "danger" }, false],
+ [true, false, nil, false],
+ [true, false, { "severity" => "success" }, false],
+ [true, false, { "severity" => "danger" }, false],
+ [true, true, nil, false],
+ [true, true, { "severity" => "success" }, false],
+ [true, true, { "severity" => "danger" }, true]
]
end
with_them do
before do
+ stub_feature_flags(critical_security_alert: feature_enabled)
allow(helper).to receive(:show_version_check?).and_return(show_version_check)
allow(helper).to receive(:gitlab_version_check).and_return(gitlab_version_check)
end
diff --git a/spec/migrations/20210804150320_create_base_work_item_types_spec.rb b/spec/migrations/20210804150320_create_base_work_item_types_spec.rb
index 5626b885626..e7f76eb0ae0 100644
--- a/spec/migrations/20210804150320_create_base_work_item_types_spec.rb
+++ b/spec/migrations/20210804150320_create_base_work_item_types_spec.rb
@@ -17,7 +17,8 @@ RSpec.describe CreateBaseWorkItemTypes, :migration, feature_category: :team_plan
}
end
- after(:all) do
+ # We use append_after to make sure this runs after the schema was reset to its latest state
+ append_after(:all) do
# Make sure base types are recreated after running the migration
# because migration specs are not run in a transaction
reset_work_item_types
diff --git a/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb b/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb
index 2a19dc025a7..4c7ef9ac1e8 100644
--- a/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb
+++ b/spec/migrations/20210831203408_upsert_base_work_item_types_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe UpsertBaseWorkItemTypes, :migration, feature_category: :team_plan
}
end
- after(:all) do
+ append_after(:all) do
# Make sure base types are recreated after running the migration
# because migration specs are not run in a transaction
reset_work_item_types
diff --git a/spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb b/spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb
index 32edd3615ff..db68e895b61 100644
--- a/spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb
+++ b/spec/migrations/20211126204445_add_task_to_work_item_types_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe AddTaskToWorkItemTypes, :migration, feature_category: :team_plann
}
end
- after(:all) do
+ append_after(:all) do
# Make sure base types are recreated after running the migration
# because migration specs are not run in a transaction
reset_work_item_types
diff --git a/spec/migrations/20221018050323_add_objective_and_keyresult_to_work_item_types_spec.rb b/spec/migrations/20221018050323_add_objective_and_keyresult_to_work_item_types_spec.rb
index 3ab33367303..6284608becb 100644
--- a/spec/migrations/20221018050323_add_objective_and_keyresult_to_work_item_types_spec.rb
+++ b/spec/migrations/20221018050323_add_objective_and_keyresult_to_work_item_types_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe AddObjectiveAndKeyresultToWorkItemTypes, :migration, feature_cate
}
end
- after(:all) do
+ append_after(:all) do
# Make sure base types are recreated after running the migration
# because migration specs are not run in a transaction
reset_work_item_types
diff --git a/spec/support/helpers/api_helpers.rb b/spec/support/helpers/api_helpers.rb
index 62bb9576695..a9d7c6af959 100644
--- a/spec/support/helpers/api_helpers.rb
+++ b/spec/support/helpers/api_helpers.rb
@@ -19,7 +19,7 @@ module ApiHelpers
# => "/api/v2/issues?foo=bar&private_token=..."
#
# Returns the relative path to the requested API resource
- def api(path, user = nil, version: API::API.version, personal_access_token: nil, oauth_access_token: nil, job_token: nil, access_token: nil)
+ def api(path, user = nil, version: API::API.version, personal_access_token: nil, oauth_access_token: nil, job_token: nil, access_token: nil, admin_mode: false)
full_path = "/api/#{version}#{path}"
if oauth_access_token
@@ -31,7 +31,12 @@ module ApiHelpers
elsif access_token
query_string = "access_token=#{access_token.token}"
elsif user
- personal_access_token = create(:personal_access_token, user: user)
+ personal_access_token = if admin_mode && user.admin?
+ create(:personal_access_token, :admin_mode, user: user)
+ else
+ create(:personal_access_token, user: user)
+ end
+
query_string = "private_token=#{personal_access_token.token}"
end
diff --git a/spec/tooling/danger/specs_spec.rb b/spec/tooling/danger/specs_spec.rb
index dcc1f592062..422923827a8 100644
--- a/spec/tooling/danger/specs_spec.rb
+++ b/spec/tooling/danger/specs_spec.rb
@@ -245,15 +245,16 @@ RSpec.describe Tooling::Danger::Specs, feature_category: :tooling do
" let_it_be(:user) { create(:user) }",
" end",
" describe 'GET \"time_summary\"' do",
- " end"
- ]
- end
-
- let(:matching_lines) do
- [
- "+ RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController, feature_category: :planning_analytics do",
- "+RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do",
- "+ RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do"
+ " end",
+ " \n",
+ "RSpec.describe Projects :aggregate_failures,",
+ " feature_category: planning_analytics do",
+ " \n",
+ "RSpec.describe Epics :aggregate_failures,",
+ " ee: true do",
+ "\n",
+ "RSpec.describe Issues :aggregate_failures,",
+ " feature_category: :team_planning do"
]
end
@@ -264,14 +265,24 @@ RSpec.describe Tooling::Danger::Specs, feature_category: :tooling do
"+ let_it_be(:user) { create(:user) }",
"- end",
"+ describe 'GET \"time_summary\"' do",
- "+ RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do"
+ "+ RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do",
+ "+RSpec.describe Projects :aggregate_failures,",
+ "+ feature_category: planning_analytics do",
+ "+RSpec.describe Epics :aggregate_failures,",
+ "+ ee: true do",
+ "+RSpec.describe Issues :aggregate_failures,"
]
end
+ before do
+ allow(specs.helper).to receive(:changed_lines).with(filename).and_return(changed_lines)
+ end
+
it 'adds suggestions at the correct lines', :aggregate_failures do
[
{ suggested_line: "RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do", number: 5 },
- { suggested_line: " RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do", number: 10 }
+ { suggested_line: " RSpec.describe Projects::Analytics::CycleAnalytics::SummaryController do", number: 10 },
+ { suggested_line: "RSpec.describe Epics :aggregate_failures,", number: 19 }
].each do |test_case|
comment = format(template, suggested_line: test_case[:suggested_line])
diff --git a/tooling/danger/specs.rb b/tooling/danger/specs.rb
index c7baf920314..6c0459a4344 100644
--- a/tooling/danger/specs.rb
+++ b/tooling/danger/specs.rb
@@ -45,12 +45,12 @@ module Tooling
for background information and alternative options.
SUGGEST_COMMENT
- FEATURE_CATEGORY_REGEX = /^\+.?RSpec\.describe(.+)(?!feature_category)/.freeze
+ RSPEC_TOP_LEVEL_DESCRIBE_REGEX = /^\+.?RSpec\.describe(.+)/.freeze
FEATURE_CATEGORY_SUGGESTION = <<~SUGGESTION_MARKDOWN
Consider adding `feature_category: <feature_category_name>` for this example if it is not set already.
See [testing best practices](https://docs.gitlab.com/ee/development/testing_guide/best_practices.html#feature-category-metadata).
SUGGESTION_MARKDOWN
- FEATURE_CATEGORY_EXCLUDE = 'feature_category'
+ FEATURE_CATEGORY_KEYWORD = 'feature_category'
def changed_specs_files(ee: :include)
changed_files = helper.all_changed_files
@@ -86,13 +86,26 @@ module Tooling
end
def add_suggestions_for_feature_category(filename)
- add_suggestion(
- filename,
- FEATURE_CATEGORY_REGEX,
- FEATURE_CATEGORY_SUGGESTION,
- nil,
- FEATURE_CATEGORY_EXCLUDE
- )
+ file_lines = project_helper.file_lines(filename)
+ changed_lines = helper.changed_lines(filename)
+
+ changed_lines.each_with_index do |changed_line, i|
+ next unless changed_line =~ RSPEC_TOP_LEVEL_DESCRIBE_REGEX
+
+ next_line_in_file = file_lines[file_lines.find_index(changed_line.delete_prefix('+')) + 1]
+
+ if changed_line.include?(FEATURE_CATEGORY_KEYWORD) || next_line_in_file.to_s.include?(FEATURE_CATEGORY_KEYWORD)
+ next
+ end
+
+ line_number = file_lines.find_index(changed_line.delete_prefix('+'))
+ next unless line_number
+
+ suggested_line = file_lines[line_number]
+
+ text = format(comment(FEATURE_CATEGORY_SUGGESTION), suggested_line: suggested_line)
+ markdown(text, file: filename, line: line_number + 1)
+ end
end
private