From a300f4d5c7fdd5d557288ee526986e0adb683b35 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 18 Oct 2022 12:09:21 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../cop/static_translation_definition.yml | 14 --- Gemfile | 2 +- Gemfile.checksum | 12 +-- Gemfile.lock | 31 +++---- .../javascripts/diffs/components/app.vue | 2 +- .../groups/components/overview_tabs.vue | 63 ++++++-------- .../components/gitlab_version_check.vue | 25 +++--- .../mutations/ci/job/artifacts_destroy.rb | 21 +++-- app/models/application_setting.rb | 32 +++---- app/models/ci/job_token/scope.rb | 2 +- app/models/group_group_link.rb | 2 +- .../incident_management/timeline_event.rb | 5 ++ .../incident_management/timeline_event_tag.rb | 20 +++++ .../timeline_event_tag_link.rb | 11 +++ app/models/jira_import_state.rb | 2 +- app/models/member.rb | 2 +- app/models/project.rb | 11 +-- app/models/project_group_link.rb | 2 +- app/models/user.rb | 4 +- app/models/users/banned_user.rb | 2 +- .../ci/job_artifacts/delete_service.rb | 18 +++- .../appearances/preview_sign_in.html.haml | 4 +- .../issuable/_label_page_create.html.haml | 4 +- app/workers/ci/cancel_pipeline_worker.rb | 1 + ...n_packages_total_unique_counts_monthly.yml | 7 +- ...en_packages_total_unique_counts_weekly.yml | 8 +- ...nt_management_timeline_event_tag_links.yml | 9 ++ ...ncident_management_timeline_event_tags.yml | 9 ++ ...incident_management_timeline_event_tags.rb | 19 +++++ ...ent_management_timeline_event_tag_links.rb | 33 +++++++ ...member_namespace_id_not_null_constraint.rb | 15 ++++ db/schema_migrations/20221005072353 | 1 + db/schema_migrations/20221005094926 | 1 + db/schema_migrations/20221018081416 | 1 + db/structure.sql | 62 +++++++++++++- lib/api/entities/ci/job_basic.rb | 2 +- lib/api/entities/project.rb | 2 +- lib/api/events.rb | 2 - lib/api/helpers/events_helpers.rb | 2 +- lib/gitlab/database/gitlab_schemas.yml | 2 + lib/gitlab/json.rb | 3 + qa/Gemfile | 2 +- qa/Gemfile.lock | 4 +- qa/Rakefile | 4 +- qa/qa.rb | 17 ++++ qa/qa/scenario/test/instance.rb | 36 -------- qa/qa/scenario/test/integration/github.rb | 20 ----- qa/qa/specs/qa_deprecation_toolkit_env.rb | 30 ++++--- qa/qa/specs/spec_helper.rb | 3 +- qa/qa/tools/ci/non_empty_suites.rb | 81 ++++++++---------- qa/qa/tools/delete_test_ssh_keys.rb | 2 +- qa/qa/tools/initialize_gitlab_auth.rb | 2 +- .../scenario/test/integration/github_spec.rb | 21 ----- qa/spec/tools/ci/non_empty_suites_spec.rb | 4 +- .../timeline_event_tag_links.rb | 8 ++ .../timeline_event_tags.rb | 8 ++ spec/factories/projects.rb | 4 +- spec/frontend/diffs/components/app_spec.js | 24 ++++++ .../groups/components/overview_tabs_spec.js | 8 ++ .../components/gitlab_version_check_spec.js | 18 ++-- .../ci/job_token_scope/add_project_spec.rb | 2 +- .../ci/job_token_scope/remove_project_spec.rb | 2 +- .../ci/job_token_scope_resolver_spec.rb | 6 +- .../types/ci/job_token_scope_type_spec.rb | 2 +- spec/lib/gitlab/import_export/all_models.yml | 3 + spec/lib/gitlab/json_spec.rb | 10 +++ spec/models/ci/job_token/scope_spec.rb | 4 +- .../timeline_event_spec.rb | 6 ++ .../timeline_event_tag_link_spec.rb | 10 +++ .../timeline_event_tag_spec.rb | 28 ++++++ spec/models/project_spec.rb | 25 +++++- spec/policies/project_policy_spec.rb | 2 +- .../ci/job/artifacts_destroy_spec.rb | 85 +++++++++++++++++++ .../ci/job_token_scope/add_project_spec.rb | 2 +- .../ci/job_token_scope/remove_project_spec.rb | 2 +- .../ci/project_ci_cd_settings_update_spec.rb | 6 +- .../ci/job_artifacts/delete_service_spec.rb | 27 +++++- .../add_project_service_spec.rb | 2 +- .../remove_project_service_spec.rb | 2 +- .../project_ci_cd_settings_shared_examples.rb | 10 +-- 80 files changed, 683 insertions(+), 319 deletions(-) create mode 100644 app/models/incident_management/timeline_event_tag.rb create mode 100644 app/models/incident_management/timeline_event_tag_link.rb create mode 100644 db/docs/incident_management_timeline_event_tag_links.yml create mode 100644 db/docs/incident_management_timeline_event_tags.yml create mode 100644 db/migrate/20221005072353_create_incident_management_timeline_event_tags.rb create mode 100644 db/migrate/20221005094926_create_incident_management_timeline_event_tag_links.rb create mode 100644 db/migrate/20221018081416_members_remove_member_namespace_id_not_null_constraint.rb create mode 100644 db/schema_migrations/20221005072353 create mode 100644 db/schema_migrations/20221005094926 create mode 100644 db/schema_migrations/20221018081416 delete mode 100644 qa/qa/scenario/test/instance.rb delete mode 100644 qa/qa/scenario/test/integration/github.rb delete mode 100644 qa/spec/scenario/test/integration/github_spec.rb create mode 100644 spec/factories/incident_management/timeline_event_tag_links.rb create mode 100644 spec/factories/incident_management/timeline_event_tags.rb create mode 100644 spec/models/incident_management/timeline_event_tag_link_spec.rb create mode 100644 spec/models/incident_management/timeline_event_tag_spec.rb create mode 100644 spec/requests/api/graphql/mutations/ci/job/artifacts_destroy_spec.rb diff --git a/.rubocop_todo/cop/static_translation_definition.yml b/.rubocop_todo/cop/static_translation_definition.yml index 22f5070d07c..ecbee866a6c 100644 --- a/.rubocop_todo/cop/static_translation_definition.yml +++ b/.rubocop_todo/cop/static_translation_definition.yml @@ -1,20 +1,6 @@ --- Cop/StaticTranslationDefinition: Exclude: - - 'app/models/application_setting.rb' - 'app/models/diff_viewer/image.rb' - 'app/models/diff_viewer/rich.rb' - 'app/models/diff_viewer/simple.rb' - - 'app/models/group_group_link.rb' - - 'app/models/jira_import_state.rb' - - 'app/models/member.rb' - - 'app/models/project.rb' - - 'app/models/project_group_link.rb' - - 'app/models/user.rb' - - 'app/models/users/banned_user.rb' - - 'ee/app/models/allowed_email_domain.rb' - - 'ee/app/models/dast/site_profile_secret_variable.rb' - - 'ee/app/models/group_merge_request_approval_setting.rb' - - 'ee/app/models/incident_management/escalation_policy.rb' - - 'ee/app/models/incident_management/escalation_rule.rb' - - 'ee/app/models/vulnerabilities/read.rb' diff --git a/Gemfile b/Gemfile index a671cfebe8b..65ed6078dc5 100644 --- a/Gemfile +++ b/Gemfile @@ -424,7 +424,7 @@ group :development, :test do gem 'sigdump', '~> 0.2.4', require: 'sigdump/setup' - gem 'pact', '~> 1.12' + gem 'pact', '~> 1.63' end group :development, :test, :danger do diff --git a/Gemfile.checksum b/Gemfile.checksum index 11f50976235..509fb46286c 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -141,6 +141,7 @@ {"name":"ethon","version":"0.15.0","platform":"ruby","checksum":"0809805a035bc10f54162ca99f15ded49e428e0488bcfe1c08c821e18261a74d"}, {"name":"excon","version":"0.90.0","platform":"ruby","checksum":"01beac0f20652b12de95aef931f72bcb82ffb009e1c34c42a5cf5df93f4070ae"}, {"name":"execjs","version":"2.8.1","platform":"ruby","checksum":"6d939919cfd81bcc4d6556f322c3995a70cfe4289ea0bd3b1f999b489c323088"}, +{"name":"expgen","version":"0.1.1","platform":"ruby","checksum":"4e6a0f65b210a201d6045debb3e62a24e33251a49f81a11b067d303a60d3a239"}, {"name":"expression_parser","version":"0.9.0","platform":"ruby","checksum":"2b56db3cffc48c3337f4f29f5bc2374c86e7ba29acb40269c74bb55af9f868a4"}, {"name":"extended-markdown-filter","version":"0.6.0","platform":"ruby","checksum":"46844b5740b1703a0e0674e31a17c83d1244a3198abb3aae51cad1eb152eb19e"}, {"name":"factory_bot","version":"6.2.0","platform":"ruby","checksum":"d181902cdda531cf6cef036001b3a700a7b5e04bac63976864530120b2ac7d13"}, @@ -398,9 +399,9 @@ {"name":"org-ruby","version":"0.9.12","platform":"ruby","checksum":"93cbec3a4470cb9dca6a4a98dc276a6434ea9d9e7bc2d42ea33c3aedd5d1c974"}, {"name":"orm_adapter","version":"0.5.0","platform":"ruby","checksum":"aa5d0be5d540cbb46d3a93e88061f4ece6a25f6e97d6a47122beb84fe595e9b9"}, {"name":"os","version":"1.1.1","platform":"ruby","checksum":"3db1fbc14ab8ea99b69ed8e353c894613e1b35e665fffb90414996cf8989d489"}, -{"name":"pact","version":"1.59.0","platform":"ruby","checksum":"6272cea35e4ee809493fadcba9800d4a24c262ef0778a0d1ba5d9a9b3f61fc59"}, -{"name":"pact-mock_service","version":"3.6.2","platform":"ruby","checksum":"cc91229484ae428b6eb3a6673c178046cbf6610ee6536ca6cbac060b6071f547"}, -{"name":"pact-support","version":"1.15.1","platform":"ruby","checksum":"c364596fe9fe78c4f93028013262d5d97867a680fa6acc35dda946447cdf1d1f"}, +{"name":"pact","version":"1.63.0","platform":"ruby","checksum":"cc2991ed242bf182c6a4abadfd492b2923d09a9b3ed24578126cc056921cb151"}, +{"name":"pact-mock_service","version":"3.10.0","platform":"ruby","checksum":"898ec3b8d96f1934d15941c701ca7d5fef5ccff32022d9a196fb82073cd95e27"}, +{"name":"pact-support","version":"1.18.1","platform":"ruby","checksum":"4a25961c8b1c4132e433a8eaa838b1e6914c6d3aae48eee705b9860a5e8b0476"}, {"name":"parallel","version":"1.22.1","platform":"ruby","checksum":"ebdf1f0c51f182df38522f70ba770214940bef998cdb6e00f36492b29699761f"}, {"name":"parser","version":"3.1.2.1","platform":"ruby","checksum":"57e49821b52d5fe7baffaca44ed77e9754688c9bbc68443b5293a722fdb161e0"}, {"name":"parslet","version":"1.8.2","platform":"ruby","checksum":"08d1ab3721cd3f175bfbee8788b2ddff71f92038f2d69bd65454c22bb9fbd98a"}, @@ -438,7 +439,7 @@ {"name":"rack-oauth2","version":"1.21.3","platform":"ruby","checksum":"4e72a79dd6a866692e84422a552b27c38a5a1918ded06661e04910f2bbe676ba"}, {"name":"rack-protection","version":"2.2.2","platform":"ruby","checksum":"fd41414dbabbec274af0bdb1f72a48504449de4d979782c9af38cbb5dfff3299"}, {"name":"rack-proxy","version":"0.7.4","platform":"ruby","checksum":"a8bb373583d8a3165d8caf5af5fd7c32c9e8a91b983fbc531efa0e3d6617d2d4"}, -{"name":"rack-test","version":"1.1.0","platform":"ruby","checksum":"154161f40f162b1c009a655b7b0c5de3a3102cc6d7d2e94b64e1f46ace800866"}, +{"name":"rack-test","version":"2.0.2","platform":"ruby","checksum":"adadd0e957f63a34199a9fdf905a920a0b0a50795735095b4ac4bd3c13385466"}, {"name":"rack-timeout","version":"0.6.3","platform":"ruby","checksum":"1754892eacc124d405e7f1145731ec9b7421ebd1bee5d51ddc18b72c204d0ab3"}, {"name":"rails","version":"6.1.6.1","platform":"ruby","checksum":"17024921a3913fb341f584542b06adf6bb12977a8b92d5fce093c3996c963686"}, {"name":"rails-controller-testing","version":"1.0.5","platform":"ruby","checksum":"741448db59366073e86fc965ba403f881c636b79a2c39a48d0486f2607182e94"}, @@ -448,7 +449,6 @@ {"name":"railties","version":"6.1.6.1","platform":"ruby","checksum":"bafecdf2dcbe4ea44e1ab7081fd797aa87ae9bbcd0f3a4372b662a1b93949733"}, {"name":"rainbow","version":"3.1.1","platform":"ruby","checksum":"039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a"}, {"name":"rake","version":"13.0.6","platform":"ruby","checksum":"5ce4bf5037b4196c24ac62834d8db1ce175470391026bd9e557d669beeb19097"}, -{"name":"randexp","version":"0.1.7","platform":"ruby","checksum":"3026510ecf6a8e8642b9b96fa44bb41af6d24058023b7df77cf280f08e14e4c8"}, {"name":"rb-fsevent","version":"0.11.2","platform":"ruby","checksum":"43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe"}, {"name":"rb-inotify","version":"0.10.1","platform":"ruby","checksum":"050062d4f31d307cca52c3f6a7f4b946df8de25fc4bd373e1a5142e41034a7ca"}, {"name":"rbtrace","version":"0.4.14","platform":"ruby","checksum":"162bbf89cecabfc4f09c869b655f6f3a679c4870ebb7cbdcadf7393a81cc1769"}, @@ -586,7 +586,7 @@ {"name":"timecop","version":"0.9.1","platform":"ruby","checksum":"374b543f0961dbd487e96d09ac812d4fdfeb603ec705bbff241ba060d0a9f534"}, {"name":"timeliness","version":"0.3.10","platform":"ruby","checksum":"c357233ce19dc53148e8b29dfddde134689f18f52b32928e9dfe12ebcf4a773f"}, {"name":"timfel-krb5-auth","version":"0.8.3","platform":"ruby","checksum":"ab388c9d747fa3cd95baf2cc1c03253e372d8c680adcc543670f4f099854bb80"}, -{"name":"tins","version":"1.31.0","platform":"ruby","checksum":"20b5ea997dc046358fd05f15d39636bd7946798591b9c5741cc41f69853c7894"}, +{"name":"tins","version":"1.31.1","platform":"ruby","checksum":"51c4a347c25c630d310cbc2c040ffb84e266c8227f2ade881f1130ee4f9fbecf"}, {"name":"toml-rb","version":"2.0.1","platform":"ruby","checksum":"5016c6c77ac72bca5fe67c372722bdfdd4479a6fe1a1c4ff2a486e247849b274"}, {"name":"tomlrb","version":"1.3.0","platform":"ruby","checksum":"68666bf53fa70ba686a48a7435ce7e086f5227c58c4c993bd9792f4760f2a503"}, {"name":"tpm-key_attestation","version":"0.9.0","platform":"ruby","checksum":"e469ad9111a68dab4d04596e1c0621d7c877c2e3e247f765af3c04f1adf2b8cd"}, diff --git a/Gemfile.lock b/Gemfile.lock index 9d0d013037d..24e4c532f08 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -420,6 +420,8 @@ GEM ffi (>= 1.15.0) excon (0.90.0) execjs (2.8.1) + expgen (0.1.1) + parslet expression_parser (0.9.0) extended-markdown-filter (0.6.0) html-pipeline (~> 2.0) @@ -999,29 +1001,29 @@ GEM rubypants (~> 0.2) orm_adapter (0.5.0) os (1.1.1) - pact (1.59.0) + pact (1.63.0) pact-mock_service (~> 3.0, >= 3.3.1) - pact-support (~> 1.15) - rack-test (>= 0.6.3, < 2.0.0) + pact-support (~> 1.16, >= 1.16.9) + rack-test (>= 0.6.3, < 3.0.0) rspec (~> 3.0) term-ansicolor (~> 1.0) thor (>= 0.20, < 2.0) webrick (~> 1.3) - pact-mock_service (3.6.2) + pact-mock_service (3.10.0) filelock (~> 1.1) find_a_port (~> 1.0.1) json - pact-support (~> 1.12, >= 1.12.0) + pact-support (~> 1.16, >= 1.16.4) rack (~> 2.0) rspec (>= 2.14) term-ansicolor (~> 1.0) thor (>= 0.19, < 2.0) webrick (~> 1.3) - pact-support (1.15.1) - awesome_print (~> 1.1) - randexp (~> 0.1.7) - rspec (>= 2.14) - term-ansicolor (~> 1.0) + pact-support (1.18.1) + awesome_print (~> 1.9) + diff-lcs (~> 1.4) + expgen (~> 0.1) + rainbow (~> 3.1.1) parallel (1.22.1) parser (3.1.2.1) ast (~> 2.4.1) @@ -1087,8 +1089,8 @@ GEM rack rack-proxy (0.7.4) rack - rack-test (1.1.0) - rack (>= 1.0, < 3) + rack-test (2.0.2) + rack (>= 1.3) rack-timeout (0.6.3) rails (6.1.6.1) actioncable (= 6.1.6.1) @@ -1125,7 +1127,6 @@ GEM thor (~> 1.0) rainbow (3.1.1) rake (13.0.6) - randexp (0.1.7) rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) @@ -1415,7 +1416,7 @@ GEM timecop (0.9.1) timeliness (0.3.10) timfel-krb5-auth (0.8.3) - tins (1.31.0) + tins (1.31.1) sync toml-rb (2.0.1) citrus (~> 3.0, > 3.0) @@ -1725,7 +1726,7 @@ DEPENDENCIES omniauth_crowd (~> 2.4.0)! openssl (= 2.2.1) org-ruby (~> 0.9.12) - pact (~> 1.12) + pact (~> 1.63) parallel (~> 1.19) parslet (~> 1.8) peek (~> 1.1) diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue index 2c330d8c851..bc49464a560 100644 --- a/app/assets/javascripts/diffs/components/app.vue +++ b/app/assets/javascripts/diffs/components/app.vue @@ -472,7 +472,7 @@ export default { fetchData(toggleTree = true) { this.fetchDiffFilesMeta() .then(({ real_size = 0 }) => { - this.diffFilesLength = parseInt(real_size, 10); + this.diffFilesLength = parseInt(real_size, 10) || 0; if (toggleTree) { this.setTreeDisplay(); } diff --git a/app/assets/javascripts/groups/components/overview_tabs.vue b/app/assets/javascripts/groups/components/overview_tabs.vue index 84e992b6365..15a0c686548 100644 --- a/app/assets/javascripts/groups/components/overview_tabs.vue +++ b/app/assets/javascripts/groups/components/overview_tabs.vue @@ -19,34 +19,35 @@ export default { components: { GlTabs, GlTab, GroupsApp, GlSearchBoxByType, GlSorting, GlSortingItem }, inject: ['endpoints', 'initialSort'], data() { + const tabs = [ + { + title: this.$options.i18n[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS], + key: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, + renderEmptyState: true, + lazy: this.$route.name !== ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, + service: new GroupsService(this.endpoints[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS]), + store: new GroupsStore({ showSchemaMarkup: true }), + }, + { + title: this.$options.i18n[ACTIVE_TAB_SHARED], + key: ACTIVE_TAB_SHARED, + renderEmptyState: false, + lazy: this.$route.name !== ACTIVE_TAB_SHARED, + service: new GroupsService(this.endpoints[ACTIVE_TAB_SHARED]), + store: new GroupsStore(), + }, + { + title: this.$options.i18n[ACTIVE_TAB_ARCHIVED], + key: ACTIVE_TAB_ARCHIVED, + renderEmptyState: false, + lazy: this.$route.name !== ACTIVE_TAB_ARCHIVED, + service: new GroupsService(this.endpoints[ACTIVE_TAB_ARCHIVED]), + store: new GroupsStore(), + }, + ]; return { - tabs: [ - { - title: this.$options.i18n[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS], - key: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, - renderEmptyState: true, - lazy: false, - service: new GroupsService(this.endpoints[ACTIVE_TAB_SUBGROUPS_AND_PROJECTS]), - store: new GroupsStore({ showSchemaMarkup: true }), - }, - { - title: this.$options.i18n[ACTIVE_TAB_SHARED], - key: ACTIVE_TAB_SHARED, - renderEmptyState: false, - lazy: true, - service: new GroupsService(this.endpoints[ACTIVE_TAB_SHARED]), - store: new GroupsStore(), - }, - { - title: this.$options.i18n[ACTIVE_TAB_ARCHIVED], - key: ACTIVE_TAB_ARCHIVED, - renderEmptyState: false, - lazy: true, - service: new GroupsService(this.endpoints[ACTIVE_TAB_ARCHIVED]), - store: new GroupsStore(), - }, - ], - activeTabIndex: 0, + tabs, + activeTabIndex: tabs.findIndex((tab) => tab.key === this.$route.name), sort: SORTING_ITEM_NAME, isAscending: true, search: '', @@ -75,14 +76,6 @@ export default { ) || SORTING_ITEM_NAME; this.sort = sort; this.isAscending = sort.asc === sortQueryStringValue; - - const activeTabIndex = this.tabs.findIndex((tab) => tab.key === this.$route.name); - - if (activeTabIndex === -1) { - return; - } - - this.activeTabIndex = activeTabIndex; }, methods: { handleTabInput(tabIndex) { diff --git a/app/assets/javascripts/vue_shared/components/gitlab_version_check.vue b/app/assets/javascripts/vue_shared/components/gitlab_version_check.vue index 25c4b01af44..c2be5e4f7a1 100644 --- a/app/assets/javascripts/vue_shared/components/gitlab_version_check.vue +++ b/app/assets/javascripts/vue_shared/components/gitlab_version_check.vue @@ -57,7 +57,7 @@ export default { this.status = res.data.severity; this.track('rendered_version_badge', { - label: this.status, + label: this.title, }); } }) @@ -66,19 +66,24 @@ export default { this.status = null; }); }, + onClick() { + this.track('click_version_badge', { label: this.title }); + }, }, UPGRADE_DOCS_URL, }; diff --git a/app/graphql/mutations/ci/job/artifacts_destroy.rb b/app/graphql/mutations/ci/job/artifacts_destroy.rb index c27ab9c4d89..34c58fc1240 100644 --- a/app/graphql/mutations/ci/job/artifacts_destroy.rb +++ b/app/graphql/mutations/ci/job/artifacts_destroy.rb @@ -25,12 +25,21 @@ module Mutations def resolve(id:) job = authorized_find!(id: id) - result = ::Ci::JobArtifacts::DestroyBatchService.new(job.job_artifacts, pick_up_at: Time.current).execute - { - job: job, - destroyed_artifacts_count: result[:destroyed_artifacts_count], - errors: Array(result[:errors]) - } + result = ::Ci::JobArtifacts::DeleteService.new(job).execute + + if result.success? + { + job: job, + destroyed_artifacts_count: result.payload[:destroyed_artifacts_count], + errors: Array(result.payload[:errors]) + } + else + { + job: job, + destroyed_artifacts_count: 0, + errors: Array(result.message) + } + end end end end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index fa8951bf642..f83aa79b461 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -120,7 +120,7 @@ class ApplicationSetting < ApplicationRecord if: :help_page_support_url_column_exists? validates :help_page_documentation_base_url, - length: { maximum: 255, message: _("is too long (maximum is %{count} characters)") }, + length: { maximum: 255, message: N_("is too long (maximum is %{count} characters)") }, allow_blank: true, addressable_url: true @@ -148,7 +148,7 @@ class ApplicationSetting < ApplicationRecord if: :akismet_enabled validates :spam_check_api_key, - length: { maximum: 2000, message: _('is too long (maximum is %{count} characters)') }, + length: { maximum: 2000, message: N_('is too long (maximum is %{count} characters)') }, allow_blank: true validates :unique_ips_limit_per_user, @@ -228,7 +228,7 @@ class ApplicationSetting < ApplicationRecord validates :default_artifacts_expire_in, presence: true, duration: true validates :container_expiration_policies_enable_historic_entries, - inclusion: { in: [true, false], message: _('must be a boolean value') } + inclusion: { in: [true, false], message: N_('must be a boolean value') } validates :container_registry_token_expire_delay, presence: true, @@ -320,8 +320,8 @@ class ApplicationSetting < ApplicationRecord validates :personal_access_token_prefix, format: { with: %r{\A[a-zA-Z0-9_+=/@:.-]+\z}, - message: _("can contain only letters of the Base64 alphabet (RFC4648) with the addition of '@', ':' and '.'") }, - length: { maximum: 20, message: _('is too long (maximum is %{count} characters)') }, + message: N_("can contain only letters of the Base64 alphabet (RFC4648) with the addition of '@', ':' and '.'") }, + length: { maximum: 20, message: N_('is too long (maximum is %{count} characters)') }, allow_blank: true validates :commit_email_hostname, format: { with: /\A[^@]+\z/ } @@ -369,7 +369,7 @@ class ApplicationSetting < ApplicationRecord validates :email_restrictions, untrusted_regexp: true - validates :hashed_storage_enabled, inclusion: { in: [true], message: _("Hashed storage can't be disabled anymore for new projects") } + validates :hashed_storage_enabled, inclusion: { in: [true], message: N_("Hashed storage can't be disabled anymore for new projects") } validates :container_registry_delete_tags_service_timeout, :container_registry_cleanup_tags_service_max_list_size, @@ -377,7 +377,7 @@ class ApplicationSetting < ApplicationRecord numericality: { only_integer: true, greater_than_or_equal_to: 0 } validates :container_registry_expiration_policies_caching, - inclusion: { in: [true, false], message: _('must be a boolean value') } + inclusion: { in: [true, false], message: N_('must be a boolean value') } validates :container_registry_import_max_tags_count, :container_registry_import_max_retries, @@ -404,11 +404,11 @@ class ApplicationSetting < ApplicationRecord numericality: { only_integer: true, greater_than_or_equal_to: 0 } validates :invisible_captcha_enabled, - inclusion: { in: [true, false], message: _('must be a boolean value') } + inclusion: { in: [true, false], message: N_('must be a boolean value') } validates :invitation_flow_enforcement, :can_create_group, allow_nil: false, - inclusion: { in: [true, false], message: _('must be a boolean value') } + inclusion: { in: [true, false], message: N_('must be a boolean value') } Gitlab::SSHPublicKey.supported_types.each do |type| validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type } @@ -513,11 +513,11 @@ class ApplicationSetting < ApplicationRecord rsa_key: true, allow_nil: true validates :rate_limiting_response_text, - length: { maximum: 255, message: _('is too long (maximum is %{count} characters)') }, + length: { maximum: 255, message: N_('is too long (maximum is %{count} characters)') }, allow_blank: true validates :jira_connect_application_key, - length: { maximum: 255, message: _('is too long (maximum is %{count} characters)') }, + length: { maximum: 255, message: N_('is too long (maximum is %{count} characters)') }, allow_blank: true with_options(presence: true, numericality: { only_integer: true, greater_than: 0 }) do @@ -561,7 +561,7 @@ class ApplicationSetting < ApplicationRecord allow_nil: false validates :admin_mode, - inclusion: { in: [true, false], message: _('must be a boolean value') } + inclusion: { in: [true, false], message: N_('must be a boolean value') } validates :external_pipeline_validation_service_url, addressable_url: true, allow_blank: true @@ -574,7 +574,7 @@ class ApplicationSetting < ApplicationRecord inclusion: { in: ApplicationSetting.whats_new_variants.keys } validates :floc_enabled, - inclusion: { in: [true, false], message: _('must be a boolean value') } + inclusion: { in: [true, false], message: N_('must be a boolean value') } enum sidekiq_job_limiter_mode: { Gitlab::SidekiqMiddleware::SizeLimiter::Validator::TRACK_MODE => 0, @@ -589,7 +589,7 @@ class ApplicationSetting < ApplicationRecord numericality: { only_integer: true, greater_than_or_equal_to: 0 } validates :sentry_enabled, - inclusion: { in: [true, false], message: _('must be a boolean value') } + inclusion: { in: [true, false], message: N_('must be a boolean value') } validates :sentry_dsn, addressable_url: true, presence: true, length: { maximum: 255 }, if: :sentry_enabled? @@ -601,7 +601,7 @@ class ApplicationSetting < ApplicationRecord if: :sentry_enabled? validates :error_tracking_enabled, - inclusion: { in: [true, false], message: _('must be a boolean value') } + inclusion: { in: [true, false], message: N_('must be a boolean value') } validates :error_tracking_api_url, presence: true, addressable_url: true, @@ -670,7 +670,7 @@ class ApplicationSetting < ApplicationRecord attr_encrypted :jitsu_administrator_password, encryption_options_base_32_aes_256_gcm validates :disable_feed_token, - inclusion: { in: [true, false], message: _('must be a boolean value') } + inclusion: { in: [true, false], message: N_('must be a boolean value') } before_validation :ensure_uuid! before_validation :coerce_repository_storages_weighted, if: :repository_storages_weighted_changed? diff --git a/app/models/ci/job_token/scope.rb b/app/models/ci/job_token/scope.rb index 26a49d6a730..1aa49b95201 100644 --- a/app/models/ci/job_token/scope.rb +++ b/app/models/ci/job_token/scope.rb @@ -23,7 +23,7 @@ module Ci def includes?(target_project) # if the setting is disabled any project is considered to be in scope. - return true unless source_project.ci_job_token_scope_enabled? + return true unless source_project.ci_outbound_job_token_scope_enabled? target_project.id == source_project.id || Ci::JobToken::ProjectScopeLink.from_project(source_project).to_project(target_project).exists? diff --git a/app/models/group_group_link.rb b/app/models/group_group_link.rb index 7005c8593bd..15949570f9c 100644 --- a/app/models/group_group_link.rb +++ b/app/models/group_group_link.rb @@ -8,7 +8,7 @@ class GroupGroupLink < ApplicationRecord validates :shared_group, presence: true validates :shared_group_id, uniqueness: { scope: [:shared_with_group_id], - message: _('The group has already been shared with this group') } + message: N_('The group has already been shared with this group') } validates :shared_with_group, presence: true validates :group_access, inclusion: { in: Gitlab::Access.all_values }, presence: true diff --git a/app/models/incident_management/timeline_event.rb b/app/models/incident_management/timeline_event.rb index de193308558..735d4e4298c 100644 --- a/app/models/incident_management/timeline_event.rb +++ b/app/models/incident_management/timeline_event.rb @@ -21,6 +21,11 @@ module IncidentManagement validates :note, presence: true, length: { maximum: 10_000 } validates :note_html, length: { maximum: 10_000 } + has_many :timeline_event_tag_links, class_name: 'IncidentManagement::TimelineEventTagLink' + has_many :timeline_event_tags, + class_name: 'IncidentManagement::TimelineEventTag', + through: :timeline_event_tag_links + scope :order_occurred_at_asc_id_asc, -> { reorder(occurred_at: :asc, id: :asc) } end end diff --git a/app/models/incident_management/timeline_event_tag.rb b/app/models/incident_management/timeline_event_tag.rb new file mode 100644 index 00000000000..cde3afcaa16 --- /dev/null +++ b/app/models/incident_management/timeline_event_tag.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module IncidentManagement + class TimelineEventTag < ApplicationRecord + self.table_name = 'incident_management_timeline_event_tags' + + belongs_to :project, inverse_of: :incident_management_timeline_event_tags + + has_many :timeline_event_tag_links, + class_name: 'IncidentManagement::TimelineEventTagLink' + + has_many :timeline_events, + class_name: 'IncidentManagement::TimelineEvent', + through: :timeline_event_tag_links + + validates :name, presence: true, format: { with: /\A[^,]+\z/ } + validates :name, uniqueness: { scope: :project_id } + validates :name, length: { maximum: 255 } + end +end diff --git a/app/models/incident_management/timeline_event_tag_link.rb b/app/models/incident_management/timeline_event_tag_link.rb new file mode 100644 index 00000000000..912339717a8 --- /dev/null +++ b/app/models/incident_management/timeline_event_tag_link.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module IncidentManagement + class TimelineEventTagLink < ApplicationRecord + self.table_name = 'incident_management_timeline_event_tag_links' + + belongs_to :timeline_event_tag, class_name: 'IncidentManagement::TimelineEventTag' + + belongs_to :timeline_event, class_name: 'IncidentManagement::TimelineEvent' + end +end diff --git a/app/models/jira_import_state.rb b/app/models/jira_import_state.rb index 76b5f1def6a..97d6cd00fb8 100644 --- a/app/models/jira_import_state.rb +++ b/app/models/jira_import_state.rb @@ -24,7 +24,7 @@ class JiraImportState < ApplicationRecord validates :project, uniqueness: { conditions: -> { where.not(status: STATUSES.values_at(:failed, :finished)) }, - message: _('Cannot have multiple Jira imports running at the same time') + message: N_('Cannot have multiple Jira imports running at the same time') } before_save :ensure_error_message_size diff --git a/app/models/member.rb b/app/models/member.rb index f23705816b6..ff1d8f18c25 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -55,7 +55,7 @@ class Member < ApplicationRecord validate :signup_email_valid?, on: :create, if: ->(member) { member.invite_email.present? } validates :user_id, uniqueness: { - message: _('project bots cannot be added to other groups / projects') + message: N_('project bots cannot be added to other groups / projects') }, if: :project_bot? validate :access_level_inclusion diff --git a/app/models/project.rb b/app/models/project.rb index a5589c71f73..7b61010ab01 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -262,6 +262,7 @@ class Project < ApplicationRecord has_many :source_of_merge_requests, foreign_key: 'source_project_id', class_name: 'MergeRequest' has_many :issues has_many :incident_management_issuable_escalation_statuses, through: :issues, inverse_of: :project, class_name: 'IncidentManagement::IssuableEscalationStatus' + has_many :incident_management_timeline_event_tags, inverse_of: :project, class_name: 'IncidentManagement::TimelineEventTag' has_many :labels, class_name: 'ProjectLabel' has_many :integrations has_many :events @@ -476,7 +477,7 @@ class Project < ApplicationRecord delegate :dashboard_timezone, to: :metrics_setting, allow_nil: true, prefix: true delegate :default_git_depth, :default_git_depth=, to: :ci_cd_settings, prefix: :ci, allow_nil: true delegate :forward_deployment_enabled, :forward_deployment_enabled=, to: :ci_cd_settings, prefix: :ci, allow_nil: true - delegate :job_token_scope_enabled, :job_token_scope_enabled=, to: :ci_cd_settings, prefix: :ci, allow_nil: true + delegate :job_token_scope_enabled, :job_token_scope_enabled=, to: :ci_cd_settings, prefix: :ci_outbound, allow_nil: true delegate :inbound_job_token_scope_enabled, :inbound_job_token_scope_enabled=, to: :ci_cd_settings, prefix: :ci, allow_nil: true delegate :keep_latest_artifact, :keep_latest_artifact=, to: :ci_cd_settings, allow_nil: true delegate :opt_in_jwt, :opt_in_jwt=, to: :ci_cd_settings, prefix: :ci, allow_nil: true @@ -503,7 +504,7 @@ class Project < ApplicationRecord validates :description, length: { maximum: 2000 }, allow_blank: true validates :ci_config_path, format: { without: %r{(\.{2}|\A/)}, - message: _('cannot include leading slash or directory traversal.') }, + message: N_('cannot include leading slash or directory traversal.') }, length: { maximum: 255 }, allow_blank: true validates :name, @@ -699,13 +700,13 @@ class Project < ApplicationRecord enum auto_cancel_pending_pipelines: { disabled: 0, enabled: 1 } chronic_duration_attr :build_timeout_human_readable, :build_timeout, - default: 3600, error_message: _('Maximum job timeout has a value which could not be accepted') + default: 3600, error_message: N_('Maximum job timeout has a value which could not be accepted') validates :build_timeout, allow_nil: true, numericality: { greater_than_or_equal_to: 10.minutes, less_than: MAX_BUILD_TIMEOUT, only_integer: true, - message: _('needs to be between 10 minutes and 1 month') } + message: N_('needs to be between 10 minutes and 1 month') } # Used by Projects::CleanupService to hold a map of rewritten object IDs mount_uploader :bfg_object_map, AttachmentUploader @@ -2896,7 +2897,7 @@ class Project < ApplicationRecord ci_cd_settings.allow_fork_pipelines_to_run_in_parent_project? end - def ci_job_token_scope_enabled? + def ci_outbound_job_token_scope_enabled? return false unless ci_cd_settings ci_cd_settings.job_token_scope_enabled? diff --git a/app/models/project_group_link.rb b/app/models/project_group_link.rb index 2ba3c74df5b..9f9447c1de2 100644 --- a/app/models/project_group_link.rb +++ b/app/models/project_group_link.rb @@ -9,7 +9,7 @@ class ProjectGroupLink < ApplicationRecord validates :project_id, presence: true validates :group, presence: true - validates :group_id, uniqueness: { scope: [:project_id], message: _("already shared with this group") } + validates :group_id, uniqueness: { scope: [:project_id], message: N_("already shared with this group") } validates :group_access, presence: true validates :group_access, inclusion: { in: Gitlab::Access.values }, presence: true validate :different_group diff --git a/app/models/user.rb b/app/models/user.rb index 74035414970..b36b00fcbaf 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -286,10 +286,10 @@ class User < ApplicationRecord validate :check_username_format, if: :username_changed? validates :theme_id, allow_nil: true, inclusion: { in: Gitlab::Themes.valid_ids, - message: _("%{placeholder} is not a valid theme") % { placeholder: '%{value}' } } + message: ->(*) { _("%{placeholder} is not a valid theme") % { placeholder: '%{value}' } } } validates :color_scheme_id, allow_nil: true, inclusion: { in: Gitlab::ColorSchemes.valid_ids, - message: _("%{placeholder} is not a valid color scheme") % { placeholder: '%{value}' } } + message: ->(*) { _("%{placeholder} is not a valid color scheme") % { placeholder: '%{value}' } } } validates :website_url, allow_blank: true, url: true, if: :website_url_changed? diff --git a/app/models/users/banned_user.rb b/app/models/users/banned_user.rb index c52b6d4b728..615668e2b55 100644 --- a/app/models/users/banned_user.rb +++ b/app/models/users/banned_user.rb @@ -7,6 +7,6 @@ module Users belongs_to :user validates :user, presence: true - validates :user_id, uniqueness: { message: _("banned user already exists") } + validates :user_id, uniqueness: { message: N_("banned user already exists") } end end diff --git a/app/services/ci/job_artifacts/delete_service.rb b/app/services/ci/job_artifacts/delete_service.rb index 65cae03312e..c9d590eccc4 100644 --- a/app/services/ci/job_artifacts/delete_service.rb +++ b/app/services/ci/job_artifacts/delete_service.rb @@ -15,13 +15,23 @@ module Ci method: 'Ci::JobArtifacts::DeleteService#execute', project_id: build.project_id ) + return ServiceResponse.error( + message: 'Action temporarily disabled. The project this job belongs to is undergoing stats refresh.', + reason: :project_stats_refresh + ) end - # fix_expire_at is false because in this case we want to explicitly delete the job artifacts - # this flag is a workaround that will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/355833 - Ci::JobArtifacts::DestroyBatchService.new(build.job_artifacts.erasable, fix_expire_at: false).execute + result = Ci::JobArtifacts::DestroyBatchService.new(build.job_artifacts.erasable).execute - ServiceResponse.success + if result.fetch(:status) == :success + ServiceResponse.success(payload: + { + destroyed_artifacts_count: result.fetch(:destroyed_artifacts_count), + statistics_updates: result.fetch(:statistics_updates) + }) + else + ServiceResponse.error(message: result.fetch(:message)) + end end private diff --git a/app/views/admin/application_settings/appearances/preview_sign_in.html.haml b/app/views/admin/application_settings/appearances/preview_sign_in.html.haml index 2e4ab714048..1c2350e2835 100644 --- a/app/views/admin/application_settings/appearances/preview_sign_in.html.haml +++ b/app/views/admin/application_settings/appearances/preview_sign_in.html.haml @@ -9,5 +9,5 @@ = label_tag :password = password_field_tag :password, nil, disabled: true, class: "form-control gl-form-input bottom", title: title .form-group - = button_tag _("Sign in"), disabled: true, class: "btn gl-button btn-confirm", type: "button", title: title - + = render Pajamas::ButtonComponent.new(variant: :confirm, disabled: true, button_options: { title: title }) do + = _("Sign in") diff --git a/app/views/shared/issuable/_label_page_create.html.haml b/app/views/shared/issuable/_label_page_create.html.haml index ec78b3f7ce3..eb3acd8e055 100644 --- a/app/views/shared/issuable/_label_page_create.html.haml +++ b/app/views/shared/issuable/_label_page_create.html.haml @@ -19,7 +19,7 @@ %input.js-add-list{ type: "checkbox", name: "add_list", checked: add_list } %span= _('Add list') .clearfix - %button.gl-button.btn.btn-confirm.float-left.js-new-label-btn{ type: "button" } + = render Pajamas::ButtonComponent.new(variant: :confirm, button_options: { class: 'float-left js-new-label-btn' }) do = _('Create') - %button.gl-button.btn.btn-default.float-right.js-cancel-label-btn{ type: "button" } + = render Pajamas::ButtonComponent.new(button_options: { class: 'float-right js-cancel-label-btn' }) do = _('Cancel') diff --git a/app/workers/ci/cancel_pipeline_worker.rb b/app/workers/ci/cancel_pipeline_worker.rb index 147839a0625..2735498b6bb 100644 --- a/app/workers/ci/cancel_pipeline_worker.rb +++ b/app/workers/ci/cancel_pipeline_worker.rb @@ -10,6 +10,7 @@ module Ci idempotent! deduplicate :until_executed urgency :high + loggable_arguments 1 def perform(pipeline_id, auto_canceled_by_pipeline_id) ::Ci::Pipeline.find_by_id(pipeline_id).try do |pipeline| diff --git a/config/metrics/counts_28d/20210216184850_deploy_token_packages_total_unique_counts_monthly.yml b/config/metrics/counts_28d/20210216184850_deploy_token_packages_total_unique_counts_monthly.yml index 6b6c40495a6..59427c74b0f 100644 --- a/config/metrics/counts_28d/20210216184850_deploy_token_packages_total_unique_counts_monthly.yml +++ b/config/metrics/counts_28d/20210216184850_deploy_token_packages_total_unique_counts_monthly.yml @@ -1,7 +1,7 @@ --- data_category: optional key_path: redis_hll_counters.deploy_token_packages.deploy_token_packages_total_unique_counts_monthly -description: A monthly count of packages published to the registry using a deploy +description: A monthly count of unique users who published packages to the registry using a deploy token product_section: ops product_stage: package @@ -11,8 +11,11 @@ value_type: number status: active time_frame: 28d data_source: redis_hll -instrumentation_class: RedisHLLMetric +instrumentation_class: AggregatedMetric options: + aggregate: + operator: OR + attribute: user_id events: - i_package_composer_deploy_token - i_package_conan_deploy_token diff --git a/config/metrics/counts_7d/20210216184848_deploy_token_packages_total_unique_counts_weekly.yml b/config/metrics/counts_7d/20210216184848_deploy_token_packages_total_unique_counts_weekly.yml index 28adf6d6d01..19bb7f0ba3c 100644 --- a/config/metrics/counts_7d/20210216184848_deploy_token_packages_total_unique_counts_weekly.yml +++ b/config/metrics/counts_7d/20210216184848_deploy_token_packages_total_unique_counts_weekly.yml @@ -1,7 +1,8 @@ --- data_category: optional key_path: redis_hll_counters.deploy_token_packages.deploy_token_packages_total_unique_counts_weekly -description: A weekly count of packages published to the registry using a deploy token +description: A weekly count of unique users who published packages to the registry using a deploy + token product_section: ops product_stage: package product_group: package @@ -10,8 +11,11 @@ value_type: number status: active time_frame: 7d data_source: redis_hll -instrumentation_class: RedisHLLMetric +instrumentation_class: AggregatedMetric options: + aggregate: + operator: OR + attribute: user_id events: - i_package_composer_deploy_token - i_package_conan_deploy_token diff --git a/db/docs/incident_management_timeline_event_tag_links.yml b/db/docs/incident_management_timeline_event_tag_links.yml new file mode 100644 index 00000000000..429371aefb7 --- /dev/null +++ b/db/docs/incident_management_timeline_event_tag_links.yml @@ -0,0 +1,9 @@ +--- +table_name: incident_management_timeline_event_tag_links +classes: +- IncidentManagement::TimelineEventTagLink +feature_categories: +- incident_management +description: Persists links between timeline event tags and timeline events. +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100271 +milestone: '15.6' diff --git a/db/docs/incident_management_timeline_event_tags.yml b/db/docs/incident_management_timeline_event_tags.yml new file mode 100644 index 00000000000..47dedaf3de2 --- /dev/null +++ b/db/docs/incident_management_timeline_event_tags.yml @@ -0,0 +1,9 @@ +--- +table_name: incident_management_timeline_event_tags +classes: +- IncidentManagement::TimelineEventTag +feature_categories: +- incident_management +description: Persists tags for timeline events in a project. +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/100271 +milestone: '15.6' diff --git a/db/migrate/20221005072353_create_incident_management_timeline_event_tags.rb b/db/migrate/20221005072353_create_incident_management_timeline_event_tags.rb new file mode 100644 index 00000000000..c046548af01 --- /dev/null +++ b/db/migrate/20221005072353_create_incident_management_timeline_event_tags.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class CreateIncidentManagementTimelineEventTags < Gitlab::Database::Migration[2.0] + enable_lock_retries! + + def up + create_table :incident_management_timeline_event_tags do |t| + t.timestamps_with_timezone null: false + t.references :project, null: false, index: false, foreign_key: { on_delete: :cascade } + t.text :name, limit: 255, null: false + + t.index [:project_id, :name], unique: true, name: 'index_im_timeline_event_tags_name_project_id' + end + end + + def down + drop_table :incident_management_timeline_event_tags + end +end diff --git a/db/migrate/20221005094926_create_incident_management_timeline_event_tag_links.rb b/db/migrate/20221005094926_create_incident_management_timeline_event_tag_links.rb new file mode 100644 index 00000000000..b3ec8f97738 --- /dev/null +++ b/db/migrate/20221005094926_create_incident_management_timeline_event_tag_links.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +class CreateIncidentManagementTimelineEventTagLinks < Gitlab::Database::Migration[2.0] + enable_lock_retries! + + def up + create_table :incident_management_timeline_event_tag_links do |t| + t.references :timeline_event, + null: false, + index: { name: 'index_im_timeline_event_id' }, + foreign_key: { to_table: :incident_management_timeline_events, column: :timeline_event_id, on_delete: :cascade } + + t.references :timeline_event_tag, + null: false, + index: false, + foreign_key: { + to_table: :incident_management_timeline_event_tags, + column: :timeline_event_tag_id, + on_delete: :cascade + } + + t.index [:timeline_event_tag_id, :timeline_event_id], + unique: true, + name: 'index_im_timeline_event_tags_on_tag_id_and_event_id' + + t.datetime_with_timezone :created_at, null: false + end + end + + def down + drop_table :incident_management_timeline_event_tag_links + end +end diff --git a/db/migrate/20221018081416_members_remove_member_namespace_id_not_null_constraint.rb b/db/migrate/20221018081416_members_remove_member_namespace_id_not_null_constraint.rb new file mode 100644 index 00000000000..e4cbcd24bc4 --- /dev/null +++ b/db/migrate/20221018081416_members_remove_member_namespace_id_not_null_constraint.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class MembersRemoveMemberNamespaceIdNotNullConstraint < Gitlab::Database::Migration[2.0] + disable_ddl_transaction! + + CONSTRAINT_NAME = 'check_508774aac0' + + def up + remove_not_null_constraint :members, :member_namespace_id, constraint_name: CONSTRAINT_NAME + end + + def down + add_not_null_constraint :members, :member_namespace_id, validate: false, constraint_name: CONSTRAINT_NAME + end +end diff --git a/db/schema_migrations/20221005072353 b/db/schema_migrations/20221005072353 new file mode 100644 index 00000000000..3249a062aa7 --- /dev/null +++ b/db/schema_migrations/20221005072353 @@ -0,0 +1 @@ +6b90dfb738c597a45ecaae792e97e1ae0decb93779ecc35fbc2fbaedafb5b9d1 \ No newline at end of file diff --git a/db/schema_migrations/20221005094926 b/db/schema_migrations/20221005094926 new file mode 100644 index 00000000000..7606c06170f --- /dev/null +++ b/db/schema_migrations/20221005094926 @@ -0,0 +1 @@ +ab93968543b6aec0bc304a2c0dc051f63a29b4765df11432fba45325e5e75e55 \ No newline at end of file diff --git a/db/schema_migrations/20221018081416 b/db/schema_migrations/20221018081416 new file mode 100644 index 00000000000..4b92b46709a --- /dev/null +++ b/db/schema_migrations/20221018081416 @@ -0,0 +1 @@ +25030e3ba7c6632fa86100c2db320d1e0c431f992e2b3e333a98e7e03bd31a49 \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 960c736090d..10b2d26b1e0 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -16411,6 +16411,40 @@ CREATE SEQUENCE incident_management_pending_issue_escalations_id_seq ALTER SEQUENCE incident_management_pending_issue_escalations_id_seq OWNED BY incident_management_pending_issue_escalations.id; +CREATE TABLE incident_management_timeline_event_tag_links ( + id bigint NOT NULL, + timeline_event_id bigint NOT NULL, + timeline_event_tag_id bigint NOT NULL, + created_at timestamp with time zone NOT NULL +); + +CREATE SEQUENCE incident_management_timeline_event_tag_links_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE incident_management_timeline_event_tag_links_id_seq OWNED BY incident_management_timeline_event_tag_links.id; + +CREATE TABLE incident_management_timeline_event_tags ( + id bigint NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + project_id bigint NOT NULL, + name text NOT NULL, + CONSTRAINT check_8717184e2c CHECK ((char_length(name) <= 255)) +); + +CREATE SEQUENCE incident_management_timeline_event_tags_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER SEQUENCE incident_management_timeline_event_tags_id_seq OWNED BY incident_management_timeline_event_tags.id; + CREATE TABLE incident_management_timeline_events ( id bigint NOT NULL, created_at timestamp with time zone NOT NULL, @@ -23743,6 +23777,10 @@ ALTER TABLE ONLY incident_management_pending_alert_escalations ALTER COLUMN id S ALTER TABLE ONLY incident_management_pending_issue_escalations ALTER COLUMN id SET DEFAULT nextval('incident_management_pending_issue_escalations_id_seq'::regclass); +ALTER TABLE ONLY incident_management_timeline_event_tag_links ALTER COLUMN id SET DEFAULT nextval('incident_management_timeline_event_tag_links_id_seq'::regclass); + +ALTER TABLE ONLY incident_management_timeline_event_tags ALTER COLUMN id SET DEFAULT nextval('incident_management_timeline_event_tags_id_seq'::regclass); + ALTER TABLE ONLY incident_management_timeline_events ALTER COLUMN id SET DEFAULT nextval('incident_management_timeline_events_id_seq'::regclass); ALTER TABLE ONLY index_statuses ALTER COLUMN id SET DEFAULT nextval('index_statuses_id_seq'::regclass); @@ -25102,9 +25140,6 @@ ALTER TABLE ONLY chat_teams ALTER TABLE vulnerability_scanners ADD CONSTRAINT check_37608c9db5 CHECK ((char_length(vendor) <= 255)) NOT VALID; -ALTER TABLE members - ADD CONSTRAINT check_508774aac0 CHECK ((member_namespace_id IS NOT NULL)) NOT VALID; - ALTER TABLE sprints ADD CONSTRAINT check_ccd8a1eae0 CHECK ((start_date IS NOT NULL)) NOT VALID; @@ -25702,6 +25737,12 @@ ALTER TABLE ONLY incident_management_pending_alert_escalations ALTER TABLE ONLY incident_management_pending_issue_escalations ADD CONSTRAINT incident_management_pending_issue_escalations_pkey PRIMARY KEY (id, process_at); +ALTER TABLE ONLY incident_management_timeline_event_tag_links + ADD CONSTRAINT incident_management_timeline_event_tag_links_pkey PRIMARY KEY (id); + +ALTER TABLE ONLY incident_management_timeline_event_tags + ADD CONSTRAINT incident_management_timeline_event_tags_pkey PRIMARY KEY (id); + ALTER TABLE ONLY incident_management_timeline_events ADD CONSTRAINT incident_management_timeline_events_pkey PRIMARY KEY (id); @@ -29054,6 +29095,12 @@ CREATE INDEX index_im_issuable_escalation_statuses_on_policy_id ON incident_mana CREATE UNIQUE INDEX index_im_oncall_schedules_on_project_id_and_iid ON incident_management_oncall_schedules USING btree (project_id, iid); +CREATE INDEX index_im_timeline_event_id ON incident_management_timeline_event_tag_links USING btree (timeline_event_id); + +CREATE UNIQUE INDEX index_im_timeline_event_tags_name_project_id ON incident_management_timeline_event_tags USING btree (project_id, name); + +CREATE UNIQUE INDEX index_im_timeline_event_tags_on_tag_id_and_event_id ON incident_management_timeline_event_tag_links USING btree (timeline_event_tag_id, timeline_event_id); + CREATE INDEX index_im_timeline_events_author_id ON incident_management_timeline_events USING btree (author_id); CREATE INDEX index_im_timeline_events_issue_id ON incident_management_timeline_events USING btree (issue_id); @@ -33925,6 +33972,9 @@ ALTER TABLE ONLY issue_user_mentions ALTER TABLE ONLY merge_request_assignees ADD CONSTRAINT fk_rails_579d375628 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; +ALTER TABLE ONLY incident_management_timeline_event_tag_links + ADD CONSTRAINT fk_rails_57baccd7f9 FOREIGN KEY (timeline_event_id) REFERENCES incident_management_timeline_events(id) ON DELETE CASCADE; + ALTER TABLE ONLY packages_debian_project_architectures ADD CONSTRAINT fk_rails_5808663adf FOREIGN KEY (distribution_id) REFERENCES packages_debian_project_distributions(id) ON DELETE CASCADE; @@ -34129,6 +34179,9 @@ ALTER TABLE ONLY group_crm_settings ALTER TABLE ONLY clusters_applications_ingress ADD CONSTRAINT fk_rails_753a7b41c1 FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE; +ALTER TABLE ONLY incident_management_timeline_event_tag_links + ADD CONSTRAINT fk_rails_753b8b6ee3 FOREIGN KEY (timeline_event_tag_id) REFERENCES incident_management_timeline_event_tags(id) ON DELETE CASCADE; + ALTER TABLE ONLY release_links ADD CONSTRAINT fk_rails_753be7ae29 FOREIGN KEY (release_id) REFERENCES releases(id) ON DELETE CASCADE; @@ -34756,6 +34809,9 @@ ALTER TABLE ONLY deployment_merge_requests ALTER TABLE ONLY packages_debian_group_component_files ADD CONSTRAINT fk_rails_dd262386e9 FOREIGN KEY (component_id) REFERENCES packages_debian_group_components(id) ON DELETE RESTRICT; +ALTER TABLE ONLY incident_management_timeline_event_tags + ADD CONSTRAINT fk_rails_dd5c91484e FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; + ALTER TABLE ONLY user_callouts ADD CONSTRAINT fk_rails_ddfdd80f3d FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; diff --git a/lib/api/entities/ci/job_basic.rb b/lib/api/entities/ci/job_basic.rb index 3d9318ec428..fb975475cf5 100644 --- a/lib/api/entities/ci/job_basic.rb +++ b/lib/api/entities/ci/job_basic.rb @@ -21,7 +21,7 @@ module API expose :project do expose :ci_job_token_scope_enabled do |job| - job.project.ci_job_token_scope_enabled? + job.project.ci_outbound_job_token_scope_enabled? end end end diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb index b3907397fbb..f158695f605 100644 --- a/lib/api/entities/project.rb +++ b/lib/api/entities/project.rb @@ -104,7 +104,7 @@ module API expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] } expose :ci_default_git_depth expose :ci_forward_deployment_enabled - expose :ci_job_token_scope_enabled + expose(:ci_job_token_scope_enabled) { |p, _| p.ci_outbound_job_token_scope_enabled? } expose :ci_separated_caches expose :ci_opt_in_jwt expose :ci_allow_fork_pipelines_to_run_in_parent_project diff --git a/lib/api/events.rb b/lib/api/events.rb index 521a9e6d351..0a0141484ef 100644 --- a/lib/api/events.rb +++ b/lib/api/events.rb @@ -20,8 +20,6 @@ module API use :pagination use :event_filter_params use :sort_params - optional :scope, type: String, desc: 'Include all events across a user\'s projects', - documentation: { example: 'all' } end get do diff --git a/lib/api/helpers/events_helpers.rb b/lib/api/helpers/events_helpers.rb index cef34ca8d05..bf3b76bb92d 100644 --- a/lib/api/helpers/events_helpers.rb +++ b/lib/api/helpers/events_helpers.rb @@ -6,7 +6,7 @@ module API extend Grape::API::Helpers params :event_filter_params do - optional :action, type: String, values: Event.actions.keys, desc: 'Event action to filter on' + optional :action, type: String, values: Event.actions, desc: 'Event action to filter on' optional :target_type, type: String, values: Event.target_types, desc: 'Event target type to filter on' optional :before, type: Date, desc: 'Include only events created before this date' optional :after, type: Date, desc: 'Include only events created after this date' diff --git a/lib/gitlab/database/gitlab_schemas.yml b/lib/gitlab/database/gitlab_schemas.yml index 0f93f6df54d..c4a9cf8b80f 100644 --- a/lib/gitlab/database/gitlab_schemas.yml +++ b/lib/gitlab/database/gitlab_schemas.yml @@ -264,6 +264,8 @@ incident_management_oncall_shifts: :gitlab_main incident_management_pending_alert_escalations: :gitlab_main incident_management_pending_issue_escalations: :gitlab_main incident_management_timeline_events: :gitlab_main +incident_management_timeline_event_tags: :gitlab_main +incident_management_timeline_event_tag_links: :gitlab_main index_statuses: :gitlab_main in_product_marketing_emails: :gitlab_main insights: :gitlab_main diff --git a/lib/gitlab/json.rb b/lib/gitlab/json.rb index ce07752f88c..823d6202b1e 100644 --- a/lib/gitlab/json.rb +++ b/lib/gitlab/json.rb @@ -34,6 +34,7 @@ module Gitlab alias_method :parse!, :parse alias_method :load, :parse + alias_method :decode, :parse # Restricted method for converting a Ruby object to JSON. If you # need to pass options to this, you should use `.generate` instead, @@ -56,6 +57,8 @@ module Gitlab adapter_generate(object, opts) end + alias_method :encode, :generate + # Generates JSON for an object and makes it look purdy # # The Oj variant in this looks seriously weird but these are the settings diff --git a/qa/Gemfile b/qa/Gemfile index 8a4ec1f2f76..0f16739b477 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -16,7 +16,7 @@ gem 'rspec-retry', '~> 0.6.1', require: 'rspec/retry' gem 'rspec_junit_formatter', '~> 0.6.0' gem 'faker', '~> 2.23' gem 'knapsack', '~> 4.0' -gem 'parallel_tests', '~> 2.32' +gem 'parallel_tests', '~> 3.13' gem 'rotp', '~> 6.2.0' gem 'timecop', '~> 0.9.5' gem 'parallel', '~> 1.19' diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index 2f3bfef01a4..1f94b534ef1 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -185,7 +185,7 @@ GEM oj (3.13.21) os (1.1.4) parallel (1.19.2) - parallel_tests (2.32.0) + parallel_tests (3.13.0) parallel parser (3.1.2.1) ast (~> 2.4.1) @@ -316,7 +316,7 @@ DEPENDENCIES nokogiri (~> 1.13, >= 1.13.8) octokit (~> 5.6.1) parallel (~> 1.19) - parallel_tests (~> 2.32) + parallel_tests (~> 3.13) pry-byebug (~> 3.5.1) rainbow (~> 3.0.0) rake (~> 13) diff --git a/qa/Rakefile b/qa/Rakefile index ada27596ae4..6f94c63b4de 100644 --- a/qa/Rakefile +++ b/qa/Rakefile @@ -11,7 +11,7 @@ end desc "Initialize GitLab with an access token" task :initialize_gitlab_auth, [:address] do |_, args| - QA::Tools::InitializeGitLabAuth.new(args).run + QA::Tools::InitializeGitlabAuth.new(args).run end desc "Generate Performance Testdata" @@ -46,7 +46,7 @@ task generate_data_and_run_load_test: [:generate_perf_testdata, :run_artillery_l desc "Deletes test ssh keys a user" task :delete_test_ssh_keys, [:title_portion, :delete_before, :dry_run] do |_, args| - QA::Tools::DeleteTestSSHKeys.new(args).run + QA::Tools::DeleteTestSshKeys.new(args).run end desc "Deletes projects directly under the provided group" diff --git a/qa/qa.rb b/qa/qa.rb index 99a8a34d6d8..bf6b75a1278 100644 --- a/qa/qa.rb +++ b/qa/qa.rb @@ -30,6 +30,22 @@ module QA loader.ignore("#{root}/specs/features") loader.ignore("#{root}/specs/spec_helper.rb") + # we need to eager load scenario classes + # zeitwerk does not have option to configure what to eager load, so all exceptions have to be defined + loader.do_not_eager_load("#{root}/ce") + loader.do_not_eager_load("#{root}/ee") + loader.do_not_eager_load("#{root}/flow") + loader.do_not_eager_load("#{root}/git") + loader.do_not_eager_load("#{root}/mobile") + loader.do_not_eager_load("#{root}/page") + loader.do_not_eager_load("#{root}/resource") + loader.do_not_eager_load("#{root}/runtime") + loader.do_not_eager_load("#{root}/service") + loader.do_not_eager_load("#{root}/specs") + loader.do_not_eager_load("#{root}/support") + loader.do_not_eager_load("#{root}/tools") + loader.do_not_eager_load("#{root}/vendor") + loader.inflector.inflect( "ce" => "CE", "ee" => "EE", @@ -74,6 +90,7 @@ module QA end loader.setup + loader.eager_load end # Custom warning processing diff --git a/qa/qa/scenario/test/instance.rb b/qa/qa/scenario/test/instance.rb deleted file mode 100644 index b4098619e4e..00000000000 --- a/qa/qa/scenario/test/instance.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -module QA - module Scenario - module Test - # This class exists for back-compatibility so that gitlab-qa can continue - # to call Test::Instance instead of Test::Instance::All until at least - # the current latest GitLab version has the Test::Instance::All class. - # As of Aug, 22nd 2018. Only GitLab >= 11.3 has this class. - module Instance - include Bootable - - def self.perform(*args) - self.tap do |scenario| - yield scenario if block_given? - break scenario.do_perform(*args) - end - end - - def self.do_perform(address, *rspec_options) - Runtime::Scenario.define(:gitlab_address, address) - - ## - # Perform before hooks, which are different for CE and EE - # - Runtime::Release.perform_before_hooks - - Specs::Runner.perform do |specs| - specs.tty = true - specs.options = rspec_options if rspec_options.any? - end - end - end - end - end -end diff --git a/qa/qa/scenario/test/integration/github.rb b/qa/qa/scenario/test/integration/github.rb deleted file mode 100644 index 857a1f00bd5..00000000000 --- a/qa/qa/scenario/test/integration/github.rb +++ /dev/null @@ -1,20 +0,0 @@ -# frozen_string_literal: true - -module QA - module Scenario - module Test - module Integration - class Github < Test::Instance::All - tags :github - - def perform(address, *rspec_options) - # This test suite requires a GitHub personal access token - Runtime::Env.require_github_access_token! - - super - end - end - end - end - end -end diff --git a/qa/qa/specs/qa_deprecation_toolkit_env.rb b/qa/qa/specs/qa_deprecation_toolkit_env.rb index 21ef5a6f229..5224a2e9ae0 100644 --- a/qa/qa/specs/qa_deprecation_toolkit_env.rb +++ b/qa/qa/specs/qa_deprecation_toolkit_env.rb @@ -5,20 +5,24 @@ require 'deprecation_toolkit/rspec' require 'concurrent/utility/monotonic_time' require 'active_support/gem_version' -module QaDeprecationToolkitEnv - # Taken from https://github.com/jeremyevans/ruby-warning/blob/1.1.0/lib/warning.rb#L18 - # rubocop:disable Layout/LineLength - def self.kwargs_warning - %r{warning: (?:Using the last argument (?:for `.+' )?as keyword parameters is deprecated; maybe \*\* should be added to the call|Passing the keyword argument (?:for `.+' )?as the last hash parameter is deprecated|Splitting the last argument (?:for `.+' )?into positional and keyword parameters is deprecated|The called method (?:`.+' )?is defined here)\n\z} - end - # rubocop:enable Layout/LineLength +module QA + module Specs + class QaDeprecationToolkitEnv + # Taken from https://github.com/jeremyevans/ruby-warning/blob/1.1.0/lib/warning.rb#L18 + # rubocop:disable Layout/LineLength + def self.kwargs_warning + %r{warning: (?:Using the last argument (?:for `.+' )?as keyword parameters is deprecated; maybe \*\* should be added to the call|Passing the keyword argument (?:for `.+' )?as the last hash parameter is deprecated|Splitting the last argument (?:for `.+' )?into positional and keyword parameters is deprecated|The called method (?:`.+' )?is defined here)\n\z} + end + # rubocop:enable Layout/LineLength - def self.configure! - # Enable ruby deprecations for keywords, it's suppressed by default in Ruby 2.7 - Warning[:deprecated] = true + def self.configure! + # Enable ruby deprecations for keywords, it's suppressed by default in Ruby 2.7 + Warning[:deprecated] = true - DeprecationToolkit::Configuration.test_runner = :rspec - DeprecationToolkit::Configuration.deprecation_path = 'deprecations' - DeprecationToolkit::Configuration.warnings_treated_as_deprecation = [kwargs_warning] + DeprecationToolkit::Configuration.test_runner = :rspec + DeprecationToolkit::Configuration.deprecation_path = 'deprecations' + DeprecationToolkit::Configuration.warnings_treated_as_deprecation = [kwargs_warning] + end + end end end diff --git a/qa/qa/specs/spec_helper.rb b/qa/qa/specs/spec_helper.rb index b9e67c2fa72..97901042883 100644 --- a/qa/qa/specs/spec_helper.rb +++ b/qa/qa/specs/spec_helper.rb @@ -2,8 +2,7 @@ require_relative '../../qa' -require_relative 'qa_deprecation_toolkit_env' -QaDeprecationToolkitEnv.configure! +QA::Specs::QaDeprecationToolkitEnv.configure! Knapsack::Adapters::RSpecAdapter.bind if QA::Runtime::Env.knapsack? diff --git a/qa/qa/tools/ci/non_empty_suites.rb b/qa/qa/tools/ci/non_empty_suites.rb index 687c11a3e62..2319237fa25 100644 --- a/qa/qa/tools/ci/non_empty_suites.rb +++ b/qa/qa/tools/ci/non_empty_suites.rb @@ -10,42 +10,11 @@ module QA class NonEmptySuites include Helpers - # rubocop:disable Layout/LineLength - SCENARIOS = [ - { klass: "Test::Instance::All" }, - { klass: "Test::Instance::Smoke" }, - { klass: "Test::Instance::Reliable" }, - { klass: "Test::Instance::ReviewBlocking" }, - { klass: "Test::Instance::ReviewNonBlocking" }, - { klass: "Test::Instance::CloudActivation" }, - { klass: "Test::Instance::Integrations" }, - { klass: "Test::Instance::Jira" }, - { klass: "Test::Instance::LargeSetup" }, - { klass: "Test::Instance::Metrics" }, - { klass: "Test::Instance::ObjectStorage" }, - { klass: "Test::Instance::Packages" }, - { klass: "Test::Instance::RepositoryStorage" }, - { klass: "Test::Integration::ServicePingDisabled" }, - { klass: "Test::Integration::LDAPNoTLS" }, - { klass: "Test::Integration::LDAPTLS" }, - { klass: "Test::Integration::LDAPNoServer" }, - { klass: "Test::Integration::InstanceSAML" }, - { klass: "Test::Integration::RegistryWithCDN" }, - { klass: "Test::Integration::RegistryTLS" }, - { klass: "Test::Integration::Registry" }, - { klass: "Test::Integration::SMTP" }, - { klass: "QA::EE::Scenario::Test::Integration::Elasticsearch" }, - { klass: "QA::EE::Scenario::Test::Integration::GroupSAML" }, - { - klass: "QA::EE::Scenario::Test::Geo", - args: "--primary-address http://dummy1.test --primary-name gitlab-primary --secondary-address http://dummy2.test --secondary-name gitlab-secondary --without-setup" - }, - { - klass: "Test::Integration::Mattermost", - args: "--mattermost-address http://mattermost.test" - } + # @return [Array] scenarios that never run in package-and-test pipeline + IGNORED_SCENARIOS = [ + "QA::EE::Scenario::Test::Geo", + "QA::Scenario::Test::Instance::Airgapped" ].freeze - # rubocop:enable Layout/LineLength def initialize(qa_tests) @qa_tests = qa_tests @@ -56,38 +25,58 @@ module QA # @return [String] def fetch logger.info("Checking for runnable suites") - scenarios = SCENARIOS.each_with_object([]) do |scenario, runnable_scenarios| - logger.info(" fetching runnable specs for '#{scenario[:klass]}'") + scenarios.each_with_object([]) do |scenario, runnable_scenarios| + logger.info(" fetching runnable specs for '#{scenario}'") + next logger.info(" scenario is in ignore list, skipping") if IGNORED_SCENARIOS.include?(scenario) - out, err, status = run_command(**scenario) + out, err, status = run_command(scenario) unless status.success? - logger.error(" example count failed!\n#{err}") + logger.error(" example count failed!\n#{err}") next end count = out.split("\n").last.to_i logger.info(" found #{count} examples to run") - runnable_scenarios << scenario[:klass] if count > 0 - end - - scenarios.join(",") + runnable_scenarios << scenario if count > 0 + end.join(",") end private attr_reader :qa_tests + # Get all defined scenarios + # + # @return [Array] + def scenarios + foss_scenarios = scenario_classes(QA::Scenario::Test) + return foss_scenarios unless QA.const_defined?("QA::EE") + + foss_scenarios + scenario_classes(QA::EE::Scenario::Test) + end + + # Fetch scenario classes recursively + # + # @param [Module] mod + # @return [Array] + def scenario_classes(mod) + mod.constants.map do |const| + c = mod.const_get(const, false) + next c.to_s if c.is_a?(Class) + + scenario_classes(c) + end.flatten + end + # Run scenario count command # # @param [String] klass - # @param [String] args # @return [String] - def run_command(klass:, args: nil) + def run_command(klass) cmd = ["bundle exec bin/qa"] cmd << klass cmd << "--count-examples-only --address http://dummy1.test" - cmd << args if args cmd << "-- #{qa_tests}" unless qa_tests.blank? Open3.capture3(cmd.join(" ")) diff --git a/qa/qa/tools/delete_test_ssh_keys.rb b/qa/qa/tools/delete_test_ssh_keys.rb index 9e5728a5509..c10188eae6d 100644 --- a/qa/qa/tools/delete_test_ssh_keys.rb +++ b/qa/qa/tools/delete_test_ssh_keys.rb @@ -12,7 +12,7 @@ module QA module Tools - class DeleteTestSSHKeys + class DeleteTestSshKeys include Support::API ITEMS_PER_PAGE = '100' diff --git a/qa/qa/tools/initialize_gitlab_auth.rb b/qa/qa/tools/initialize_gitlab_auth.rb index 18e90f0d739..6d586f95ecf 100644 --- a/qa/qa/tools/initialize_gitlab_auth.rb +++ b/qa/qa/tools/initialize_gitlab_auth.rb @@ -6,7 +6,7 @@ module QA # Also creates a personal access token # @example # $ bundle exec rake 'initialize_gitlab_auth[http://gitlab.test]' - class InitializeGitLabAuth + class InitializeGitlabAuth attr_reader :address def initialize(address:) diff --git a/qa/spec/scenario/test/integration/github_spec.rb b/qa/spec/scenario/test/integration/github_spec.rb deleted file mode 100644 index 4ad42b4f5cc..00000000000 --- a/qa/spec/scenario/test/integration/github_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe QA::Scenario::Test::Integration::Github do - describe '#perform' do - let(:env) { spy('Runtime::Env', knapsack?: false, dry_run: false) } - - before do - stub_const('QA::Runtime::Env', env) - end - - it_behaves_like 'a QA scenario class' do - let(:tags) { [:github] } - - it 'requires a GitHub access token' do - subject.perform(args) - - expect(env).to have_received(:require_github_access_token!) - end - end - end -end diff --git a/qa/spec/tools/ci/non_empty_suites_spec.rb b/qa/spec/tools/ci/non_empty_suites_spec.rb index d9bfe1eebe7..f71e07aa4b4 100644 --- a/qa/spec/tools/ci/non_empty_suites_spec.rb +++ b/qa/spec/tools/ci/non_empty_suites_spec.rb @@ -9,11 +9,11 @@ RSpec.describe QA::Tools::Ci::NonEmptySuites do allow(Gitlab::QA::TestLogger).to receive(:logger).and_return(Logger.new(StringIO.new)) allow(Open3).to receive(:capture3).and_return(["output\n0", "", status]) allow(Open3).to receive(:capture3) - .with("bundle exec bin/qa Test::Instance::All --count-examples-only --address http://dummy1.test") + .with("bundle exec bin/qa QA::Scenario::Test::Instance::All --count-examples-only --address http://dummy1.test") .and_return(["output\n1", "", status]) end it "returns runnable test suites" do - expect(non_empty_suites.fetch).to eq("Test::Instance::All") + expect(non_empty_suites.fetch).to eq("QA::Scenario::Test::Instance::All") end end diff --git a/spec/factories/incident_management/timeline_event_tag_links.rb b/spec/factories/incident_management/timeline_event_tag_links.rb new file mode 100644 index 00000000000..883aca29f99 --- /dev/null +++ b/spec/factories/incident_management/timeline_event_tag_links.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :incident_management_timeline_event_tag_link, class: 'IncidentManagement::TimelineEventTagLink' do + association :timeline_event_tag, factory: :incident_management_timeline_event_tag + association :timeline_event, factory: :incident_management_timeline_event + end +end diff --git a/spec/factories/incident_management/timeline_event_tags.rb b/spec/factories/incident_management/timeline_event_tags.rb new file mode 100644 index 00000000000..6333872ee4f --- /dev/null +++ b/spec/factories/incident_management/timeline_event_tags.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :incident_management_timeline_event_tag, class: 'IncidentManagement::TimelineEventTag' do + project + name { 'Start time' } + end +end diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index 1e14b3088db..b62995dce42 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -54,7 +54,7 @@ FactoryBot.define do import_last_error { nil } forward_deployment_enabled { nil } restrict_user_defined_variables { nil } - ci_job_token_scope_enabled { nil } + ci_outbound_job_token_scope_enabled { nil } ci_inbound_job_token_scope_enabled { nil } runner_token_expiration_interval { nil } runner_token_expiration_interval_human_readable { nil } @@ -113,7 +113,7 @@ FactoryBot.define do project.merge_trains_enabled = evaluator.merge_trains_enabled unless evaluator.merge_trains_enabled.nil? project.keep_latest_artifact = evaluator.keep_latest_artifact unless evaluator.keep_latest_artifact.nil? project.restrict_user_defined_variables = evaluator.restrict_user_defined_variables unless evaluator.restrict_user_defined_variables.nil? - project.ci_job_token_scope_enabled = evaluator.ci_job_token_scope_enabled unless evaluator.ci_job_token_scope_enabled.nil? + project.ci_outbound_job_token_scope_enabled = evaluator.ci_outbound_job_token_scope_enabled unless evaluator.ci_outbound_job_token_scope_enabled.nil? project.ci_inbound_job_token_scope_enabled = evaluator.ci_inbound_job_token_scope_enabled unless evaluator.ci_inbound_job_token_scope_enabled.nil? project.runner_token_expiration_interval = evaluator.runner_token_expiration_interval unless evaluator.runner_token_expiration_interval.nil? project.runner_token_expiration_interval_human_readable = evaluator.runner_token_expiration_interval_human_readable unless evaluator.runner_token_expiration_interval_human_readable.nil? diff --git a/spec/frontend/diffs/components/app_spec.js b/spec/frontend/diffs/components/app_spec.js index b88206c3b9a..936f4744e94 100644 --- a/spec/frontend/diffs/components/app_spec.js +++ b/spec/frontend/diffs/components/app_spec.js @@ -152,6 +152,30 @@ describe('diffs/components/app', () => { }); }); + describe('fetch diff with no changes', () => { + beforeEach(() => { + const fetchResolver = () => { + store.state.diffs.retrievingBatches = false; + return Promise.resolve({ real_size: null }); + }; + + createComponent(); + jest.spyOn(wrapper.vm, 'fetchDiffFilesMeta').mockImplementation(fetchResolver); + + return nextTick(); + }); + + it('diff counter to be 0 after fetch', async () => { + expect(wrapper.vm.diffFilesLength).toEqual(0); + wrapper.vm.fetchData(false); + + await nextTick(); + + expect(wrapper.vm.fetchDiffFilesMeta).toHaveBeenCalled(); + expect(wrapper.vm.diffFilesLength).toEqual(0); + }); + }); + describe('codequality diff', () => { it('does not fetch code quality data on FOSS', async () => { createComponent(); diff --git a/spec/frontend/groups/components/overview_tabs_spec.js b/spec/frontend/groups/components/overview_tabs_spec.js index be352301f43..93e087e10f2 100644 --- a/spec/frontend/groups/components/overview_tabs_spec.js +++ b/spec/frontend/groups/components/overview_tabs_spec.js @@ -141,6 +141,14 @@ describe('OverviewTabs', () => { expect(tabPanel.vm.$attrs.lazy).toBe(false); }); + it('sets `lazy` prop to `false` for initially active tab and `true` for all other tabs', async () => { + await createComponent({ route: { name: ACTIVE_TAB_SHARED, params: { group: 'foo/bar' } } }); + + expect(findTabPanels().at(0).vm.$attrs.lazy).toBe(true); + expect(findTabPanels().at(1).vm.$attrs.lazy).toBe(false); + expect(findTabPanels().at(2).vm.$attrs.lazy).toBe(true); + }); + describe.each([ [ { name: ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, params: { group: 'foo/bar/baz' } }, diff --git a/spec/frontend/vue_shared/components/gitlab_version_check_spec.js b/spec/frontend/vue_shared/components/gitlab_version_check_spec.js index 5f63d38c532..38f28837cc1 100644 --- a/spec/frontend/vue_shared/components/gitlab_version_check_spec.js +++ b/spec/frontend/vue_shared/components/gitlab_version_check_spec.js @@ -1,6 +1,6 @@ import { GlBadge } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; import { mockTracking } from 'helpers/tracking_helper'; import { helpPagePath } from '~/helpers/help_page_helper'; @@ -27,7 +27,7 @@ describe('GitlabVersionCheck', () => { mock = new MockAdapter(axios); mock.onGet().replyOnce(response.code, response.res); - wrapper = shallowMount(GitlabVersionCheck); + wrapper = shallowMountExtended(GitlabVersionCheck); }; const dummyGon = { @@ -42,6 +42,7 @@ describe('GitlabVersionCheck', () => { window.gon = originalGon; }); + const findGlBadgeClickWrapper = () => wrapper.findByTestId('badge-click-wrapper'); const findGlBadge = () => wrapper.findComponent(GlBadge); describe.each` @@ -81,7 +82,8 @@ describe('GitlabVersionCheck', () => { await waitForPromises(); // Ensure we wrap up the axios call }); - it(`does${renders ? '' : ' not'} render GlBadge`, () => { + it(`does${renders ? '' : ' not'} render Badge Click Wrapper and GlBadge`, () => { + expect(findGlBadgeClickWrapper().exists()).toBe(renders); expect(findGlBadge().exists()).toBe(renders); }); }); @@ -110,9 +112,9 @@ describe('GitlabVersionCheck', () => { expect(findGlBadge().attributes('variant')).toBe(expectedUI.variant); }); - it(`tracks rendered_version_badge with status ${expectedUI.variant}`, () => { + it(`tracks rendered_version_badge with label ${expectedUI.title}`, () => { expect(trackingSpy).toHaveBeenCalledWith(undefined, 'rendered_version_badge', { - label: expectedUI.variant, + label: expectedUI.title, }); }); @@ -120,11 +122,11 @@ describe('GitlabVersionCheck', () => { expect(findGlBadge().attributes('href')).toBe(UPGRADE_DOCS_URL); }); - it(`tracks click_version_badge with status ${expectedUI.variant} when badge is clicked`, async () => { - await findGlBadge().vm.$emit('click'); + it(`tracks click_version_badge with label ${expectedUI.title} when badge is clicked`, async () => { + await findGlBadgeClickWrapper().trigger('click'); expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_version_badge', { - label: expectedUI.variant, + label: expectedUI.title, }); }); }); diff --git a/spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb b/spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb index 412be5f16a4..727db7e2361 100644 --- a/spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb +++ b/spec/graphql/mutations/ci/job_token_scope/add_project_spec.rb @@ -8,7 +8,7 @@ RSpec.describe Mutations::Ci::JobTokenScope::AddProject do describe '#resolve' do let_it_be(:project) do - create(:project, ci_job_token_scope_enabled: true).tap(&:save!) + create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) end let_it_be(:target_project) { create(:project) } diff --git a/spec/graphql/mutations/ci/job_token_scope/remove_project_spec.rb b/spec/graphql/mutations/ci/job_token_scope/remove_project_spec.rb index 0e706ea6e0c..d399e73f394 100644 --- a/spec/graphql/mutations/ci/job_token_scope/remove_project_spec.rb +++ b/spec/graphql/mutations/ci/job_token_scope/remove_project_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Mutations::Ci::JobTokenScope::RemoveProject do end describe '#resolve' do - let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) } let_it_be(:target_project) { create(:project) } let_it_be(:link) do diff --git a/spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb b/spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb index 1bfd6fbf6b9..59ece15b745 100644 --- a/spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb +++ b/spec/graphql/resolvers/ci/job_token_scope_resolver_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Resolvers::Ci::JobTokenScopeResolver do include GraphqlHelpers let_it_be(:current_user) { create(:user) } - let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) } specify do expect(described_class).to have_nullable_graphql_type(::Types::Ci::JobTokenScopeType) @@ -21,7 +21,7 @@ RSpec.describe Resolvers::Ci::JobTokenScopeResolver do end it 'returns the same project in the allow list of projects for the Ci Job Token when scope is not enabled' do - allow(project).to receive(:ci_job_token_scope_enabled?).and_return(false) + allow(project).to receive(:ci_outbound_job_token_scope_enabled?).and_return(false) expect(resolve_scope.all_projects).to contain_exactly(project) end @@ -40,7 +40,7 @@ RSpec.describe Resolvers::Ci::JobTokenScopeResolver do context 'when job token scope is disabled' do before do - project.update!(ci_job_token_scope_enabled: false) + project.update!(ci_outbound_job_token_scope_enabled: false) end it 'resolves projects' do diff --git a/spec/graphql/types/ci/job_token_scope_type_spec.rb b/spec/graphql/types/ci/job_token_scope_type_spec.rb index 18f4d762d1e..569b59d6c70 100644 --- a/spec/graphql/types/ci/job_token_scope_type_spec.rb +++ b/spec/graphql/types/ci/job_token_scope_type_spec.rb @@ -12,7 +12,7 @@ RSpec.describe GitlabSchema.types['CiJobTokenScopeType'] do end describe 'query' do - let(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) } let_it_be(:current_user) { create(:user) } let(:query) do diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index f2cdbc35f26..ccc4f1f7149 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -61,6 +61,8 @@ issues: - requirement - incident_management_issuable_escalation_status - incident_management_timeline_events +- incident_management_timeline_event_tags +- incident_management_timeline_event_links - pending_escalations - customer_relations_contacts - issue_customer_relations_contacts @@ -622,6 +624,7 @@ project: - incident_management_oncall_rotations - incident_management_escalation_policies - incident_management_issuable_escalation_statuses +- incident_management_timeline_event_tags - debian_distributions - merge_request_metrics - security_orchestration_policy_configuration diff --git a/spec/lib/gitlab/json_spec.rb b/spec/lib/gitlab/json_spec.rb index 7c093049e18..73276288765 100644 --- a/spec/lib/gitlab/json_spec.rb +++ b/spec/lib/gitlab/json_spec.rb @@ -8,6 +8,12 @@ RSpec.describe Gitlab::Json do end describe ".parse" do + it "is aliased" do + [:parse!, :load, :decode].each do |method| + expect(described_class.method(method)).to eq(described_class.method(:parse)) + end + end + context "legacy_mode is disabled by default" do it "parses an object" do expect(subject.parse('{ "foo": "bar" }')).to eq({ "foo" => "bar" }) @@ -178,6 +184,10 @@ RSpec.describe Gitlab::Json do { test: true, "foo.bar" => "baz", is_json: 1, some: [1, 2, 3] } end + it "is aliased" do + expect(described_class.method(:encode)).to eq(described_class.method(:generate)) + end + it "generates JSON" do expected_string = <<~STR.chomp {"test":true,"foo.bar":"baz","is_json":1,"some":[1,2,3]} diff --git a/spec/models/ci/job_token/scope_spec.rb b/spec/models/ci/job_token/scope_spec.rb index 4b95adf8476..1e3f6d044d2 100644 --- a/spec/models/ci/job_token/scope_spec.rb +++ b/spec/models/ci/job_token/scope_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Ci::JobToken::Scope do - let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) } let(:scope) { described_class.new(project) } @@ -53,7 +53,7 @@ RSpec.describe Ci::JobToken::Scope do context 'when project scope setting is disabled' do before do - project.ci_job_token_scope_enabled = false + project.ci_outbound_job_token_scope_enabled = false end it 'considers any project to be part of the scope' do diff --git a/spec/models/incident_management/timeline_event_spec.rb b/spec/models/incident_management/timeline_event_spec.rb index c68bcd4a9ed..d288cc1a75d 100644 --- a/spec/models/incident_management/timeline_event_spec.rb +++ b/spec/models/incident_management/timeline_event_spec.rb @@ -13,6 +13,12 @@ RSpec.describe IncidentManagement::TimelineEvent do it { is_expected.to belong_to(:incident) } it { is_expected.to belong_to(:updated_by_user) } it { is_expected.to belong_to(:promoted_from_note) } + it { is_expected.to have_many(:timeline_event_tag_links).class_name('IncidentManagement::TimelineEventTagLink') } + + it do + is_expected.to have_many(:timeline_event_tags) + .class_name('IncidentManagement::TimelineEventTag').through(:timeline_event_tag_links) + end end describe 'validations' do diff --git a/spec/models/incident_management/timeline_event_tag_link_spec.rb b/spec/models/incident_management/timeline_event_tag_link_spec.rb new file mode 100644 index 00000000000..fe31a6604c1 --- /dev/null +++ b/spec/models/incident_management/timeline_event_tag_link_spec.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe IncidentManagement::TimelineEventTagLink do + describe 'associations' do + it { is_expected.to belong_to(:timeline_event) } + it { is_expected.to belong_to(:timeline_event_tag) } + end +end diff --git a/spec/models/incident_management/timeline_event_tag_spec.rb b/spec/models/incident_management/timeline_event_tag_spec.rb new file mode 100644 index 00000000000..cff8ad8469f --- /dev/null +++ b/spec/models/incident_management/timeline_event_tag_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe IncidentManagement::TimelineEventTag do + describe 'associations' do + it { is_expected.to belong_to(:project) } + it { is_expected.to have_many(:timeline_event_tag_links).class_name('IncidentManagement::TimelineEventTagLink') } + + it { + is_expected.to have_many(:timeline_events) + .class_name('IncidentManagement::TimelineEvent').through(:timeline_event_tag_links) + } + end + + describe 'validations' do + subject { build(:incident_management_timeline_event_tag) } + + it { is_expected.to validate_presence_of(:name) } + it { is_expected.to validate_length_of(:name).is_at_most(255) } + it { is_expected.to validate_uniqueness_of(:name).scoped_to([:project_id]) } + + it { is_expected.to allow_value('Test tag 1').for(:name) } + it { is_expected.not_to allow_value('Test tag, 1').for(:name) } + it { is_expected.not_to allow_value('').for(:name) } + it { is_expected.not_to allow_value('s' * 256).for(:name) } + end +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 767cda952b0..75887e49dc9 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -149,6 +149,7 @@ RSpec.describe Project, factory_default: :keep do it { is_expected.to have_one(:build_artifacts_size_refresh).class_name('Projects::BuildArtifactsSizeRefresh') } it { is_expected.to have_many(:project_callouts).class_name('Users::ProjectCallout').with_foreign_key(:project_id) } it { is_expected.to have_many(:pipeline_metadata).class_name('Ci::PipelineMetadata') } + it { is_expected.to have_many(:incident_management_timeline_event_tags).class_name('IncidentManagement::TimelineEventTag') } # GitLab Pages it { is_expected.to have_many(:pages_domains) } @@ -845,6 +846,8 @@ RSpec.describe Project, factory_default: :keep do end describe 'delegation' do + let_it_be(:project) { create(:project) } + [:add_guest, :add_reporter, :add_developer, :add_maintainer, :add_member, :add_members].each do |method| it { is_expected.to delegate_method(method).to(:team) } end @@ -887,8 +890,24 @@ RSpec.describe Project, factory_default: :keep do end include_examples 'ci_cd_settings delegation' do - # Skip attributes defined in EE code + let(:attributes_with_prefix) do + { + 'group_runners_enabled' => '', + 'default_git_depth' => 'ci_', + 'forward_deployment_enabled' => 'ci_', + 'keep_latest_artifact' => '', + 'restrict_user_defined_variables' => '', + 'runner_token_expiration_interval' => '', + 'separated_caches' => 'ci_', + 'opt_in_jwt' => 'ci_', + 'allow_fork_pipelines_to_run_in_parent_project' => 'ci_', + 'inbound_job_token_scope_enabled' => 'ci_', + 'job_token_scope_enabled' => 'ci_outbound_' + } + end + let(:exclude_attributes) do + # Skip attributes defined in EE code %w( merge_pipelines_enabled merge_trains_enabled @@ -909,8 +928,8 @@ RSpec.describe Project, factory_default: :keep do end end - describe '#ci_job_token_scope_enabled?' do - it_behaves_like 'a ci_cd_settings predicate method', prefix: 'ci_' do + describe '#ci_outbound_job_token_scope_enabled?' do + it_behaves_like 'a ci_cd_settings predicate method', prefix: 'ci_outbound_' do let(:delegated_method) { :job_token_scope_enabled? } end end diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb index a7662634793..40ee2e662b2 100644 --- a/spec/policies/project_policy_spec.rb +++ b/spec/policies/project_policy_spec.rb @@ -2437,7 +2437,7 @@ RSpec.describe ProjectPolicy do before do current_user.set_ci_job_token_scope!(job) current_user.external = external_user - scope_project.update!(ci_job_token_scope_enabled: token_scope_enabled) + scope_project.update!(ci_outbound_job_token_scope_enabled: token_scope_enabled) end it "enforces the expected permissions" do diff --git a/spec/requests/api/graphql/mutations/ci/job/artifacts_destroy_spec.rb b/spec/requests/api/graphql/mutations/ci/job/artifacts_destroy_spec.rb new file mode 100644 index 00000000000..bdad80995ea --- /dev/null +++ b/spec/requests/api/graphql/mutations/ci/job/artifacts_destroy_spec.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'JobArtifactsDestroy' do + include GraphqlHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:job) { create(:ci_build) } + + let(:mutation) do + variables = { + id: job.to_global_id.to_s + } + graphql_mutation(:job_artifacts_destroy, variables, <<~FIELDS) + job { + name + } + destroyedArtifactsCount + errors + FIELDS + end + + before do + create(:ci_job_artifact, :archive, job: job) + create(:ci_job_artifact, :junit, job: job) + end + + context 'when the user is not allowed to destroy the job artifacts' do + it 'returns an error' do + post_graphql_mutation(mutation, current_user: user) + + expect(graphql_errors).not_to be_empty + expect(job.reload.job_artifacts.count).to be(2) + end + end + + context 'when the user is allowed to destroy the job artifacts' do + before do + job.project.add_maintainer(user) + end + + it 'destroys the job artifacts and returns the expected data' do + expected_data = { + 'jobArtifactsDestroy' => { + 'errors' => [], + 'destroyedArtifactsCount' => 2, + 'job' => { + 'name' => job.name + } + } + } + + post_graphql_mutation(mutation, current_user: user) + + expect(response).to have_gitlab_http_status(:success) + expect(graphql_data).to eq(expected_data) + expect(job.reload.job_artifacts.count).to be(0) + end + + context 'when the the project this job belongs to is undergoing stats refresh' do + it 'destroys no artifacts and returns the correct error' do + allow_next_found_instance_of(Project) do |project| + allow(project).to receive(:refreshing_build_artifacts_size?).and_return(true) + end + + expected_data = { + 'jobArtifactsDestroy' => { + 'errors' => ['Action temporarily disabled. The project this job belongs to is undergoing stats refresh.'], + 'destroyedArtifactsCount' => 0, + 'job' => { + 'name' => job.name + } + } + } + + post_graphql_mutation(mutation, current_user: user) + + expect(response).to have_gitlab_http_status(:success) + expect(graphql_data).to eq(expected_data) + expect(job.reload.job_artifacts.count).to be(2) + end + end + end +end diff --git a/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb b/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb index 5269c60b50a..b2f84ab2869 100644 --- a/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/job_token_scope/add_project_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe 'CiJobTokenScopeAddProject' do include GraphqlHelpers - let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) } let_it_be(:target_project) { create(:project) } let(:variables) do diff --git a/spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb b/spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb index b62291d1ebd..2b0adf89f40 100644 --- a/spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/job_token_scope/remove_project_spec.rb @@ -5,7 +5,7 @@ require 'spec_helper' RSpec.describe 'CiJobTokenScopeRemoveProject' do include GraphqlHelpers - let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) } let_it_be(:target_project) { create(:project) } let_it_be(:link) do diff --git a/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb b/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb index 6cca618726b..c808cf5ede9 100644 --- a/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb +++ b/spec/requests/api/graphql/mutations/ci/project_ci_cd_settings_update_spec.rb @@ -8,7 +8,7 @@ RSpec.describe 'ProjectCiCdSettingsUpdate' do let_it_be(:project) do create(:project, keep_latest_artifact: true, - ci_job_token_scope_enabled: true, + ci_outbound_job_token_scope_enabled: true, ci_inbound_job_token_scope_enabled: true ).tap(&:save!) end @@ -66,7 +66,7 @@ RSpec.describe 'ProjectCiCdSettingsUpdate' do project.reload expect(response).to have_gitlab_http_status(:success) - expect(project.ci_job_token_scope_enabled).to eq(false) + expect(project.ci_outbound_job_token_scope_enabled).to eq(false) end it 'does not update job_token_scope_enabled if not specified' do @@ -77,7 +77,7 @@ RSpec.describe 'ProjectCiCdSettingsUpdate' do project.reload expect(response).to have_gitlab_http_status(:success) - expect(project.ci_job_token_scope_enabled).to eq(true) + expect(project.ci_outbound_job_token_scope_enabled).to eq(true) end describe 'inbound_job_token_scope_enabled' do diff --git a/spec/services/ci/job_artifacts/delete_service_spec.rb b/spec/services/ci/job_artifacts/delete_service_spec.rb index 62a755eb44a..78e8be48255 100644 --- a/spec/services/ci/job_artifacts/delete_service_spec.rb +++ b/spec/services/ci/job_artifacts/delete_service_spec.rb @@ -14,6 +14,7 @@ RSpec.describe Ci::JobArtifacts::DeleteService do result = service.execute expect(result).to be_success + expect(result[:destroyed_artifacts_count]).to be(2) end it 'deletes erasable artifacts' do @@ -24,7 +25,7 @@ RSpec.describe Ci::JobArtifacts::DeleteService do expect { service.execute }.not_to change { build.has_trace? }.from(true) end - context 'when project is undergoing statistics refresh' do + context 'when project is undergoing stats refresh' do before do allow(build.project).to receive(:refreshing_build_artifacts_size?).and_return(true) end @@ -36,6 +37,30 @@ RSpec.describe Ci::JobArtifacts::DeleteService do service.execute end + + it 'returns an error response with the correct message and reason' do + result = service.execute + + expect(result).to be_error + expect(result[:message]).to be('Action temporarily disabled. ' \ + 'The project this job belongs to is undergoing stats refresh.') + expect(result[:reason]).to be(:project_stats_refresh) + end + end + + context 'when an error response is received from DestroyBatchService' do + before do + allow_next_instance_of(Ci::JobArtifacts::DestroyBatchService) do |service| + allow(service).to receive(:execute).and_return({ status: :error, message: 'something went wrong' }) + end + end + + it 'returns an error response with the correct message' do + result = service.execute + + expect(result).to be_error + expect(result[:message]).to be('something went wrong') + end end end end diff --git a/spec/services/ci/job_token_scope/add_project_service_spec.rb b/spec/services/ci/job_token_scope/add_project_service_spec.rb index bb6df4268dd..bf7df3a5595 100644 --- a/spec/services/ci/job_token_scope/add_project_service_spec.rb +++ b/spec/services/ci/job_token_scope/add_project_service_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Ci::JobTokenScope::AddProjectService do let(:service) { described_class.new(project, current_user) } - let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) } let_it_be(:target_project) { create(:project) } let_it_be(:current_user) { create(:user) } diff --git a/spec/services/ci/job_token_scope/remove_project_service_spec.rb b/spec/services/ci/job_token_scope/remove_project_service_spec.rb index 155e60ac48e..c3f9081cbd8 100644 --- a/spec/services/ci/job_token_scope/remove_project_service_spec.rb +++ b/spec/services/ci/job_token_scope/remove_project_service_spec.rb @@ -4,7 +4,7 @@ require 'spec_helper' RSpec.describe Ci::JobTokenScope::RemoveProjectService do let(:service) { described_class.new(project, current_user) } - let_it_be(:project) { create(:project, ci_job_token_scope_enabled: true).tap(&:save!) } + let_it_be(:project) { create(:project, ci_outbound_job_token_scope_enabled: true).tap(&:save!) } let_it_be(:target_project) { create(:project) } let_it_be(:current_user) { create(:user) } diff --git a/spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb b/spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb index c92e819db19..3caf58da4d2 100644 --- a/spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb +++ b/spec/support/shared_examples/models/project_ci_cd_settings_shared_examples.rb @@ -5,12 +5,14 @@ RSpec.shared_examples 'ci_cd_settings delegation' do context 'when ci_cd_settings is destroyed but project is not' do it 'allows methods delegated to ci_cd_settings to be nil', :aggregate_failures do - project = create(:project) attributes = project.ci_cd_settings.attributes.keys - %w(id project_id) - exclude_attributes + + expect(attributes).to match_array(attributes_with_prefix.keys) + project.ci_cd_settings.destroy! project.reload - attributes.each do |attr| - method = project.respond_to?("ci_#{attr}") ? "ci_#{attr}" : attr + attributes_with_prefix.each do |attr, prefix| + method = "#{prefix}#{attr}" expect(project.send(method)).to be_nil, "#{attr} was not nil" end end @@ -20,8 +22,6 @@ end RSpec.shared_examples 'a ci_cd_settings predicate method' do |prefix: ''| using RSpec::Parameterized::TableSyntax - let_it_be(:project) { create(:project) } - context 'when ci_cd_settings is nil' do before do allow(project).to receive(:ci_cd_settings).and_return(nil)