diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index 7e1af03225e..b282ccc1871 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -145,7 +145,6 @@ Dangerfile ^[End-to-end] @gl-quality /qa/ -/gems/gitlab-cng/ ^[LDAP] @dblessing @mkozono /ee/lib/ee/gitlab/auth/ldap/ @@ -1307,11 +1306,11 @@ lib/gitlab/checks/** # opportunity to refine specific rules defined in this section. # Note that frontend, CI templates and other concerns should be kept within # the same section. -/app/**/ci/ -/ee/app/**/ci/ +/app/**/ci/*.rb +/ee/app/**/ci/*.rb /lib/**/ci/ /ee/lib/**/ci/ -/ee/app/**/merge_trains/ +/ee/app/**/merge_trains/*.rb /app/controllers/admin/jobs_controller.rb /app/controllers/admin/runner_projects_controller.rb /app/controllers/admin/runners_controller.rb @@ -1402,6 +1401,16 @@ lib/gitlab/checks/** /lib/gitlab/ci/templates/Jobs/SAST.*.yml @gitlab-org/secure/static-analysis /lib/gitlab/ci/templates/Jobs/Secret-Detection.*.yml @gitlab-org/secure/static-analysis +# Verify Frontend, optional approvals +^[Verify frontend] @gitlab-org/ci-cd/verify/frontend +/**/javascripts/ci/ +/**/javascripts/token_access/ +/**/javascripts/admin/application_settings/runner_token_expiration/ +/**/javascripts/editor/schema/ci.json +/app/**/ci/*.haml +/ee/app/**/ci/*.haml +/ee/app/**/merge_trains/*.haml + ## Verify:Runner Fleet Backend ^[Runner Fleet Backend] @gitlab-org/ci-cd/runner-fleet-team/backend-approvers /app/controllers/admin/runner*.rb diff --git a/.gitlab/ci/gitlab-gems.gitlab-ci.yml b/.gitlab/ci/gitlab-gems.gitlab-ci.yml index 7bdff6271af..c5905120396 100644 --- a/.gitlab/ci/gitlab-gems.gitlab-ci.yml +++ b/.gitlab/ci/gitlab-gems.gitlab-ci.yml @@ -44,6 +44,3 @@ include: - local: .gitlab/ci/templates/gem.gitlab-ci.yml inputs: gem_name: "gitlab-housekeeper" - - local: .gitlab/ci/templates/gem.gitlab-ci.yml - inputs: - gem_name: "gitlab-cng" diff --git a/.gitlab/ci/qa.gitlab-ci.yml b/.gitlab/ci/qa.gitlab-ci.yml index 4b8fbecfee4..b525c0342ed 100644 --- a/.gitlab/ci/qa.gitlab-ci.yml +++ b/.gitlab/ci/qa.gitlab-ci.yml @@ -10,7 +10,23 @@ SETUP_DB: "false" before_script: - !reference [.default-before_script, before_script] - - cd qa && bundle install + - cd ${WORKING_DIR:-qa} && bundle install + +.qa-ruby-cng-gems-cache: &qa-ruby-cng-gems-cache + key: + prefix: "qa-ruby-gems-${BUILD_OS}-${OS_VERSION}-ruby-${RUBY_VERSION}" + files: + - qa/gems/cng-gitlab/Gemfile.lock + paths: + - qa/gems/cng-gitlab/vendor/bundle + policy: pull + +.qa-cng-job-base: + extends: + - .qa-job-base + variables: + WORKING_DIR: qa/gems/gitlab-cng + cache: *qa-ruby-cng-gems-cache .e2e-trigger-base: extends: .production # this makes sure GITLAB_ALLOW_SEPARATE_CI_DATABASE is passed to the child pipeline @@ -49,12 +65,9 @@ - artifact: $DYNAMIC_PIPELINE_YML job: e2e-test-pipeline-generate -qa:internal: - extends: - - .qa-job-base - - .qa:rules:internal - script: - - bundle exec rspec -O .rspec_internal +# ========================================== +# /qa caching +# ========================================== cache-qa-gems: extends: @@ -65,6 +78,14 @@ cache-qa-gems: script: - echo "Cache has been updated and ready to be uploaded." +cache-cng-gems: + extends: + - .qa-cng-job-base + - cache-qa-gems + cache: + <<: *qa-ruby-cng-gems-cache + policy: pull-push + # E2E runners have separate infra setup and different cache bucket cache-qa-gems-e2e-runners: extends: @@ -73,6 +94,56 @@ cache-qa-gems-e2e-runners: tags: - e2e +# ========================================== +# /qa linting and unit testing +# ========================================== + +qa:metadata-lint: + extends: + - .qa-job-base + - .qa:rules:metadata-lint + variables: + QA_EXPORT_TEST_METRICS: "false" + # Disable warnings in browserslist which can break on backports + # https://github.com/browserslist/browserslist/blob/a287ec6/node.js#L367-L384 + BROWSERSLIST_IGNORE_OLD_DATA: "true" + script: + - run_timed_command "bundle exec bin/qa Test::Instance::All http://localhost:3000 --test-metadata-only" + - cd .. + - run_timed_command "./scripts/qa/testcases-check qa/tmp/test-metadata.json" + - run_timed_command "./scripts/qa/quarantine-types-check qa/tmp/test-metadata.json" + artifacts: + expire_in: 31d + when: always + paths: + - qa/tmp/ + +qa:cng-lint: + extends: + - .qa-cng-job-base + - .qa:rules:gitlab-cng + stage: lint + script: + - bundle exec rubocop + +qa:rspec-cng: + extends: + - .qa-cng-job-base + - .qa:rules:gitlab-cng + script: + - bundle exec rspec --force-color + +qa:rspec-internal: + extends: + - .qa-job-base + - .qa:rules:internal + script: + - bundle exec rspec -O .rspec_internal + +# ========================================== +# E2E testing +# ========================================== + # If a rename is required for this job, please notify the Delivery team (`@gitlab-org/delivery`) e2e:package-and-test-ee: extends: diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 715258e3b63..881bbcff6c8 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -684,7 +684,7 @@ - "ee/{lib/,spec/}tasks/gitlab/custom_roles/*" .cng-orchestrator-patterns: &cng-orchestrator-patterns - - gems/gitlab-cng/**/*.rb + - qa/gems/gitlab-cng/**/*.rb ################## # Conditions set # @@ -1522,6 +1522,18 @@ when: never - !reference [.qa:rules:update-gem-cache, rules] +.qa:rules:metadata-lint: + rules: + - <<: *if-default-refs + changes: *qa-patterns + - <<: *if-default-refs + changes: *ci-qa-patterns + +.qa:rules:gitlab-cng: + rules: + - <<: *if-default-refs + changes: *cng-orchestrator-patterns + .qa:rules:code-merge-request: rules: - <<: *if-merge-request @@ -2444,13 +2456,6 @@ - <<: *if-default-refs changes: *code-backstage-qa-patterns -.static-analysis:rules:qa:metadata-lint: - rules: - - <<: *if-default-refs - changes: *qa-patterns - - <<: *if-default-refs - changes: [".gitlab/ci/static-analysis.gitlab-ci.yml"] - .static-analysis:rules:haml-lint: rules: - <<: *if-default-refs diff --git a/.gitlab/ci/static-analysis.gitlab-ci.yml b/.gitlab/ci/static-analysis.gitlab-ci.yml index 2bde2a7881a..5b7f5ef416f 100644 --- a/.gitlab/ci/static-analysis.gitlab-ci.yml +++ b/.gitlab/ci/static-analysis.gitlab-ci.yml @@ -124,32 +124,6 @@ rubocop: fi fi -qa:metadata-lint: - extends: - - .static-analysis-base - - .static-analysis:rules:qa:metadata-lint - before_script: - - !reference [.default-before_script, before_script] - - cd qa/ - - bundle_install_script - script: - - run_timed_command "bundle exec bin/qa Test::Instance::All http://localhost:3000 --test-metadata-only" - - cd .. - - run_timed_command "./scripts/qa/testcases-check qa/tmp/test-metadata.json" - - run_timed_command "./scripts/qa/quarantine-types-check qa/tmp/test-metadata.json" - variables: - USE_BUNDLE_INSTALL: "false" - SETUP_DB: "false" - QA_EXPORT_TEST_METRICS: "false" - # Disable warnings in browserslist which can break on backports - # https://github.com/browserslist/browserslist/blob/a287ec6/node.js#L367-L384 - BROWSERSLIST_IGNORE_OLD_DATA: "true" - artifacts: - expire_in: 31d - when: always - paths: - - qa/tmp/ - feature-flags-usage: extends: - .static-analysis-base diff --git a/.rubocop.yml b/.rubocop.yml index e94e5dedc68..335a68031d6 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -32,6 +32,7 @@ AllCops: TargetRailsVersion: 7.0 Exclude: - 'gems/**/*' + - 'qa/gems/**/*' - 'vendor/**/*' - 'node_modules/**/*' - 'db/fixtures/**/*' diff --git a/Gemfile b/Gemfile index b82729bdcce..2ca1492fff7 100644 --- a/Gemfile +++ b/Gemfile @@ -373,7 +373,7 @@ gem 'pg_query', '~> 5.1.0', feature_category: :database gem 'gitlab-schema-validation', path: 'gems/gitlab-schema-validation' # rubocop:todo Gemfile/MissingFeatureCategory gem 'gitlab-http', path: 'gems/gitlab-http' # rubocop:todo Gemfile/MissingFeatureCategory -gem 'premailer-rails', '~> 1.10.3' # rubocop:todo Gemfile/MissingFeatureCategory +gem 'premailer-rails', '~> 1.12.0', feature_category: :notifications gem 'gitlab-labkit', '~> 0.36.0', feature_category: :shared gem 'thrift', '>= 0.16.0' # rubocop:todo Gemfile/MissingFeatureCategory diff --git a/Gemfile.checksum b/Gemfile.checksum index d8add4d17c2..2e5566213b8 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -225,7 +225,7 @@ {"name":"gitlab-mail_room","version":"0.0.25","platform":"ruby","checksum":"223ce7c3c0797b6015eaa37147884e6ddc7be9a7ee90a424358c96bc18613b1a"}, {"name":"gitlab-markup","version":"1.9.0","platform":"ruby","checksum":"7eda045a08ec2d110084252fa13a8c9eac8bdac0e302035ca7db4b82bcbd7ed4"}, {"name":"gitlab-net-dns","version":"0.9.2","platform":"ruby","checksum":"f726d978479d43810819f12a45c0906d775a07e34df111bbe693fffbbef3059d"}, -{"name":"gitlab-sdk","version":"0.3.0","platform":"ruby","checksum":"22260f148451155c2e7bdfa1ea9f3e50061a7c31700cb80f8859713560b88903"}, +{"name":"gitlab-sdk","version":"0.3.1","platform":"ruby","checksum":"48ba49084f4ab92df7c7ef9f347020d9dfdf6ed9c1e782b67264e98ffe6ea710"}, {"name":"gitlab-styles","version":"12.0.1","platform":"ruby","checksum":"d8a302b0ab0e1f18e2d11501760f1b85c5e70b5e5ca628828a0786c7984ed133"}, {"name":"gitlab_chronic_duration","version":"0.12.0","platform":"ruby","checksum":"0d766944d415b5c831f176871ee8625783fc0c5bfbef2d79a3a616f207ffc16d"}, {"name":"gitlab_omniauth-ldap","version":"2.2.0","platform":"ruby","checksum":"bb4d20acb3b123ed654a8f6a47d3fac673ece7ed0b6992edb92dca14bad2838c"}, @@ -492,8 +492,8 @@ {"name":"pg_query","version":"5.1.0","platform":"ruby","checksum":"b7f7f47c864f08ccbed46a8244906fb6ee77ee344fd27250717963928c93145d"}, {"name":"plist","version":"3.7.0","platform":"ruby","checksum":"703ca90a7cb00e8263edd03da2266627f6741d280c910abbbac07c95ffb2f073"}, {"name":"png_quantizator","version":"0.2.1","platform":"ruby","checksum":"6023d4d064125c3a7e02929c95b7320ed6ac0d7341f9e8de0c9ea6576ef3106b"}, -{"name":"premailer","version":"1.16.0","platform":"ruby","checksum":"03e4402c448e6bae13fb5f6301a8bde4f3508e1bff90ae7c0972c7be94694786"}, -{"name":"premailer-rails","version":"1.10.3","platform":"ruby","checksum":"7cdcb97027866f7a81c490c6d15ada7f39666b5f6375f0821b7e97e0483b112f"}, +{"name":"premailer","version":"1.23.0","platform":"ruby","checksum":"f0d7f6ba299559c96ddf982aa5263f85e5617c86437c8d8ffff120813b2d7efb"}, +{"name":"premailer-rails","version":"1.12.0","platform":"ruby","checksum":"c13815d161b9bc7f7d3d81396b0bb0a61a90fa9bd89931548bf4e537c7710400"}, {"name":"prime","version":"0.1.2","platform":"ruby","checksum":"d4e956cadfaf04de036dc7dc74f95bf6a285a62cc509b28b7a66b245d19fe3a4"}, {"name":"prism","version":"0.29.0","platform":"ruby","checksum":"a52c843a0308a7f5faf28e9abd36e3284280fc7c34acba05d858cb009ba7475f"}, {"name":"proc_to_ast","version":"0.1.0","platform":"ruby","checksum":"92a73fa66e2250a83f8589f818b0751bcf227c68f85916202df7af85082f8691"}, diff --git a/Gemfile.lock b/Gemfile.lock index 91beefbb7ad..da61e26b34a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -723,7 +723,7 @@ GEM redis-namespace (>= 1.8.2) gitlab-markup (1.9.0) gitlab-net-dns (0.9.2) - gitlab-sdk (0.3.0) + gitlab-sdk (0.3.1) activesupport (>= 5.2.0) rake (~> 13.0) snowplow-tracker (~> 0.8.0) @@ -1369,12 +1369,13 @@ GEM google-protobuf (>= 3.22.3) plist (3.7.0) png_quantizator (0.2.1) - premailer (1.16.0) + premailer (1.23.0) addressable - css_parser (>= 1.6.0) + css_parser (>= 1.12.0) htmlentities (>= 4.0.0) - premailer-rails (1.10.3) + premailer-rails (1.12.0) actionmailer (>= 3) + net-smtp premailer (~> 1.7, >= 1.7.9) prime (0.1.2) forwardable @@ -2159,7 +2160,7 @@ DEPENDENCIES pg (~> 1.5.6) pg_query (~> 5.1.0) png_quantizator (~> 0.2.1) - premailer-rails (~> 1.10.3) + premailer-rails (~> 1.12.0) prometheus-client-mmap (~> 1.1, >= 1.1.1) pry-byebug pry-rails (~> 0.3.9) diff --git a/app/assets/javascripts/ci/pipeline_details/dag/dag.vue b/app/assets/javascripts/ci/pipeline_details/dag/dag.vue index 2461c11efee..d9fa22a5f3a 100644 --- a/app/assets/javascripts/ci/pipeline_details/dag/dag.vue +++ b/app/assets/javascripts/ci/pipeline_details/dag/dag.vue @@ -149,7 +149,10 @@ export default { }, methods: { addAnnotationToMap({ uid, source, target }) { - this.$set(this.annotationsMap, uid, { source, target }); + this.annotationsMap = { + ...this.annotationsMap, + [uid]: { source, target }, + }; }, processGraphData(data) { let parsed; diff --git a/app/assets/javascripts/import_entities/import_groups/constants.js b/app/assets/javascripts/import_entities/import_groups/constants.js index d6624cb3304..9a63d261310 100644 --- a/app/assets/javascripts/import_entities/import_groups/constants.js +++ b/app/assets/javascripts/import_entities/import_groups/constants.js @@ -29,13 +29,13 @@ export const TARGET_NAMESPACE_FIELD = 'targetNamespace'; export const ROOT_NAMESPACE = { fullPath: '', id: null }; -const PLACEHOLDER_STATUS_PENDING_ASSIGNMENT = 'pending_assignment'; -export const PLACEHOLDER_STATUS_AWAITING_APPROVAL = 'awaiting_approval'; -const PLACEHOLDER_STATUS_REJECTED = 'rejected'; -export const PLACEHOLDER_STATUS_REASSIGNING = 'reassignment_in_progress'; -const PLACEHOLDER_STATUS_FAILED = 'failed'; -export const PLACEHOLDER_STATUS_KEPT_AS_PLACEHOLDER = 'keep_as_placeholder'; -export const PLACEHOLDER_STATUS_COMPLETED = 'completed'; +const PLACEHOLDER_STATUS_PENDING_ASSIGNMENT = 'PENDING_ASSIGNMENT'; +export const PLACEHOLDER_STATUS_AWAITING_APPROVAL = 'AWAITING_APPROVAL'; +const PLACEHOLDER_STATUS_REJECTED = 'REJECTED'; +export const PLACEHOLDER_STATUS_REASSIGNING = 'REASSIGNMENT_IN_PROGRESS'; +const PLACEHOLDER_STATUS_FAILED = 'FAILED'; +export const PLACEHOLDER_STATUS_KEPT_AS_PLACEHOLDER = 'KEEP_AS_PLACEHOLDER'; +export const PLACEHOLDER_STATUS_COMPLETED = 'COMPLETED'; export const placeholderUserBadges = { [PLACEHOLDER_STATUS_PENDING_ASSIGNMENT]: { diff --git a/app/assets/javascripts/members/components/placeholders/app.vue b/app/assets/javascripts/members/components/placeholders/app.vue index 9ced8a0d6bd..0ab075587ca 100644 --- a/app/assets/javascripts/members/components/placeholders/app.vue +++ b/app/assets/javascripts/members/components/placeholders/app.vue @@ -1,7 +1,13 @@ @@ -34,7 +91,14 @@ export default { {{ tabCount }} - + @@ -43,7 +107,15 @@ export default { {{ tabCount }} - + diff --git a/app/assets/javascripts/members/components/placeholders/placeholders_table.vue b/app/assets/javascripts/members/components/placeholders/placeholders_table.vue index 8d7ca7431a3..4a137aa139c 100644 --- a/app/assets/javascripts/members/components/placeholders/placeholders_table.vue +++ b/app/assets/javascripts/members/components/placeholders/placeholders_table.vue @@ -1,5 +1,12 @@ - + + diff --git a/app/assets/javascripts/members/placeholders/graphql/fragments/import_source_user.fragment.graphql b/app/assets/javascripts/members/placeholders/graphql/fragments/import_source_user.fragment.graphql new file mode 100644 index 00000000000..0eb19fe181e --- /dev/null +++ b/app/assets/javascripts/members/placeholders/graphql/fragments/import_source_user.fragment.graphql @@ -0,0 +1,15 @@ +#import "~/graphql_shared/fragments/user.fragment.graphql" + +fragment ImportSourceUser on ImportSourceUser { + id + sourceUsername + sourceName + sourceHostname + status + placeholderUser { + ...User + } + reassignToUser { + ...User + } +} diff --git a/app/assets/javascripts/members/placeholders/graphql/queries/import_source_users.query.graphql b/app/assets/javascripts/members/placeholders/graphql/queries/import_source_users.query.graphql new file mode 100644 index 00000000000..c9343237e85 --- /dev/null +++ b/app/assets/javascripts/members/placeholders/graphql/queries/import_source_users.query.graphql @@ -0,0 +1,16 @@ +#import "~/graphql_shared/fragments/page_info.fragment.graphql" +#import "../fragments/import_source_user.fragment.graphql" + +query importSourceUsers($fullPath: ID!, $before: String, $after: String, $first: Int, $last: Int) { + namespace(fullPath: $fullPath) { + id + importSourceUsers(before: $before, after: $after, first: $first, last: $last) { + nodes { + ...ImportSourceUser + } + pageInfo { + ...PageInfo + } + } + } +} diff --git a/app/assets/javascripts/notes/components/comment_type_dropdown.vue b/app/assets/javascripts/notes/components/comment_type_dropdown.vue index bd8f4a331b8..271e00f1c80 100644 --- a/app/assets/javascripts/notes/components/comment_type_dropdown.vue +++ b/app/assets/javascripts/notes/components/comment_type_dropdown.vue @@ -142,7 +142,7 @@ export default { data-track-action="click_button" data-testid="comment-button" > - + {{ commentButtonTitle }} -
+
{{ $options.i18n.headlines.ticketVisibility }}
(note, _) { note.part_of_discussion? && note.resolvable? } do |note| + next unless discussion.project + resolve_project_discussion_path(discussion.project, discussion.noteable_collection_name, discussion.noteable, discussion.id) end diff --git a/app/services/bulk_imports/create_service.rb b/app/services/bulk_imports/create_service.rb index d58620eb089..b7b851eb705 100644 --- a/app/services/bulk_imports/create_service.rb +++ b/app/services/bulk_imports/create_service.rb @@ -66,10 +66,13 @@ module BulkImports private + attr_accessor :source_entity_identifier + def validate! client.validate_instance_version! - validate_setting_enabled! client.validate_import_scopes! + validate_source_full_path! + validate_setting_enabled! end def create_bulk_import @@ -102,22 +105,35 @@ module BulkImports end end + def validate_source_full_path! + gql_query = query_type(entity_type) + + response = graphql_client.execute( + graphql_client.parse(gql_query.to_s), + { full_path: source_full_path } + ).original_hash + + self.source_entity_identifier = ::GlobalID.parse(response.dig(*gql_query.data_path, 'id'))&.model_id + + raise BulkImports::Error.source_full_path_validation_failure(source_full_path) if source_entity_identifier.nil? + end + def validate_setting_enabled! - source_full_path, source_type = Array.wrap(params)[0].values_at(:source_full_path, :source_type) - entity_type = ENTITY_TYPES_MAPPING.fetch(source_type) - if /^[0-9]+$/.match?(source_full_path) - query = query_type(entity_type) - response = graphql_client.execute( - graphql_client.parse(query.to_s), - { full_path: source_full_path } - ).original_hash - - source_entity_identifier = ::GlobalID.parse(response.dig(*query.data_path, 'id')).model_id - else - source_entity_identifier = ERB::Util.url_encode(source_full_path) - end - client.get("/#{entity_type}/#{source_entity_identifier}/export_relations/status") + rescue BulkImports::NetworkError => e + raise BulkImports::Error.not_authorized(source_full_path) if e.message.include?("URL is blocked") + raise BulkImports::Error.setting_not_enabled if e.response.code == 404 + raise BulkImports::Error.not_authorized(source_full_path) if e.response.code == 403 + + raise e + end + + def entity_type + @entity_type ||= ENTITY_TYPES_MAPPING.fetch(Array.wrap(params)[0][:source_type]) + end + + def source_full_path + @source_full_path ||= Array.wrap(params)[0][:source_full_path] end def track_access_level(entity_params) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index c4fdeea6be1..bd0d9f1c8cf 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -443,7 +443,7 @@ class NotificationService def send_service_desk_notification(note) return unless note.noteable_type == 'Issue' return if note.confidential - return unless note.project.service_desk_enabled? + return unless note.project&.service_desk_enabled? issue = note.noteable recipients = issue.issue_email_participants diff --git a/app/services/packages/create_event_service.rb b/app/services/packages/create_event_service.rb index 8eac30f0022..d89e079966d 100644 --- a/app/services/packages/create_event_service.rb +++ b/app/services/packages/create_event_service.rb @@ -2,13 +2,32 @@ module Packages class CreateEventService < BaseService + INTERNAL_EVENTS_NAMES = { + 'pull_package' => 'pull_package_from_registry' + }.freeze + def execute ::Packages::Event.unique_counters_for(event_scope, event_name, originator_type).each do |event_name| ::Gitlab::UsageDataCounters::HLLRedisCounter.track_event(event_name, values: current_user.id) end - ::Packages::Event.counters_for(event_scope, event_name, originator_type).each do |event_name| - ::Gitlab::UsageDataCounters::PackageEventCounter.count(event_name) + if INTERNAL_EVENTS_NAMES.key?(event_name) + user = current_user if current_user.is_a?(User) + + Gitlab::InternalEvents.track_event( + INTERNAL_EVENTS_NAMES[event_name], + user: user, + project: project, + namespace: params[:namespace], + additional_properties: { + label: event_scope.to_s, + property: originator_type.to_s + } + ) + else + ::Packages::Event.counters_for(event_scope, event_name, originator_type).each do |event_name| + ::Gitlab::UsageDataCounters::PackageEventCounter.count(event_name) + end end end diff --git a/app/services/service_desk_settings/update_service.rb b/app/services/service_desk_settings/update_service.rb index 8139ccc92f0..3b0c8e01410 100644 --- a/app/services/service_desk_settings/update_service.rb +++ b/app/services/service_desk_settings/update_service.rb @@ -13,10 +13,6 @@ module ServiceDeskSettings feature_flag: :issue_email_participants, field: :add_external_participants_from_cc ) - apply_feature_flag_restrictions!( - feature_flag: :service_desk_tickets_confidentiality, - field: :tickets_confidential_by_default - ) # We want to know when custom email got enabled write_log_message = params[:custom_email_enabled].present? && !settings.custom_email_enabled? diff --git a/app/views/groups/packages/index.html.haml b/app/views/groups/packages/index.html.haml index b0700c8a51b..4dd432ea79c 100644 --- a/app/views/groups/packages/index.html.haml +++ b/app/views/groups/packages/index.html.haml @@ -2,13 +2,4 @@ .row .col-12 - #js-vue-packages-list{ data: { - full_path: @group.full_path, - endpoint: group_packages_path(@group), - page_type: 'groups', - empty_list_illustration: image_path('illustrations/empty-state/empty-package-md.svg'), - npm_instance_url: package_registry_instance_url(:npm), - project_list_url: '', - settings_path: show_group_package_registry_settings(@group) ? group_settings_packages_and_registries_path(@group) : '', - can_delete_packages: can_delete_group_packages?(@group).to_s, - group_list_url: group_packages_path(@group) } } + #js-vue-packages-list{ data: group_packages_template_data(@group) } diff --git a/app/views/layouts/_mailer.html.haml b/app/views/layouts/_mailer.html.haml index d1734c50c85..2f438cbfb97 100644 --- a/app/views/layouts/_mailer.html.haml +++ b/app/views/layouts/_mailer.html.haml @@ -6,13 +6,19 @@ %meta{ content: "IE=edge", "http-equiv" => "X-UA-Compatible" }/ %title= message.subject - -# Avoid premailer processing of client-specific styles (@media tag not supported) - -# We need to inline the contents here because mail clients (e.g. iOS Mail, Outlook) - -# do not support linked stylesheets. - %style{ type: 'text/css', 'data-premailer': 'ignore' } - = asset_to_string('mailer_client_specific.css').html_safe + -# Vite in dev mode isn't too great about reading assets a string, + -# so we do not inline it with vite. Similar solution to: + -# app/views/layouts/errors.html.haml + - if vite_enabled? + = universal_stylesheet_link_tag 'mailer_client_specific', data: { premailer: 'ignore' } + - else + -# Avoid premailer processing of client-specific styles (@media tag not supported) + -# We need to inline the contents here because mail clients (e.g. iOS Mail, Outlook) + -# do not support linked stylesheets. + %style{ type: 'text/css', 'data-premailer': 'ignore' } + = asset_to_string('mailer_client_specific.css').html_safe - = stylesheet_link_tag 'mailer.css' + = universal_stylesheet_link_tag 'mailer' %body = yield :preview_text -# Test stub for RSpec testing of Notify and associated modules diff --git a/app/views/projects/network/_head.html.haml b/app/views/projects/network/_head.html.haml index 2b2066c0591..fdb29455d87 100644 --- a/app/views/projects/network/_head.html.haml +++ b/app/views/projects/network/_head.html.haml @@ -1,6 +1,6 @@ .row-content-block.second-block.content-component-block.gl-px-0.gl-py-3 .gl-w-max.gl-max-w-full - #js-graph-ref-switcher{ data: { project_id: @project.id, ref: @ref, network_path: project_network_path(@project) } } + #js-graph-ref-switcher{ data: { project_id: @project.id, ref: @ref, network_path: project_network_path(@project, @ref, ref_type: @ref_type) } } .oneline = _("You can move around the graph by using the arrow keys.") diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index 1b18ded6e67..2e008209570 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -1,6 +1,6 @@ - breadcrumb_title _("Graph") - page_title _("Graph"), @ref -- network_path = project_network_path(@project, @id, ref_type: @ref_type) +- network_path = project_network_path(@project, @ref, ref_type: @ref_type) = render "head" .gl-mt-5 .project-network.gl-border-1.gl-border-solid.gl-border-gray-300 diff --git a/app/views/projects/packages/packages/index.html.haml b/app/views/projects/packages/packages/index.html.haml index ba183d680dc..0abc3c19fae 100644 --- a/app/views/projects/packages/packages/index.html.haml +++ b/app/views/projects/packages/packages/index.html.haml @@ -2,13 +2,4 @@ .row .col-12 - #js-vue-packages-list{ data: { - full_path: @project.full_path, - endpoint: project_packages_path(@project), - page_type: 'projects', - empty_list_illustration: image_path('illustrations/empty-state/empty-package-md.svg'), - npm_instance_url: package_registry_instance_url(:npm), - project_list_url: project_packages_path(@project), - settings_path: show_package_registry_settings(@project) ? project_settings_packages_and_registries_path(@project) : '', - can_delete_packages: can_delete_packages?(@project).to_s, - group_list_url: '' } } + #js-vue-packages-list{ data: project_packages_template_data(@project) } diff --git a/config/events/pull_package_from_registry.yml b/config/events/pull_package_from_registry.yml new file mode 100644 index 00000000000..62af976de3f --- /dev/null +++ b/config/events/pull_package_from_registry.yml @@ -0,0 +1,23 @@ +--- +description: Package pulled from the registry +internal_events: true +action: pull_package_from_registry +identifiers: + - project + - namespace + - user +additional_properties: + label: + description: The name of the package type + property: + description: The auth type. Either 'guest', 'user' or 'deploy_token' +product_group: package_registry +milestone: '17.1' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151919 +distributions: + - ce + - ee +tiers: + - free + - premium + - ultimate diff --git a/config/feature_flags/development/group_level_vulnerability_report_grouping.yml b/config/feature_flags/development/group_level_vulnerability_report_grouping.yml index 163d46f6b62..364f6e198ce 100644 --- a/config/feature_flags/development/group_level_vulnerability_report_grouping.yml +++ b/config/feature_flags/development/group_level_vulnerability_report_grouping.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/432778 milestone: '16.7' type: development group: group::threat insights -default_enabled: false +default_enabled: true diff --git a/config/feature_flags/wip/service_desk_tickets_confidentiality.yml b/config/feature_flags/wip/service_desk_tickets_confidentiality.yml deleted file mode 100644 index 2364c1ec9ad..00000000000 --- a/config/feature_flags/wip/service_desk_tickets_confidentiality.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: service_desk_tickets_confidentiality -feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/33091 -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/150025 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/457181 -milestone: '17.0' -group: group::respond -type: wip -default_enabled: false diff --git a/config/initializers/premailer.rb b/config/initializers/premailer.rb index b07dc792cab..ad80d80f079 100644 --- a/config/initializers/premailer.rb +++ b/config/initializers/premailer.rb @@ -8,5 +8,5 @@ Premailer::Rails.config.merge!( remove_ids: false, remove_scripts: false, output_encoding: 'US-ASCII', - strategies: [:asset_pipeline] + strategies: ::Rails.env.production? ? [:asset_pipeline] : [:asset_pipeline, :network] ) diff --git a/config/metrics/counts_all/20210216182857_package_events_i_package_composer_pull_package.yml b/config/metrics/counts_all/20210216182857_package_events_i_package_composer_pull_package.yml index 3900ca08c95..2a1778dc519 100644 --- a/config/metrics/counts_all/20210216182857_package_events_i_package_composer_pull_package.yml +++ b/config/metrics/counts_all/20210216182857_package_events_i_package_composer_pull_package.yml @@ -6,11 +6,11 @@ product_group: package_registry value_type: number status: active time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_composer_pull_package +data_source: internal_events +events: + - name: pull_package_from_registry + filter: + label: composer distribution: - ee - ce diff --git a/config/metrics/counts_all/20210216182903_package_events_i_package_conan_pull_package.yml b/config/metrics/counts_all/20210216182903_package_events_i_package_conan_pull_package.yml index 53f4eaf4b90..0cf9bb5d81d 100644 --- a/config/metrics/counts_all/20210216182903_package_events_i_package_conan_pull_package.yml +++ b/config/metrics/counts_all/20210216182903_package_events_i_package_conan_pull_package.yml @@ -6,11 +6,11 @@ product_group: package_registry value_type: number status: active time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_conan_pull_package +data_source: internal_events +events: + - name: pull_package_from_registry + filter: + label: conan distribution: - ee - ce diff --git a/config/metrics/counts_all/20210216182915_package_events_i_package_debian_pull_package.yml b/config/metrics/counts_all/20210216182915_package_events_i_package_debian_pull_package.yml index ab393e0e18f..1653e5e077a 100644 --- a/config/metrics/counts_all/20210216182915_package_events_i_package_debian_pull_package.yml +++ b/config/metrics/counts_all/20210216182915_package_events_i_package_debian_pull_package.yml @@ -6,11 +6,11 @@ product_group: package_registry value_type: number status: active time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_debian_pull_package +data_source: internal_events +events: + - name: pull_package_from_registry + filter: + label: debian distribution: - ee - ce diff --git a/config/metrics/counts_all/20210216182929_package_events_i_package_generic_pull_package.yml b/config/metrics/counts_all/20210216182929_package_events_i_package_generic_pull_package.yml index ea60ef83ee0..53a4ffc0c9c 100644 --- a/config/metrics/counts_all/20210216182929_package_events_i_package_generic_pull_package.yml +++ b/config/metrics/counts_all/20210216182929_package_events_i_package_generic_pull_package.yml @@ -6,11 +6,11 @@ product_group: package_registry value_type: number status: active time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_generic_pull_package +data_source: internal_events +events: + - name: pull_package_from_registry + filter: + label: generic distribution: - ee - ce diff --git a/config/metrics/counts_all/20210216182934_package_events_i_package_golang_pull_package.yml b/config/metrics/counts_all/20210216182934_package_events_i_package_golang_pull_package.yml index a9515888699..a5b19e1c794 100644 --- a/config/metrics/counts_all/20210216182934_package_events_i_package_golang_pull_package.yml +++ b/config/metrics/counts_all/20210216182934_package_events_i_package_golang_pull_package.yml @@ -6,11 +6,11 @@ product_group: package_registry value_type: number status: active time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_golang_pull_package +data_source: internal_events +events: + - name: pull_package_from_registry + filter: + label: golang distribution: - ee - ce diff --git a/config/metrics/counts_all/20210216182940_package_events_i_package_maven_pull_package.yml b/config/metrics/counts_all/20210216182940_package_events_i_package_maven_pull_package.yml index 4f8c3e6d47f..02c10bf41be 100644 --- a/config/metrics/counts_all/20210216182940_package_events_i_package_maven_pull_package.yml +++ b/config/metrics/counts_all/20210216182940_package_events_i_package_maven_pull_package.yml @@ -6,11 +6,11 @@ product_group: package_registry value_type: number status: active time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_maven_pull_package +data_source: internal_events +events: + - name: pull_package_from_registry + filter: + label: maven distribution: - ee - ce diff --git a/config/metrics/counts_all/20210216182946_package_events_i_package_npm_pull_package.yml b/config/metrics/counts_all/20210216182946_package_events_i_package_npm_pull_package.yml index 5c01c00bb79..2c14c730936 100644 --- a/config/metrics/counts_all/20210216182946_package_events_i_package_npm_pull_package.yml +++ b/config/metrics/counts_all/20210216182946_package_events_i_package_npm_pull_package.yml @@ -6,11 +6,11 @@ product_group: package_registry value_type: number status: active time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_npm_pull_package +data_source: internal_events +events: + - name: pull_package_from_registry + filter: + label: npm distribution: - ee - ce diff --git a/config/metrics/counts_all/20210216182952_package_events_i_package_nuget_pull_package.yml b/config/metrics/counts_all/20210216182952_package_events_i_package_nuget_pull_package.yml index 696a2c93636..8479c22abc3 100644 --- a/config/metrics/counts_all/20210216182952_package_events_i_package_nuget_pull_package.yml +++ b/config/metrics/counts_all/20210216182952_package_events_i_package_nuget_pull_package.yml @@ -6,11 +6,11 @@ product_group: package_registry value_type: number status: active time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_nuget_pull_package +data_source: internal_events +events: + - name: pull_package_from_registry + filter: + label: nuget distribution: - ee - ce diff --git a/config/metrics/counts_all/20210216182956_package_events_i_package_pull_package.yml b/config/metrics/counts_all/20210216182956_package_events_i_package_pull_package.yml index 68fa25fdcf0..c8b3953b866 100644 --- a/config/metrics/counts_all/20210216182956_package_events_i_package_pull_package.yml +++ b/config/metrics/counts_all/20210216182956_package_events_i_package_pull_package.yml @@ -6,11 +6,9 @@ product_group: package_registry value_type: number status: active time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_pull_package +data_source: internal_events +events: + - name: pull_package_from_registry distribution: - ee - ce diff --git a/config/metrics/counts_all/20210216182958_package_events_i_package_pull_package_by_deploy_token.yml b/config/metrics/counts_all/20210216182958_package_events_i_package_pull_package_by_deploy_token.yml index 78ce22e149e..b100c9dcc9d 100644 --- a/config/metrics/counts_all/20210216182958_package_events_i_package_pull_package_by_deploy_token.yml +++ b/config/metrics/counts_all/20210216182958_package_events_i_package_pull_package_by_deploy_token.yml @@ -7,11 +7,11 @@ product_group: package_registry value_type: number status: active time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_pull_package_by_deploy_token +data_source: internal_events +events: + - name: pull_package_from_registry + filter: + property: deploy_token distribution: - ee - ce diff --git a/config/metrics/counts_all/20210216183000_package_events_i_package_pull_package_by_guest.yml b/config/metrics/counts_all/20210216183000_package_events_i_package_pull_package_by_guest.yml index 211716c980c..a284687fc86 100644 --- a/config/metrics/counts_all/20210216183000_package_events_i_package_pull_package_by_guest.yml +++ b/config/metrics/counts_all/20210216183000_package_events_i_package_pull_package_by_guest.yml @@ -7,13 +7,11 @@ product_group: package_registry value_type: number status: active time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_pull_package_by_guest +data_source: internal_events events: - - name: package_pulled_by_guest + - name: pull_package_from_registry + filter: + property: guest distribution: - ee - ce diff --git a/config/metrics/counts_all/20210216183002_package_events_i_package_pull_package_by_user.yml b/config/metrics/counts_all/20210216183002_package_events_i_package_pull_package_by_user.yml index 9f4c6abcbab..a3bffb30f95 100644 --- a/config/metrics/counts_all/20210216183002_package_events_i_package_pull_package_by_user.yml +++ b/config/metrics/counts_all/20210216183002_package_events_i_package_pull_package_by_user.yml @@ -7,11 +7,11 @@ product_group: package_registry value_type: number status: active time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_pull_package_by_user +data_source: internal_events +events: + - name: pull_package_from_registry + filter: + property: user distribution: - ee - ce diff --git a/config/metrics/counts_all/20210216183005_package_events_i_package_push_package_by_deploy_token.yml b/config/metrics/counts_all/20210216183005_package_events_i_package_push_package_by_deploy_token.yml index 1bc0a80c94e..26ba5279c09 100644 --- a/config/metrics/counts_all/20210216183005_package_events_i_package_push_package_by_deploy_token.yml +++ b/config/metrics/counts_all/20210216183005_package_events_i_package_push_package_by_deploy_token.yml @@ -12,8 +12,6 @@ instrumentation_class: RedisMetric options: prefix: package_events event: i_package_push_package_by_deploy_token -events: - - name: package_pushed_using_deploy_token distribution: - ee - ce diff --git a/config/metrics/counts_all/20210216183013_package_events_i_package_pypi_pull_package.yml b/config/metrics/counts_all/20210216183013_package_events_i_package_pypi_pull_package.yml index a11571a9dd5..4da3a11bf35 100644 --- a/config/metrics/counts_all/20210216183013_package_events_i_package_pypi_pull_package.yml +++ b/config/metrics/counts_all/20210216183013_package_events_i_package_pypi_pull_package.yml @@ -7,11 +7,11 @@ product_group: package_registry value_type: number status: active time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_pypi_pull_package +data_source: internal_events +events: + - name: pull_package_from_registry + filter: + label: pypi distribution: - ee - ce diff --git a/config/metrics/counts_all/20210303153002_package_events_i_package_rubygems_pull_package.yml b/config/metrics/counts_all/20210303153002_package_events_i_package_rubygems_pull_package.yml index 292c7f4b837..91e84326b49 100644 --- a/config/metrics/counts_all/20210303153002_package_events_i_package_rubygems_pull_package.yml +++ b/config/metrics/counts_all/20210303153002_package_events_i_package_rubygems_pull_package.yml @@ -8,11 +8,11 @@ status: active milestone: '13.10' introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/53480 time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_rubygems_pull_package +data_source: internal_events +events: + - name: pull_package_from_registry + filter: + label: rubygems distribution: - ce - ee diff --git a/config/metrics/counts_all/20210410012202_package_events_i_package_terraform_module_pull_package.yml b/config/metrics/counts_all/20210410012202_package_events_i_package_terraform_module_pull_package.yml index 93393055e3d..74fc4b739ef 100644 --- a/config/metrics/counts_all/20210410012202_package_events_i_package_terraform_module_pull_package.yml +++ b/config/metrics/counts_all/20210410012202_package_events_i_package_terraform_module_pull_package.yml @@ -8,11 +8,11 @@ status: active milestone: '13.11' introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55018 time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_terraform_module_pull_package +data_source: internal_events +events: + - name: pull_package_from_registry + filter: + label: terraform_module distribution: - ce - ee diff --git a/config/metrics/counts_all/20210517073546_package_events_i_package_helm_pull_package.yml b/config/metrics/counts_all/20210517073546_package_events_i_package_helm_pull_package.yml index 7604a5654af..55a439217fe 100644 --- a/config/metrics/counts_all/20210517073546_package_events_i_package_helm_pull_package.yml +++ b/config/metrics/counts_all/20210517073546_package_events_i_package_helm_pull_package.yml @@ -8,11 +8,11 @@ status: active milestone: "14.0" introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61014 time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_helm_pull_package +data_source: internal_events +events: + - name: pull_package_from_registry + filter: + label: helm distribution: - ce - ee diff --git a/config/metrics/counts_all/20220906074055_package_events_i_package_rpm_pull_package.yml b/config/metrics/counts_all/20220906074055_package_events_i_package_rpm_pull_package.yml index eeb6f73592a..a855a30d801 100644 --- a/config/metrics/counts_all/20220906074055_package_events_i_package_rpm_pull_package.yml +++ b/config/metrics/counts_all/20220906074055_package_events_i_package_rpm_pull_package.yml @@ -8,11 +8,11 @@ status: active milestone: '15.6' introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97133 time_frame: all -data_source: redis -instrumentation_class: RedisMetric -options: - prefix: package_events - event: i_package_rpm_pull_package +data_source: internal_events +events: + - name: pull_package_from_registry + filter: + label: rpm distribution: - ce - ee diff --git a/db/click_house/migrate/main/20240625095647_create_code_suggestion_acceptance_tables.rb b/db/click_house/migrate/main/20240625095647_create_code_suggestion_acceptance_tables.rb new file mode 100644 index 00000000000..79947f39ee4 --- /dev/null +++ b/db/click_house/migrate/main/20240625095647_create_code_suggestion_acceptance_tables.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +class CreateCodeSuggestionAcceptanceTables < ClickHouse::Migration + def up + execute <<~SQL + CREATE TABLE IF NOT EXISTS code_suggestion_daily_events + ( + user_id UInt64 DEFAULT 0, + date Date32 DEFAULT toDate(now64()), + event UInt8 DEFAULT 0, + occurrences UInt64 DEFAULT 0, + ) ENGINE = SummingMergeTree + PARTITION BY toYear(date) + ORDER BY (user_id, date, event) + SETTINGS index_granularity = 64 + SQL + + execute <<~SQL + CREATE MATERIALIZED VIEW IF NOT EXISTS code_suggestion_daily_events_mv + TO code_suggestion_daily_events + AS + SELECT + user_id, + toDate(timestamp) as date, + event, + 1 as occurrences + FROM code_suggestion_usages + SQL + end + + def down + execute <<~SQL + DROP VIEW IF EXISTS code_suggestion_daily_events_mv + SQL + + execute <<~SQL + DROP TABLE IF EXISTS code_suggestion_daily_events + SQL + end +end diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 8df4b27c500..4e77b12a3f2 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -16595,8 +16595,10 @@ Extra metadata for AI message. | Name | Type | Description | | ---- | ---- | ----------- | -| `codeContributorsCount` | [`Int!`](#int) | Number of code contributors. | -| `codeSuggestionsContributorsCount` | [`Int!`](#int) | Number of code contributors who used GitLab Duo Code Suggestions features. | +| `codeContributorsCount` | [`Int`](#int) | Number of code contributors. | +| `codeSuggestionsAcceptedCount` | [`Int`](#int) | Total count of code suggestions accepted by code contributors. | +| `codeSuggestionsContributorsCount` | [`Int`](#int) | Number of code contributors who used GitLab Duo Code Suggestions features. | +| `codeSuggestionsShownCount` | [`Int`](#int) | Total count of code suggestions shown to code contributors. | ### `AiSelfHostedModel` diff --git a/doc/ci/environments/index.md b/doc/ci/environments/index.md index a8f2d1d6975..4fb9ef44fa4 100644 --- a/doc/ci/environments/index.md +++ b/doc/ci/environments/index.md @@ -268,7 +268,9 @@ To achieve the same result as renaming an environment: Sometimes, instead of using an [industry standard](https://en.wikipedia.org/wiki/Deployment_environment) environment name, like `production`, you might want to use a code name, like `customer-portal`. While there is no technical reason not to use a name like `customer-portal`, the name -no longer indicates that the environment is used for production. +no longer indicates that the environment is used for production. This can affect how metrics +like [deployment frequency](../../user/analytics/dora_metrics.md#how-deployment-frequency-is-calculated) +are calculated. To indicate that a specific environment is for a specific use, you can use tiers: @@ -282,6 +284,7 @@ you can use tiers: | `other` | | By default, GitLab assumes a tier based on [the environment name](../yaml/index.md#environmentname). +You cannot set an environment tier using the UI. Instead, you can use the [`deployment_tier` keyword](../yaml/index.md#environmentdeployment_tier) to specify a tier. ## Configure manual deployments diff --git a/doc/ci/services/postgres.md b/doc/ci/services/postgres.md index 44d0f728c7e..2c2e0c02168 100644 --- a/doc/ci/services/postgres.md +++ b/doc/ci/services/postgres.md @@ -16,8 +16,6 @@ do this with the Docker and Shell executors of GitLab Runner. ## Use PostgreSQL with the Docker executor -NOTE: - To pass variables set in the GitLab UI to service containers, you must [define the variables](../variables/index.md#define-a-cicd-variable-in-the-ui). You must define your variables as either Group or Project, then call the variables in your job as shown in the following workaround. diff --git a/doc/development/testing_guide/end_to_end/test_pipelines.md b/doc/development/testing_guide/end_to_end/test_pipelines.md index 7bf74005192..512a787406c 100644 --- a/doc/development/testing_guide/end_to_end/test_pipelines.md +++ b/doc/development/testing_guide/end_to_end/test_pipelines.md @@ -194,7 +194,7 @@ This stage is responsible for [allure test report](index.md#allure-report) gener The `e2e:test-on-cng` child pipeline runs tests against [Cloud Native GitLab](https://gitlab.com/gitlab-org/build/CNG) installation. Unlike `review-apps`, this pipeline uses local [kind](https://github.com/kubernetes-sigs/kind) Kubernetes cluster. -Deployment is managed by the [`cng`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/gems/gitlab-cng/README.md) +Deployment is managed by the [`cng`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/gems/gitlab-cng/README.md) orchestrator tool, which you can also use to locally recreate CI/CD deployments. This pipeline is executed every two hours on scheduled pipelines and runs the following validations: @@ -235,6 +235,6 @@ This stage is responsible for [allure test report](index.md#allure-report) gener To help with debugging: -- Each test job prints a list of arguments that you can pass to the [`cng`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/gems/gitlab-cng/README.md) +- Each test job prints a list of arguments that you can pass to the [`cng`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/qa/gems/gitlab-cng/README.md) orchestrator to exactly recreate the same deployment for local debugging. - Cluster events log and all pod logs are saved in E2E test job artifacts. diff --git a/doc/user/packages/nuget_repository/index.md b/doc/user/packages/nuget_repository/index.md index 9dce4762757..3c18c458e2d 100644 --- a/doc/user/packages/nuget_repository/index.md +++ b/doc/user/packages/nuget_repository/index.md @@ -52,11 +52,12 @@ Prerequisites: - Your GitLab username. - A personal access token or deploy token. For repository authentication: - - You can generate a [personal access token](../../../user/profile/personal_access_tokens.md) - with the scope set to `api`. - - You can generate a [deploy token](../../project/deploy_tokens/index.md) - with the scope set to `read_package_registry`, `write_package_registry`, or - both. + - You can generate a [personal access token](../../../user/profile/personal_access_tokens.md). + - To install packages from the repository, the scope of the token must include `read_api` or `api`. + - To publish packages to the repository, the scope of the token must include `api`. + - You can generate a [deploy token](../../project/deploy_tokens/index.md). + - To install packages from the repository, the scope of the token must include `read_package_registry`. + - To publish packages to the repository, the scope of the token must include `write_package_registry`. - A name for your source. - Depending on the [endpoint level](#use-the-gitlab-endpoint-for-nuget-packages) you use, either: - Your project ID, which is found on your [project overview page](../../project/working_with_projects.md#access-a-project-by-using-the-project-id). diff --git a/doc/user/project/service_desk/configure.md b/doc/user/project/service_desk/configure.md index f5ce2c55d6a..7c5e7027b43 100644 --- a/doc/user/project/service_desk/configure.md +++ b/doc/user/project/service_desk/configure.md @@ -188,11 +188,7 @@ To edit the custom email display name: ## Default ticket visibility -> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/33091) in GitLab 17.2 [with a flag](../../../administration/feature_flags.md) named `service_desk_ticket_confidentiality`. Disabled by default. - -FLAG: -The availability of this feature is controlled by a feature flag. -For more information, see the history. +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/33091) in GitLab 17.2. New tickets are confidential by default, so only project members with at least the Reporter role can view them. diff --git a/doc/user/project/service_desk/index.md b/doc/user/project/service_desk/index.md index cc911fb2a37..af358c197a2 100644 --- a/doc/user/project/service_desk/index.md +++ b/doc/user/project/service_desk/index.md @@ -54,6 +54,7 @@ Meanwhile: - [Customize emails sent to external participants](configure.md#customize-emails-sent-to-external-participants) - [Use a custom template for Service Desk tickets](configure.md#use-a-custom-template-for-service-desk-tickets) - [Support Bot user](configure.md#support-bot-user) + - [Default ticket visibility](configure.md#default-ticket-visibility) - [Reopen issues when an external participant comments](configure.md#reopen-issues-when-an-external-participant-comments) - [Custom email address](configure.md#custom-email-address) - [Use an additional Service Desk alias email](configure.md#use-an-additional-service-desk-alias-email) diff --git a/gems/gitlab-cng/.gitlab-ci.yml b/gems/gitlab-cng/.gitlab-ci.yml deleted file mode 100644 index 4ddaf94cdaf..00000000000 --- a/gems/gitlab-cng/.gitlab-ci.yml +++ /dev/null @@ -1,4 +0,0 @@ -include: - - local: gems/gem.gitlab-ci.yml - inputs: - gem_name: gitlab-cng diff --git a/gems/gitlab-cng/.rubocop.yml b/gems/gitlab-cng/.rubocop.yml deleted file mode 100644 index 83c3b0a0fd9..00000000000 --- a/gems/gitlab-cng/.rubocop.yml +++ /dev/null @@ -1,18 +0,0 @@ -inherit_from: - - ../config/rubocop.yml - -Gemfile/MissingFeatureCategory: - Enabled: false - -Rails/Output: - Enabled: false - -Rails/Pluck: - Enabled: false - -Rails/Exit: - Enabled: false - -RSpec/MultipleMemoizedHelpers: - AllowSubject: true - Max: 8 diff --git a/gems/gitlab-cng/Gemfile b/gems/gitlab-cng/Gemfile deleted file mode 100644 index be173b205f7..00000000000 --- a/gems/gitlab-cng/Gemfile +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -source "https://rubygems.org" - -gemspec diff --git a/lib/api/helpers/packages_helpers.rb b/lib/api/helpers/packages_helpers.rb index 632aaa1c528..7f59fc8869c 100644 --- a/lib/api/helpers/packages_helpers.rb +++ b/lib/api/helpers/packages_helpers.rb @@ -99,7 +99,13 @@ module API strong_memoize_attr :user_project_with_read_package def track_package_event(action, scope, **args) - service = ::Packages::CreateEventService.new(nil, current_user, event_name: action, scope: scope) + service = ::Packages::CreateEventService.new( + args[:project], + current_user, + namespace: args[:namespace], + event_name: action, + scope: scope + ) service.execute category = args.delete(:category) || self.options[:for].name @@ -115,9 +121,19 @@ module API ) if action.to_s == 'push_package' && service.originator_type == :deploy_token - track_snowplow_event("push_package_by_deploy_token", category, args) + track_snowplow_event( + 'push_package_by_deploy_token', + 'package_pushed_using_deploy_token', + category, + args + ) elsif action.to_s == 'pull_package' && service.originator_type == :guest - track_snowplow_event("pull_package_by_guest", category, args) + track_snowplow_event( + 'pull_package_by_guest', + 'package_pulled_by_guest', + category, + args + ) end end @@ -132,17 +148,17 @@ module API private - def track_snowplow_event(action_name, category, args) + def track_snowplow_event(action_name, snowplow_event_name, category, args) event_name = "i_package_#{action_name}" key_path = "counts.package_events_i_package_#{action_name}" - service_ping_context = Gitlab::Usage::MetricDefinition.context_for(key_path).to_context + context = Gitlab::Tracking::ServicePingContext.new(data_source: :redis, event: snowplow_event_name).to_context Gitlab::Tracking.event( category, action_name, property: event_name, label: key_path, - context: [service_ping_context], + context: [context], **args ) end diff --git a/lib/bulk_imports/error.rb b/lib/bulk_imports/error.rb index 57a55abafa6..35eb21788d4 100644 --- a/lib/bulk_imports/error.rb +++ b/lib/bulk_imports/error.rb @@ -31,8 +31,18 @@ module BulkImports path: full_path)) end + def self.source_full_path_validation_failure(full_path) + self.new(format(s_("BulkImport|Import failed. '%{path}' not found."), + path: full_path)) + end + + def self.not_authorized(full_path) + self.new(format(s_("BulkImport|Import failed. You don't have permission to export '%{path}'."), + path: full_path)) + end + def self.setting_not_enabled - self.new(s_("BulkImport|Group import disabled on source or destination instance. " \ + self.new(s_("BulkImport|Migration by direct transfer disabled on source or destination instance. " \ "Ask an administrator to enable it on both instances and try again.")) end end diff --git a/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml index 6320943e838..8a6219646ef 100644 --- a/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/SAST.gitlab-ci.yml @@ -155,6 +155,8 @@ semgrep-sast: - '**/*.c++' - '**/*.cp' - '**/*.cxx' + - '**/*.h' + - '**/*.hpp' - '**/*.go' - '**/*.java' - '**/*.html' diff --git a/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml index 99834b5bc56..60fec1423db 100644 --- a/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Jobs/SAST.latest.gitlab-ci.yml @@ -166,6 +166,8 @@ semgrep-sast: - '**/*.c++' - '**/*.cp' - '**/*.cxx' + - '**/*.h' + - '**/*.hpp' - '**/*.go' - '**/*.java' - '**/*.html' @@ -192,6 +194,8 @@ semgrep-sast: - '**/*.c++' - '**/*.cp' - '**/*.cxx' + - '**/*.h' + - '**/*.hpp' - '**/*.go' - '**/*.java' - '**/*.html' diff --git a/lib/gitlab/usage_data_counters/counter_events/package_events.yml b/lib/gitlab/usage_data_counters/counter_events/package_events.yml index 129bf77c7f0..eef4e299862 100644 --- a/lib/gitlab/usage_data_counters/counter_events/package_events.yml +++ b/lib/gitlab/usage_data_counters/counter_events/package_events.yml @@ -1,40 +1,27 @@ --- - i_package_composer_delete_package -- i_package_composer_pull_package - i_package_composer_push_package - i_package_conan_delete_package -- i_package_conan_pull_package - i_package_conan_push_package - i_package_debian_delete_package -- i_package_debian_pull_package - i_package_debian_push_package - i_package_delete_package - i_package_delete_package_by_deploy_token - i_package_delete_package_by_guest - i_package_delete_package_by_user - i_package_generic_delete_package -- i_package_generic_pull_package - i_package_generic_push_package - i_package_golang_delete_package -- i_package_golang_pull_package - i_package_golang_push_package -- i_package_helm_pull_package - i_package_helm_push_package - i_package_maven_delete_package -- i_package_maven_pull_package - i_package_maven_push_package - i_package_npm_delete_package -- i_package_npm_pull_package - i_package_npm_push_package - i_package_nuget_delete_package -- i_package_nuget_pull_package - i_package_nuget_push_package - i_package_nuget_pull_symbol_package - i_package_nuget_push_symbol_package -- i_package_pull_package -- i_package_pull_package_by_deploy_token -- i_package_pull_package_by_guest -- i_package_pull_package_by_user - i_package_pull_symbol_package - i_package_pull_symbol_package_by_deploy_token - i_package_pull_symbol_package_by_guest @@ -48,13 +35,9 @@ - i_package_push_symbol_package_by_guest - i_package_push_symbol_package_by_user - i_package_pypi_delete_package -- i_package_pypi_pull_package - i_package_pypi_push_package - i_package_rubygems_delete_package -- i_package_rubygems_pull_package - i_package_rubygems_push_package - i_package_terraform_module_delete_package -- i_package_terraform_module_pull_package - i_package_terraform_module_push_package - i_package_rpm_push_package -- i_package_rpm_pull_package diff --git a/lib/gitlab/usage_data_counters/hll_redis_legacy_events.yml b/lib/gitlab/usage_data_counters/hll_redis_legacy_events.yml index 2b4cfbef343..b719162e118 100644 --- a/lib/gitlab/usage_data_counters/hll_redis_legacy_events.yml +++ b/lib/gitlab/usage_data_counters/hll_redis_legacy_events.yml @@ -524,8 +524,6 @@ - p_ci_templates_workflows_branch_pipelines - p_ci_templates_workflows_mergerequest_pipelines - p_terraform_state_api_unique_users -- package_pulled_by_guest -- package_pushed_using_deploy_token - project_action - project_created_analytics_dashboard - project_initialized_product_analytics diff --git a/lib/gitlab/usage_data_counters/total_counter_redis_key_overrides.yml b/lib/gitlab/usage_data_counters/total_counter_redis_key_overrides.yml index d2f6e46f8ab..d851382c619 100644 --- a/lib/gitlab/usage_data_counters/total_counter_redis_key_overrides.yml +++ b/lib/gitlab/usage_data_counters/total_counter_redis_key_overrides.yml @@ -8,35 +8,52 @@ # ... # '{event_counters}_analytics_dashboard_viewed': USAGE_PRODUCT_ANALYTICS_VIEW_DASHBOARD +'{event_counters}_click_external_link_license_compliance': USAGE_USERS_CLICKING_LICENSE_TESTING_VISITING_EXTERNAL_WEBSITE +'{event_counters}_click_full_report_license_compliance': USAGE_USERS_VISITING_TESTING_LICENSE_COMPLIANCE_FULL_REPORT +'{event_counters}_create_commit_from_web_ide': WEB_IDE_COMMITS_COUNT '{event_counters}_create_commit_note': USAGE_NOTE_CREATE_COMMIT +'{event_counters}_create_design_management_design': USAGE_DESIGN_MANAGEMENT_DESIGNS_CREATE +'{event_counters}_create_merge_request': USAGE_MERGE_REQUEST_CREATE +'{event_counters}_create_merge_request_from_web_ide': WEB_IDE_MERGE_REQUESTS_COUNT +'{event_counters}_create_merge_request_note': USAGE_NOTE_CREATE_MERGEREQUEST '{event_counters}_create_snippet': USAGE_SNIPPET_CREATE '{event_counters}_create_snippet_note': USAGE_NOTE_CREATE_SNIPPET +'{event_counters}_create_wiki_page': USAGE_WIKI_PAGES_CREATE +'{event_counters}_delete_design_management_design': USAGE_DESIGN_MANAGEMENT_DESIGNS_DELETE +'{event_counters}_delete_wiki_page': USAGE_WIKI_PAGES_DELETE '{event_counters}_licenses_list_viewed': USAGE_LICENSES_LIST_VIEWS -'{event_counters}_update_snippet': USAGE_SNIPPET_UPDATE -'{event_counters}_usage_data_download_payload_clicked': USAGE_SERVICE_USAGE_DATA_DOWNLOAD_PAYLOAD_CLICK -'{event_counters}_value_streams_dashboard_viewed': USAGE_VALUE_STREAMS_DASHBOARD_VIEWS -'{event_counters}_view_cycle_analytics': USAGE_CYCLE_ANALYTICS_VIEWS -'{event_counters}_view_productivity_analytics': USAGE_PRODUCTIVITY_ANALYTICS_VIEWS -'{event_counters}_web_ide_viewed': WEB_IDE_VIEWS_COUNT +'{event_counters}_perform_navbar_search': NAVBAR_SEARCHES_COUNT +'{event_counters}_perform_search': ALL_SEARCHES_COUNT +'{event_counters}_pull_package_from_registry': USAGE_PACKAGE_EVENTS_I_PACKAGE_PULL_PACKAGE +'{event_counters}_pull_package_from_registry-filter:[label:composer]': USAGE_PACKAGE_EVENTS_I_PACKAGE_COMPOSER_PULL_PACKAGE +'{event_counters}_pull_package_from_registry-filter:[label:conan]': USAGE_PACKAGE_EVENTS_I_PACKAGE_CONAN_PULL_PACKAGE +'{event_counters}_pull_package_from_registry-filter:[label:debian]': USAGE_PACKAGE_EVENTS_I_PACKAGE_DEBIAN_PULL_PACKAGE +'{event_counters}_pull_package_from_registry-filter:[label:generic]': USAGE_PACKAGE_EVENTS_I_PACKAGE_GENERIC_PULL_PACKAGE +'{event_counters}_pull_package_from_registry-filter:[label:golang]': USAGE_PACKAGE_EVENTS_I_PACKAGE_GOLANG_PULL_PACKAGE +'{event_counters}_pull_package_from_registry-filter:[label:helm]': USAGE_PACKAGE_EVENTS_I_PACKAGE_HELM_PULL_PACKAGE +'{event_counters}_pull_package_from_registry-filter:[label:maven]': USAGE_PACKAGE_EVENTS_I_PACKAGE_MAVEN_PULL_PACKAGE +'{event_counters}_pull_package_from_registry-filter:[label:npm]': USAGE_PACKAGE_EVENTS_I_PACKAGE_NPM_PULL_PACKAGE +'{event_counters}_pull_package_from_registry-filter:[label:nuget]': USAGE_PACKAGE_EVENTS_I_PACKAGE_NUGET_PULL_PACKAGE +'{event_counters}_pull_package_from_registry-filter:[label:pypi]': USAGE_PACKAGE_EVENTS_I_PACKAGE_PYPI_PULL_PACKAGE +'{event_counters}_pull_package_from_registry-filter:[label:rpm]': USAGE_PACKAGE_EVENTS_I_PACKAGE_RPM_PULL_PACKAGE +'{event_counters}_pull_package_from_registry-filter:[label:rubygems]': USAGE_PACKAGE_EVENTS_I_PACKAGE_RUBYGEMS_PULL_PACKAGE +'{event_counters}_pull_package_from_registry-filter:[label:terraform_module]': USAGE_PACKAGE_EVENTS_I_PACKAGE_TERRAFORM_MODULE_PULL_PACKAGE +'{event_counters}_pull_package_from_registry-filter:[property:deploy_token]': USAGE_PACKAGE_EVENTS_I_PACKAGE_PULL_PACKAGE_BY_DEPLOY_TOKEN +'{event_counters}_pull_package_from_registry-filter:[property:guest]': USAGE_PACKAGE_EVENTS_I_PACKAGE_PULL_PACKAGE_BY_GUEST +'{event_counters}_pull_package_from_registry-filter:[property:user]': USAGE_PACKAGE_EVENTS_I_PACKAGE_PULL_PACKAGE_BY_USER +'{event_counters}_source_code_pushed': USAGE_SOURCE_CODE_PUSHES '{event_counters}_status_page_incident_published': USAGE_STATUS_PAGE_INCIDENT_PUBLISHES '{event_counters}_status_page_incident_unpublished': USAGE_STATUS_PAGE_INCIDENT_UNPUBLISHES -'{event_counters}_create_wiki_page': USAGE_WIKI_PAGES_CREATE -'{event_counters}_delete_wiki_page': USAGE_WIKI_PAGES_DELETE -'{event_counters}_update_wiki_page': USAGE_WIKI_PAGES_UPDATE -'{event_counters}_view_wiki_page': USAGE_WIKI_PAGES_VIEW -'{event_counters}_click_full_report_license_compliance': USAGE_USERS_VISITING_TESTING_LICENSE_COMPLIANCE_FULL_REPORT -'{event_counters}_click_external_link_license_compliance': USAGE_USERS_CLICKING_LICENSE_TESTING_VISITING_EXTERNAL_WEBSITE -'{event_counters}_perform_search': ALL_SEARCHES_COUNT -'{event_counters}_perform_navbar_search': NAVBAR_SEARCHES_COUNT -'{event_counters}_source_code_pushed': USAGE_SOURCE_CODE_PUSHES -'{event_counters}_create_design_management_design': USAGE_DESIGN_MANAGEMENT_DESIGNS_CREATE -'{event_counters}_update_design_management_design': USAGE_DESIGN_MANAGEMENT_DESIGNS_UPDATE -'{event_counters}_delete_design_management_design': USAGE_DESIGN_MANAGEMENT_DESIGNS_DELETE -'{event_counters}_create_merge_request': USAGE_MERGE_REQUEST_CREATE -'{event_counters}_create_merge_request_note': USAGE_NOTE_CREATE_MERGEREQUEST -'{event_counters}_create_commit_from_web_ide': WEB_IDE_COMMITS_COUNT -'{event_counters}_create_merge_request_from_web_ide': WEB_IDE_MERGE_REQUESTS_COUNT '{event_counters}_trigger_audit_event-filter:[label:delete_epic]': USAGE_AUDIT_EVENTS_DELETE_EPIC '{event_counters}_trigger_audit_event-filter:[label:delete_issue]': USAGE_AUDIT_EVENTS_DELETE_ISSUE '{event_counters}_trigger_audit_event-filter:[label:delete_merge_request]': USAGE_AUDIT_EVENTS_DELETE_MERGE_REQUEST '{event_counters}_trigger_audit_event-filter:[label:delete_work_item]': USAGE_AUDIT_EVENTS_DELETE_WORK_ITEM +'{event_counters}_update_design_management_design': USAGE_DESIGN_MANAGEMENT_DESIGNS_UPDATE +'{event_counters}_update_snippet': USAGE_SNIPPET_UPDATE +'{event_counters}_update_wiki_page': USAGE_WIKI_PAGES_UPDATE +'{event_counters}_usage_data_download_payload_clicked': USAGE_SERVICE_USAGE_DATA_DOWNLOAD_PAYLOAD_CLICK +'{event_counters}_value_streams_dashboard_viewed': USAGE_VALUE_STREAMS_DASHBOARD_VIEWS +'{event_counters}_view_cycle_analytics': USAGE_CYCLE_ANALYTICS_VIEWS +'{event_counters}_view_productivity_analytics': USAGE_PRODUCTIVITY_ANALYTICS_VIEWS +'{event_counters}_view_wiki_page': USAGE_WIKI_PAGES_VIEW +'{event_counters}_web_ide_viewed': WEB_IDE_VIEWS_COUNT diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 08bd73ae78c..651d5b8d314 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -9783,21 +9783,24 @@ msgstr "" msgid "BulkImport|Following data will not be migrated: %{bullets} Contact system administrator of %{host} to upgrade GitLab if you need this data in your migration" msgstr "" -msgid "BulkImport|Group import disabled on source or destination instance. Ask an administrator to enable it on both instances and try again." -msgstr "" - msgid "BulkImport|Import completed" msgstr "" msgid "BulkImport|Import failed. '%{path}' already exists. Change the destination and try again." msgstr "" +msgid "BulkImport|Import failed. '%{path}' not found." +msgstr "" + msgid "BulkImport|Import failed. Destination '%{destination}' is invalid, or you don't have permission." msgstr "" msgid "BulkImport|Import failed. Destination URL %{url}" msgstr "" +msgid "BulkImport|Import failed. You don't have permission to export '%{path}'." +msgstr "" + msgid "BulkImport|Import failed: Destination cannot be a subgroup of the source group. Change the destination and try again." msgstr "" @@ -9837,6 +9840,9 @@ msgstr "" msgid "BulkImport|Maximum download file size when importing from source GitLab instances by direct transfer." msgstr "" +msgid "BulkImport|Migration by direct transfer disabled on source or destination instance. Ask an administrator to enable it on both instances and try again." +msgstr "" + msgid "BulkImport|Migration details" msgstr "" @@ -57740,6 +57746,9 @@ msgstr "" msgid "UserMapping|Source: %{source_hostname}" msgstr "" +msgid "UserMapping|There was a problem fetching placeholder users." +msgstr "" + msgid "UserProfile|%{count} %{file}" msgstr "" diff --git a/qa/Dockerfile b/qa/Dockerfile index 6600db17833..1db95ad1f33 100644 --- a/qa/Dockerfile +++ b/qa/Dockerfile @@ -41,10 +41,11 @@ WORKDIR /home/gitlab/qa ## # Install qa dependencies or fetch from cache if unchanged # -COPY ./qa/Gemfile* /home/gitlab/qa/ -COPY ./vendor/gems/ /home/gitlab/vendor/gems/ -COPY ./gems/ /home/gitlab/gems/ -RUN bundle config set --local without development \ +COPY qa/Gemfile* /home/gitlab/qa/ +COPY vendor/gems/ /home/gitlab/vendor/gems/ +COPY gems/gitlab-utils /home/gitlab/gems/gitlab-utils +COPY qa/gems /home/gitlab/qa/gems +RUN ls -la && bundle config set --local without development \ && bundle install --retry=3 COPY ./config/initializers/0_inject_enterprise_edition_module.rb /home/gitlab/config/initializers/ @@ -60,7 +61,7 @@ COPY ./qa /home/gitlab/qa ENTRYPOINT ["bin/test"] # Add ee files when passing the parameter: `--build-arg QA_BUILD_TARGET=ee` -FROM foss as ee +FROM foss AS ee # Copy VERSION to ensure the COPY succeeds to copy at least one file since ee/app/models/license.rb isn't present in FOSS # The [b] part makes ./ee/app/models/license.r[b] a pattern that is allowed to return no files (which is the case in FOSS) ONBUILD COPY VERSION ./ee/app/models/license.r[b] /home/gitlab/ee/app/models/ @@ -74,7 +75,7 @@ ONBUILD COPY ./jh/config/feature_flags /home/gitlab/jh/config/feature_flags # Add solargraph gem for devcontainer # Solargraph is only present in parent Gemfile so we just install it manually -FROM ee as dev +FROM ee AS dev RUN gem install solargraph --force FROM $QA_BUILD_TARGET diff --git a/qa/Gemfile b/qa/Gemfile index ecd1f4fabfd..c4b1b3460f1 100644 --- a/qa/Gemfile +++ b/qa/Gemfile @@ -34,6 +34,7 @@ gem "warning", "~> 1.4" gem 'chemlab', '~> 0.11', '>= 0.11.1' gem 'chemlab-library-www-gitlab-com', '~> 0.1', '>= 0.1.1' +gem 'chemlab-library-gitlab', path: 'gems/chemlab-gitlab' # dependencies for jenkins client gem 'nokogiri', '~> 1.16', '>= 1.16.6' @@ -48,6 +49,6 @@ group :development do end group :ci do - gem 'gitlab-cng', path: '../gems/gitlab-cng' + gem 'gitlab-cng', path: 'gems/gitlab-cng' gem 'junit_merge', '~> 0.1.2' end diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock index ab9893f471e..f85ec84a866 100644 --- a/qa/Gemfile.lock +++ b/qa/Gemfile.lock @@ -1,5 +1,21 @@ PATH - remote: ../gems/gitlab-cng + remote: ../gems/gitlab-utils + specs: + gitlab-utils (0.1.0) + actionview (>= 6.1.7.2) + activesupport (>= 6.1.7.2) + addressable (~> 2.8) + rake (~> 13.0) + +PATH + remote: gems/chemlab-gitlab + specs: + chemlab-library-gitlab (0.3.0) + chemlab (~> 0.9) + zeitwerk (~> 2.4) + +PATH + remote: gems/gitlab-cng specs: gitlab-cng (0.0.1) activesupport (>= 7) @@ -10,15 +26,6 @@ PATH tty-spinner (~> 0.9.3) tty-which (~> 0.5.0) -PATH - remote: ../gems/gitlab-utils - specs: - gitlab-utils (0.1.0) - actionview (>= 6.1.7.2) - activesupport (>= 6.1.7.2) - addressable (~> 2.8) - rake (~> 13.0) - GEM remote: https://rubygems.org/ specs: @@ -384,6 +391,7 @@ DEPENDENCIES capybara (~> 3.40.0) capybara-screenshot (~> 1.0.26) chemlab (~> 0.11, >= 0.11.1) + chemlab-library-gitlab! chemlab-library-www-gitlab-com (~> 0.1, >= 0.1.1) deprecation_toolkit (~> 2.2.0) factory_bot (~> 6.3.0) diff --git a/qa/chemlab-library-gitlab.gemspec b/qa/gems/chemlab-gitlab/chemlab-library-gitlab.gemspec similarity index 100% rename from qa/chemlab-library-gitlab.gemspec rename to qa/gems/chemlab-gitlab/chemlab-library-gitlab.gemspec diff --git a/qa/lib/gitlab.rb b/qa/gems/chemlab-gitlab/lib/gitlab.rb similarity index 100% rename from qa/lib/gitlab.rb rename to qa/gems/chemlab-gitlab/lib/gitlab.rb diff --git a/qa/lib/gitlab/page/admin/dashboard.rb b/qa/gems/chemlab-gitlab/lib/gitlab/page/admin/dashboard.rb similarity index 100% rename from qa/lib/gitlab/page/admin/dashboard.rb rename to qa/gems/chemlab-gitlab/lib/gitlab/page/admin/dashboard.rb diff --git a/qa/lib/gitlab/page/admin/dashboard.stub.rb b/qa/gems/chemlab-gitlab/lib/gitlab/page/admin/dashboard.stub.rb similarity index 100% rename from qa/lib/gitlab/page/admin/dashboard.stub.rb rename to qa/gems/chemlab-gitlab/lib/gitlab/page/admin/dashboard.stub.rb diff --git a/qa/lib/gitlab/page/admin/subscription.rb b/qa/gems/chemlab-gitlab/lib/gitlab/page/admin/subscription.rb similarity index 100% rename from qa/lib/gitlab/page/admin/subscription.rb rename to qa/gems/chemlab-gitlab/lib/gitlab/page/admin/subscription.rb diff --git a/qa/lib/gitlab/page/admin/subscription.stub.rb b/qa/gems/chemlab-gitlab/lib/gitlab/page/admin/subscription.stub.rb similarity index 100% rename from qa/lib/gitlab/page/admin/subscription.stub.rb rename to qa/gems/chemlab-gitlab/lib/gitlab/page/admin/subscription.stub.rb diff --git a/qa/lib/gitlab/page/group/settings/billing.rb b/qa/gems/chemlab-gitlab/lib/gitlab/page/group/settings/billing.rb similarity index 100% rename from qa/lib/gitlab/page/group/settings/billing.rb rename to qa/gems/chemlab-gitlab/lib/gitlab/page/group/settings/billing.rb diff --git a/qa/lib/gitlab/page/group/settings/billing.stub.rb b/qa/gems/chemlab-gitlab/lib/gitlab/page/group/settings/billing.stub.rb similarity index 100% rename from qa/lib/gitlab/page/group/settings/billing.stub.rb rename to qa/gems/chemlab-gitlab/lib/gitlab/page/group/settings/billing.stub.rb diff --git a/qa/lib/gitlab/page/group/settings/usage_quotas.rb b/qa/gems/chemlab-gitlab/lib/gitlab/page/group/settings/usage_quotas.rb similarity index 100% rename from qa/lib/gitlab/page/group/settings/usage_quotas.rb rename to qa/gems/chemlab-gitlab/lib/gitlab/page/group/settings/usage_quotas.rb diff --git a/qa/lib/gitlab/page/group/settings/usage_quotas.stub.rb b/qa/gems/chemlab-gitlab/lib/gitlab/page/group/settings/usage_quotas.stub.rb similarity index 100% rename from qa/lib/gitlab/page/group/settings/usage_quotas.stub.rb rename to qa/gems/chemlab-gitlab/lib/gitlab/page/group/settings/usage_quotas.stub.rb diff --git a/qa/lib/gitlab/page/subscriptions/new.rb b/qa/gems/chemlab-gitlab/lib/gitlab/page/subscriptions/new.rb similarity index 100% rename from qa/lib/gitlab/page/subscriptions/new.rb rename to qa/gems/chemlab-gitlab/lib/gitlab/page/subscriptions/new.rb diff --git a/qa/lib/gitlab/page/subscriptions/new.stub.rb b/qa/gems/chemlab-gitlab/lib/gitlab/page/subscriptions/new.stub.rb similarity index 100% rename from qa/lib/gitlab/page/subscriptions/new.stub.rb rename to qa/gems/chemlab-gitlab/lib/gitlab/page/subscriptions/new.stub.rb diff --git a/gems/gitlab-cng/.gitignore b/qa/gems/gitlab-cng/.gitignore similarity index 100% rename from gems/gitlab-cng/.gitignore rename to qa/gems/gitlab-cng/.gitignore diff --git a/gems/gitlab-cng/.rspec b/qa/gems/gitlab-cng/.rspec similarity index 100% rename from gems/gitlab-cng/.rspec rename to qa/gems/gitlab-cng/.rspec diff --git a/qa/gems/gitlab-cng/.rubocop.yml b/qa/gems/gitlab-cng/.rubocop.yml new file mode 100644 index 00000000000..4eaa79976d7 --- /dev/null +++ b/qa/gems/gitlab-cng/.rubocop.yml @@ -0,0 +1,27 @@ +# We need to ignore exclusions defined in parent RuboCop configuration +# (AllCops/Exclude: 'gems/**/*') if RuboCop cop is run within `gems/...`. +# See https://gitlab.com/gitlab-org/gitlab/-/issues/417281 +<% + relative_path = Dir.pwd.delete_prefix(File.expand_path('../../..')) + RuboCop::ConfigLoader.ignore_parent_exclusion = relative_path.start_with?('/qa/gems/') +%> +--- +inherit_gem: + gitlab-styles: + - rubocop-default.yml + +AllCops: + TargetRubyVersion: 3.0 + SuggestExtensions: false + NewCops: disable + +Rails: + Enabled: false + +CodeReuse/ActiveRecord: + Exclude: + - "**/*_spec.rb" + +RSpec/MultipleMemoizedHelpers: + Max: 25 + AllowSubject: true diff --git a/gems/gitlab-cng/.simplecov b/qa/gems/gitlab-cng/.simplecov similarity index 100% rename from gems/gitlab-cng/.simplecov rename to qa/gems/gitlab-cng/.simplecov diff --git a/qa/gems/gitlab-cng/Gemfile b/qa/gems/gitlab-cng/Gemfile new file mode 100644 index 00000000000..1645b0a66de --- /dev/null +++ b/qa/gems/gitlab-cng/Gemfile @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gemspec + +group :test do + gem "climate_control", "~> 1.2" + gem "gitlab-styles", "~> 11.0" + gem "pry", "~> 0.14.2" + gem "rspec", "~> 3.0" + gem "simplecov", "~> 0.22.0" +end diff --git a/gems/gitlab-cng/Gemfile.lock b/qa/gems/gitlab-cng/Gemfile.lock similarity index 100% rename from gems/gitlab-cng/Gemfile.lock rename to qa/gems/gitlab-cng/Gemfile.lock diff --git a/gems/gitlab-cng/LICENSE b/qa/gems/gitlab-cng/LICENSE similarity index 100% rename from gems/gitlab-cng/LICENSE rename to qa/gems/gitlab-cng/LICENSE diff --git a/gems/gitlab-cng/README.md b/qa/gems/gitlab-cng/README.md similarity index 100% rename from gems/gitlab-cng/README.md rename to qa/gems/gitlab-cng/README.md diff --git a/gems/gitlab-cng/bin/console b/qa/gems/gitlab-cng/bin/console similarity index 100% rename from gems/gitlab-cng/bin/console rename to qa/gems/gitlab-cng/bin/console diff --git a/gems/gitlab-cng/exe/cng b/qa/gems/gitlab-cng/exe/cng similarity index 100% rename from gems/gitlab-cng/exe/cng rename to qa/gems/gitlab-cng/exe/cng diff --git a/gems/gitlab-cng/gitlab-cng.gemspec b/qa/gems/gitlab-cng/gitlab-cng.gemspec similarity index 76% rename from gems/gitlab-cng/gitlab-cng.gemspec rename to qa/gems/gitlab-cng/gitlab-cng.gemspec index 5f0e0898f66..abc29435de0 100644 --- a/gems/gitlab-cng/gitlab-cng.gemspec +++ b/qa/gems/gitlab-cng/gitlab-cng.gemspec @@ -26,10 +26,4 @@ Gem::Specification.new do |spec| spec.add_dependency "tty-prompt", "~> 0.23.1" spec.add_dependency "tty-spinner", "~> 0.9.3" spec.add_dependency "tty-which", "~> 0.5.0" - - spec.add_development_dependency "climate_control", "~> 1.2" - spec.add_development_dependency "gitlab-styles", "~> 11.0" - spec.add_development_dependency "pry", "~> 0.14.2" - spec.add_development_dependency "rspec", "~> 3.0" - spec.add_development_dependency "simplecov", "~> 0.22.0" end diff --git a/gems/gitlab-cng/lib/gitlab/cng.rb b/qa/gems/gitlab-cng/lib/gitlab/cng.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/cli.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/cli.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/cli.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/cli.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/commands/_command.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/commands/_command.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/commands/_command.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/commands/_command.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/commands/create.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/commands/create.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/commands/create.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/commands/create.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/commands/destroy.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/commands/destroy.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/commands/destroy.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/commands/destroy.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/commands/doctor.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/commands/doctor.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/commands/doctor.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/commands/doctor.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/commands/log.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/commands/log.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/commands/log.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/commands/log.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/commands/subcommands/deployment.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/commands/subcommands/deployment.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/commands/subcommands/deployment.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/commands/subcommands/deployment.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/commands/version.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/commands/version.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/commands/version.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/commands/version.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/_base.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/_base.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/_base.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/_base.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/cleanup/_base.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/cleanup/_base.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/cleanup/_base.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/cleanup/_base.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/cleanup/kind.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/cleanup/kind.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/cleanup/kind.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/cleanup/kind.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/kind.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/kind.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/kind.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/configurations/kind.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/default_values.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/default_values.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/deployment/default_values.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/default_values.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/installation.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/installation.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/deployment/installation.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/deployment/installation.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/helm/client.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/helm/client.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/helm/client.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/helm/client.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/ci.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/ci.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/helpers/ci.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/ci.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/file.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/file.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/helpers/file.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/file.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/output.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/output.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/helpers/output.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/output.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/shell.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/shell.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/helpers/shell.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/shell.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/spinner.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/spinner.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/helpers/spinner.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/spinner.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/thor.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/thor.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/helpers/thor.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/helpers/thor.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/kind/cluster.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/kind/cluster.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/kind/cluster.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/kind/cluster.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/kubectl/client.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/kubectl/client.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/kubectl/client.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/kubectl/client.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/kubectl/resources/_base.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/kubectl/resources/_base.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/kubectl/resources/_base.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/kubectl/resources/_base.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/kubectl/resources/configmap.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/kubectl/resources/configmap.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/kubectl/resources/configmap.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/kubectl/resources/configmap.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/lib/kubectl/resources/secret.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/lib/kubectl/resources/secret.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/lib/kubectl/resources/secret.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/lib/kubectl/resources/secret.rb diff --git a/gems/gitlab-cng/lib/gitlab/cng/version.rb b/qa/gems/gitlab-cng/lib/gitlab/cng/version.rb similarity index 100% rename from gems/gitlab-cng/lib/gitlab/cng/version.rb rename to qa/gems/gitlab-cng/lib/gitlab/cng/version.rb diff --git a/gems/gitlab-cng/spec/command_helper.rb b/qa/gems/gitlab-cng/spec/command_helper.rb similarity index 100% rename from gems/gitlab-cng/spec/command_helper.rb rename to qa/gems/gitlab-cng/spec/command_helper.rb diff --git a/gems/gitlab-cng/spec/integration/cng_spec.rb b/qa/gems/gitlab-cng/spec/integration/cng_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/integration/cng_spec.rb rename to qa/gems/gitlab-cng/spec/integration/cng_spec.rb diff --git a/gems/gitlab-cng/spec/integration/gitlab/cng/cli_spec.rb b/qa/gems/gitlab-cng/spec/integration/gitlab/cng/cli_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/integration/gitlab/cng/cli_spec.rb rename to qa/gems/gitlab-cng/spec/integration/gitlab/cng/cli_spec.rb diff --git a/gems/gitlab-cng/spec/spec_helper.rb b/qa/gems/gitlab-cng/spec/spec_helper.rb similarity index 100% rename from gems/gitlab-cng/spec/spec_helper.rb rename to qa/gems/gitlab-cng/spec/spec_helper.rb diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/commands/create_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/create_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/unit/gitlab/cng/commands/create_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/create_spec.rb diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/commands/destroy_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/destroy_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/unit/gitlab/cng/commands/destroy_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/destroy_spec.rb diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/commands/doctor_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/doctor_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/unit/gitlab/cng/commands/doctor_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/doctor_spec.rb diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/commands/log_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/log_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/unit/gitlab/cng/commands/log_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/log_spec.rb diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/commands/subcommands/deployment_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/subcommands/deployment_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/unit/gitlab/cng/commands/subcommands/deployment_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/subcommands/deployment_spec.rb diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/commands/version_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/version_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/unit/gitlab/cng/commands/version_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/commands/version_spec.rb diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/base_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/base_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/base_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/base_spec.rb diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/cleanup/kind_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/cleanup/kind_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/cleanup/kind_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/cleanup/kind_spec.rb diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/kind_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/kind_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/kind_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/configurations/kind_spec.rb diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/default_values_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/default_values_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/unit/gitlab/cng/deployment/default_values_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/default_values_spec.rb diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/installation_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/installation_spec.rb similarity index 96% rename from gems/gitlab-cng/spec/unit/gitlab/cng/deployment/installation_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/installation_spec.rb index 40ade8969fb..03fd1a2c8b2 100644 --- a/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/installation_spec.rb +++ b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/deployment/installation_spec.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -# rubocop:disable RSpec/MultipleMemoizedHelpers -- allows to reuse many of the mock definitions, with less helpers a lot more value duplication would occur RSpec.describe Gitlab::Cng::Deployment::Installation, :aggregate_failures do describe "with setup" do subject(:installation) do @@ -171,4 +170,3 @@ RSpec.describe Gitlab::Cng::Deployment::Installation, :aggregate_failures do end end end -# rubocop:enable RSpec/MultipleMemoizedHelpers diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/helm/client_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/helm/client_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/unit/gitlab/cng/helm/client_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/helm/client_spec.rb diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/helpers/output_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/helpers/output_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/unit/gitlab/cng/helpers/output_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/helpers/output_spec.rb diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/helpers/shell_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/helpers/shell_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/unit/gitlab/cng/helpers/shell_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/helpers/shell_spec.rb diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/helpers/spinner_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/helpers/spinner_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/unit/gitlab/cng/helpers/spinner_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/helpers/spinner_spec.rb diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/kind/cluster_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/kind/cluster_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/unit/gitlab/cng/kind/cluster_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/kind/cluster_spec.rb diff --git a/gems/gitlab-cng/spec/unit/gitlab/cng/kubectl/client_spec.rb b/qa/gems/gitlab-cng/spec/unit/gitlab/cng/kubectl/client_spec.rb similarity index 100% rename from gems/gitlab-cng/spec/unit/gitlab/cng/kubectl/client_spec.rb rename to qa/gems/gitlab-cng/spec/unit/gitlab/cng/kubectl/client_spec.rb diff --git a/qa/qa.rb b/qa/qa.rb index 66f800f6caa..628acbbe43b 100644 --- a/qa/qa.rb +++ b/qa/qa.rb @@ -7,8 +7,6 @@ require 'gitlab/utils/all' require_relative '../lib/gitlab_edition' require_relative '../config/initializers/0_inject_enterprise_edition_module' -require_relative 'lib/gitlab' - require_relative '../config/bundler_setup' Bundler.require(:default) @@ -19,6 +17,7 @@ require 'active_support/core_ext/hash' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/module/delegation' require 'active_support/parameter_filter' +require 'gitlab' module QA root = "#{__dir__}/qa" diff --git a/spec/dot_gitlab_ci/rules_spec.rb b/spec/dot_gitlab_ci/rules_spec.rb index 977cbd0fc63..5363355d9aa 100644 --- a/spec/dot_gitlab_ci/rules_spec.rb +++ b/spec/dot_gitlab_ci/rules_spec.rb @@ -252,6 +252,7 @@ RSpec.describe '.gitlab/ci/rules.gitlab-ci.yml', feature_category: :tooling do Dir.glob('node_modules/**/*', File::FNM_DOTMATCH) + Dir.glob('patches/*') + Dir.glob('public/assets/**/.*') + + Dir.glob('qa/{,**/}.*') + Dir.glob('qa/.{,**/}*') + Dir.glob('qa/**/.gitlab-ci.yml') + Dir.glob('shared/**/*') + diff --git a/spec/features/projects/commit/user_comments_on_commit_spec.rb b/spec/features/projects/commit/user_comments_on_commit_spec.rb index 37ae85a206f..530b6111c8c 100644 --- a/spec/features/projects/commit/user_comments_on_commit_spec.rb +++ b/spec/features/projects/commit/user_comments_on_commit_spec.rb @@ -38,10 +38,7 @@ RSpec.describe "User comments on commit", :js, feature_category: :source_code_ma expect(page).to have_field("note[note]", with: "#{comment_text} #{emoji_code}") - # Submit comment from the `Preview` tab to get rid of a separate `it` block - # which would specially tests if everything gets cleared from the note form. - click_button("Preview") - click_button("Comment") + send_keys([:meta, :enter]) end wait_for_requests diff --git a/spec/features/projects/settings/service_desk_setting_spec.rb b/spec/features/projects/settings/service_desk_setting_spec.rb index 78bf8a8208b..abb86663976 100644 --- a/spec/features/projects/settings/service_desk_setting_spec.rb +++ b/spec/features/projects/settings/service_desk_setting_spec.rb @@ -106,6 +106,5 @@ RSpec.describe 'Service Desk Setting', :js, :clean_gitlab_redis_cache, feature_c visit edit_project_path(project) expect(page).to have_pushed_frontend_feature_flags(issueEmailParticipants: true) - expect(page).to have_pushed_frontend_feature_flags(serviceDeskTicketsConfidentiality: true) end end diff --git a/spec/frontend/members/components/placeholders/app_spec.js b/spec/frontend/members/components/placeholders/app_spec.js index 61a86c50094..065b31e6992 100644 --- a/spec/frontend/members/components/placeholders/app_spec.js +++ b/spec/frontend/members/components/placeholders/app_spec.js @@ -1,20 +1,153 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; import { shallowMount } from '@vue/test-utils'; import { GlTabs } from '@gitlab/ui'; +import { createAlert } from '~/alert'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; import PlaceholdersTabApp from '~/members/components/placeholders/app.vue'; +import PlaceholdersTable from '~/members/components/placeholders/placeholders_table.vue'; +import importSourceUsersQuery from '~/members/placeholders/graphql/queries/import_source_users.query.graphql'; +import { mockSourceUsersQueryResponse } from './mock_data'; + +Vue.use(VueApollo); +jest.mock('~/alert'); describe('PlaceholdersTabApp', () => { let wrapper; + let mockApollo; - const createComponent = () => { - wrapper = shallowMount(PlaceholdersTabApp, {}); + const mockGroup = { + path: 'imported-group', + name: 'Imported group', + }; + const sourceUsersQueryHandler = jest.fn().mockResolvedValue(mockSourceUsersQueryResponse()); + + const createComponent = ({ queryHandler = sourceUsersQueryHandler } = {}) => { + mockApollo = createMockApollo([[importSourceUsersQuery, queryHandler]]); + + wrapper = shallowMount(PlaceholdersTabApp, { + apolloProvider: mockApollo, + provide: { + group: mockGroup, + }, + }); }; const findTabs = () => wrapper.findComponent(GlTabs); + const findPlaceholdersTable = () => wrapper.findComponent(PlaceholdersTable); it('renders tabs', () => { createComponent(); expect(findTabs().exists()).toBe(true); }); + + describe('when sourceUsers query is loading', () => { + it('renders placeholders table as loading', () => { + createComponent(); + + expect(findPlaceholdersTable().props('isLoading')).toBe(true); + }); + }); + + describe('when sourceUsers query fails', () => { + beforeEach(async () => { + const sourceUsersFailedQueryHandler = jest.fn().mockRejectedValue(new Error('GraphQL error')); + + createComponent({ + queryHandler: sourceUsersFailedQueryHandler, + }); + await waitForPromises(); + }); + + it('creates an alert', () => { + expect(createAlert).toHaveBeenCalledWith({ + message: 'There was a problem fetching placeholder users.', + }); + }); + }); + + describe('when sourceUsers query succeeds', () => { + beforeEach(async () => { + createComponent(); + await waitForPromises(); + }); + + it('fetches sourceUsers', () => { + expect(sourceUsersQueryHandler).toHaveBeenCalledTimes(1); + expect(sourceUsersQueryHandler).toHaveBeenCalledWith({ + after: null, + before: null, + fullPath: mockGroup.path, + first: 20, + }); + }); + + it('renders placeholders table', () => { + const mockSourceUsers = mockSourceUsersQueryResponse().data.namespace.importSourceUsers; + + expect(findPlaceholdersTable().props()).toMatchObject({ + isLoading: false, + items: mockSourceUsers.nodes, + pageInfo: mockSourceUsers.pageInfo, + }); + }); + }); + + describe('when sourceUsers query succeeds and has pagination', () => { + const sourceUsersPaginatedQueryHandler = jest.fn(); + const mockPageInfo = { + endCursor: 'end834', + hasNextPage: true, + hasPreviousPage: true, + startCursor: 'start971', + }; + + beforeEach(async () => { + sourceUsersPaginatedQueryHandler + .mockResolvedValueOnce(mockSourceUsersQueryResponse({ pageInfo: mockPageInfo })) + .mockResolvedValueOnce(mockSourceUsersQueryResponse()); + + createComponent({ + queryHandler: sourceUsersPaginatedQueryHandler, + }); + await waitForPromises(); + }); + + describe('when "prev" event is emitted', () => { + beforeEach(() => { + findPlaceholdersTable().vm.$emit('prev'); + }); + + it('fetches sourceUsers with previous results', () => { + expect(sourceUsersPaginatedQueryHandler).toHaveBeenCalledTimes(2); + expect(sourceUsersPaginatedQueryHandler).toHaveBeenCalledWith( + expect.objectContaining({ + after: null, + before: mockPageInfo.startCursor, + last: 20, + }), + ); + }); + }); + + describe('when "next" event is emitted', () => { + beforeEach(() => { + findPlaceholdersTable().vm.$emit('next'); + }); + + it('fetches sourceUsers with next results', () => { + expect(sourceUsersPaginatedQueryHandler).toHaveBeenCalledTimes(2); + expect(sourceUsersPaginatedQueryHandler).toHaveBeenCalledWith( + expect.objectContaining({ + after: mockPageInfo.endCursor, + before: null, + first: 20, + }), + ); + }); + }); + }); }); diff --git a/spec/frontend/members/components/placeholders/mock_data.js b/spec/frontend/members/components/placeholders/mock_data.js index b672d50a1a9..e2be79f0901 100644 --- a/spec/frontend/members/components/placeholders/mock_data.js +++ b/spec/frontend/members/components/placeholders/mock_data.js @@ -1,99 +1,90 @@ -export const mockPlaceholderUsers = [ - { - avatar_url: - 'https://www.gravatar.com/avatar/258d8dc916db8cea2cafb6c3cd0cb0246efe061421dbd83ec3a350428cabda4f?s=80&d=identicon', - name: 'Placeholder 1', - username: 'placeholder-1', - source_hostname: 'https://gitlab.com', - source_username: '@old_root', - status: 'pending_assignment', - }, - { - avatar_url: - 'https://www.gravatar.com/avatar/258d8dc916db8cea2cafb6c3cd0cb0246efe061421dbd83ec3a350428cabda4f?s=80&d=identicon', - name: 'Placeholder 2', - username: 'placeholder-2', - source_hostname: 'https://gitlab.com', - source_username: '@old_root', - status: 'awaiting_approval', - reassignToUser: { - avatar_url: - 'https://www.gravatar.com/avatar/258d8dc916db8cea2cafb6c3cd0cb0246efe061421dbd83ec3a350428cabda4f?s=80&d=identicon', - name: 'Administrator', - username: '@root2', - }, - }, - { - avatar_url: - 'https://www.gravatar.com/avatar/258d8dc916db8cea2cafb6c3cd0cb0246efe061421dbd83ec3a350428cabda4f?s=80&d=identicon', - name: 'Placeholder 3', - username: 'placeholder-3', - source_hostname: 'https://gitlab.com', - source_username: '@old_root', - status: 'rejected', - }, - { - avatar_url: - 'https://www.gravatar.com/avatar/258d8dc916db8cea2cafb6c3cd0cb0246efe061421dbd83ec3a350428cabda4f?s=80&d=identicon', - name: 'Placeholder 4', - username: 'placeholder-4', - source_hostname: 'https://gitlab.com', - source_username: '@old_root', - status: 'reassignment_in_progress', - reassignToUser: { - avatar_url: - 'https://www.gravatar.com/avatar/258d8dc916db8cea2cafb6c3cd0cb0246efe061421dbd83ec3a350428cabda4f?s=80&d=identicon', - name: 'Administrator', - username: '@root4', - }, - }, - { - avatar_url: - 'https://www.gravatar.com/avatar/258d8dc916db8cea2cafb6c3cd0cb0246efe061421dbd83ec3a350428cabda4f?s=80&d=identicon', - name: 'Placeholder 5', - username: 'placeholder-5', - source_hostname: 'https://gitlab.com', - source_username: '@old_root', - status: 'failed', - reassignToUser: { - avatar_url: - 'https://www.gravatar.com/avatar/258d8dc916db8cea2cafb6c3cd0cb0246efe061421dbd83ec3a350428cabda4f?s=80&d=identicon', - name: 'Administrator', - username: '@root5', - }, - }, - { - avatar_url: - 'https://www.gravatar.com/avatar/258d8dc916db8cea2cafb6c3cd0cb0246efe061421dbd83ec3a350428cabda4f?s=80&d=identicon', - name: 'Placeholder 6', - username: 'placeholder-6', - source_hostname: 'https://gitlab.com', - source_username: '@old_root', - status: 'keep_as_placeholder', - reassignToUser: { - avatar_url: - 'https://www.gravatar.com/avatar/258d8dc916db8cea2cafb6c3cd0cb0246efe061421dbd83ec3a350428cabda4f?s=80&d=identicon', - name: 'Administrator', - username: '@root6', - }, - }, - { - avatar_url: - 'https://www.gravatar.com/avatar/258d8dc916db8cea2cafb6c3cd0cb0246efe061421dbd83ec3a350428cabda4f?s=80&d=identicon', - name: 'Placeholder 7', - username: 'placeholder-7', - source_hostname: 'https://gitlab.com', - source_username: '@old_root', - status: 'completed', - reassignToUser: { - avatar_url: - 'https://www.gravatar.com/avatar/258d8dc916db8cea2cafb6c3cd0cb0246efe061421dbd83ec3a350428cabda4f?s=80&d=identicon', - name: 'Administrator', - username: '@root7', - }, - }, +const createMockPlaceholderUser = (index) => { + return { + __typename: 'UserCore', + id: `gid://gitlab/User/382${index}`, + avatarUrl: '/avatar1', + name: `Placeholder ${index}`, + username: `placeholder_${index}`, + webUrl: '/', + webPath: '/', + }; +}; + +const createMockReassignUser = (index) => { + return { + __typename: 'UserCore', + id: `gid://gitlab/User/741${index}`, + avatarUrl: '/avatar2', + name: `Reassigned ${index}`, + username: `reassigned_${index}`, + webUrl: '/', + webPath: '/', + }; +}; + +const createMockSourceUser = (index, { status, reassignToUser = false } = {}) => { + return { + __typename: 'ImportSourceUser', + id: `gid://gitlab/Import::SourceUser/${index}`, + sourceHostname: 'https://gitlab.com', + sourceName: `Old User ${index}`, + sourceUsername: `old_user_${index}`, + status, + placeholderUser: createMockPlaceholderUser(index), + reassignToUser: reassignToUser ? createMockReassignUser(index) : null, + }; +}; + +export const mockSourceUsers = [ + createMockSourceUser(1, { + status: 'PENDING_ASSIGNMENT', + }), + createMockSourceUser(2, { + status: 'AWAITING_APPROVAL', + reassignToUser: true, + }), + createMockSourceUser(3, { + status: 'REJECTED', + }), + createMockSourceUser(4, { + status: 'REASSIGNMENT_IN_PROGRESS', + reassignToUser: true, + }), + createMockSourceUser(5, { + status: 'FAILED', + reassignToUser: true, + }), + createMockSourceUser(6, { + status: 'KEEP_AS_PLACEHOLDER', + reassignToUser: true, + }), + createMockSourceUser(7, { + status: 'COMPLETED', + reassignToUser: true, + }), ]; +export const mockSourceUsersQueryResponse = ({ pageInfo = {} } = {}) => ({ + data: { + namespace: { + __typename: 'Namespace', + id: 'gid://gitlab/Group/1', + importSourceUsers: { + __typename: 'ImportSourceUserConnection', + nodes: mockSourceUsers, + pageInfo: { + __typename: 'PageInfo', + hasNextPage: false, + hasPreviousPage: false, + startCursor: '', + endCursor: '', + ...pageInfo, + }, + }, + }, + }, +}); + export const mockUser1 = { __typename: 'UserCore', id: 'gid://gitlab/User/1', diff --git a/spec/frontend/members/components/placeholders/placeholder_actions_spec.js b/spec/frontend/members/components/placeholders/placeholder_actions_spec.js index 27dcb989928..66d15776df2 100644 --- a/spec/frontend/members/components/placeholders/placeholder_actions_spec.js +++ b/spec/frontend/members/components/placeholders/placeholder_actions_spec.js @@ -9,7 +9,7 @@ import waitForPromises from 'helpers/wait_for_promises'; import PlaceholderActions from '~/members/components/placeholders/placeholder_actions.vue'; import searchUsersQuery from '~/graphql_shared/queries/users_search_all_paginated.query.graphql'; import { - mockPlaceholderUsers, + mockSourceUsers, mockUser1, mockUser2, mockUsersQueryResponse, @@ -210,15 +210,15 @@ describe('PlaceholderActions', () => { }); }); - describe('when status is awaiting_approval', () => { - const mockPlaceholder = mockPlaceholderUsers[3]; + describe('when status is AWAITING_APPROVAL', () => { + const mockPlaceholder = mockSourceUsers[3]; beforeEach(() => { createComponent({ props: { placeholder: { ...mockPlaceholder, - status: 'awaiting_approval', + status: 'AWAITING_APPROVAL', }, }, }); @@ -256,15 +256,15 @@ describe('PlaceholderActions', () => { }); }); - describe('when status is reassignment_in_progress', () => { - const mockPlaceholder = mockPlaceholderUsers[3]; + describe('when status is REASSIGNMENT_IN_PROGRESS', () => { + const mockPlaceholder = mockSourceUsers[3]; beforeEach(() => { createComponent({ props: { placeholder: { ...mockPlaceholder, - status: 'reassignment_in_progress', + status: 'REASSIGNMENT_IN_PROGRESS', }, }, }); diff --git a/spec/frontend/members/components/placeholders/placeholders_table_spec.js b/spec/frontend/members/components/placeholders/placeholders_table_spec.js index d6df442cb0d..efa6ab56182 100644 --- a/spec/frontend/members/components/placeholders/placeholders_table_spec.js +++ b/spec/frontend/members/components/placeholders/placeholders_table_spec.js @@ -1,13 +1,13 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import { mount, shallowMount } from '@vue/test-utils'; -import { GlAvatarLabeled, GlBadge, GlTableLite } from '@gitlab/ui'; +import { GlAvatarLabeled, GlBadge, GlKeysetPagination, GlLoadingIcon, GlTable } from '@gitlab/ui'; import { createMockDirective, getBinding } from 'helpers/vue_mock_directive'; import createMockApollo from 'helpers/mock_apollo_helper'; import PlaceholdersTable from '~/members/components/placeholders/placeholders_table.vue'; import PlaceholderActions from '~/members/components/placeholders/placeholder_actions.vue'; -import { mockPlaceholderUsers } from './mock_data'; +import { mockSourceUsers } from './mock_data'; Vue.use(VueApollo); @@ -16,7 +16,8 @@ describe('PlaceholdersTable', () => { let mockApollo; const defaultProps = { - items: mockPlaceholderUsers, + isLoading: false, + items: mockSourceUsers, }; const createComponent = ({ mountFn = shallowMount, props = {} } = {}) => { @@ -34,7 +35,9 @@ describe('PlaceholdersTable', () => { }); }; - const findTable = () => wrapper.findComponent(GlTableLite); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findPagination = () => wrapper.findComponent(GlKeysetPagination); + const findTable = () => wrapper.findComponent(GlTable); const findTableRows = () => findTable().findAll('tbody > tr'); const findTableFields = () => findTable() @@ -45,7 +48,7 @@ describe('PlaceholdersTable', () => { createComponent({ mountFn: mount }); expect(findTable().exists()).toBe(true); - expect(findTableRows().length).toBe(mockPlaceholderUsers.length); + expect(findTableRows().length).toBe(mockSourceUsers.length); expect(findTableFields()).toEqual([ 'Placeholder user', 'Source', @@ -54,22 +57,32 @@ describe('PlaceholdersTable', () => { ]); }); - it('renders avatar', () => { + it('renders loading icon when table is loading', () => { + createComponent({ + props: { isLoading: true }, + }); + + expect(findLoadingIcon().exists()).toBe(true); + }); + + it('renders avatar for placeholder user', () => { createComponent({ mountFn: mount }); const avatar = findTableRows().at(0).findComponent(GlAvatarLabeled); + const { placeholderUser } = mockSourceUsers[0]; expect(avatar.props()).toMatchObject({ - label: mockPlaceholderUsers[0].name, - subLabel: mockPlaceholderUsers[0].username, + label: placeholderUser.name, + subLabel: `@${placeholderUser.username}`, }); - expect(avatar.attributes('src')).toBe(mockPlaceholderUsers[0].avatar_url); + expect(avatar.attributes('src')).toBe(placeholderUser.avatarUrl); }); it('renders source info', () => { createComponent({ mountFn: mount }); - expect(findTableRows().at(0).text()).toContain(mockPlaceholderUsers[0].source_hostname); + expect(findTableRows().at(0).text()).toContain(mockSourceUsers[0].sourceHostname); + expect(findTableRows().at(0).text()).toContain(mockSourceUsers[0].sourceUsername); }); it('renders status badge with tooltip', () => { @@ -89,18 +102,19 @@ describe('PlaceholdersTable', () => { const firstRow = findTableRows().at(0); const actions = firstRow.findComponent(PlaceholderActions); - expect(actions.props('placeholder')).toEqual(mockPlaceholderUsers[0]); + expect(actions.props('placeholder')).toEqual(mockSourceUsers[0]); }); - it('renders avatar of final user when item is reassigned', () => { + it('renders avatar for final user when item is reassigned', () => { createComponent({ mountFn: mount }); const reassignedItemRow = findTableRows().at(5); const actionsAvatar = reassignedItemRow.findAllComponents(GlAvatarLabeled).at(1); + const { reassignToUser } = mockSourceUsers[5]; expect(actionsAvatar.props()).toMatchObject({ - label: mockPlaceholderUsers[5].reassignToUser.name, - subLabel: mockPlaceholderUsers[5].reassignToUser.username, + label: reassignToUser.name, + subLabel: `@${reassignToUser.username}`, }); }); @@ -115,7 +129,7 @@ describe('PlaceholdersTable', () => { actions.vm.$emit('confirm', selectedUserId); - expect(wrapper.emitted('confirm')[0]).toEqual([mockPlaceholderUsers[2], selectedUserId]); + expect(wrapper.emitted('confirm')[0]).toEqual([mockSourceUsers[2], selectedUserId]); }); it('emits "cancel" event with item and selectedUserId', () => { @@ -123,7 +137,7 @@ describe('PlaceholdersTable', () => { actions.vm.$emit('cancel'); - expect(wrapper.emitted('cancel')[0]).toEqual([mockPlaceholderUsers[2]]); + expect(wrapper.emitted('cancel')[0]).toEqual([mockSourceUsers[2]]); }); }); @@ -146,4 +160,60 @@ describe('PlaceholdersTable', () => { ]); }); }); + + describe('pagination', () => { + describe.each` + hasNextPage | hasPreviousPage | expectPagination + ${false} | ${false} | ${false} + ${false} | ${true} | ${true} + ${true} | ${false} | ${true} + ${true} | ${true} | ${true} + `( + 'when hasNextPage=$hasNextPage and hasPreviousPage=$hasPreviousPage', + ({ hasNextPage, hasPreviousPage, expectPagination }) => { + beforeEach(() => { + createComponent({ + props: { + pageInfo: { + hasNextPage, + hasPreviousPage, + }, + }, + }); + }); + + it(`${expectPagination ? 'renders' : 'does not render'} pagination`, () => { + expect(findPagination().exists()).toBe(expectPagination); + }); + }, + ); + + it('emits "prev" event', () => { + createComponent({ + props: { + pageInfo: { + hasPreviousPage: true, + }, + }, + }); + + findPagination().vm.$emit('prev'); + + expect(wrapper.emitted('prev')[0]).toEqual([]); + }); + + it('emits "next" event', () => { + createComponent({ + props: { + pageInfo: { + hasNextPage: true, + }, + }, + }); + + findPagination().vm.$emit('next'); + + expect(wrapper.emitted('next')[0]).toEqual([]); + }); + }); }); diff --git a/spec/frontend/notes/components/comment_type_dropdown_spec.js b/spec/frontend/notes/components/comment_type_dropdown_spec.js index 9d5878a4ce8..e1cb1c9a8bc 100644 --- a/spec/frontend/notes/components/comment_type_dropdown_spec.js +++ b/spec/frontend/notes/components/comment_type_dropdown_spec.js @@ -28,6 +28,12 @@ describe('CommentTypeDropdown component', () => { ); }; + it('has correct button type for quick submit', () => { + mountComponent(); + + expect(findCommentButton().attributes('type')).toBe('submit'); + }); + it.each` isInternalNote | isReviewDropdown | buttonText ${false} | ${false} | ${COMMENT_FORM.comment} diff --git a/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js b/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js index ce031c04ba5..55724d42b04 100644 --- a/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js +++ b/spec/frontend/projects/settings_service_desk/components/service_desk_setting_spec.js @@ -39,7 +39,6 @@ describe('ServiceDeskSetting', () => { provide: { glFeatures: { issueEmailParticipants: true, - serviceDeskTicketsConfidentiality: true, }, ...provide, }, @@ -285,16 +284,6 @@ describe('ServiceDeskSetting', () => { }); }); }); - - describe('when feature flag service_desk_tickets_confidentiality is disabled', () => { - it('is not rendered', () => { - wrapper = createComponent({ - provide: { glFeatures: { serviceDeskTicketsConfidentiality: false } }, - }); - - expect(findAreTicketsConfidentialByDefaultCheckbox().exists()).toBe(false); - }); - }); }); describe('reopen issue on external participant note checkbox', () => { diff --git a/spec/helpers/packages_helper_spec.rb b/spec/helpers/packages_helper_spec.rb index 95d5b39ad3b..2d21961f9f1 100644 --- a/spec/helpers/packages_helper_spec.rb +++ b/spec/helpers/packages_helper_spec.rb @@ -73,6 +73,42 @@ RSpec.describe PackagesHelper, feature_category: :package_registry do end end + describe '#track_package_event' do + let_it_be(:project) { create(:project) } + + let(:action) { 'push_package' } + let(:scope) { :terraform_module } + let(:category) { described_class.name } + let(:namespace) { project.namespace } + let(:user) { project.creator } + let(:create_event_service) { instance_double(::Packages::CreateEventService) } + + before do + allow(helper).to receive(:current_user).and_return(user) + allow(Packages::CreateEventService).to receive(:new).and_return(create_event_service) + allow(create_event_service).to receive(:execute) + end + + it 'tracks a snowplow event' do + helper.track_package_event(action, scope, category: category, namespace: namespace, user: user, project: project) + + expect_snowplow_event( + category: category, + action: action, + user: user, + project: project, + namespace: namespace + ) + end + + it 'calls CreateEventService with correct parameters and executes it' do + helper.track_package_event(action, scope, category: category, namespace: namespace, user: user, project: project) + + expect(Packages::CreateEventService).to have_received(:new).with(project, user, event_name: action, scope: scope) + expect(create_event_service).to have_received(:execute) + end + end + describe '#show_cleanup_policy_link' do let_it_be(:user) { create(:user) } let_it_be_with_reload(:container_repository) { create(:container_repository) } @@ -252,6 +288,97 @@ RSpec.describe PackagesHelper, feature_category: :package_registry do end end + describe '#group_packages_template_data' do + let_it_be(:group) { create(:group) } + let_it_be(:user) { create(:user) } + + before do + allow(helper).to receive(:current_user) { user } + stub_config(packages: { enabled: true }) + end + + subject { helper.group_packages_template_data(group) } + + it 'returns the correct data' do + is_expected.to include( + empty_list_illustration: match_asset_path('illustrations/empty-state/empty-package-md.svg'), + endpoint: group_packages_path(group), + full_path: group.full_path, + group_list_url: group_packages_path(group), + npm_instance_url: package_registry_instance_url(:npm), + page_type: 'groups', + project_list_url: '' + ) + end + + context 'when user has permission' do + before do + group.add_owner(user) + end + + it 'returns the correct data' do + is_expected.to include( + can_delete_packages: 'true', + settings_path: group_settings_packages_and_registries_path(group) + ) + end + end + + context 'when user does not have permission' do + it 'returns the correct data' do + is_expected.to include( + can_delete_packages: 'false', + settings_path: '' + ) + end + end + end + + describe '#project_packages_template_data' do + let_it_be(:user) { create(:user) } + + subject { helper.project_packages_template_data(project) } + + before do + allow(helper).to receive(:current_user) { user } + stub_config(packages: { enabled: true }) + end + + it 'returns the correct data' do + is_expected.to include( + empty_list_illustration: match_asset_path('illustrations/empty-state/empty-package-md.svg'), + endpoint: project_packages_path(project), + full_path: project.full_path, + group_list_url: '', + npm_instance_url: package_registry_instance_url(:npm), + page_type: 'projects', + project_list_url: project_packages_path(project) + ) + end + + context 'when user has permission' do + before do + project.add_owner(user) + end + + it 'returns the correct data' do + is_expected.to include( + can_delete_packages: 'true', + settings_path: project_settings_packages_and_registries_path(project) + ) + end + end + + context 'when user does not have permission' do + it 'returns the correct data' do + is_expected.to include( + can_delete_packages: 'false', + settings_path: '' + ) + end + end + end + describe '#settings_data' do let(:user) { build_stubbed(:user) } diff --git a/spec/lib/api/helpers/packages_helpers_spec.rb b/spec/lib/api/helpers/packages_helpers_spec.rb index e20bb01e060..71daab85dac 100644 --- a/spec/lib/api/helpers/packages_helpers_spec.rb +++ b/spec/lib/api/helpers/packages_helpers_spec.rb @@ -306,6 +306,28 @@ RSpec.describe API::Helpers::PackagesHelpers, feature_category: :package_registr end end + context 'with internal event event' do + let(:user) { project.creator } + let(:category) { described_class.name } + let(:namespace) { project.namespace } + + it 'calls internal events' do + expect(Gitlab::InternalEvents).to receive(:track_event) + .with('pull_package_from_registry', + additional_properties: { + label: 'terraform_module', + property: 'user' + }, + user: user, + project: project, + namespace: namespace + ) + + args = { category: category, user: user, project: project, namespace: namespace } + helper.track_package_event('pull_package', :terraform_module, **args) + end + end + context 'when using deploy token and action is push package' do let(:user) { create(:deploy_token, write_registry: true, projects: [project]) } let(:scope) { :rubygems } @@ -314,7 +336,7 @@ RSpec.describe API::Helpers::PackagesHelpers, feature_category: :package_registr let(:label) { 'counts.package_events_i_package_push_package_by_deploy_token' } let(:property) { 'i_package_push_package_by_deploy_token' } let(:service_ping_context) do - [Gitlab::Usage::MetricDefinition.context_for('counts.package_events_i_package_push_package_by_deploy_token').to_h] + [Gitlab::Tracking::ServicePingContext.new(data_source: :redis, event: 'package_pushed_using_deploy_token').to_h] end it 'logs a snowplow event' do @@ -342,7 +364,7 @@ RSpec.describe API::Helpers::PackagesHelpers, feature_category: :package_registr let(:label) { 'counts.package_events_i_package_pull_package_by_guest' } let(:property) { 'i_package_pull_package_by_guest' } let(:service_ping_context) do - [Gitlab::Usage::MetricDefinition.context_for('counts.package_events_i_package_pull_package_by_guest').to_h] + [Gitlab::Tracking::ServicePingContext.new(data_source: :redis, event: 'package_pulled_by_guest').to_h] end it 'logs a snowplow event' do diff --git a/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb b/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb index 6a78c58ac3c..c4e42e7b0d2 100644 --- a/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb +++ b/spec/lib/gitlab/email/handler/service_desk_handler_spec.rb @@ -108,16 +108,6 @@ RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler, feature_category: :se end it_behaves_like 'a new issue request' - - context 'when feature flag service_desk_tickets_confidentiality is disabled' do - let(:confidential_ticket) { true } - - before do - stub_feature_flags(service_desk_tickets_confidentiality: false) - end - - it_behaves_like 'a new issue request' - end end end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 4326de27dc3..2a502a100ec 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -23,6 +23,9 @@ issues: - own_label_links - labels - own_labels +- own_notes +- own_resource_state_events +- own_resource_label_events - own_description_versions - sync_object - last_edited_by @@ -1027,6 +1030,9 @@ epic: - own_label_links - labels - own_labels +- own_notes +- own_resource_state_events +- own_resource_label_events - own_description_versions - sync_object - todos diff --git a/spec/models/service_desk_setting_spec.rb b/spec/models/service_desk_setting_spec.rb index f6518aba399..c419bae8658 100644 --- a/spec/models/service_desk_setting_spec.rb +++ b/spec/models/service_desk_setting_spec.rb @@ -172,21 +172,6 @@ RSpec.describe ServiceDeskSetting, feature_category: :service_desk do it { is_expected.to be expected_value } end - - context 'when feature flag service_desk_tickets_confidentiality is disabled' do - let(:project) { build(:project, :private) } - - subject(:setting) do - build(:service_desk_setting, project: project, tickets_confidential_by_default: false) - .tickets_confidential_by_default? - end - - before do - stub_feature_flags(service_desk_tickets_confidentiality: false) - end - - it { is_expected.to be true } - end end describe 'associations' do diff --git a/spec/requests/api/bulk_imports_spec.rb b/spec/requests/api/bulk_imports_spec.rb index 2176ea306cf..f2d0270b782 100644 --- a/spec/requests/api/bulk_imports_spec.rb +++ b/spec/requests/api/bulk_imports_spec.rb @@ -146,7 +146,7 @@ RSpec.describe API::BulkImports, feature_category: :importers do end let(:source_entity_type) { BulkImports::CreateService::ENTITY_TYPES_MAPPING.fetch(params[:entities][0][:source_type]) } - let(:source_entity_identifier) { ERB::Util.url_encode(params[:entities][0][:source_full_path]) } + let(:source_entity_identifier) { '165' } before do allow_next_instance_of(BulkImports::Clients::HTTP) do |instance| @@ -158,6 +158,14 @@ RSpec.describe API::BulkImports, feature_category: :importers do .to receive(:instance_enterprise) .and_return(false) end + + allow_next_instance_of(BulkImports::Clients::Graphql) do |client| + allow(client).to receive(:parse) + allow(client).to receive(:execute).and_return( + instance_double(GraphQL::Client::Response, original_hash: { 'data' => { 'group' => { 'id' => "gid://gitlab/Group/#{source_entity_identifier}" } } }) + ) + end + stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=access_token") .to_return(status: 200, body: "", headers: {}) @@ -340,11 +348,45 @@ RSpec.describe API::BulkImports, feature_category: :importers do } end - it 'returns blocked url message in the error', :aggregate_failures do + it 'returns not accessible message in the error', :aggregate_failures do + stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=token") + .to_return(status: 403, body: "Forbidden 403", headers: {}) + request - expect(response).to have_gitlab_http_status(:unprocessable_entity) - expect(json_response['message']).to eq("URL is blocked: Only allowed schemes are http, https") + expect(json_response['message']).to eq("Import failed. You don't have permission to export 'full_path'.") + end + end + + context 'when resource is not found on source instance' do + let(:params) do + { + configuration: { + url: 'http://gitlab.example', + access_token: 'access_token' + }, + entities: [ + source_type: 'group_entity', + source_full_path: 'full_path', + destination_slug: 'destination_slug', + destination_namespace: 'destination_namespace' + ] + } + end + + before do + allow_next_instance_of(BulkImports::Clients::Graphql) do |client| + allow(client).to receive(:parse) + allow(client).to receive(:execute).and_return( + instance_double(GraphQL::Client::Response, original_hash: { 'data' => { 'group' => nil } }) + ) + end + end + + it 'returns not found message', :aggregate_failures do + request + + expect(json_response['message']).to eq("Import failed. 'full_path' not found.") end end @@ -364,16 +406,14 @@ RSpec.describe API::BulkImports, feature_category: :importers do } end - it 'returns blocked url error', :aggregate_failures do + it 'returns disabled instance error message', :aggregate_failures do stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=access_token") - .to_return(status: 404, body: "{'error':'404 Not Found'}") + .to_return(status: 404, body: "Unsuccessful response 404", headers: {}) request - expect(response).to have_gitlab_http_status(:unprocessable_entity) - expect(json_response['message']).to eq( - "Unsuccessful response 404 from /api/v4/groups/full_path/export_relations/status. Body: {'error':'404 Not Found'}" - ) + expect(json_response['message']).to include("Migration by direct transfer disabled on source or destination instance. " \ + "Ask an administrator to enable it on both instances and try again.") end end diff --git a/spec/requests/projects/network_controller_spec.rb b/spec/requests/projects/network_controller_spec.rb index dee95c6e70e..f3b43fa2d61 100644 --- a/spec/requests/projects/network_controller_spec.rb +++ b/spec/requests/projects/network_controller_spec.rb @@ -41,6 +41,14 @@ RSpec.describe Projects::NetworkController, feature_category: :source_code_manag subject expect(assigns(:url)).to eq(project_network_path(project, ref, format: :json)) end + + context 'when path includes a space' do + it 'still renders the page' do + get [project_network_path(project, ref), '/%20'].join + + expect(response).to have_gitlab_http_status(:ok) + end + end end end end diff --git a/spec/services/bulk_imports/create_service_spec.rb b/spec/services/bulk_imports/create_service_spec.rb index 6c55b81a6de..141f20eb03d 100644 --- a/spec/services/bulk_imports/create_service_spec.rb +++ b/spec/services/bulk_imports/create_service_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe BulkImports::CreateService, feature_category: :importers do + include GraphqlHelpers + let(:user) { create(:user) } let(:credentials) { { url: 'http://gitlab.example', access_token: 'token' } } let(:destination_group) { create(:group, path: 'destination1') } @@ -62,67 +64,132 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do end end - # response when authorize_admin_project in API endpoint fails - context 'when direct transfer status query returns a 403' do - it 'raises a ServiceResponse::Error' do - expect_next_instance_of(BulkImports::Clients::HTTP) do |client| - expect(client).to receive(:validate_instance_version!).and_return(true) - expect(client).to receive(:get) - .with("/groups/full%2Fpath%2Fto%2Fgroup1/export_relations/status") - .and_raise(BulkImports::NetworkError, '403 Forbidden') + context 'when the resource is not found while validating the source_full_path' do + before do + allow_next_instance_of(BulkImports::Clients::HTTP) do |client| + allow(client) + .to receive(:validate_instance_version!) + .and_return(true) + + allow(client) + .to receive(:validate_import_scopes!) + .and_return(true) + end + end + + it 'rescues the error and raises a BulkImports::Error' do + expect_next_instance_of(BulkImports::Clients::Graphql) do |client| + expect(client) + .to receive(:execute) + .and_return(instance_double(GraphQL::Client::Response, original_hash: { 'data' => { 'group' => nil } })) + + expect(client).to receive(:parse) end result = subject.execute expect(result).to be_a(ServiceResponse) expect(result).to be_error - expect(result.message).to eq("403 Forbidden") + expect(result.message).to eq("Import failed. 'full/path/to/group1' not found.") + end + end + + # response when authorize_admin_project in API endpoint fails + context 'when direct transfer status query returns a 403' do + before do + allow_next_instance_of(BulkImports::Clients::HTTP) do |client| + allow(client).to receive(:validate_instance_version!).and_return(true) + allow(client).to receive(:validate_import_scopes!).and_return(true) + end + + allow_next_instance_of(BulkImports::Clients::Graphql) do |client| + allow(client) + .to receive(:execute) + .and_return(instance_double(GraphQL::Client::Response, original_hash: { + 'data' => { 'group' => { 'id' => 'gid://gitlab/Group/165' } } + } )) + + allow(client).to receive(:parse) + end + end + + it 'raises a ServiceResponse::Error' do + stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/165/export_relations/status?page=1&per_page=30&private_token=token") + .to_return(status: 403) + + result = subject.execute + + expect(result).to be_a(ServiceResponse) + expect(result).to be_error + expect(result.message).to eq("Import failed. You don't have permission to export 'full/path/to/group1'.") end end context 'when direct transfer setting query returns a 404' do it 'raises a ServiceResponse::Error' do - stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token').to_return(status: 404) - stub_request(:get, 'http://gitlab.example/api/v4/metadata?private_token=token') - .to_return( - status: 200, - body: source_version.to_json, - headers: { 'Content-Type' => 'application/json' } - ) - stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=token") - .to_return(status: 404) - - expect_next_instance_of(BulkImports::Clients::HTTP) do |client| - expect(client).to receive(:get).and_raise(BulkImports::Error.setting_not_enabled) + allow_next_instance_of(BulkImports::Clients::HTTP) do |client| + allow(client).to receive(:validate_instance_version!).and_return(true) + allow(client).to receive(:validate_import_scopes!).and_return(true) end + allow_next_instance_of(BulkImports::Clients::Graphql) do |client| + allow(client) + .to receive(:execute) + .and_return(instance_double(GraphQL::Client::Response, original_hash: { + 'data' => { 'group' => { 'id' => 'gid://gitlab/Group/165' } } + } )) + + allow(client).to receive(:parse) + end + + stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/165/export_relations/status?page=1&per_page=30&private_token=token") + .to_return(status: 404) + result = subject.execute expect(result).to be_a(ServiceResponse) expect(result).to be_error expect(result.message) .to eq( - "Group import disabled on source or destination instance. " \ + "Migration by direct transfer disabled on source or destination instance. " \ "Ask an administrator to enable it on both instances and try again." ) end end + context 'when direct transfer setting query raises any other NetworkError' do + it 'raises a ServiceResponse::Error' do + allow_next_instance_of(BulkImports::Clients::HTTP) do |client| + allow(client).to receive(:validate_instance_version!).and_return(true) + allow(client).to receive(:validate_import_scopes!).and_return(true) + end + + allow_next_instance_of(BulkImports::Clients::Graphql) do |client| + allow(client) + .to receive(:execute) + .and_return(instance_double(GraphQL::Client::Response, original_hash: { + 'data' => { 'group' => { 'id' => 'gid://gitlab/Group/165' } } + } )) + + allow(client).to receive(:parse) + end + + stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/165/export_relations/status?page=1&per_page=30&private_token=token") + .to_return(status: 408) + + result = subject.execute + + expect(result).to be_a(ServiceResponse) + expect(result).to be_error + expect(result.message) + .to include("Unsuccessful response 408") + end + end + context 'when required scopes are not present' do it 'returns ServiceResponse with error if token does not have api scope' do - stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token').to_return(status: 404) - stub_request(:get, 'http://gitlab.example/api/v4/metadata?private_token=token') - .to_return( - status: 200, - body: source_version.to_json, - headers: { 'Content-Type' => 'application/json' } - ) - stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=token") - .to_return( - status: 200 - ) - allow_next_instance_of(BulkImports::Clients::HTTP) do |client| + allow(client).to receive(:validate_instance_version!).and_return(true) allow(client).to receive(:validate_import_scopes!) .and_raise(BulkImports::Error.scope_or_url_validation_failure) end @@ -140,19 +207,12 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do context 'when token validation succeeds' do before do - stub_request(:get, 'http://gitlab.example/api/v4/version?private_token=token').to_return(status: 404) - stub_request(:get, 'http://gitlab.example/api/v4/metadata?private_token=token') - .to_return(status: 200, body: source_version.to_json, headers: { 'Content-Type' => 'application/json' }) - stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=token") - .to_return( - status: 200 - ) - stub_request(:get, 'http://gitlab.example/api/v4/personal_access_tokens/self?private_token=token') - .to_return( - status: 200, - body: { 'scopes' => ['api'] }.to_json, - headers: { 'Content-Type' => 'application/json' } - ) + allow(subject).to receive(:validate!).and_return(true) + + allow_next_instance_of(BulkImports::Clients::HTTP) do |client| + allow(client).to receive(:instance_version).and_return(source_version[:version]) + allow(client).to receive(:instance_enterprise).and_return(false) + end parent_group.add_owner(user) end @@ -161,6 +221,7 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do expect { subject.execute }.to change { BulkImport.count }.by(1) last_bulk_import = BulkImport.last + expect(last_bulk_import.user).to eq(user) expect(last_bulk_import.source_version).to eq(source_version[:version]) expect(last_bulk_import.user).to eq(user) @@ -247,12 +308,38 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do allow_next_instance_of(BulkImports::Clients::HTTP) do |instance| allow(instance).to receive(:instance_version).and_return(source_version) allow(instance).to receive(:instance_enterprise).and_return(false) - stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=token") + + allow_next_instance_of(BulkImports::Clients::Graphql) do |client| + allow(client) + .to receive(:execute) + .and_return(instance_double(GraphQL::Client::Response, original_hash: { + 'data' => { 'group' => { 'id' => 'gid://gitlab/Group/165' } } + } )) + + allow(client).to receive(:parse) + end + + stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/165/export_relations/status?page=1&per_page=30&private_token=token") .to_return( status: 200 ) end + allow_next_instance_of(BulkImports::Clients::Graphql) do |client| + allow(client) + .to receive(:execute) + .and_return(instance_double(GraphQL::Client::Response, original_hash: { + 'data' => { 'group' => { 'id' => 'gid://gitlab/Group/165' } } + } )) + + allow(client).to receive(:parse) + end + + stub_request(:get, "http://gitlab.example/api/v4/#{source_entity_type}/#{source_entity_identifier}/export_relations/status?page=1&per_page=30&private_token=token") + .to_return( + status: 200 + ) + parent_group.add_owner(user) end @@ -431,7 +518,7 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do let(:entity_source_id) { 'gid://gitlab/Model/12345' } let(:graphql_client) { instance_double(BulkImports::Clients::Graphql) } let(:http_client) { instance_double(BulkImports::Clients::HTTP) } - let(:http_response) { double(code: 200, success?: true) } # rubocop:disable RSpec/VerifiedDoubles + let(:http_response) { instance_double(HTTParty::Response, code: 200, success?: true) } before do allow(BulkImports::Clients::HTTP).to receive(:new).and_return(http_client) @@ -447,7 +534,8 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do context 'when the source_full_path contains only integer characters' do let(:query_string) { BulkImports::Groups::Graphql::GetGroupQuery.new(context: nil).to_s } let(:graphql_response) do - double(original_hash: { 'data' => { 'group' => { 'id' => entity_source_id } } }) # rubocop:disable RSpec/VerifiedDoubles + instance_double(GraphQL::Client::Response, + original_hash: { 'data' => { 'group' => { 'id' => entity_source_id } } }) end let(:params) do @@ -488,7 +576,8 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do context 'when the source_full_path contains only integer characters' do let(:query_string) { BulkImports::Projects::Graphql::GetProjectQuery.new(context: nil).to_s } let(:graphql_response) do - double(original_hash: { 'data' => { 'project' => { 'id' => entity_source_id } } }) # rubocop:disable RSpec/VerifiedDoubles + instance_double(GraphQL::Client::Response, + original_hash: { 'data' => { 'project' => { 'id' => entity_source_id } } }) end let(:params) do @@ -527,6 +616,10 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do end describe '#validate_destination_namespace' do + before do + allow(subject).to receive(:validate!).and_return(true) + end + context 'when the destination_namespace does not exist' do let(:params) do [ @@ -634,6 +727,10 @@ RSpec.describe BulkImports::CreateService, feature_category: :importers do end describe '#validate_destination_full_path' do + before do + allow(subject).to receive(:validate!).and_return(true) + end + context 'when the source_type is a group' do context 'when the provided destination_slug already exists in the destination_namespace' do let_it_be(:existing_subgroup) { create(:group, path: 'existing-subgroup', parent_id: parent_group.id ) } diff --git a/spec/services/issuable/discussions_list_service_spec.rb b/spec/services/issuable/discussions_list_service_spec.rb index 9c791ce9cd3..3b7ea7843e1 100644 --- a/spec/services/issuable/discussions_list_service_spec.rb +++ b/spec/services/issuable/discussions_list_service_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Issuable::DiscussionsListService, feature_category: :team_plannin describe 'fetching notes for issue' do let_it_be(:issuable) { create(:issue, project: project) } - it_behaves_like 'listing issuable discussions', :guest, 1, 7 + it_behaves_like 'listing issuable discussions', user_role: :guest, internal_discussions: 1, total_discussions: 7 context 'without notes widget' do let_it_be(:issuable) { create(:work_item, project: project) } @@ -34,13 +34,13 @@ RSpec.describe Issuable::DiscussionsListService, feature_category: :team_plannin context 'when issue exists at the group level' do let_it_be(:issuable) { create(:issue, :group_level, namespace: group) } - it_behaves_like 'listing issuable discussions', :guest, 1, 7 + it_behaves_like 'listing issuable discussions', user_role: :guest, internal_discussions: 1, total_discussions: 7 end end describe 'fetching notes for merge requests' do let_it_be(:issuable) { create(:merge_request, source_project: project, target_project: project) } - it_behaves_like 'listing issuable discussions', :reporter, 0, 6 + it_behaves_like 'listing issuable discussions', user_role: :reporter, internal_discussions: 0, total_discussions: 6 end end diff --git a/spec/services/packages/create_event_service_spec.rb b/spec/services/packages/create_event_service_spec.rb index 45c758ec866..a1b03df8726 100644 --- a/spec/services/packages/create_event_service_spec.rb +++ b/spec/services/packages/create_event_service_spec.rb @@ -2,8 +2,9 @@ require 'spec_helper' RSpec.describe Packages::CreateEventService, feature_category: :package_registry do + let_it_be(:project) { create(:project) } let(:scope) { 'generic' } - let(:event_name) { 'push_package' } + let(:event_name) { 'pull_package' } let(:params) do { @@ -12,59 +13,149 @@ RSpec.describe Packages::CreateEventService, feature_category: :package_registry } end - subject { described_class.new(nil, user, params).execute } + subject(:service) { described_class.new(project, user, params).execute } describe '#execute' do - shared_examples 'redis package unique event creation' do |originator_type, expected_scope| - it 'tracks the event' do - expect(::Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event).with(/package/, values: user.id) + let(:label) { 'generic' } + let(:event_attrs) do + { + category: 'InternalEventTracking', + label: label, + namespace: project.namespace, + project: project, + property: property, + user: nil + } + end - subject + let(:common_metrics) do + ['counts.package_events_i_package_pull_package', "counts.package_events_i_package_#{label}_pull_package"] + end + + shared_examples 'updates the correct metrics' do + context 'with a user' do + let_it_be(:user) { create(:user) } + + let(:property) { 'user' } + let(:event_attrs) { super().merge(user: user) } + + it 'updates the correct metrics' do + expect { service }.to trigger_internal_events('pull_package_from_registry').with(event_attrs) + .and increment_usage_metrics( + 'counts.package_events_i_package_pull_package_by_user', + *common_metrics + ).and not_increment_usage_metrics( + 'counts.package_events_i_package_pull_package_by_deploy_token', + 'counts.package_events_i_package_pull_package_by_guest' + ) + end + end + + context 'with a deploy token' do + let_it_be(:user) { create(:deploy_token) } + + let(:property) { 'deploy_token' } + + it 'updates the correct metrics' do + expect { service }.to trigger_internal_events('pull_package_from_registry').with(event_attrs) + .and increment_usage_metrics( + 'counts.package_events_i_package_pull_package_by_deploy_token', + *common_metrics + ).and not_increment_usage_metrics( + 'counts.package_events_i_package_pull_package_by_guest', + 'counts.package_events_i_package_pull_package_by_user' + ) + end + end + + context 'with no user' do + let_it_be(:user) { nil } + + let(:property) { 'guest' } + + it 'updates the correct metrics' do + expect { service }.to trigger_internal_events('pull_package_from_registry').with(event_attrs) + .and increment_usage_metrics( + 'counts.package_events_i_package_pull_package_by_guest', + *common_metrics + ).and not_increment_usage_metrics( + 'counts.package_events_i_package_pull_package_by_deploy_token', + 'counts.package_events_i_package_pull_package_by_user' + ) + end end end - shared_examples 'redis package count event creation' do |originator_type, expected_scope| - it 'tracks the event' do - expect(::Gitlab::UsageDataCounters::PackageEventCounter).to receive(:count).at_least(:once) - - subject - end - end - - context 'with a user' do - let(:user) { create(:user) } - - it_behaves_like 'redis package unique event creation', 'user', 'generic' - it_behaves_like 'redis package count event creation', 'user', 'generic' - end - - context 'with a deploy token' do - let(:user) { create(:deploy_token) } - - it_behaves_like 'redis package unique event creation', 'deploy_token', 'generic' - it_behaves_like 'redis package count event creation', 'deploy_token', 'generic' - end - - context 'with no user' do - let(:user) { nil } - - it_behaves_like 'redis package count event creation', 'guest', 'generic' - end + it_behaves_like 'updates the correct metrics' context 'with a package as scope' do let(:scope) { create(:npm_package) } + let(:label) { 'npm' } - context 'as guest' do - let(:user) { nil } + it_behaves_like 'updates the correct metrics' + end - it_behaves_like 'redis package count event creation', 'guest', 'npm' + context 'when using non-internal events' do + let(:event_name) { 'push_package' } + + shared_examples 'redis package unique event creation' do + it 'tracks the event' do + expect(::Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event).with(/package/, values: user.id) + + subject + end end - context 'with user' do + shared_examples 'redis package count event creation' do + it 'tracks the event' do + expect(::Gitlab::UsageDataCounters::PackageEventCounter).to receive(:count).at_least(:once) + + subject + end + end + + context 'with a user' do let(:user) { create(:user) } - it_behaves_like 'redis package unique event creation', 'user', 'npm' - it_behaves_like 'redis package count event creation', 'user', 'npm' + it_behaves_like 'redis package unique event creation' + it_behaves_like 'redis package count event creation' + end + + context 'with a deploy token' do + let(:user) { create(:deploy_token) } + + it_behaves_like 'redis package unique event creation' + it_behaves_like 'redis package count event creation' + end + + context 'with no user' do + let(:user) { nil } + + it_behaves_like 'redis package count event creation' + end + + context 'with a package as scope' do + let(:scope) { create(:npm_package) } + + context 'as guest' do + let(:user) { nil } + + it_behaves_like 'redis package count event creation' + end + + context 'with user' do + let(:user) { create(:user) } + + it_behaves_like 'redis package unique event creation' + it_behaves_like 'redis package count event creation' + end + + context 'with a deploy token' do + let(:user) { create(:deploy_token) } + + it_behaves_like 'redis package unique event creation' + it_behaves_like 'redis package count event creation' + end end end end diff --git a/spec/services/service_desk_settings/update_service_spec.rb b/spec/services/service_desk_settings/update_service_spec.rb index e2a00020147..d07df3dac3b 100644 --- a/spec/services/service_desk_settings/update_service_spec.rb +++ b/spec/services/service_desk_settings/update_service_spec.rb @@ -70,23 +70,6 @@ RSpec.describe ServiceDeskSettings::UpdateService, :aggregate_failures, feature_ ) end end - - context 'when service_desk_tickets_confidentiality feature flag is disabled' do - before do - stub_feature_flags(service_desk_tickets_confidentiality: false) - end - - it 'updates service desk setting but not tickets_confidential_by_default value' do - response = described_class.new(settings.project, user, params).execute - - expect(response).to be_success - expect(settings.reset).to have_attributes( - outgoing_name: 'some name', - project_key: 'foo', - tickets_confidential_by_default: true - ) - end - end end context 'when project_key is an empty string' do diff --git a/spec/support/shared_examples/services/issuable/discussions_list_shared_examples.rb b/spec/support/shared_examples/services/issuable/discussions_list_shared_examples.rb index 4188c4aa56c..edabdcaa4c0 100644 --- a/spec/support/shared_examples/services/issuable/discussions_list_shared_examples.rb +++ b/spec/support/shared_examples/services/issuable/discussions_list_shared_examples.rb @@ -1,8 +1,14 @@ # frozen_string_literal: true -RSpec.shared_examples 'listing issuable discussions' do |user_role, internal_discussion_count, total_discussions_count| +RSpec.shared_examples 'listing issuable discussions' do |user_role:, internal_discussions:, total_discussions:| before_all do create_notes(issuable, "some user comment") + + if issuable.try(:sync_object).present? + create_notes(issuable.sync_object, "some user comment") + internal_discussions *= 2 + total_discussions *= 2 + end end context 'when user cannot read issue' do @@ -41,8 +47,7 @@ RSpec.shared_examples 'listing issuable discussions' do |user_role, internal_dis it "returns non confidential notes" do discussions = discussions_service.execute - - non_conf_discussion_count = total_discussions_count - internal_discussion_count + non_conf_discussion_count = total_discussions - internal_discussions expect(discussions.count).to eq(non_conf_discussion_count) expect(discussions.count { |disc| disc.notes.any?(&:confidential) }).to eq(0) expect(discussions.count { |disc| !disc.notes.any?(&:confidential) }).to eq(non_conf_discussion_count) @@ -53,10 +58,9 @@ RSpec.shared_examples 'listing issuable discussions' do |user_role, internal_dis context 'and can read confidential notes' do it "returns all notes" do discussions = discussions_service.execute - - expect(discussions.count).to eq(total_discussions_count) - expect(discussions.count { |disc| disc.notes.any?(&:confidential) }).to eq(internal_discussion_count) - non_conf_discussion_count = total_discussions_count - internal_discussion_count + expect(discussions.count).to eq(total_discussions) + expect(discussions.count { |disc| disc.notes.any?(&:confidential) }).to eq(internal_discussions) + non_conf_discussion_count = total_discussions - internal_discussions expect(discussions.count { |disc| !disc.notes.any?(&:confidential) }).to eq(non_conf_discussion_count) end end @@ -106,7 +110,7 @@ def create_notes(issuable, note_body) ) create(:resource_label_event, user: current_user, "#{assoc_name}": issuable, label: label, action: 'remove') - unless issuable.is_a?(Epic) + if !issuable.is_a?(Epic) && !(issuable.is_a?(WorkItem) && issuable.work_item_type.name == 'Epic') create(:resource_milestone_event, "#{assoc_name}": issuable, milestone: milestone, action: 'add') create(:resource_milestone_event, "#{assoc_name}": issuable, milestone: milestone, action: 'remove') end diff --git a/spec/views/groups/packages/index.html.haml_spec.rb b/spec/views/groups/packages/index.html.haml_spec.rb index 3c6305d1ed9..4ca37ba887b 100644 --- a/spec/views/groups/packages/index.html.haml_spec.rb +++ b/spec/views/groups/packages/index.html.haml_spec.rb @@ -16,42 +16,4 @@ RSpec.describe 'groups/packages/index.html.haml', feature_category: :package_reg expect(rendered).to have_selector('#js-vue-packages-list') end - - describe 'settings path' do - it 'without permission sets empty settings path' do - allow(view).to receive(:show_group_package_registry_settings).and_return(false) - - render - - expect(rendered).to have_selector('[data-settings-path=""]') - end - - it 'with permission sets group settings path' do - allow(view).to receive(:show_group_package_registry_settings).and_return(true) - - render - - expect(rendered).to have_selector( - "[data-settings-path=\"#{group_settings_packages_and_registries_path(group)}\"]" - ) - end - end - - describe 'can_delete_packages' do - it 'without permission sets false' do - allow(view).to receive(:can_delete_group_packages?).and_return(false) - - render - - expect(rendered).to have_selector('[data-can-delete-packages="false"]') - end - - it 'with permission sets true' do - allow(view).to receive(:can_delete_group_packages?).and_return(true) - - render - - expect(rendered).to have_selector('[data-can-delete-packages="true"]') - end - end end diff --git a/spec/views/projects/packages/index.html.haml_spec.rb b/spec/views/projects/packages/index.html.haml_spec.rb index e59db289ad4..a749146c6ac 100644 --- a/spec/views/projects/packages/index.html.haml_spec.rb +++ b/spec/views/projects/packages/index.html.haml_spec.rb @@ -16,42 +16,4 @@ RSpec.describe 'projects/packages/packages/index.html.haml', feature_category: : expect(rendered).to have_selector('#js-vue-packages-list') end - - describe 'settings path' do - it 'without permission sets empty settings path' do - allow(view).to receive(:show_package_registry_settings).and_return(false) - - render - - expect(rendered).to have_selector('[data-settings-path=""]') - end - - it 'with permission sets project settings path' do - allow(view).to receive(:show_package_registry_settings).and_return(true) - - render - - expect(rendered).to have_selector( - "[data-settings-path=\"#{project_settings_packages_and_registries_path(project)}\"]" - ) - end - end - - describe 'can_delete_packages' do - it 'without permission sets empty settings path' do - allow(view).to receive(:can_delete_packages?).and_return(false) - - render - - expect(rendered).to have_selector('[data-can-delete-packages="false"]') - end - - it 'with permission sets project settings path' do - allow(view).to receive(:can_delete_packages?).and_return(true) - - render - - expect(rendered).to have_selector('[data-can-delete-packages="true"]') - end - end end