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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/CODEOWNERS6
-rw-r--r--.gitlab/issue_templates/Doc_cleanup.md32
-rw-r--r--.rubocop_todo/layout/first_array_element_indentation.yml20
-rw-r--r--app/assets/javascripts/repository/components/table/index.vue5
-rw-r--r--app/assets/javascripts/repository/log_tree.js3
-rw-r--r--app/assets/javascripts/repository/utils/commit.js4
-rw-r--r--app/controllers/projects/packages/package_files_controller.rb1
-rw-r--r--app/graphql/types/packages/package_details_type.rb2
-rw-r--r--app/helpers/notify_helper.rb19
-rw-r--r--app/helpers/users_helper.rb1
-rw-r--r--app/models/ci/build.rb14
-rw-r--r--app/models/ci/pipeline.rb7
-rw-r--r--app/models/ci/processable.rb1
-rw-r--r--app/models/ci/stage.rb3
-rw-r--r--app/models/commit_status.rb2
-rw-r--r--app/models/concerns/ci/has_deployment_name.rb15
-rw-r--r--app/models/concerns/ci/partitionable.rb15
-rw-r--r--app/models/concerns/ci/track_environment_usage.rb31
-rw-r--r--app/models/packages/package.rb6
-rw-r--r--app/services/ci/after_requeue_job_service.rb36
-rw-r--r--app/services/google_cloud/fetch_google_ip_list_service.rb89
-rw-r--r--app/uploaders/object_storage/cdn/google_cdn.rb76
-rw-r--r--app/uploaders/object_storage/cdn/google_ip_cache.rb60
-rw-r--r--app/views/notify/approved_merge_request_email.html.haml2
-rw-r--r--app/views/notify/unapproved_merge_request_email.html.haml22
-rw-r--r--app/workers/all_queues.yml9
-rw-r--r--app/workers/ci/build_finished_worker.rb2
-rw-r--r--app/workers/google_cloud/fetch_google_ip_list_worker.rb17
-rw-r--r--config/feature_flags/development/ci_requeue_with_dag_object_hierarchy.yml8
-rw-r--r--config/feature_flags/development/restyle_login_page.yml2
-rw-r--r--config/sidekiq_queues.yml2
-rw-r--r--db/migrate/20220901124637_add_last_downloaded_at_to_packages.rb7
-rw-r--r--db/schema_migrations/202209011246371
-rw-r--r--db/structure.sql3
-rw-r--r--doc/administration/audit_event_streaming.md6
-rw-r--r--doc/administration/audit_events.md2
-rw-r--r--doc/administration/geo/replication/container_registry.md2
-rw-r--r--doc/administration/geo/replication/disable_geo.md2
-rw-r--r--doc/administration/geo/replication/object_storage.md2
-rw-r--r--doc/administration/geo/replication/remove_geo_site.md2
-rw-r--r--doc/administration/geo/replication/troubleshooting.md8
-rw-r--r--doc/administration/geo/replication/tuning.md2
-rw-r--r--doc/administration/gitaly/praefect.md2
-rw-r--r--doc/administration/gitaly/troubleshooting.md2
-rw-r--r--doc/administration/housekeeping.md2
-rw-r--r--doc/administration/integration/kroki.md2
-rw-r--r--doc/administration/integration/mailgun.md2
-rw-r--r--doc/administration/integration/plantuml.md2
-rw-r--r--doc/administration/maintenance_mode/index.md6
-rw-r--r--doc/administration/monitoring/gitlab_self_monitoring_project/index.md6
-rw-r--r--doc/administration/monitoring/performance/gitlab_configuration.md2
-rw-r--r--doc/administration/monitoring/performance/grafana_configuration.md6
-rw-r--r--doc/administration/monitoring/performance/performance_bar.md2
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md2
-rw-r--r--doc/administration/operations/fast_ssh_key_lookup.md4
-rw-r--r--doc/administration/packages/container_registry.md2
-rw-r--r--doc/administration/pages/index.md18
-rw-r--r--doc/administration/pages/source.md2
-rw-r--r--doc/administration/raketasks/project_import_export.md2
-rw-r--r--doc/administration/raketasks/storage.md2
-rw-r--r--doc/administration/repository_checks.md4
-rw-r--r--doc/administration/repository_storage_types.md2
-rw-r--r--doc/administration/sidekiq/extra_sidekiq_processes.md2
-rw-r--r--doc/administration/static_objects_external_storage.md2
-rw-r--r--doc/administration/whats-new.md2
-rw-r--r--doc/api/graphql/reference/index.md14
-rw-r--r--doc/api/merge_request_context_commits.md32
-rw-r--r--doc/api/packages.md1
-rw-r--r--doc/api/project_templates.md16
-rw-r--r--doc/api/system_hooks.md2
-rw-r--r--doc/ci/caching/index.md4
-rw-r--r--doc/ci/ci_cd_for_external_repos/index.md2
-rw-r--r--doc/ci/enable_or_disable_ci.md4
-rw-r--r--doc/ci/environments/deployment_approvals.md4
-rw-r--r--doc/ci/environments/environments_dashboard.md2
-rw-r--r--doc/ci/lint.md4
-rw-r--r--doc/development/documentation/index.md2
-rw-r--r--doc/development/internal_users.md2
-rw-r--r--doc/install/azure/index.md2
-rw-r--r--doc/user/admin_area/review_abuse_reports.md4
-rw-r--r--doc/user/analytics/ci_cd_analytics.md10
-rw-r--r--doc/user/analytics/code_review_analytics.md2
-rw-r--r--doc/user/analytics/issue_analytics.md2
-rw-r--r--doc/user/analytics/merge_request_analytics.md6
-rw-r--r--doc/user/analytics/productivity_analytics.md6
-rw-r--r--doc/user/analytics/repository_analytics.md2
-rw-r--r--doc/user/analytics/value_stream_analytics.md10
-rw-r--r--doc/user/clusters/agent/ci_cd_workflow.md4
-rw-r--r--doc/user/clusters/agent/install/index.md2
-rw-r--r--doc/user/clusters/agent/vulnerabilities.md2
-rw-r--r--doc/user/clusters/agent/work_with_agent.md8
-rw-r--r--doc/user/clusters/management_project.md2
-rw-r--r--doc/user/clusters/management_project_template.md2
-rw-r--r--doc/user/compliance/compliance_report/index.md6
-rw-r--r--doc/user/compliance/license_compliance/index.md2
-rw-r--r--doc/user/crm/index.md2
-rw-r--r--doc/user/group/contribution_analytics/index.md2
-rw-r--r--doc/user/group/epics/epic_boards.md6
-rw-r--r--doc/user/group/import/index.md2
-rw-r--r--doc/user/group/insights/index.md4
-rw-r--r--doc/user/group/issues_analytics/index.md2
-rw-r--r--doc/user/group/iterations/index.md14
-rw-r--r--doc/user/group/repositories_analytics/index.md4
-rw-r--r--doc/user/group/saml_sso/index.md6
-rw-r--r--doc/user/group/saml_sso/scim_setup.md2
-rw-r--r--doc/user/group/settings/group_access_tokens.md6
-rw-r--r--doc/user/group/settings/import_export.md4
-rw-r--r--doc/user/group/subgroups/index.md10
-rw-r--r--doc/user/infrastructure/clusters/connect/index.md6
-rw-r--r--doc/user/infrastructure/clusters/connect/new_civo_cluster.md2
-rw-r--r--doc/user/infrastructure/clusters/connect/new_eks_cluster.md2
-rw-r--r--doc/user/infrastructure/clusters/connect/new_gke_cluster.md2
-rw-r--r--doc/user/operations_dashboard/index.md2
-rw-r--r--doc/user/packages/dependency_proxy/index.md6
-rw-r--r--doc/user/packages/harbor_container_registry/index.md2
-rw-r--r--doc/user/permissions.md4
-rw-r--r--doc/user/project/deploy_keys/index.md10
-rw-r--r--doc/user/project/deploy_tokens/index.md4
-rw-r--r--doc/user/project/import/gitlab_com.md2
-rw-r--r--doc/user/project/import/repo_by_url.md2
-rw-r--r--doc/user/project/members/index.md16
-rw-r--r--doc/user/project/merge_requests/cherry_pick_changes.md3
-rw-r--r--doc/user/project/pages/getting_started/pages_ci_cd_template.md2
-rw-r--r--doc/user/project/pages/getting_started/pages_ui.md2
-rw-r--r--doc/user/project/protected_tags.md2
-rw-r--r--doc/user/project/releases/index.md8
-rw-r--r--doc/user/project/service_desk.md4
-rw-r--r--doc/user/project/settings/import_export.md2
-rw-r--r--doc/user/project/settings/index.md2
-rw-r--r--doc/user/project/settings/project_access_tokens.md6
-rw-r--r--doc/user/project/time_tracking.md2
-rw-r--r--doc/user/public_access.md4
-rw-r--r--doc/user/search/index.md4
-rw-r--r--doc/user/snippets.md14
-rw-r--r--lib/api/composer_packages.rb5
-rw-r--r--lib/api/concerns/packages/debian_package_endpoints.rb4
-rw-r--r--lib/api/debian_group_packages.rb2
-rw-r--r--lib/api/debian_project_packages.rb2
-rw-r--r--lib/api/entities/package.rb1
-rw-r--r--lib/api/generic_packages.rb2
-rw-r--r--lib/api/helm_packages.rb2
-rw-r--r--lib/api/helpers/packages/conan/api_helpers.rb2
-rw-r--r--lib/api/helpers/packages_helpers.rb5
-rw-r--r--lib/api/maven_packages.rb11
-rw-r--r--lib/api/npm_project_packages.rb2
-rw-r--r--lib/api/nuget_project_packages.rb2
-rw-r--r--lib/api/pypi_packages.rb4
-rw-r--r--lib/api/rubygem_packages.rb2
-rw-r--r--lib/gitlab/application_rate_limiter.rb3
-rw-r--r--lib/gitlab/ci/pipeline/chain/assign_partition.rb4
-rw-r--r--lib/gitlab/ci/processable_object_hierarchy.rb33
-rw-r--r--locale/gitlab.pot14
-rw-r--r--spec/factories/commit_statuses.rb2
-rw-r--r--spec/factories/packages/packages.rb4
-rw-r--r--spec/fixtures/api/schemas/graphql/packages/package_details.json6
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/packages/package.json7
-rw-r--r--spec/frontend/repository/components/table/index_spec.js39
-rw-r--r--spec/frontend/repository/log_tree_spec.js8
-rw-r--r--spec/frontend/repository/utils/commit_spec.js2
-rw-r--r--spec/helpers/notify_helper_spec.rb32
-rw-r--r--spec/helpers/users_helper_spec.rb30
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/processable_object_hierarchy_spec.rb82
-rw-r--r--spec/models/ci/build_spec.rb26
-rw-r--r--spec/models/ci/job_artifact_spec.rb2
-rw-r--r--spec/models/ci/pipeline_spec.rb32
-rw-r--r--spec/models/ci/stage_spec.rb29
-rw-r--r--spec/models/commit_status_spec.rb33
-rw-r--r--spec/models/concerns/ci/has_deployment_name_spec.rb34
-rw-r--r--spec/models/concerns/ci/track_environment_usage_spec.rb61
-rw-r--r--spec/models/packages/package_spec.rb12
-rw-r--r--spec/requests/api/commit_statuses_spec.rb20
-rw-r--r--spec/requests/api/conan_instance_packages_spec.rb6
-rw-r--r--spec/requests/api/conan_project_packages_spec.rb6
-rw-r--r--spec/requests/api/debian_group_packages_spec.rb10
-rw-r--r--spec/requests/api/debian_project_packages_spec.rb10
-rw-r--r--spec/requests/api/generic_packages_spec.rb18
-rw-r--r--spec/requests/api/graphql/packages/composer_spec.rb2
-rw-r--r--spec/requests/api/graphql/packages/conan_spec.rb2
-rw-r--r--spec/requests/api/graphql/packages/helm_spec.rb2
-rw-r--r--spec/requests/api/graphql/packages/maven_spec.rb6
-rw-r--r--spec/requests/api/graphql/packages/nuget_spec.rb2
-rw-r--r--spec/requests/api/graphql/packages/package_spec.rb13
-rw-r--r--spec/requests/api/graphql/packages/pypi_spec.rb2
-rw-r--r--spec/requests/api/maven_packages_spec.rb17
-rw-r--r--spec/requests/api/npm_project_packages_spec.rb3
-rw-r--r--spec/requests/api/project_packages_spec.rb13
-rw-r--r--spec/requests/projects/packages/package_files_controller_spec.rb30
-rw-r--r--spec/serializers/pipeline_details_entity_spec.rb2
-rw-r--r--spec/services/ci/after_requeue_job_service_spec.rb97
-rw-r--r--spec/services/ci/create_pipeline_service/partitioning_spec.rb4
-rw-r--r--spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb2
-rw-r--r--spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_fail_and_retry_1.yml55
-rw-r--r--spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_fail_and_retry_2.yml63
-rw-r--r--spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_subsequent_manual_jobs.yml65
-rw-r--r--spec/services/google_cloud/fetch_google_ip_list_service_spec.rb73
-rw-r--r--spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb3
-rw-r--r--spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb6
-rw-r--r--spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/requests/api/packages_shared_examples.rb7
-rw-r--r--spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb1
-rw-r--r--spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb1
-rw-r--r--spec/uploaders/object_storage/cdn/google_cdn_spec.rb48
-rw-r--r--spec/uploaders/object_storage/cdn/google_ip_cache_spec.rb84
-rw-r--r--spec/workers/google_cloud/fetch_google_ip_list_worker_spec.rb15
206 files changed, 1612 insertions, 591 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index c57c2965101..4dab0bd5bf5 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -4,9 +4,9 @@
* @gitlab-org/maintainers/rails-backend @gitlab-org/maintainers/frontend @gitlab-org/maintainers/database @gl-quality/qe-maintainers @gitlab-org/delivery @gitlab-org/maintainers/cicd-templates @kwiebers @nolith @jacobvosmaer-gitlab
-CODEOWNERS @clefelhocz1 @timzallmann @cdu1 @whaber @dsatcher @sgoldstein @jeromezng @stanhu @susantacker @dianalogan @kpaizee @sselhorn
-docs/CODEOWNERS @clefelhocz1 @timzallmann @cdu1 @whaber @dsatcher @sgoldstein @jeromezng @stanhu @susantacker @dianalogan @kpaizee @sselhorn
-.gitlab/CODEOWNERS @clefelhocz1 @timzallmann @cdu1 @whaber @dsatcher @sgoldstein @jeromezng @stanhu @susantacker @dianalogan @kpaizee @sselhorn
+CODEOWNERS @clefelhocz1 @timzallmann @cdu1 @wayne @dsatcher @sgoldstein @jeromezng @stanhu @susantacker @dianalogan @kpaizee @sselhorn
+docs/CODEOWNERS @clefelhocz1 @timzallmann @cdu1 @wayne @dsatcher @sgoldstein @jeromezng @stanhu @susantacker @dianalogan @kpaizee @sselhorn
+.gitlab/CODEOWNERS @clefelhocz1 @timzallmann @cdu1 @wayne @dsatcher @sgoldstein @jeromezng @stanhu @susantacker @dianalogan @kpaizee @sselhorn
## Allows release tooling to update the Gitaly Version
GITALY_SERVER_VERSION @project_278964_bot6 @gitlab-org/maintainers/rails-backend @gitlab-org/delivery
diff --git a/.gitlab/issue_templates/Doc_cleanup.md b/.gitlab/issue_templates/Doc_cleanup.md
index 58a51e15803..79cf2662b07 100644
--- a/.gitlab/issue_templates/Doc_cleanup.md
+++ b/.gitlab/issue_templates/Doc_cleanup.md
@@ -7,18 +7,27 @@
* feature development should use the Feature Request template.
-->
-If you are a community contributor:
+If you are a community contributor, **do not work on the issue if it is not assigned to you yet**.
-1. To work on an issue, type `@gl-docsteam I would like to work on this issue.`
- in a comment. A technical writer
- will assign the issue to you. Do not work on the issue before it is assigned to you.
- If someone has already chosen the issue, pick another or view docs [in the docs directory](https://gitlab.com/gitlab-org/gitlab/-/tree/master/doc)
+Additionally, please review these points before working on this issue:
+
+1. If you would like to work on the issue, type `@gl-docsteam I would like to work on this issue.`
+ in a comment. A technical writer will assign the issue to you. If someone has already chosen this issue,
+ pick another issue, or view docs [in the docs directory](https://gitlab.com/gitlab-org/gitlab/-/tree/master/doc)
and open a merge request for any page you feel can be improved.
-1. Create a merge request for the issue. If this is for a Hackathon, do not create the merge request
- before the Hackathon has started or it will not be counted towards the Hackathon. If you were not
- assigned the issue, do not create a merge request. It will not be accepted.
-1. Copy the link to this issue and add it to the merge request's description, which will link
- the merge request and the issue together.
+1. Carefully review the [merge request guidelines for contributors](https://docs.gitlab.com/ee/development/contributing/merge_request_workflow.html#merge-request-guidelines-for-contributors).
+1. Carefully review the [commit message guidelines](https://docs.gitlab.com/ee/development/contributing/merge_request_workflow.html#commit-messages-guidelines).
+1. Create a merge request for the issue:
+ - If you were not assigned the issue, do not create a merge request. It will not be accepted.
+ - If this is for a Hackathon, do not create the merge request before the Hackathon has started
+ or it will not be counted towards the Hackathon.
+ - Unless otherwise stated below, we expect one merge request per issue, so combine
+ all changes together. If there is too much work for you to handle in one merge request,
+ you can create more, but try to keep the number of merge requests as small as possible.
+ - Select the **Documentation** merge request description template, and fill it out
+ with the details of your work.
+ - Copy the link to this issue and add it to the merge request's description,
+ which links the merge request and the issue together.
1. After your merge request is accepted and merged, close this issue.
If you notice things you'd like to fix that are not part of the issue, open separate merge requests for those issues.
@@ -37,6 +46,9 @@ Thank you again for contributing to the GitLab documentation!
* several moderate changes on one page, a few intermediate changes across five pages, or several very small
* changes for up to 10 pages. Larger items should be broken out into other issues to better distribute
* the opportunities for contributors.
+*
+* If you expect the work to take more than one MR to resolve, explain approximately
+* how many MRs you expect to receive for the issue.
-->
## Additional information
diff --git a/.rubocop_todo/layout/first_array_element_indentation.yml b/.rubocop_todo/layout/first_array_element_indentation.yml
index 177ec291b64..dd4f9d62462 100644
--- a/.rubocop_todo/layout/first_array_element_indentation.yml
+++ b/.rubocop_todo/layout/first_array_element_indentation.yml
@@ -5,26 +5,6 @@ Layout/FirstArrayElementIndentation:
- 'ee/lib/ee/api/helpers/award_emoji.rb'
- 'ee/spec/graphql/mutations/incident_management/escalation_policy/create_spec.rb'
- 'ee/spec/lib/gitlab/graphql/loaders/bulk_epic_aggregate_loader_spec.rb'
- - 'ee/spec/lib/gitlab/usage/metrics/instrumentations/count_ci_builds_metric_spec.rb'
- - 'ee/spec/lib/gitlab/usage/metrics/instrumentations/count_users_creating_ci_builds_metric_spec.rb'
- - 'ee/spec/lib/gitlab/vulnerabilities/parser_spec.rb'
- - 'ee/spec/models/analytics/cycle_analytics/group_value_stream_spec.rb'
- - 'ee/spec/models/application_setting_spec.rb'
- - 'ee/spec/models/approval_state_spec.rb'
- - 'ee/spec/models/burndown_spec.rb'
- - 'ee/spec/models/concerns/ee/noteable_spec.rb'
- - 'ee/spec/models/concerns/geo/verification_state_spec.rb'
- - 'ee/spec/models/ee/iterations/cadence_spec.rb'
- - 'ee/spec/models/ee/namespace_spec.rb'
- - 'ee/spec/models/ee/release_spec.rb'
- - 'ee/spec/models/group_wiki_repository_spec.rb'
- - 'ee/spec/models/namespace_setting_spec.rb'
- - 'ee/spec/models/project_spec.rb'
- - 'ee/spec/models/requirements_management/test_report_spec.rb'
- - 'ee/spec/models/security/orchestration_policy_configuration_spec.rb'
- - 'ee/spec/models/security/orchestration_policy_rule_schedule_spec.rb'
- - 'ee/spec/models/security/scan_spec.rb'
- - 'ee/spec/models/security/training_provider_spec.rb'
- 'ee/spec/models/snippet_repository_spec.rb'
- 'ee/spec/policies/project_policy_spec.rb'
- 'ee/spec/requests/admin/user_permission_exports_controller_spec.rb'
diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue
index 1f6b5e98122..99eb167172b 100644
--- a/app/assets/javascripts/repository/components/table/index.vue
+++ b/app/assets/javascripts/repository/components/table/index.vue
@@ -2,6 +2,7 @@
import { GlSkeletonLoader, GlButton } from '@gitlab/ui';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { sprintf, __ } from '~/locale';
+import { joinPaths } from '~/lib/utils/url_utility';
import getRefMixin from '../../mixins/get_ref';
import projectPathQuery from '../../queries/project_path.query.graphql';
import TableHeader from './header.vue';
@@ -108,7 +109,9 @@ export default {
return {};
}
- return this.commits.find((commitEntry) => commitEntry.fileName === fileName);
+ return this.commits.find(
+ (commitEntry) => commitEntry.filePath === joinPaths(this.path, fileName),
+ );
},
},
};
diff --git a/app/assets/javascripts/repository/log_tree.js b/app/assets/javascripts/repository/log_tree.js
index 9345a8406e3..a5bcd9e6b5e 100644
--- a/app/assets/javascripts/repository/log_tree.js
+++ b/app/assets/javascripts/repository/log_tree.js
@@ -1,6 +1,7 @@
import produce from 'immer';
import { normalizeData } from 'ee_else_ce/repository/utils/commit';
import axios from '~/lib/utils/axios_utils';
+import { joinPaths } from '~/lib/utils/url_utility';
import commitsQuery from './queries/commits.query.graphql';
import projectPathQuery from './queries/project_path.query.graphql';
import refQuery from './queries/ref.query.graphql';
@@ -16,7 +17,7 @@ function setNextOffset(offset) {
}
export function resolveCommit(commits, path, { resolve, entry }) {
- const commit = commits.find((c) => c.filePath === `${path}/${entry.name}`);
+ const commit = commits.find((c) => c.filePath === joinPaths(path, entry.name));
if (commit) {
resolve(commit);
diff --git a/app/assets/javascripts/repository/utils/commit.js b/app/assets/javascripts/repository/utils/commit.js
index 878b4fdd71a..247e30d20fc 100644
--- a/app/assets/javascripts/repository/utils/commit.js
+++ b/app/assets/javascripts/repository/utils/commit.js
@@ -1,3 +1,5 @@
+import { joinPaths } from '~/lib/utils/url_utility';
+
export function normalizeData(data, path, extra = () => {}) {
return data.map((d) => ({
sha: d.commit.id,
@@ -6,7 +8,7 @@ export function normalizeData(data, path, extra = () => {}) {
committedDate: d.commit.committed_date,
commitPath: d.commit_path,
fileName: d.file_name,
- filePath: `${path}/${d.file_name}`,
+ filePath: joinPaths(path, d.file_name),
__typename: 'LogTreeCommit',
...extra(d),
}));
diff --git a/app/controllers/projects/packages/package_files_controller.rb b/app/controllers/projects/packages/package_files_controller.rb
index 32aadb4fcf4..1aa91ee1189 100644
--- a/app/controllers/projects/packages/package_files_controller.rb
+++ b/app/controllers/projects/packages/package_files_controller.rb
@@ -11,6 +11,7 @@ module Projects
def download
package_file = project.package_files.find(params[:id])
+ package_file.package.touch_last_downloaded_at
send_upload(package_file.file, attachment: package_file.file_name)
end
end
diff --git a/app/graphql/types/packages/package_details_type.rb b/app/graphql/types/packages/package_details_type.rb
index 0413177ef14..6c0d955ed77 100644
--- a/app/graphql/types/packages/package_details_type.rb
+++ b/app/graphql/types/packages/package_details_type.rb
@@ -26,6 +26,8 @@ module Types
field :pypi_setup_url, GraphQL::Types::String, null: true, description: 'Url of the PyPi project setup endpoint.'
field :pypi_url, GraphQL::Types::String, null: true, description: 'Url of the PyPi project endpoint.'
+ field :last_downloaded_at, Types::TimeType, null: true, description: 'Last time that a file of this package was downloaded.'
+
def versions
object.versions
end
diff --git a/app/helpers/notify_helper.rb b/app/helpers/notify_helper.rb
index e15b1b21fe1..b7ab1c2e2d1 100644
--- a/app/helpers/notify_helper.rb
+++ b/app/helpers/notify_helper.rb
@@ -21,15 +21,14 @@ module NotifyHelper
(source.description || default_description).truncate(200, separator: ' ')
end
- def merge_request_approved_description(merge_request, approved_by)
- s_('Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}')
- .html_safe % {
- mr_highlight: '<span style="font-weight: 600;color:#333333;">'.html_safe,
- highlight_end: '</span>'.html_safe,
- mr_link: link_to(merge_request.to_reference, merge_request_url(merge_request), style: "font-weight: 600;color:#3777b0;text-decoration:none").html_safe,
- approved_highlight: '<span>'.html_safe,
- approver_avatar: content_tag(:img, nil, height: "24", src: avatar_icon_for_user(approved_by, 24, only_path: false), style: "border-radius:12px;margin:-7px 0 -7px 3px;", width: "24", alt: "Avatar", class: "avatar").html_safe,
- approver_link: link_to(approved_by.name, user_url(approved_by), style: "color:#333333;text-decoration:none;", class: "muted").html_safe
- }
+ def merge_request_hash_param(merge_request, reviewer)
+ {
+ mr_highlight: '<span style="font-weight: 600;color:#333333;">'.html_safe,
+ highlight_end: '</span>'.html_safe,
+ mr_link: link_to(merge_request.to_reference, merge_request_url(merge_request), style: "font-weight: 600;color:#3777b0;text-decoration:none").html_safe,
+ reviewer_highlight: '<span>'.html_safe,
+ reviewer_avatar: content_tag(:img, nil, height: "24", src: avatar_icon_for_user(reviewer, 24, only_path: false), style: "border-radius:12px;margin:-7px 0 -7px 3px;", width: "24", alt: "Avatar", class: "avatar").html_safe,
+ reviewer_link: link_to(reviewer.name, user_url(reviewer), style: "color:#333333;text-decoration:none;", class: "muted").html_safe
+ }
end
end
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index fb0f739934e..271fa47dd97 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -94,6 +94,7 @@ module UsersHelper
[].tap do |badges|
badges << blocked_user_badge(user) if user.blocked?
badges << { text: s_('AdminUsers|Admin'), variant: 'success' } if user.admin?
+ badges << { text: s_('AdminUsers|Bot'), variant: 'muted' } if user.bot?
badges << { text: s_('AdminUsers|External'), variant: 'secondary' } if user.external?
badges << { text: s_("AdminUsers|It's you!"), variant: 'muted' } if current_user == user
badges << { text: s_("AdminUsers|Locked"), variant: 'warning' } if user.access_locked?
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 3dcd18d1b8c..4e58f877217 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -11,7 +11,7 @@ module Ci
include Presentable
include Importable
include Ci::HasRef
- include HasDeploymentName
+ include Ci::TrackEnvironmentUsage
extend ::Gitlab::Utils::Override
@@ -1099,18 +1099,6 @@ module Ci
.include?(exit_code)
end
- def track_deployment_usage
- Gitlab::Utils::UsageData.track_usage_event('ci_users_executing_deployment_job', user_id) if user_id.present? && count_user_deployment?
- end
-
- def track_verify_usage
- Gitlab::Utils::UsageData.track_usage_event('ci_users_executing_verify_environment_job', user_id) if user_id.present? && count_user_verification?
- end
-
- def count_user_verification?
- has_environment? && environment_action == 'verify'
- end
-
def each_report(report_types)
job_artifacts_for_types(report_types).each do |report_artifact|
report_artifact.each_blob do |blob|
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index f0568f33b1f..1e328c3c573 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -2,6 +2,7 @@
module Ci
class Pipeline < Ci::ApplicationRecord
+ include Ci::Partitionable
include Ci::HasStatus
include Importable
include AfterCommitQueue
@@ -31,7 +32,7 @@ module Ci
sha_attribute :source_sha
sha_attribute :target_sha
-
+ partitionable scope: ->(_) { Ci::Pipeline.current_partition_value }
# Ci::CreatePipelineService returns Ci::Pipeline so this is the only place
# where we can pass additional information from the service. This accessor
# is used for storing the processed metadata for linting purposes.
@@ -482,6 +483,10 @@ module Ci
@auto_devops_pipelines_completed_total ||= Gitlab::Metrics.counter(:auto_devops_pipelines_completed_total, 'Number of completed auto devops pipelines')
end
+ def self.current_partition_value
+ 100
+ end
+
def uses_needs?
processables.where(scheduling_type: :dag).any?
end
diff --git a/app/models/ci/processable.rb b/app/models/ci/processable.rb
index a2ff49077be..09dc9d4bce1 100644
--- a/app/models/ci/processable.rb
+++ b/app/models/ci/processable.rb
@@ -3,6 +3,7 @@
module Ci
class Processable < ::CommitStatus
include Gitlab::Utils::StrongMemoize
+ include FromUnion
extend ::Gitlab::Utils::Override
has_one :resource, class_name: 'Ci::Resource', foreign_key: 'build_id', inverse_of: :processable
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index e65b273bf96..46a9e3f6494 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -2,11 +2,14 @@
module Ci
class Stage < Ci::ApplicationRecord
+ include Ci::Partitionable
include Importable
include Ci::HasStatus
include Gitlab::OptimisticLocking
include Presentable
+ partitionable scope: :pipeline
+
enum status: Ci::HasStatus::STATUSES_ENUM
belongs_to :project
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 7df0525fa35..05a258e6e26 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
class CommitStatus < Ci::ApplicationRecord
+ include Ci::Partitionable
include Ci::HasStatus
include Importable
include AfterCommitQueue
@@ -11,6 +12,7 @@ class CommitStatus < Ci::ApplicationRecord
include IgnorableColumns
self.table_name = 'ci_builds'
+ partitionable scope: :pipeline
ignore_column :trace, remove_with: '15.6', remove_after: '2022-10-22'
belongs_to :user
diff --git a/app/models/concerns/ci/has_deployment_name.rb b/app/models/concerns/ci/has_deployment_name.rb
deleted file mode 100644
index 887653e846e..00000000000
--- a/app/models/concerns/ci/has_deployment_name.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-module Ci
- module HasDeploymentName
- extend ActiveSupport::Concern
-
- def count_user_deployment?
- deployment_name?
- end
-
- def deployment_name?
- self.class::DEPLOYMENT_NAMES.any? { |n| name.downcase.include?(n) }
- end
- end
-end
diff --git a/app/models/concerns/ci/partitionable.rb b/app/models/concerns/ci/partitionable.rb
index 08a4a6c950f..710ee1ba64f 100644
--- a/app/models/concerns/ci/partitionable.rb
+++ b/app/models/concerns/ci/partitionable.rb
@@ -11,6 +11,9 @@ module Ci
#
# belongs_to :pipeline
# partitionable scope: :pipeline
+ # # Or
+ # partitionable scope: ->(record) { record.partition_value }
+ #
#
module Partitionable
extend ActiveSupport::Concern
@@ -21,9 +24,10 @@ module Ci
validates :partition_id, presence: true
def set_partition_id
- return unless partition_scope_record
+ return if partition_id_changed? && partition_id.present?
+ return unless partition_scope_value
- self.partition_id = partition_scope_record.partition_id
+ self.partition_id = partition_scope_value
end
end
@@ -31,9 +35,10 @@ module Ci
private
def partitionable(scope:)
- define_method(:partition_scope_record) do
- strong_memoize(:partition_scope_record) do
- scope.to_proc.call(self)
+ define_method(:partition_scope_value) do
+ strong_memoize(:partition_scope_value) do
+ record = scope.to_proc.call(self)
+ record.respond_to?(:partition_id) ? record.partition_id : record
end
end
end
diff --git a/app/models/concerns/ci/track_environment_usage.rb b/app/models/concerns/ci/track_environment_usage.rb
new file mode 100644
index 00000000000..45d9cdeeb59
--- /dev/null
+++ b/app/models/concerns/ci/track_environment_usage.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module Ci
+ module TrackEnvironmentUsage
+ extend ActiveSupport::Concern
+
+ def track_deployment_usage
+ return unless user_id.present? && count_user_deployment?
+
+ Gitlab::Utils::UsageData.track_usage_event('ci_users_executing_deployment_job', user_id)
+ end
+
+ def track_verify_environment_usage
+ return unless user_id.present? && verifies_environment?
+
+ Gitlab::Utils::UsageData.track_usage_event('ci_users_executing_verify_environment_job', user_id)
+ end
+
+ def verifies_environment?
+ has_environment? && environment_action == 'verify'
+ end
+
+ def count_user_deployment?
+ deployment_name?
+ end
+
+ def deployment_name?
+ self.class::DEPLOYMENT_NAMES.any? { |n| name.downcase.include?(n) }
+ end
+ end
+end
diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb
index 431c18d02f0..b4c09d99bb0 100644
--- a/app/models/packages/package.rb
+++ b/app/models/packages/package.rb
@@ -333,6 +333,12 @@ class Packages::Package < ApplicationRecord
name.gsub(/#{Gitlab::Regex::Packages::PYPI_NORMALIZED_NAME_REGEX_STRING}/o, '-').downcase
end
+ def touch_last_downloaded_at
+ ::Gitlab::Database::LoadBalancing::Session.without_sticky_writes do
+ update_column(:last_downloaded_at, Time.zone.now)
+ end
+ end
+
private
def composer_tag_version?
diff --git a/app/services/ci/after_requeue_job_service.rb b/app/services/ci/after_requeue_job_service.rb
index 1ae4639751b..634c547a623 100644
--- a/app/services/ci/after_requeue_job_service.rb
+++ b/app/services/ci/after_requeue_job_service.rb
@@ -21,9 +21,16 @@ module Ci
@processable.pipeline.reset_source_bridge!(current_user)
end
+ # rubocop: disable CodeReuse/ActiveRecord
def dependent_jobs
+ return legacy_dependent_jobs unless ::Feature.enabled?(:ci_requeue_with_dag_object_hierarchy, project)
+
ordered_by_dag(
- stage_dependent_jobs.or(needs_dependent_jobs).ordered_by_stage
+ ::Ci::Processable
+ .from_union(needs_dependent_jobs, stage_dependent_jobs)
+ .skipped
+ .ordered_by_stage
+ .preload(:needs)
)
end
@@ -34,22 +41,37 @@ module Ci
end
def stage_dependent_jobs
- skipped_jobs.after_stage(@processable.stage_idx)
+ @processable.pipeline.processables.after_stage(@processable.stage_idx)
end
def needs_dependent_jobs
- skipped_jobs.scheduling_type_dag.with_needs([@processable.name])
+ ::Gitlab::Ci::ProcessableObjectHierarchy.new(
+ ::Ci::Processable.where(id: @processable.id)
+ ).descendants
end
- def skipped_jobs
- @skipped_jobs ||= @processable.pipeline.processables.skipped
+ def legacy_skipped_jobs
+ @legacy_skipped_jobs ||= @processable.pipeline.processables.skipped
+ end
+
+ def legacy_dependent_jobs
+ ordered_by_dag(
+ legacy_stage_dependent_jobs.or(legacy_needs_dependent_jobs).ordered_by_stage.preload(:needs)
+ )
+ end
+
+ def legacy_stage_dependent_jobs
+ legacy_skipped_jobs.after_stage(@processable.stage_idx)
+ end
+
+ def legacy_needs_dependent_jobs
+ legacy_skipped_jobs.scheduling_type_dag.with_needs([@processable.name])
end
- # rubocop: disable CodeReuse/ActiveRecord
def ordered_by_dag(jobs)
sorted_job_names = sort_jobs(jobs).each_with_index.to_h
- jobs.preload(:needs).group_by(&:stage_idx).flat_map do |_, stage_jobs|
+ jobs.group_by(&:stage_idx).flat_map do |_, stage_jobs|
stage_jobs.sort_by { |job| sorted_job_names.fetch(job.name) }
end
end
diff --git a/app/services/google_cloud/fetch_google_ip_list_service.rb b/app/services/google_cloud/fetch_google_ip_list_service.rb
new file mode 100644
index 00000000000..f7739971603
--- /dev/null
+++ b/app/services/google_cloud/fetch_google_ip_list_service.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+module GoogleCloud
+ class FetchGoogleIpListService
+ include BaseServiceUtility
+
+ GOOGLE_IP_RANGES_URL = 'https://www.gstatic.com/ipranges/cloud.json'
+ RESPONSE_BODY_LIMIT = 1.megabyte
+ EXPECTED_CONTENT_TYPE = 'application/json'
+
+ IpListNotRetrievedError = Class.new(StandardError)
+
+ def execute
+ # Prevent too many workers from hitting the same HTTP endpoint
+ if ::Gitlab::ApplicationRateLimiter.throttled?(:fetch_google_ip_list, scope: nil)
+ return error("#{self.class} was rate limited")
+ end
+
+ subnets = fetch_and_update_cache!
+
+ Gitlab::AppJsonLogger.info(class: self.class.name,
+ message: 'Successfully retrieved Google IP list',
+ subnet_count: subnets.count)
+
+ success({ subnets: subnets })
+ rescue IpListNotRetrievedError => err
+ Gitlab::ErrorTracking.log_exception(err)
+ error('Google IP list not retrieved')
+ end
+
+ private
+
+ # Attempts to retrieve and parse the list of IPs from Google. Updates
+ # the internal cache so that the data is accessible.
+ #
+ # Returns an array of IPAddr objects consisting of subnets.
+ def fetch_and_update_cache!
+ parsed_response = fetch_google_ip_list
+
+ parse_google_prefixes(parsed_response).tap do |subnets|
+ ::ObjectStorage::CDN::GoogleIpCache.update!(subnets)
+ end
+ end
+
+ def fetch_google_ip_list
+ response = Gitlab::HTTP.get(GOOGLE_IP_RANGES_URL, follow_redirects: false, allow_local_requests: false)
+
+ validate_response!(response)
+
+ response.parsed_response
+ end
+
+ def validate_response!(response)
+ raise IpListNotRetrievedError, "response was #{response.code}" unless response.code == 200
+ raise IpListNotRetrievedError, "response was nil" unless response.body
+
+ parsed_response = response.parsed_response
+
+ unless response.content_type == EXPECTED_CONTENT_TYPE && parsed_response.is_a?(Hash)
+ raise IpListNotRetrievedError, "response was not JSON"
+ end
+
+ if response.body&.bytesize.to_i > RESPONSE_BODY_LIMIT
+ raise IpListNotRetrievedError, "response was too large: #{response.body.bytesize}"
+ end
+
+ prefixes = parsed_response['prefixes']
+
+ raise IpListNotRetrievedError, "JSON was type #{prefixes.class}, expected Array" unless prefixes.is_a?(Array)
+ raise IpListNotRetrievedError, "#{GOOGLE_IP_RANGES_URL} did not return any IP ranges" if prefixes.empty?
+
+ response.parsed_response
+ end
+
+ def parse_google_prefixes(parsed_response)
+ ranges = parsed_response['prefixes'].map do |prefix|
+ ip_range = prefix['ipv4Prefix'] || prefix['ipv6Prefix']
+
+ next unless ip_range
+
+ IPAddr.new(ip_range)
+ end.compact
+
+ raise IpListNotRetrievedError, "#{GOOGLE_IP_RANGES_URL} did not return any IP ranges" if ranges.empty?
+
+ ranges
+ end
+ end
+end
diff --git a/app/uploaders/object_storage/cdn/google_cdn.rb b/app/uploaders/object_storage/cdn/google_cdn.rb
index a8efe80b378..ea7683f131c 100644
--- a/app/uploaders/object_storage/cdn/google_cdn.rb
+++ b/app/uploaders/object_storage/cdn/google_cdn.rb
@@ -6,18 +6,12 @@ module ObjectStorage
class GoogleCDN
include Gitlab::Utils::StrongMemoize
- IpListNotRetrievedError = Class.new(StandardError)
-
- GOOGLE_CDN_LIST_KEY = 'google_cdn_ip_list'
- GOOGLE_IP_RANGES_URL = 'https://www.gstatic.com/ipranges/cloud.json'
- EXPECTED_CONTENT_TYPE = 'application/json'
- RESPONSE_BODY_LIMIT = 1.megabyte
- CACHE_EXPIRATION_TIME = 1.day
-
attr_reader :options
def initialize(options)
@options = HashWithIndifferentAccess.new(options.to_h)
+
+ GoogleIpCache.async_refresh unless GoogleIpCache.ready?
end
def use_cdn?(request_ip)
@@ -26,9 +20,8 @@ module ObjectStorage
ip = IPAddr.new(request_ip)
return false if ip.private?
- return false unless google_ip_ranges.present?
- !google_ip?(request_ip)
+ !GoogleIpCache.google_ip?(request_ip)
end
def signed_url(path, expiry: 10.minutes)
@@ -71,69 +64,6 @@ module ObjectStorage
options['url']
end
end
-
- def google_ip?(request_ip)
- google_ip_ranges.any? { |range| range.include?(request_ip) }
- end
-
- def google_ip_ranges
- strong_memoize(:google_ip_ranges) do
- cache_value(GOOGLE_CDN_LIST_KEY) { fetch_google_ip_list }
- end
- rescue IpListNotRetrievedError => err
- Gitlab::ErrorTracking.log_exception(err)
- []
- end
-
- def cache_value(key, expires_in: CACHE_EXPIRATION_TIME, &block)
- l1_cache.fetch(key, expires_in: expires_in) do
- l2_cache.fetch(key, expires_in: expires_in) { yield }
- end
- end
-
- def l1_cache
- Gitlab::ProcessMemoryCache.cache_backend
- end
-
- def l2_cache
- Rails.cache
- end
-
- def fetch_google_ip_list
- response = Gitlab::HTTP.get(GOOGLE_IP_RANGES_URL)
-
- raise IpListNotRetrievedError, "response was #{response.code}" unless response.code == 200
-
- if response.body&.bytesize.to_i > RESPONSE_BODY_LIMIT
- raise IpListNotRetrievedError, "response was too large: #{response.body.bytesize}"
- end
-
- parsed_response = response.parsed_response
-
- unless response.content_type == EXPECTED_CONTENT_TYPE && parsed_response.is_a?(Hash)
- raise IpListNotRetrievedError, "response was not JSON"
- end
-
- parse_google_prefixes(parsed_response)
- end
-
- def parse_google_prefixes(parsed_response)
- prefixes = parsed_response['prefixes']
-
- raise IpListNotRetrievedError, "JSON was type #{prefixes.class}, expected Array" unless prefixes.is_a?(Array)
-
- ranges = prefixes.map do |prefix|
- ip_range = prefix['ipv4Prefix'] || prefix['ipv6Prefix']
-
- next unless ip_range
-
- IPAddr.new(ip_range)
- end.compact
-
- raise IpListNotRetrievedError, "#{GOOGLE_IP_RANGES_URL} did not return any IP ranges" if ranges.empty?
-
- ranges
- end
end
end
end
diff --git a/app/uploaders/object_storage/cdn/google_ip_cache.rb b/app/uploaders/object_storage/cdn/google_ip_cache.rb
new file mode 100644
index 00000000000..35ec7ce0c6e
--- /dev/null
+++ b/app/uploaders/object_storage/cdn/google_ip_cache.rb
@@ -0,0 +1,60 @@
+# rubocop:disable Naming/FileName
+# frozen_string_literal: true
+
+module ObjectStorage
+ module CDN
+ class GoogleIpCache
+ GOOGLE_CDN_LIST_KEY = 'google_cdn_ip_list'
+ CACHE_EXPIRATION_TIME = 1.day
+
+ class << self
+ def update!(subnets)
+ caches.each { |cache| cache.write(GOOGLE_CDN_LIST_KEY, subnets) }
+ end
+
+ def ready?
+ caches.any? { |cache| cache.exist?(GOOGLE_CDN_LIST_KEY) }
+ end
+
+ def google_ip?(request_ip)
+ google_ip_ranges = cached_value(GOOGLE_CDN_LIST_KEY)
+
+ return false unless google_ip_ranges
+
+ google_ip_ranges.any? { |range| range.include?(request_ip) }
+ end
+
+ def async_refresh
+ ::GoogleCloud::FetchGoogleIpListWorker.perform_async
+ end
+
+ private
+
+ def caches
+ [l1_cache, l2_cache]
+ end
+
+ def l1_cache
+ Gitlab::ProcessMemoryCache.cache_backend
+ end
+
+ def l2_cache
+ Rails.cache
+ end
+
+ def cached_value(key)
+ l1_cache.fetch(key) do
+ result = l2_cache.fetch(key)
+
+ # Don't populate the L1 cache if we can't find the entry
+ break unless result
+
+ result
+ end
+ end
+ end
+ end
+ end
+end
+
+# rubocop:enable Naming/FileName
diff --git a/app/views/notify/approved_merge_request_email.html.haml b/app/views/notify/approved_merge_request_email.html.haml
index 68823fbe6b5..0b20d4f3d3a 100644
--- a/app/views/notify/approved_merge_request_email.html.haml
+++ b/app/views/notify/approved_merge_request_email.html.haml
@@ -93,7 +93,7 @@
%tr{ style: 'width:100%;' }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;text-align:center;" }
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:18px;width:18px;margin-bottom:-4px;", alt: "Merge request icon" }
- = merge_request_approved_description(@merge_request, @approved_by)
+ = s_('Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{reviewer_highlight}was approved by%{highlight_end} %{reviewer_avatar} %{reviewer_link}').html_safe % merge_request_hash_param(@merge_request, @approved_by)
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
diff --git a/app/views/notify/unapproved_merge_request_email.html.haml b/app/views/notify/unapproved_merge_request_email.html.haml
index 0b8fbe14228..94e2d0377aa 100644
--- a/app/views/notify/unapproved_merge_request_email.html.haml
+++ b/app/views/notify/unapproved_merge_request_email.html.haml
@@ -79,9 +79,11 @@
%img{ alt: "✗", height: "13", src: image_url('mailers/approval/icon-x-orange-inverted.gif'), style: "display:block;", width: "13" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
- if @merge_request.respond_to? :approvals_required
- %span Merge request was unapproved (#{@merge_request.approvals.count}/#{@merge_request.approvals_required})
+ %span
+ = s_('Notify|Merge request was unapproved (%{approvals_count}/%{approvals_required})') % {approvals_count: @merge_request.approvals.count, approvals_required: @merge_request.approvals_required}
- else
- %span Merge request was unapproved
+ %span
+ = s_('Notify|Merge request was unapproved')
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
@@ -92,12 +94,7 @@
%tr{ style: 'width:100%;' }
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;text-align:center;" }
%img{ src: image_url('mailers/approval/icon-merge-request-gray.gif'), style: "height:18px;width:18px;margin-bottom:-4px;", alt: "Merge request icon" }
- %span{ style: "font-weight: 600;color:#333333;" } Merge request
- %a{ href: merge_request_url(@merge_request), style: "font-weight: 600;color:#3777b0;text-decoration:none" }= @merge_request.to_reference
- %span was unapproved by
- %img.avatar{ height: "24", src: avatar_icon_for_user(@unapproved_by, 24), style: "border-radius:12px;margin:-7px 0 -7px 3px;", width: "24", alt: "Avatar" }/
- %a.muted{ href: user_url(@unapproved_by), style: "color:#333333;text-decoration:none;" }
- = @unapproved_by.name
+ = s_('Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{reviewer_highlight}was unapproved by%{highlight_end} %{reviewer_avatar} %{reviewer_link}').html_safe % merge_request_hash_param(@merge_request, @unapproved_by)
%tr.spacer
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
&nbsp;
@@ -106,7 +103,8 @@
%table.info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
%tbody
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" }
+ = _('Project')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;" }
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
@@ -116,7 +114,8 @@
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
= @project.name
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
+ = _('Branch')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
@@ -127,7 +126,8 @@
%span.muted{ style: "color:#333333;text-decoration:none;" }
= @merge_request.source_branch
%tr
- %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Author
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
+ = _('Author')
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;color:#333333;font-weight:400;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
%tbody
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 23904bc55bb..9b282340d0a 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -2433,6 +2433,15 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: google_cloud_fetch_google_ip_list
+ :worker_name: GoogleCloud::FetchGoogleIpListWorker
+ :feature_category: :build_artifacts
+ :has_external_dependencies: false
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: group_destroy
:worker_name: GroupDestroyWorker
:feature_category: :subgroups
diff --git a/app/workers/ci/build_finished_worker.rb b/app/workers/ci/build_finished_worker.rb
index 05c17eea250..7503ea3d800 100644
--- a/app/workers/ci/build_finished_worker.rb
+++ b/app/workers/ci/build_finished_worker.rb
@@ -39,7 +39,7 @@ module Ci
build.execute_hooks
ChatNotificationWorker.perform_async(build.id) if build.pipeline.chat?
build.track_deployment_usage
- build.track_verify_usage
+ build.track_verify_environment_usage
if build.failed? && !build.auto_retry_expected?
::Ci::MergeRequests::AddTodoWhenBuildFailsWorker.perform_async(build.id)
diff --git a/app/workers/google_cloud/fetch_google_ip_list_worker.rb b/app/workers/google_cloud/fetch_google_ip_list_worker.rb
new file mode 100644
index 00000000000..b14b4e735dc
--- /dev/null
+++ b/app/workers/google_cloud/fetch_google_ip_list_worker.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module GoogleCloud
+ class FetchGoogleIpListWorker
+ include ApplicationWorker
+
+ data_consistency :delayed
+ feature_category :build_artifacts
+ urgency :low
+ deduplicate :until_executing
+ idempotent!
+
+ def perform
+ GoogleCloud::FetchGoogleIpListService.new.execute
+ end
+ end
+end
diff --git a/config/feature_flags/development/ci_requeue_with_dag_object_hierarchy.yml b/config/feature_flags/development/ci_requeue_with_dag_object_hierarchy.yml
new file mode 100644
index 00000000000..5e27510629c
--- /dev/null
+++ b/config/feature_flags/development/ci_requeue_with_dag_object_hierarchy.yml
@@ -0,0 +1,8 @@
+---
+name: ci_requeue_with_dag_object_hierarchy
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97156
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/373148
+milestone: '15.4'
+type: development
+group: group::pipeline authoring
+default_enabled: false
diff --git a/config/feature_flags/development/restyle_login_page.yml b/config/feature_flags/development/restyle_login_page.yml
index ed3ae3ef6ea..bfe99590e6e 100644
--- a/config/feature_flags/development/restyle_login_page.yml
+++ b/config/feature_flags/development/restyle_login_page.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/368387
milestone: '15.2'
type: development
group: group::authentication and authorization
-default_enabled: false
+default_enabled: true
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index 69342184f54..40f3b6a2abd 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -203,6 +203,8 @@
- 1
- - google_cloud_create_cloudsql_instance
- 1
+- - google_cloud_fetch_google_ip_list
+ - 1
- - group_destroy
- 1
- - group_export
diff --git a/db/migrate/20220901124637_add_last_downloaded_at_to_packages.rb b/db/migrate/20220901124637_add_last_downloaded_at_to_packages.rb
new file mode 100644
index 00000000000..0172ab573ea
--- /dev/null
+++ b/db/migrate/20220901124637_add_last_downloaded_at_to_packages.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddLastDownloadedAtToPackages < Gitlab::Database::Migration[2.0]
+ def change
+ add_column :packages_packages, :last_downloaded_at, :datetime_with_timezone
+ end
+end
diff --git a/db/schema_migrations/20220901124637 b/db/schema_migrations/20220901124637
new file mode 100644
index 00000000000..b5a08a248e0
--- /dev/null
+++ b/db/schema_migrations/20220901124637
@@ -0,0 +1 @@
+59ea43b60e0fb009823d82e99494a7fcb31eeaddc0a6ccbf43009977cdd32526 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index ae5fb74d8f2..8c1c41cd2ff 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -18899,7 +18899,8 @@ CREATE TABLE packages_packages (
version character varying,
package_type smallint NOT NULL,
creator_id integer,
- status smallint DEFAULT 0 NOT NULL
+ status smallint DEFAULT 0 NOT NULL,
+ last_downloaded_at timestamp with time zone
);
CREATE SEQUENCE packages_packages_id_seq
diff --git a/doc/administration/audit_event_streaming.md b/doc/administration/audit_event_streaming.md
index 71ec916d2a3..9ec7b81bfd0 100644
--- a/doc/administration/audit_event_streaming.md
+++ b/doc/administration/audit_event_streaming.md
@@ -39,7 +39,7 @@ Streaming destinations receive **all** audit event data, which could include sen
Users with the Owner role for a group can add streaming destinations for it:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Security & Compliance > Audit events**.
1. On the main area, select **Streams** tab.
1. Select **Add streaming destination** to show the section for adding destinations.
@@ -161,7 +161,7 @@ Users with the Owner role for a group can update streaming destinations.
To update a streaming destinations custom HTTP headers:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Security & Compliance > Audit events**.
1. On the main area, select **Streams** tab.
1. To the right of the item, select **Edit** (**{pencil}**).
@@ -279,7 +279,7 @@ the destination's value when [listing streaming destinations](#list-streaming-de
Users with the Owner role for a group can list streaming destinations and see the verification tokens:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Security & Compliance > Audit events**.
1. On the main area, select the **Streams**.
1. View the verification token on the right side of each item.
diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md
index 47b8de2440b..b6c267bfd0c 100644
--- a/doc/administration/audit_events.md
+++ b/doc/administration/audit_events.md
@@ -309,7 +309,7 @@ audit events.
To export the audit events to CSV:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Audit Events**.
1. Select the available search [filters](#search).
1. Select **Export as CSV**.
diff --git a/doc/administration/geo/replication/container_registry.md b/doc/administration/geo/replication/container_registry.md
index 510c8745349..01ba81b6dbe 100644
--- a/doc/administration/geo/replication/container_registry.md
+++ b/doc/administration/geo/replication/container_registry.md
@@ -160,7 +160,7 @@ For each application and Sidekiq node on the **secondary** site:
To verify Container Registry replication is working, on the **secondary** site:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Nodes**.
The initial replication, or "backfill", is probably still in progress.
diff --git a/doc/administration/geo/replication/disable_geo.md b/doc/administration/geo/replication/disable_geo.md
index f0658ae45a2..84bc2e034b9 100644
--- a/doc/administration/geo/replication/disable_geo.md
+++ b/doc/administration/geo/replication/disable_geo.md
@@ -36,7 +36,7 @@ to do that.
To remove the **primary** site:
1. [Remove all secondary Geo sites](remove_geo_site.md)
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Nodes**.
1. Select **Remove** for the **primary** node.
1. Confirm by selecting **Remove** when the prompt appears.
diff --git a/doc/administration/geo/replication/object_storage.md b/doc/administration/geo/replication/object_storage.md
index d2e10678f8c..0336a1669f9 100644
--- a/doc/administration/geo/replication/object_storage.md
+++ b/doc/administration/geo/replication/object_storage.md
@@ -41,7 +41,7 @@ whether they are stored on the local file system or in object storage.
To enable GitLab replication:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Nodes**.
1. Select **Edit** on the **secondary** site.
1. In the **Synchronization Settings** section, find the **Allow this secondary node to replicate content on Object Storage**
diff --git a/doc/administration/geo/replication/remove_geo_site.md b/doc/administration/geo/replication/remove_geo_site.md
index 0d6715a93b7..b136f6cc8b8 100644
--- a/doc/administration/geo/replication/remove_geo_site.md
+++ b/doc/administration/geo/replication/remove_geo_site.md
@@ -9,7 +9,7 @@ type: howto
**Secondary** sites can be removed from the Geo cluster using the Geo administration page of the **primary** site. To remove a **secondary** site:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Nodes**.
1. Select the **Remove** button for the **secondary** site you want to remove.
1. Confirm by selecting **Remove** when the prompt appears.
diff --git a/doc/administration/geo/replication/troubleshooting.md b/doc/administration/geo/replication/troubleshooting.md
index 82107db79da..9575182dcf9 100644
--- a/doc/administration/geo/replication/troubleshooting.md
+++ b/doc/administration/geo/replication/troubleshooting.md
@@ -26,7 +26,7 @@ Before attempting more advanced troubleshooting:
On the **primary** site:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Sites**.
We perform the following health checks on each **secondary** site
@@ -275,7 +275,7 @@ sudo gitlab-rake gitlab:geo:check
Checking Geo ... Finished
```
- Ensure you have added the secondary site in the **Menu > Admin > Geo > Sites** on the web interface for the **primary** site.
+ Ensure you have added the secondary site in the **Main menu > Admin > Geo > Sites** on the web interface for the **primary** site.
Also ensure you entered the `gitlab_rails['geo_node_name']`
when adding the secondary site in the Admin Area of the **primary** site.
In GitLab 12.3 and earlier, edit the secondary site in the Admin Area of the **primary**
@@ -668,7 +668,7 @@ to start again from scratch, there are a few steps that can help you:
### Design repository failures on mirrored projects and project imports
-On the top bar, under **Menu > Admin > Geo > Sites**,
+On the top bar, under **Main menu > Admin > Geo > Sites**,
if the Design repositories progress bar shows
`Synced` and `Failed` greater than 100%, and negative `Queued`, the instance
is likely affected by
@@ -1004,7 +1004,7 @@ site's URL matches its external URL.
On the **primary** site:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Sites**.
1. Find the affected **secondary** site and select **Edit**.
1. Ensure the **URL** field matches the value found in `/etc/gitlab/gitlab.rb`
diff --git a/doc/administration/geo/replication/tuning.md b/doc/administration/geo/replication/tuning.md
index 755ab45a76c..370c50c93db 100644
--- a/doc/administration/geo/replication/tuning.md
+++ b/doc/administration/geo/replication/tuning.md
@@ -14,7 +14,7 @@ in the background.
On the **primary** site:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Geo > Sites**.
1. Select **Edit** of the secondary site you want to tune.
1. Under **Tuning settings**, there are several variables that can be tuned to
diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md
index 3c2c760ee5c..bd03aa1bdbc 100644
--- a/doc/administration/gitaly/praefect.md
+++ b/doc/administration/gitaly/praefect.md
@@ -1115,7 +1115,7 @@ Particular attention should be shown to:
1. Check that the Praefect storage is configured to store new repositories:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Repository**.
1. Expand the **Repository storage** section.
diff --git a/doc/administration/gitaly/troubleshooting.md b/doc/administration/gitaly/troubleshooting.md
index e308fa9da43..285ec3d2631 100644
--- a/doc/administration/gitaly/troubleshooting.md
+++ b/doc/administration/gitaly/troubleshooting.md
@@ -20,7 +20,7 @@ and our advice on [parsing the `gitaly/current` file](../logs/log_parsing.md#par
When using standalone Gitaly servers, you must make sure they are the same version
as GitLab to ensure full compatibility:
-1. On the top bar, select **Menu > Admin** on your GitLab instance.
+1. On the top bar, select **Main menu > Admin** on your GitLab instance.
1. On the left sidebar, select **Overview > Gitaly Servers**.
1. Confirm all Gitaly servers indicate that they are up to date.
diff --git a/doc/administration/housekeeping.md b/doc/administration/housekeeping.md
index 31b7ac9fd3e..cb0156f8e2d 100644
--- a/doc/administration/housekeeping.md
+++ b/doc/administration/housekeeping.md
@@ -27,7 +27,7 @@ GitLab automatically runs `git gc` and `git repack` on repositories after Git pu
You can change how often this happens or turn it off:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Repository maintenance**.
1. In the **Housekeeping** section, configure the [housekeeping options](#housekeeping-options).
diff --git a/doc/administration/integration/kroki.md b/doc/administration/integration/kroki.md
index a6fcfe6c80f..fb4659175b0 100644
--- a/doc/administration/integration/kroki.md
+++ b/doc/administration/integration/kroki.md
@@ -56,7 +56,7 @@ read the [Kroki installation](https://docs.kroki.io/kroki/setup/install/#_images
You need to enable Kroki integration from Settings under Admin Area.
To do that, log in with an administrator account and follow these steps:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. Go to **Settings > General**.
1. Expand the **Kroki** section.
1. Select **Enable Kroki** checkbox.
diff --git a/doc/administration/integration/mailgun.md b/doc/administration/integration/mailgun.md
index c007116d213..37e81f220cf 100644
--- a/doc/administration/integration/mailgun.md
+++ b/doc/administration/integration/mailgun.md
@@ -43,7 +43,7 @@ After configuring your Mailgun domain for the webhook endpoints,
you're ready to enable the Mailgun integration:
1. Sign in to GitLab as an [Administrator](../../user/permissions.md) user.
-1. On the top bar, select **Menu >** **{admin}** **Admin**.
+1. On the top bar, select **Main menu >** **{admin}** **Admin**.
1. On the left sidebar, go to **Settings > General** and expand the **Mailgun** section.
1. Select the **Enable Mailgun** check box.
1. Enter the Mailgun HTTP webhook signing key as described in
diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md
index 58c9e01a151..de790c7ce40 100644
--- a/doc/administration/integration/plantuml.md
+++ b/doc/administration/integration/plantuml.md
@@ -211,7 +211,7 @@ stop;
After configuring your local PlantUML server, you're ready to enable the PlantUML integration:
1. Sign in to GitLab as an [Administrator](../../user/permissions.md) user.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, go to **Settings > General** and expand the **PlantUML** section.
1. Select the **Enable PlantUML** checkbox.
1. Set the PlantUML instance as `https://gitlab.example.com/-/plantuml/`,
diff --git a/doc/administration/maintenance_mode/index.md b/doc/administration/maintenance_mode/index.md
index 00e28e650ea..60de8e2fd3a 100644
--- a/doc/administration/maintenance_mode/index.md
+++ b/doc/administration/maintenance_mode/index.md
@@ -21,7 +21,7 @@ Maintenance Mode allows most external actions that do not change internal state.
There are three ways to enable Maintenance Mode as an administrator:
- **Web UI**:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand **Maintenance Mode**, and toggle **Enable Maintenance Mode**.
You can optionally add a message for the banner as well.
@@ -45,7 +45,7 @@ There are three ways to enable Maintenance Mode as an administrator:
There are three ways to disable Maintenance Mode:
- **Web UI**:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand **Maintenance Mode**, and toggle **Enable Maintenance Mode**.
You can optionally add a message for the banner as well.
@@ -173,7 +173,7 @@ it is recommended that you disable all cron jobs except for those related to Geo
To monitor queues and disable jobs:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
### Incident management
diff --git a/doc/administration/monitoring/gitlab_self_monitoring_project/index.md b/doc/administration/monitoring/gitlab_self_monitoring_project/index.md
index 2553638291d..fc205f49e69 100644
--- a/doc/administration/monitoring/gitlab_self_monitoring_project/index.md
+++ b/doc/administration/monitoring/gitlab_self_monitoring_project/index.md
@@ -38,12 +38,12 @@ This project can be used to:
## Create the self monitoring project
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling** and expand **Self monitoring**.
1. Toggle **Self monitoring** on.
1. After your GitLab instance creates the project, GitLab displays a link to the
project in the text above the **Self monitoring** toggle. You can also find it
- from the top bar by selecting **Menu > Project**, then selecting **Your projects**.
+ from the top bar by selecting **Main menu > Project**, then selecting **Your projects**.
## Delete the self monitoring project
@@ -51,7 +51,7 @@ WARNING:
Deleting the self monitoring project removes any changes made to the project. If
you create the project again, it's created in its default state.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, go to **Settings > Metrics and profiling** and expand **Self monitoring**.
1. Toggle **Self monitoring** off.
1. In the confirmation dialog that opens, select **Delete self monitoring project**.
diff --git a/doc/administration/monitoring/performance/gitlab_configuration.md b/doc/administration/monitoring/performance/gitlab_configuration.md
index 128ddad6555..74db35eebc2 100644
--- a/doc/administration/monitoring/performance/gitlab_configuration.md
+++ b/doc/administration/monitoring/performance/gitlab_configuration.md
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
GitLab Performance Monitoring is disabled by default. To enable it and change any of its
settings:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**
(`/admin/application_settings/metrics_and_profiling`).
1. Add the necessary configuration changes.
diff --git a/doc/administration/monitoring/performance/grafana_configuration.md b/doc/administration/monitoring/performance/grafana_configuration.md
index 3a76e2e4578..6e9ea0d8d42 100644
--- a/doc/administration/monitoring/performance/grafana_configuration.md
+++ b/doc/administration/monitoring/performance/grafana_configuration.md
@@ -62,7 +62,7 @@ repository.
After setting up Grafana, you can enable a link to access it easily from the
GitLab sidebar:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**
and expand **Metrics - Grafana**.
1. Select the **Add a link to Grafana** checkbox.
@@ -72,14 +72,14 @@ GitLab sidebar:
- *Otherwise,* enter the full URL of the Grafana instance.
1. Select **Save changes**.
-GitLab displays your link in the **Menu > Admin > Monitoring > Metrics Dashboard**.
+GitLab displays your link in the **Main menu > Admin > Monitoring > Metrics Dashboard**.
## Required Scopes
> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5822) in GitLab 13.10.
When setting up Grafana through the process above, no scope shows in the screen at
-**Menu > Admin > Applications > GitLab Grafana**. However, the `read_user` scope is
+**Main menu > Admin > Applications > GitLab Grafana**. However, the `read_user` scope is
required and is provided to the application automatically. Setting any scope other than
`read_user` without also including `read_user` leads to this error when you try to log in using
GitLab as the OAuth provider:
diff --git a/doc/administration/monitoring/performance/performance_bar.md b/doc/administration/monitoring/performance/performance_bar.md
index 759f485c109..c23046158e1 100644
--- a/doc/administration/monitoring/performance/performance_bar.md
+++ b/doc/administration/monitoring/performance/performance_bar.md
@@ -108,7 +108,7 @@ The performance bar is disabled by default for non-administrators. To enable it
for a given group:
1. Sign in as a user with administrator access.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**
(`admin/application_settings/metrics_and_profiling`), and expand
**Profiling - Performance bar**.
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 9618ec0140b..00dae8e4dd5 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
To enable the GitLab Prometheus metrics:
1. Log in to GitLab as a user with administrator access.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Metrics and profiling**.
1. Find the **Metrics - Prometheus** section, and select **Add link to Prometheus**.
1. [Restart GitLab](../../restart_gitlab.md#omnibus-gitlab-restart) for the changes to take effect.
diff --git a/doc/administration/operations/fast_ssh_key_lookup.md b/doc/administration/operations/fast_ssh_key_lookup.md
index a3240a6041b..8523b881730 100644
--- a/doc/administration/operations/fast_ssh_key_lookup.md
+++ b/doc/administration/operations/fast_ssh_key_lookup.md
@@ -106,7 +106,7 @@ users as long as a large file exists.
To disable writes to the `authorized_keys` file:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Performance optimization**.
1. Clear the **Use authorized_keys file to authenticate SSH keys** checkbox.
@@ -125,7 +125,7 @@ This overview is brief. Refer to the above instructions for more context.
1. [Rebuild the `authorized_keys` file](../raketasks/maintenance.md#rebuild-authorized_keys-file).
1. Enable writes to the `authorized_keys` file.
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Network**.
1. Expand **Performance optimization**.
1. Select the **Use authorized_keys file to authenticate SSH keys** checkbox.
diff --git a/doc/administration/packages/container_registry.md b/doc/administration/packages/container_registry.md
index 69c00b42d35..13a2ade3464 100644
--- a/doc/administration/packages/container_registry.md
+++ b/doc/administration/packages/container_registry.md
@@ -322,7 +322,7 @@ the Container Registry by themselves, follow the steps below.
In GitLab, tokens for the Container Registry expire every five minutes.
To increase the token duration:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **Container Registry**.
1. For the **Authorization token duration (minutes)**, update the value.
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index c5b4852eda8..9d095bdac22 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -403,7 +403,7 @@ domain as a custom domain to their project.
If your user base is private or otherwise trusted, you can disable the
verification requirement:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Pages**.
1. Clear the **Require users to prove ownership of custom domains** checkbox.
@@ -420,7 +420,7 @@ sites served under a custom domain.
To enable it:
1. Choose an email address on which you want to receive notifications about expiring domains.
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Pages**.
1. Enter the email address for receiving notifications and accept Let's Encrypt's Terms of Service.
@@ -473,7 +473,7 @@ pre-existing applications must modify the GitLab Pages OAuth application. Follow
this:
1. Enable [access control](#access-control).
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Applications**.
1. Expand **GitLab Pages**.
1. Clear the `api` scope's checkbox and select the desired scope's checkbox (for example,
@@ -492,7 +492,7 @@ This can be helpful to restrict information published with Pages websites to the
of your instance only.
To do that:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Pages**.
1. Select the **Disable public access to Pages sites** checkbox.
@@ -705,7 +705,7 @@ Prerequisites:
To set the global maximum pages size for a project:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Pages**.
1. Enter a value under **Maximum size of pages**.
@@ -719,7 +719,7 @@ Prerequisites:
To set the maximum size of each GitLab Pages site in a group, overriding the inherited setting:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Pages**.
1. Enter a value under **Maximum size** in MB.
@@ -733,7 +733,7 @@ Prerequisites:
To set the maximum size of GitLab Pages site in a project, overriding the inherited setting:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Pages**.
1. Enter a value under **Maximum size of pages** in MB.
1. Select **Save changes**.
@@ -746,7 +746,7 @@ Prerequisite:
To set the maximum number of GitLab Pages custom domains for a project:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**, and expand **Pages**.
1. Enter a value for **Maximum number of custom domains per project**. Use `0` for unlimited domains.
1. Select **Save changes**.
@@ -1398,7 +1398,7 @@ Upgrading to an [officially supported operating system](https://about.gitlab.com
This problem comes from the permissions of the GitLab Pages OAuth application. To fix it:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Applications > GitLab Pages**.
1. Edit the application.
1. Under **Scopes**, ensure that the `api` scope is selected.
diff --git a/doc/administration/pages/source.md b/doc/administration/pages/source.md
index 5bee96081bb..14ac05e0293 100644
--- a/doc/administration/pages/source.md
+++ b/doc/administration/pages/source.md
@@ -485,7 +485,7 @@ The default for the maximum size of unpacked archives per project is 100 MB.
To change this value:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Pages**.
1. Update the value for **Maximum size of pages (MB)**.
diff --git a/doc/administration/raketasks/project_import_export.md b/doc/administration/raketasks/project_import_export.md
index f3a9845d129..00bd71af6c5 100644
--- a/doc/administration/raketasks/project_import_export.md
+++ b/doc/administration/raketasks/project_import_export.md
@@ -45,7 +45,7 @@ Note the following:
compatible as described in the [Version history](../../user/project/settings/import_export.md#version-history).
- The project import option must be enabled:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand **Visibility and access controls**.
1. Under **Import sources**, check the "Project export enabled" option.
diff --git a/doc/administration/raketasks/storage.md b/doc/administration/raketasks/storage.md
index 27b899dd1b1..fc0ff23c5b1 100644
--- a/doc/administration/raketasks/storage.md
+++ b/doc/administration/raketasks/storage.md
@@ -109,7 +109,7 @@ sudo gitlab-rake gitlab:storage:migrate_to_hashed ID_FROM=50 ID_TO=100
To monitor the progress in GitLab:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
1. Watch how long the `hashed_storage:hashed_storage_project_migrate` queue
takes to finish. After it reaches zero, you can confirm every project
diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md
index b61ad1efe63..4615499d6fa 100644
--- a/doc/administration/repository_checks.md
+++ b/doc/administration/repository_checks.md
@@ -20,7 +20,7 @@ committed to a repository. GitLab administrators can:
To check a project's repository using GitLab UI:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Projects**.
1. Select the project to check.
1. In the **Repository check** section, select **Trigger repository check**.
@@ -81,7 +81,7 @@ If a repository check fails, locate the error in the [`repocheck.log` file](logs
If periodic repository checks cause false alarms, you can clear all repository check states:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Repository** (`/admin/application_settings/repository`).
1. Expand the **Repository maintenance** section.
1. Select **Clear all repository checks**.
diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md
index 7ce629b5d71..865daba9e89 100644
--- a/doc/administration/repository_storage_types.md
+++ b/doc/administration/repository_storage_types.md
@@ -79,7 +79,7 @@ Administrators can look up a project's hashed path from its name or ID using:
To look up a project's hash path in the Admin Area:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Projects** and select the project.
The **Gitaly relative path** is displayed there and looks similar to:
diff --git a/doc/administration/sidekiq/extra_sidekiq_processes.md b/doc/administration/sidekiq/extra_sidekiq_processes.md
index 1cd3771c94d..b774b0d3e14 100644
--- a/doc/administration/sidekiq/extra_sidekiq_processes.md
+++ b/doc/administration/sidekiq/extra_sidekiq_processes.md
@@ -95,7 +95,7 @@ To start multiple processes:
To view the Sidekiq processes in GitLab:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
## Negate settings
diff --git a/doc/administration/static_objects_external_storage.md b/doc/administration/static_objects_external_storage.md
index 21949388f19..a288b19f164 100644
--- a/doc/administration/static_objects_external_storage.md
+++ b/doc/administration/static_objects_external_storage.md
@@ -16,7 +16,7 @@ storage such as a content delivery network (CDN).
To configure external storage for static objects:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Repository**.
1. Expand the **External storage for repository static objects** section.
1. Enter the base URL and an arbitrary token. When you [set up external storage](#set-up-external-storage),
diff --git a/doc/administration/whats-new.md b/doc/administration/whats-new.md
index d7b5c35b1a8..d8003d579ac 100644
--- a/doc/administration/whats-new.md
+++ b/doc/administration/whats-new.md
@@ -31,7 +31,7 @@ To access the **What's new** feature:
You can configure **What's new** to display features based on the tier,
or you can hide it. To configure it:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **What's new**, and choose one of the following options:
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 2cccf8c30e3..86e264b510e 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -13934,6 +13934,7 @@ Represents the merge access level of a branch protection.
| <a id="mergerequestsquashonmerge"></a>`squashOnMerge` | [`Boolean!`](#boolean) | Indicates if squash on merge is enabled. |
| <a id="mergerequeststate"></a>`state` | [`MergeRequestState!`](#mergerequeststate) | State of the merge request. |
| <a id="mergerequestsubscribed"></a>`subscribed` | [`Boolean!`](#boolean) | Indicates if the currently logged in user is subscribed to this merge request. |
+| <a id="mergerequestsuggestedreviewers"></a>`suggestedReviewers` **{warning-solid}** | [`SuggestedReviewersType`](#suggestedreviewerstype) | **Introduced** in 15.4. This feature is in Alpha. It can be changed or removed at any time. Suggested reviewers for merge request. Returns `null` if `suggested_reviewers` feature flag is disabled. This flag is disabled by default and only available on GitLab.com because the feature is experimental and is subject to change without notice. |
| <a id="mergerequesttargetbranch"></a>`targetBranch` | [`String!`](#string) | Target branch of the merge request. |
| <a id="mergerequesttargetbranchexists"></a>`targetBranchExists` | [`Boolean!`](#boolean) | Indicates if the target branch of the merge request exists. |
| <a id="mergerequesttargetproject"></a>`targetProject` | [`Project!`](#project) | Target project of the merge request. |
@@ -15414,6 +15415,7 @@ Represents a package details in the Package Registry.
| <a id="packagedetailstypecreatedat"></a>`createdAt` | [`Time!`](#time) | Date of creation. |
| <a id="packagedetailstypedependencylinks"></a>`dependencyLinks` | [`PackageDependencyLinkConnection`](#packagedependencylinkconnection) | Dependency link. (see [Connections](#connections)) |
| <a id="packagedetailstypeid"></a>`id` | [`PackagesPackageID!`](#packagespackageid) | ID of the package. |
+| <a id="packagedetailstypelastdownloadedat"></a>`lastDownloadedAt` | [`Time`](#time) | Last time that a file of this package was downloaded. |
| <a id="packagedetailstypemavenurl"></a>`mavenUrl` | [`String`](#string) | Url of the Maven project endpoint. |
| <a id="packagedetailstypemetadata"></a>`metadata` | [`PackageMetadata`](#packagemetadata) | Package metadata. |
| <a id="packagedetailstypename"></a>`name` | [`String!`](#string) | Name of the package. |
@@ -18044,6 +18046,18 @@ Represents an entry from the future subscriptions.
| <a id="subscriptionfutureentrytype"></a>`type` | [`String!`](#string) | Type of license the subscription will yield. |
| <a id="subscriptionfutureentryusersinlicensecount"></a>`usersInLicenseCount` | [`Int`](#int) | Number of paid user seats. |
+### `SuggestedReviewersType`
+
+Represents a Suggested Reviewers result set.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="suggestedreviewerstypereviewers"></a>`reviewers` | [`[String!]!`](#string) | List of reviewers. |
+| <a id="suggestedreviewerstypetopn"></a>`topN` | [`Int`](#int) | Number of reviewers returned. |
+| <a id="suggestedreviewerstypeversion"></a>`version` | [`String`](#string) | Suggested reviewer version. |
+
### `TaskCompletionStatus`
Completion status of tasks.
diff --git a/doc/api/merge_request_context_commits.md b/doc/api/merge_request_context_commits.md
index 9984c5abb70..08020e5a613 100644
--- a/doc/api/merge_request_context_commits.md
+++ b/doc/api/merge_request_context_commits.md
@@ -17,10 +17,10 @@ GET /projects/:id/merge_requests/:merge_request_iid/context_commits
Parameters:
-| Attribute | Type | Required | Description |
-|---------------------|---------|----------|-----------------------------------------------------------------------------------------------------------------|
-| `id` | integer | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request |
+| Attribute | Type | Required | Description |
+|---------------------|---------|----------|-------------|
+| `id` | integer | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `merge_request_iid` | integer | **{check-circle}** Yes | The internal ID of the merge request. |
```json
[
@@ -51,18 +51,18 @@ POST /projects/:id/merge_requests/:merge_request_iid/context_commits
Parameters:
-| Attribute | Type | Required | Description |
-|---------------------|---------|----------|-----------------------------------------------------------------------------------------------------------------|
-| `id` | integer | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request |
+| Attribute | Type | Required | Description |
+|---------------------|---------|----------|-------------|
+| `id` | integer | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
+| `merge_request_iid` | integer | **{check-circle}** Yes | The internal ID of the merge request. |
```plaintext
POST /projects/:id/merge_requests/
```
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `commits` | string array | yes | The context commits' SHA |
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `commits` | string array | **{check-circle}** Yes | The context commits' SHA. |
```json
[
@@ -92,8 +92,8 @@ DELETE /projects/:id/merge_requests/:merge_request_iid/context_commits
Parameters:
-| Attribute | Type | Required | Description |
-|---------------------|--------------|----------|-----------------------------------------------------------------------------------------------------------------|
-| `id` | integer | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user |
-| `merge_request_iid` | integer | yes | The internal ID of the merge request |
-| `commits` | string array | yes | The context commits' SHA |
+| Attribute | Type | Required | Description |
+|---------------------|--------------|----------|--------------|
+| `commits` | string array | **{check-circle}** Yes | The context commits' SHA. |
+| `id` | integer | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `merge_request_iid` | integer | **{check-circle}** Yes | The internal ID of the merge request. |
diff --git a/doc/api/packages.md b/doc/api/packages.md
index 3cd93bd09c1..d91f4f0de7b 100644
--- a/doc/api/packages.md
+++ b/doc/api/packages.md
@@ -213,6 +213,7 @@ Example response:
"delete_api_path": "/namespace1/project1/-/packages/1"
},
"created_at": "2019-11-27T03:37:38.711Z",
+ "last_downloaded_at": "2022-09-07T07:51:50.504Z"
"pipelines": [
{
"id": 123,
diff --git a/doc/api/project_templates.md b/doc/api/project_templates.md
index 7763087e759..14dd0761487 100644
--- a/doc/api/project_templates.md
+++ b/doc/api/project_templates.md
@@ -30,8 +30,8 @@ GET /projects/:id/templates/:type
| Attribute | Type | Required | Description |
| ---------- | ------ | -------- | ----------- |
-| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) |
-| `type` | string | yes | The type of the template. Accepted values are: `dockerfiles`, `gitignores`, `gitlab_ci_ymls`, `licenses`, `issues`, `merge_requests` |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
+| `type` | string | **{check-circle}** Yes | The type of the template. Accepted values are: `dockerfiles`, `gitignores`, `gitlab_ci_ymls`, `licenses`, `issues`, or `merge_requests`. |
Example response (licenses):
@@ -96,12 +96,12 @@ GET /projects/:id/templates/:type/:name
| Attribute | Type | Required | Description |
| ---------- | ------ | -------- | ----------- |
-| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) |
-| `type` | string | yes| The type of the template. One of: `dockerfiles`, `gitignores`, `gitlab_ci_ymls`, `licenses`, `issues`, or `merge_requests`. |
-| `name` | string | yes | The key of the template, as obtained from the collection endpoint |
-| `source_template_project_id` | integer | no | The project ID where a given template is being stored. This is useful when multiple templates from different projects have the same name. If multiple templates have the same name, the match from `closest ancestor` is returned if `source_template_project_id` is not specified |
-| `project` | string | no | The project name to use when expanding placeholders in the template. Only affects licenses |
-| `fullname` | string | no | The full name of the copyright holder to use when expanding placeholders in the template. Only affects licenses |
+| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
+| `name` | string | **{check-circle}** Yes | The key of the template, as obtained from the collection endpoint. |
+| `type` | string | **{check-circle}** Yes | The type of the template. One of: `dockerfiles`, `gitignores`, `gitlab_ci_ymls`, `licenses`, `issues`, or `merge_requests`. |
+| `fullname` | string | **{dotted-circle}** No | The full name of the copyright holder to use when expanding placeholders in the template. Affects only licenses. |
+| `project` | string | **{dotted-circle}** No | The project name to use when expanding placeholders in the template. Affects only licenses. |
+| `source_template_project_id` | integer | **{dotted-circle}** No | The project ID where a given template is being stored. Helpful when multiple templates from different projects have the same name. If multiple templates have the same name, the match from `closest ancestor` is returned if `source_template_project_id` is not specified, |
Example response (Dockerfile):
diff --git a/doc/api/system_hooks.md b/doc/api/system_hooks.md
index a883c3c1613..14339460270 100644
--- a/doc/api/system_hooks.md
+++ b/doc/api/system_hooks.md
@@ -10,7 +10,7 @@ All methods require administrator authorization.
You can configure the URL endpoint of the system hooks from the GitLab user interface:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. Select **System Hooks** (`/admin/hooks`).
Read more about [system hooks](../administration/system_hooks.md).
diff --git a/doc/ci/caching/index.md b/doc/ci/caching/index.md
index 9269b8886c7..d34f44ea9ba 100644
--- a/doc/ci/caching/index.md
+++ b/doc/ci/caching/index.md
@@ -471,7 +471,7 @@ and should only be disabled in an environment where all users with Developer rol
To use the same cache for all branches:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > CI/CD**.
1. Expand **General pipelines**.
1. Clear the **Use separate caches for protected branches** checkbox.
@@ -568,7 +568,7 @@ The next time the pipeline runs, the cache is stored in a different location.
You can clear the cache in the GitLab UI:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **CI/CD > Pipelines**.
1. In the top right, select **Clear runner caches**.
diff --git a/doc/ci/ci_cd_for_external_repos/index.md b/doc/ci/ci_cd_for_external_repos/index.md
index 826112f32be..00685df049d 100644
--- a/doc/ci/ci_cd_for_external_repos/index.md
+++ b/doc/ci/ci_cd_for_external_repos/index.md
@@ -24,7 +24,7 @@ snippets disabled. These features
To connect to an external repository:
-1. On the top bar, select **Menu > Projects > Create new project**.
+1. On the top bar, select **Main menu > Projects > Create new project**.
1. Select **Run CI/CD for external repository**.
1. Select **GitHub** or **Repository by URL**.
1. Complete the fields.
diff --git a/doc/ci/enable_or_disable_ci.md b/doc/ci/enable_or_disable_ci.md
index dac52a4540e..bac06972c7b 100644
--- a/doc/ci/enable_or_disable_ci.md
+++ b/doc/ci/enable_or_disable_ci.md
@@ -30,7 +30,7 @@ When you disable GitLab CI/CD:
To disable GitLab CI/CD in your project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Visibility, project features, permissions**.
1. In the **Repository** section, turn off **CI/CD**.
@@ -40,7 +40,7 @@ To disable GitLab CI/CD in your project:
To enable GitLab CI/CD in your project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Visibility, project features, permissions**.
1. In the **Repository** section, turn on **CI/CD**.
diff --git a/doc/ci/environments/deployment_approvals.md b/doc/ci/environments/deployment_approvals.md
index 23fa5d45196..78ce7f81ddc 100644
--- a/doc/ci/environments/deployment_approvals.md
+++ b/doc/ci/environments/deployment_approvals.md
@@ -137,7 +137,7 @@ Prerequisites:
To approve or reject a deployment to a protected environment using the UI:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Environments**.
1. Select the environment's name.
1. In the deployment's row, select **Approval options** (**{thumb-up}**).
@@ -169,7 +169,7 @@ curl --data "status=approved&comment=Looks good to me" \
### Using the UI
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Environments**.
1. Select the environment being deployed to.
1. Look for the `blocked` label.
diff --git a/doc/ci/environments/environments_dashboard.md b/doc/ci/environments/environments_dashboard.md
index c3c1e7868fd..11e9fe90e25 100644
--- a/doc/ci/environments/environments_dashboard.md
+++ b/doc/ci/environments/environments_dashboard.md
@@ -21,7 +21,7 @@ diagnose if there is a block at a particular point, or if there's
a more systemic problem you need to investigate.
You can access the dashboard on the top bar by selecting
-**Menu > Environments**.
+**Main menu > Environments**.
![Environments Dashboard with projects](img/environments_dashboard_v12_5.png)
diff --git a/doc/ci/lint.md b/doc/ci/lint.md
index 0811cc87e91..8c64d968b8b 100644
--- a/doc/ci/lint.md
+++ b/doc/ci/lint.md
@@ -24,7 +24,7 @@ configuration added with the [`includes` keyword](yaml/index.md#include).
To check CI/CD configuration with the CI lint tool:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **CI/CD > Pipelines**.
1. In the top right, select **CI lint**.
1. Paste a copy of the CI/CD configuration you want to check into the text box.
@@ -45,7 +45,7 @@ Prerequisites:
To simulate a pipeline:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **CI/CD > Pipelines**.
1. In the top right, select **CI lint**.
1. Paste a copy of the CI/CD configuration you want to check into the text box.
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index d30584de80e..f8deca1af1a 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -42,7 +42,7 @@ run only the jobs that match the type of contribution. If your contribution cont
**only** documentation changes, then only documentation-related jobs run, and
the pipeline completes much faster than a code contribution.
-If you are submitting documentation-only changes to Omnibus or Charts,
+If you are submitting documentation-only changes to Omnibus, Charts, or Operator,
the fast pipeline is not determined automatically. Instead, create branches for
docs-only merge requests using the following guide:
diff --git a/doc/development/internal_users.md b/doc/development/internal_users.md
index 95ca593e31e..24785c0cf48 100644
--- a/doc/development/internal_users.md
+++ b/doc/development/internal_users.md
@@ -8,6 +8,8 @@ info: "See the Technical Writers assigned to Development Guidelines: https://abo
# Internal users
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/97584) in GitLab 15.4, bots are indicated with a badge in user listings.
+
GitLab uses internal users (sometimes referred to as "bots") to perform
actions or functions that cannot be attributed to a regular user.
diff --git a/doc/install/azure/index.md b/doc/install/azure/index.md
index 9c42a244f88..dd2e7d678e8 100644
--- a/doc/install/azure/index.md
+++ b/doc/install/azure/index.md
@@ -248,7 +248,7 @@ in this section whenever you need to update GitLab.
To determine the version of GitLab you're currently running:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Dashboard**.
1. Find the version under the **Components** table.
diff --git a/doc/user/admin_area/review_abuse_reports.md b/doc/user/admin_area/review_abuse_reports.md
index c5b6b3d86ba..16295f340d8 100644
--- a/doc/user/admin_area/review_abuse_reports.md
+++ b/doc/user/admin_area/review_abuse_reports.md
@@ -16,7 +16,7 @@ reports in the Admin Area.
To receive notifications of new abuse reports by email:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Reporting**.
1. Expand the **Abuse reports** section.
1. Provide an email address and select **Save changes**.
@@ -33,7 +33,7 @@ To find out more about reporting abuse, see
To access abuse reports:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Abuse Reports**.
There are 3 ways to resolve an abuse report, with a button for each method:
diff --git a/doc/user/analytics/ci_cd_analytics.md b/doc/user/analytics/ci_cd_analytics.md
index 09746e91b77..b92d68222f5 100644
--- a/doc/user/analytics/ci_cd_analytics.md
+++ b/doc/user/analytics/ci_cd_analytics.md
@@ -32,7 +32,7 @@ View pipeline duration history:
To view CI/CD analytics:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > CI/CD Analytics**.
## View deployment frequency chart **(ULTIMATE)**
@@ -50,7 +50,7 @@ The deployment frequency chart is available for groups and projects.
To view the deployment frequency chart:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > CI/CD Analytics**.
1. Select the **Deployment frequency** tab.
@@ -72,7 +72,7 @@ merge requests to be deployed to a production environment. This chart is availab
To view the lead time for changes chart:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > CI/CD Analytics**.
1. Select the **Lead time** tab.
@@ -88,7 +88,7 @@ Time to restore service is one of the four [DORA metrics](index.md#devops-resear
To view the time to restore service chart:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > CI/CD Analytics**.
1. Select the **Time to restore service** tab.
@@ -104,6 +104,6 @@ Change failure rate is one of the four [DORA metrics](index.md#devops-research-a
To view the change failure rate chart:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > CI/CD Analytics**.
1. Select the **Change failure rate** tab.
diff --git a/doc/user/analytics/code_review_analytics.md b/doc/user/analytics/code_review_analytics.md
index 67ec5c41951..dd985b6b090 100644
--- a/doc/user/analytics/code_review_analytics.md
+++ b/doc/user/analytics/code_review_analytics.md
@@ -32,6 +32,6 @@ Prerequisite:
To view code review analytics:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Code Review**.
1. Filter merge requests by milestone and label.
diff --git a/doc/user/analytics/issue_analytics.md b/doc/user/analytics/issue_analytics.md
index 62fff443073..86ae45a02b8 100644
--- a/doc/user/analytics/issue_analytics.md
+++ b/doc/user/analytics/issue_analytics.md
@@ -15,7 +15,7 @@ prior.
To access the chart:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Issue**.
Hover over each bar to see the total number of issues.
diff --git a/doc/user/analytics/merge_request_analytics.md b/doc/user/analytics/merge_request_analytics.md
index 038b2f0c97e..4f40575a4ef 100644
--- a/doc/user/analytics/merge_request_analytics.md
+++ b/doc/user/analytics/merge_request_analytics.md
@@ -28,7 +28,7 @@ You must have at least the Reporter role to view merge request analytics.
To view merge request analytics:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Merge request**.
## View the number of merge requests in a date range
@@ -38,7 +38,7 @@ To view merge request analytics:
To view the number of merge requests merged during a specific date range:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Merge request**.
1. Optional. Filter results:
1. Select the filter bar.
@@ -63,6 +63,6 @@ created and when it's merged. Closed and un-merged merge requests are not includ
To view **Mean time to merge**:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Merge request**. The **Mean time to merge** number
is shown on the dashboard.
diff --git a/doc/user/analytics/productivity_analytics.md b/doc/user/analytics/productivity_analytics.md
index f8b28ead155..f0841dc979b 100644
--- a/doc/user/analytics/productivity_analytics.md
+++ b/doc/user/analytics/productivity_analytics.md
@@ -26,7 +26,7 @@ Prerequisite:
- You must have at least the Reporter role for the group.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Productivity**.
1. Optional. Filter results:
1. Select a project from the dropdown list.
@@ -44,7 +44,7 @@ Use the following charts in productivity analytics to view the velocity of your
merge requests to merge after they were created.
- **Trendline**: number of merge requests that were merged in a specific time period.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Productivity**.
To filter time metrics:
@@ -56,7 +56,7 @@ To filter time metrics:
To view commit statistics for your group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Productivity**.
1. Under the **Trendline** scatterplot, view the commit statistics:
- The left histogram shows the number of hours between commits, comments, and merges.
diff --git a/doc/user/analytics/repository_analytics.md b/doc/user/analytics/repository_analytics.md
index 5fd4a567b58..7723da7397d 100644
--- a/doc/user/analytics/repository_analytics.md
+++ b/doc/user/analytics/repository_analytics.md
@@ -30,7 +30,7 @@ Commits in a project's [wiki](../project/wiki/index.md#track-wiki-events) are no
To review repository analytics for a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Repository**.
## How repository analytics chart data is updated
diff --git a/doc/user/analytics/value_stream_analytics.md b/doc/user/analytics/value_stream_analytics.md
index 3d2d9b2bdf5..d5756c5f822 100644
--- a/doc/user/analytics/value_stream_analytics.md
+++ b/doc/user/analytics/value_stream_analytics.md
@@ -32,7 +32,7 @@ Value stream analytics is also available for [groups](../group/value_stream_anal
To view value stream analytics for your project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Value stream**.
1. To view metrics for a particular stage, select a stage below the **Filter results** text box.
1. Optional. Filter the results:
@@ -61,7 +61,7 @@ Value stream analytics shows the median time spent by issues or merge requests i
To view the median time spent in each stage:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Value stream**.
1. Optional. Filter the results:
1. Select the **Filter results** text box.
@@ -81,7 +81,7 @@ Value stream analytics shows the lead time and cycle time for issues in your pro
To view the lead time and cycle time for issues:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Value stream**.
1. Optional. Filter the results:
1. Select the **Filter results** text box.
@@ -101,7 +101,7 @@ Lead time for changes is the median duration between when a merge request is mer
To view the lead time for changes for merge requests in your project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Value stream**.
1. Optional. Filter the results:
1. Select the **Filter results** text box.
@@ -132,7 +132,7 @@ If you have a GitLab Premium or Ultimate subscription:
To view deployment metrics for your project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Analytics > Value stream**.
1. Optional. Filter the results:
1. Select the **Filter results** text box.
diff --git a/doc/user/clusters/agent/ci_cd_workflow.md b/doc/user/clusters/agent/ci_cd_workflow.md
index 88625b89952..7a6c6dc8cd6 100644
--- a/doc/user/clusters/agent/ci_cd_workflow.md
+++ b/doc/user/clusters/agent/ci_cd_workflow.md
@@ -62,7 +62,7 @@ Authorization configuration can take one or two minutes to propagate.
To authorize the agent to access the GitLab project where you keep Kubernetes manifests:
-1. On the top bar, select **Menu > Projects** and find the project that contains the [agent configuration file](install/index.md#create-an-agent-configuration-file) (`config.yaml`).
+1. On the top bar, select **Main menu > Projects** and find the project that contains the [agent configuration file](install/index.md#create-an-agent-configuration-file) (`config.yaml`).
1. Edit the `config.yaml` file. Under the `ci_access` keyword, add the `projects` attribute.
1. For the `id`, add the path:
@@ -85,7 +85,7 @@ Choose the context to run `kubectl` commands from your CI/CD scripts.
To authorize the agent to access all of the GitLab projects in a group or subgroup:
-1. On the top bar, select **Menu > Projects** and find the project that contains the [agent configuration file](install/index.md#create-an-agent-configuration-file) (`config.yaml`).
+1. On the top bar, select **Main menu > Projects** and find the project that contains the [agent configuration file](install/index.md#create-an-agent-configuration-file) (`config.yaml`).
1. Edit the `config.yaml` file. Under the `ci_access` keyword, add the `groups` attribute.
1. For the `id`, add the path:
diff --git a/doc/user/clusters/agent/install/index.md b/doc/user/clusters/agent/install/index.md
index e826544261c..0240fbb45f0 100644
--- a/doc/user/clusters/agent/install/index.md
+++ b/doc/user/clusters/agent/install/index.md
@@ -81,7 +81,7 @@ Prerequisites:
You must register an agent before you can install the agent in your cluster. To register an agent:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
If you have an [agent configuration file](#create-an-agent-configuration-file),
it must be in this project. Your cluster manifest files should also be in this project.
1. From the left sidebar, select **Infrastructure > Kubernetes clusters**.
diff --git a/doc/user/clusters/agent/vulnerabilities.md b/doc/user/clusters/agent/vulnerabilities.md
index d8132691779..2d20675b68b 100644
--- a/doc/user/clusters/agent/vulnerabilities.md
+++ b/doc/user/clusters/agent/vulnerabilities.md
@@ -60,7 +60,7 @@ Prerequisite:
To view vulnerability information in GitLab:
-1. On the top bar, select **Menu > Projects** and find the project that contains the agent configuration file.
+1. On the top bar, select **Main menu > Projects** and find the project that contains the agent configuration file.
1. On the left sidebar, select **Infrastructure > Kubernetes clusters**.
1. Select the **Agent** tab.
1. Select an agent to view the cluster vulnerabilities.
diff --git a/doc/user/clusters/agent/work_with_agent.md b/doc/user/clusters/agent/work_with_agent.md
index 0dae164509e..b28f7546379 100644
--- a/doc/user/clusters/agent/work_with_agent.md
+++ b/doc/user/clusters/agent/work_with_agent.md
@@ -18,7 +18,7 @@ Prerequisite:
To view the list of agents:
-1. On the top bar, select **Menu > Projects** and find the project that contains your agent configuration file.
+1. On the top bar, select **Main menu > Projects** and find the project that contains your agent configuration file.
1. On the left sidebar, select **Infrastructure > Kubernetes clusters**.
1. Select **Agent** tab to view clusters connected to GitLab through the agent.
@@ -37,7 +37,7 @@ The activity logs help you to identify problems and get the information
you need for troubleshooting. You can see events from a week before the
current date. To view an agent's activity:
-1. On the top bar, select **Menu > Projects** and find the project that contains your agent configuration file.
+1. On the top bar, select **Main menu > Projects** and find the project that contains your agent configuration file.
1. On the left sidebar, select **Infrastructure > Kubernetes clusters**.
1. Select the agent you want to see activity for.
@@ -94,7 +94,7 @@ For more information about debugging, see [troubleshooting documentation](troubl
To reset the agent token without downtime:
1. Create a new token:
- 1. On the top bar, select **Menu > Projects** and find your project.
+ 1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Infrastructure > Kubernetes clusters**.
1. Select the agent you want to create a token for.
1. On the **Access tokens** tab, select **Create token**.
@@ -116,7 +116,7 @@ clean up those resources manually.
To remove an agent from the UI:
-1. On the top bar, select **Menu > Projects** and find the project that contains the agent configuration file.
+1. On the top bar, select **Main menu > Projects** and find the project that contains the agent configuration file.
1. From the left sidebar, select **Infrastructure > Kubernetes clusters**.
1. In the table, in the row for your agent, in the **Options** column, select the vertical ellipsis (**{ellipsis_v}**).
1. Select **Delete agent**.
diff --git a/doc/user/clusters/management_project.md b/doc/user/clusters/management_project.md
index 361276194b0..62f70faa630 100644
--- a/doc/user/clusters/management_project.md
+++ b/doc/user/clusters/management_project.md
@@ -61,7 +61,7 @@ To associate a cluster management project with your cluster:
**Infrastructure > Kubernetes clusters** page.
- [Group-level cluster](../group/clusters/index.md), go to your group's **Kubernetes**
page.
- - [Instance-level cluster](../instance/clusters/index.md), on the top bar, select **Menu > Admin > Kubernetes**.
+ - [Instance-level cluster](../instance/clusters/index.md), on the top bar, select **Main menu > Admin > Kubernetes**.
1. Expand **Advanced settings**.
1. From the **Cluster management project** dropdown, select the cluster management project
you created in the previous step.
diff --git a/doc/user/clusters/management_project_template.md b/doc/user/clusters/management_project_template.md
index ad6478554f2..18f1729ae63 100644
--- a/doc/user/clusters/management_project_template.md
+++ b/doc/user/clusters/management_project_template.md
@@ -45,7 +45,7 @@ If you have already configured the agent and connected a cluster with GitLab:
To create a project from the cluster management project template:
-1. On the top bar, select **Menu > Projects > Create new project**.
+1. On the top bar, select **Main menu > Projects > Create new project**.
1. Select **Create from template**.
1. From the list of templates, next to **GitLab Cluster Management**, select **Use template**.
1. Enter the project details.
diff --git a/doc/user/compliance/compliance_report/index.md b/doc/user/compliance/compliance_report/index.md
index e623e906971..96cb3f3ef38 100644
--- a/doc/user/compliance/compliance_report/index.md
+++ b/doc/user/compliance/compliance_report/index.md
@@ -31,7 +31,7 @@ Prerequisites:
To view the compliance report:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Security & Compliance > Compliance report**.
### Severity levels scale
@@ -105,7 +105,7 @@ Depending on the merge strategy, the merge commit SHA can be a merge commit, squ
To generate the Chain of Custody report:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Security & Compliance > Compliance report**.
1. Select **List of all merge commits**.
@@ -122,7 +122,7 @@ Authenticated group owners can generate a commit-specific Chain of Custody repor
- Using the GitLab UI:
- 1. On the top bar, select **Menu > Groups** and find your group.
+ 1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Security & Compliance > Compliance report**.
1. At the top of the compliance report, to the right of **List of all merge commits**, select the down arrow (**{chevron-lg-down}**).
1. Enter the merge commit SHA, and then select **Export commit custody report**.
diff --git a/doc/user/compliance/license_compliance/index.md b/doc/user/compliance/license_compliance/index.md
index 31807aedfc9..19b01e4d854 100644
--- a/doc/user/compliance/license_compliance/index.md
+++ b/doc/user/compliance/license_compliance/index.md
@@ -765,7 +765,7 @@ license.
You can enable `License-Check` one of two ways:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Merge request approvals**.
1. Select **Enable** or **Edit**.
diff --git a/doc/user/crm/index.md b/doc/user/crm/index.md
index 56ada837d54..79f18e3abf1 100644
--- a/doc/user/crm/index.md
+++ b/doc/user/crm/index.md
@@ -1,6 +1,6 @@
---
stage: Plan
-group: Product Planning
+group: Certify
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
diff --git a/doc/user/group/contribution_analytics/index.md b/doc/user/group/contribution_analytics/index.md
index ba805d6e1bb..7750782a623 100644
--- a/doc/user/group/contribution_analytics/index.md
+++ b/doc/user/group/contribution_analytics/index.md
@@ -19,7 +19,7 @@ group.
To view Contribution Analytics:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Contribution**.
## Using Contribution Analytics
diff --git a/doc/user/group/epics/epic_boards.md b/doc/user/group/epics/epic_boards.md
index f03a56d8a00..25cb88e8a7c 100644
--- a/doc/user/group/epics/epic_boards.md
+++ b/doc/user/group/epics/epic_boards.md
@@ -15,7 +15,7 @@ labels.
To view an epic board:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Epics > Boards**.
![GitLab epic board - Premium](img/epic_board_v14_1.png)
@@ -28,7 +28,7 @@ Prerequisites:
To create a new epic board:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Epics > Boards**.
1. In the upper left corner, select the dropdown with the current board name.
1. Select **Create new board**.
@@ -77,7 +77,7 @@ Prerequisites:
To create a new list:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Epics > Boards**.
1. In the upper-right corner, select **Create list**.
1. In the **New list** column expand the **Select a label** dropdown and select the label to use as
diff --git a/doc/user/group/import/index.md b/doc/user/group/import/index.md
index 0cadf89fb0c..21b389581ae 100644
--- a/doc/user/group/import/index.md
+++ b/doc/user/group/import/index.md
@@ -183,7 +183,7 @@ path. This causes a `404 Group Not Found` error. To solve this, the source group
- The GitLab UI:
- 1. On the top bar, select **Menu > Groups** and find your group.
+ 1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Advanced**.
1. Under **Change group URL**, change the group URL to include non-numeric characters.
diff --git a/doc/user/group/insights/index.md b/doc/user/group/insights/index.md
index b3fdeb0ab41..09b6cf18598 100644
--- a/doc/user/group/insights/index.md
+++ b/doc/user/group/insights/index.md
@@ -19,7 +19,7 @@ requests to be merged, and much more.
To access your group's Insights:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Insights**.
## Configure your Insights
@@ -29,7 +29,7 @@ If you want to customize it:
1. Create a new file [`.gitlab/insights.yml`](../../project/insights/index.md)
in a project that belongs to your group.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Insights**.
1. Select the project that contains your `.gitlab/insights.yml` configuration file.
diff --git a/doc/user/group/issues_analytics/index.md b/doc/user/group/issues_analytics/index.md
index 62337dabcc0..26d77edd89b 100644
--- a/doc/user/group/issues_analytics/index.md
+++ b/doc/user/group/issues_analytics/index.md
@@ -15,7 +15,7 @@ prior.
To access the chart:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Issue Analytics**.
Hover over each bar to see the total number of issues.
diff --git a/doc/user/group/iterations/index.md b/doc/user/group/iterations/index.md
index 7a1db41e4f8..4cc879ef32d 100644
--- a/doc/user/group/iterations/index.md
+++ b/doc/user/group/iterations/index.md
@@ -32,7 +32,7 @@ In GitLab, iterations are similar to milestones, with a few differences:
To view the iterations list:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. Select **Issues > Iterations**.
To view all the iterations in a cadence, ordered by descending date, select that iteration cadence.
@@ -61,7 +61,7 @@ Prerequisites:
To create an iteration:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Issues > Iterations**.
1. Select **New iteration**.
1. Enter the title, a description (optional), a start date, and a due date.
@@ -169,7 +169,7 @@ and get a more accurate understanding of scope attributable to each label.
To group issues by label:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Issues > Iterations**.
1. In the **Group by** dropdown, select **Label**.
1. Select the **Filter by label** dropdown.
@@ -198,7 +198,7 @@ Prerequisites:
To create an iteration cadence:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Issues > Iterations**.
1. Select **New iteration cadence**.
1. Enter the title and description of the iteration cadence.
@@ -220,7 +220,7 @@ Prerequisites:
To edit an iteration cadence:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Issues > Iterations**.
1. Select **Edit iteration cadence**.
@@ -234,7 +234,7 @@ doesn't delete the eight existing upcoming iterations.
#### Turn on automatic scheduling for manual iterations cadence
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Issues > Iterations**.
1. Select the three-dot menu (**{ellipsis_v}**) > **Edit cadence** for the cadence for which you want to enable automatic scheduling.
1. Check the **Enable automatic scheduling** checkbox.
@@ -285,7 +285,7 @@ Deleting an iteration cadence also deletes all iterations within that cadence.
To delete an iteration cadence:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Issues > Iterations**.
1. Select the three-dot menu (**{ellipsis_v}**) > **Delete cadence** for the cadence you want to delete.
1. Select **Delete cadence**.
diff --git a/doc/user/group/repositories_analytics/index.md b/doc/user/group/repositories_analytics/index.md
index 6e3ea7d6c0f..f130ecb61dc 100644
--- a/doc/user/group/repositories_analytics/index.md
+++ b/doc/user/group/repositories_analytics/index.md
@@ -37,7 +37,7 @@ The **Analytics > Repositories** group page displays the average test coverage o
To see the latest code coverage for each project in your group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Repositories**.
1. In the **Latest test coverage results** section, from the **Select projects** dropdown list, choose the projects you want to check.
@@ -52,7 +52,7 @@ You can get a CSV of the code coverage data for all of the projects in your grou
To get the report:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Analytics > Repositories**.
1. Select **Download historic test coverage data (.csv)**.
1. Select the projects and date range you want to include in the report.
diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md
index 43a0a92c9fd..b7ab66297f8 100644
--- a/doc/user/group/saml_sso/index.md
+++ b/doc/user/group/saml_sso/index.md
@@ -24,7 +24,7 @@ If required, you can find [a glossary of common terms](../../../integration/saml
## Configure your identity provider
1. Find the information in GitLab required for configuration:
- 1. On the top bar, select **Menu > Groups** and find your group.
+ 1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > SAML SSO**.
1. Note the **Assertion consumer service URL**, **Identifier**, and **GitLab single sign-on URL**.
1. Configure your SAML identity provider app using the noted details.
@@ -79,7 +79,7 @@ You can configure the following attributes with GitLab.com Group SAML:
GitLab provides metadata XML that can be used to configure your identity provider.
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > SAML SSO**.
1. Copy the provided **GitLab metadata URL**.
1. Follow your identity provider's documentation and paste the metadata URL when it's requested.
@@ -88,7 +88,7 @@ GitLab provides metadata XML that can be used to configure your identity provide
After you set up your identity provider to work with GitLab, you must configure GitLab to use it for authentication:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > SAML SSO**.
1. Find the SSO URL from your identity provider and enter it the **Identity provider single sign-on URL** field.
1. Find and enter the fingerprint for the SAML token signing certificate in the **Certificate** field.
diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md
index 2735985bb78..a67b2fbe02e 100644
--- a/doc/user/group/saml_sso/scim_setup.md
+++ b/doc/user/group/saml_sso/scim_setup.md
@@ -28,7 +28,7 @@ Prerequisites:
To configure GitLab SAML SSO SCIM:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > SAML SSO**.
1. Select **Generate a SCIM token**.
1. For configuration of your identity provider, save the:
diff --git a/doc/user/group/settings/group_access_tokens.md b/doc/user/group/settings/group_access_tokens.md
index d16312dddbe..6b5e137d9ab 100644
--- a/doc/user/group/settings/group_access_tokens.md
+++ b/doc/user/group/settings/group_access_tokens.md
@@ -55,7 +55,7 @@ configured for personal access tokens.
To create a group access token:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > Access Tokens**.
1. Enter a name. The token name is visible to any user with permissions to view the group.
1. Optional. Enter an expiry date for the token. The token will expire on that date at midnight UTC. An instance-wide [maximum lifetime](../../admin_area/settings/account_and_limit_settings.md#limit-the-lifetime-of-access-tokens) setting can limit the maximum allowable lifetime in self-managed instances.
@@ -112,7 +112,7 @@ or API. However, administrators can use a workaround:
To revoke a group access token:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > Access Tokens**.
1. Next to the group access token to revoke, select **Revoke**.
@@ -146,7 +146,7 @@ The scope determines the actions you can perform when you authenticate with a gr
To enable or disable group access token creation for all sub-groups in a top-level group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Permissions and group features**.
1. Under **Permissions**, turn on or off **Users can create project access tokens and group access tokens in this group**.
diff --git a/doc/user/group/settings/import_export.md b/doc/user/group/settings/import_export.md
index 730bcd61020..7a398c7d086 100644
--- a/doc/user/group/settings/import_export.md
+++ b/doc/user/group/settings/import_export.md
@@ -28,7 +28,7 @@ Prerequisite:
To enable import and export for a group:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand **Visibility and access controls**.
1. In the **Import sources** section, select the checkboxes for the sources you want.
@@ -79,7 +79,7 @@ Prerequisites:
To export the contents of a group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. In the **Advanced** section, select **Export Group**.
1. After the export is generated, you should receive an email with a link to the [exported contents](#exported-contents)
diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md
index 94491aeac4e..657ef361bd5 100644
--- a/doc/user/group/subgroups/index.md
+++ b/doc/user/group/subgroups/index.md
@@ -56,7 +56,7 @@ the private subgroup.
To view the subgroups of a group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. Select the **Subgroups and projects** tab.
1. To view a nested subgroup, expand a subgroup in the hierarchy list.
@@ -84,7 +84,7 @@ You cannot host a GitLab Pages subgroup website with a top-level domain name. Fo
To create a subgroup:
-1. On the top bar, select **Menu > Groups** and find and select the parent group to add a subgroup to.
+1. On the top bar, select **Main menu > Groups** and find and select the parent group to add a subgroup to.
1. On the parent group's overview page, in the top right, select **New subgroup**.
1. Select **Create group**.
1. Fill in the fields. View a list of [reserved names](../../reserved_names.md) that cannot be used as group names.
@@ -98,13 +98,13 @@ default:
To change who can create subgroups on a group:
- As a user with the Owner role on the group:
- 1. On the top bar, select **Menu > Groups** and find your group.
+ 1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Permissions and group features**.
1. Select a role from **Roles allowed to create subgroups**.
1. Select **Save changes**.
- As an administrator:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Groups**.
1. In the group's row select **Edit**.
1. Select a role from **Allowed to create subgroups**.
@@ -159,7 +159,7 @@ Group permissions for a member can be changed only by:
To see if a member has inherited the permissions from a parent group:
-1. On the top bar, select **Menu > Groups** and find the group.
+1. On the top bar, select **Main menu > Groups** and find the group.
1. Select **Group information > Members**.
Members list for an example subgroup _Four_:
diff --git a/doc/user/infrastructure/clusters/connect/index.md b/doc/user/infrastructure/clusters/connect/index.md
index 37e1024a32c..af728cb5c21 100644
--- a/doc/user/infrastructure/clusters/connect/index.md
+++ b/doc/user/infrastructure/clusters/connect/index.md
@@ -34,17 +34,17 @@ your cluster's level.
**Project-level clusters:**
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Infrastructure > Kubernetes clusters**.
**Group-level clusters:**
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Kubernetes**.
**Instance-level clusters:**
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Kubernetes**.
## Security implications for clusters connected with certificates
diff --git a/doc/user/infrastructure/clusters/connect/new_civo_cluster.md b/doc/user/infrastructure/clusters/connect/new_civo_cluster.md
index f2cf0193bd8..955701636bf 100644
--- a/doc/user/infrastructure/clusters/connect/new_civo_cluster.md
+++ b/doc/user/infrastructure/clusters/connect/new_civo_cluster.md
@@ -35,7 +35,7 @@ Start by [importing the example project by URL](../../../project/import/repo_by_
To import the project:
-1. On the top bar, select **Menu > Create new project**.
+1. On the top bar, select **Main menu > Create new project**.
1. Select **Import project**.
1. Select **Repository by URL**.
1. For the **Git repository URL**, enter `https://gitlab.com/civocloud/gitlab-terraform-civo.git`.
diff --git a/doc/user/infrastructure/clusters/connect/new_eks_cluster.md b/doc/user/infrastructure/clusters/connect/new_eks_cluster.md
index d78595f79dc..f6d15e6da24 100644
--- a/doc/user/infrastructure/clusters/connect/new_eks_cluster.md
+++ b/doc/user/infrastructure/clusters/connect/new_eks_cluster.md
@@ -34,7 +34,7 @@ Start by [importing the example project by URL](../../../project/import/repo_by_
To import the project:
-1. On the top bar, select **Menu > Create new project**.
+1. On the top bar, select **Main menu > Create new project**.
1. Select **Import project**.
1. Select **Repository by URL**.
1. For the **Git repository URL**, enter `https://gitlab.com/gitlab-org/configure/examples/gitlab-terraform-eks.git`.
diff --git a/doc/user/infrastructure/clusters/connect/new_gke_cluster.md b/doc/user/infrastructure/clusters/connect/new_gke_cluster.md
index fa0f04331e7..5cdc8e7bf90 100644
--- a/doc/user/infrastructure/clusters/connect/new_gke_cluster.md
+++ b/doc/user/infrastructure/clusters/connect/new_gke_cluster.md
@@ -41,7 +41,7 @@ Start by [importing the example project by URL](../../../project/import/repo_by_
To import the project:
-1. On the top bar, select **Menu > Create new project**.
+1. On the top bar, select **Main menu > Create new project**.
1. Select **Import project**.
1. Select **Repository by URL**.
1. For the **Git repository URL**, enter `https://gitlab.com/gitlab-org/configure/examples/gitlab-terraform-gke.git`.
diff --git a/doc/user/operations_dashboard/index.md b/doc/user/operations_dashboard/index.md
index 1b14582a3f0..0a7e9d6395b 100644
--- a/doc/user/operations_dashboard/index.md
+++ b/doc/user/operations_dashboard/index.md
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
The Operations Dashboard provides a summary of each project's operational health,
including pipeline and alert status.
-To access the dashboard, on the top bar, select **Menu > Operations**.
+To access the dashboard, on the top bar, select **Main menu > Operations**.
## Adding a project to the dashboard
diff --git a/doc/user/packages/dependency_proxy/index.md b/doc/user/packages/dependency_proxy/index.md
index f775ddac2ee..1310f8eedaa 100644
--- a/doc/user/packages/dependency_proxy/index.md
+++ b/doc/user/packages/dependency_proxy/index.md
@@ -37,7 +37,7 @@ For a list of planned additions, view the
To enable or turn off the Dependency Proxy for a group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > Packages and registries**.
1. Expand the **Dependency Proxy** section.
1. To enable the proxy, turn on **Enable Proxy**. To turn it off, turn the toggle off.
@@ -50,7 +50,7 @@ for the entire GitLab instance.
To view the Dependency Proxy:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Packages and registries > Dependency Proxy**.
The Dependency Proxy is not available for projects.
@@ -175,7 +175,7 @@ You can also use [custom CI/CD variables](../../../ci/variables/index.md#custom-
To store a Docker image in Dependency Proxy storage:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Packages and registries > Dependency Proxy**.
1. Copy the **Dependency Proxy image prefix**.
1. Use one of these commands. In these examples, the image is `alpine:latest`.
diff --git a/doc/user/packages/harbor_container_registry/index.md b/doc/user/packages/harbor_container_registry/index.md
index 12c61d9d12c..720e274aee5 100644
--- a/doc/user/packages/harbor_container_registry/index.md
+++ b/doc/user/packages/harbor_container_registry/index.md
@@ -12,7 +12,7 @@ You can integrate the [Harbor container registry](../../../user/project/integrat
You can view the Harbor Registry for a project or group.
-1. On the top bar, select **Menu > Projects/Groups**.
+1. On the top bar, select **Main menu > Projects/Groups**.
1. Go to the project or group that you are interested in.
1. On the left sidebar, select **Packages and registries > Harbor Registry**.
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index fb8bc829ac6..9a4590d9478 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -493,7 +493,7 @@ An administrator can flag a user as external by either of the following methods:
- [Through the API](../api/users.md#user-modification).
- Using the GitLab UI:
- 1. On the top bar, select **Menu > Admin**.
+ 1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Overview > Users** to create a new user or edit an existing one.
There, you can find the option to flag the user as external.
@@ -507,7 +507,7 @@ Additionally, users can be set as external users using:
By default, new users are not set as external users. This behavior can be changed
by an administrator:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section.
diff --git a/doc/user/project/deploy_keys/index.md b/doc/user/project/deploy_keys/index.md
index a9f19d27416..f424ec529b2 100644
--- a/doc/user/project/deploy_keys/index.md
+++ b/doc/user/project/deploy_keys/index.md
@@ -54,7 +54,7 @@ For example:
To view the deploy keys available to a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Deploy keys**.
@@ -72,7 +72,7 @@ Prerequisites:
- [Generate an SSH key pair](../../ssh.md#generate-an-ssh-key-pair). Put the private SSH
key on the host that requires access to the repository.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Deploy keys**.
1. Complete the fields.
@@ -92,7 +92,7 @@ Prerequisites:
To create a public deploy key:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Deploy Keys**.
1. Select **New deploy key**.
1. Complete the fields.
@@ -109,7 +109,7 @@ Prerequisites:
To grant a public deploy key access to a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Deploy keys**.
1. Select **Publicly accessible deploy keys**.
@@ -129,7 +129,7 @@ Prerequisites:
To disable a deploy key:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Deploy keys**.
1. Select **Disable** (**{cancel}**).
diff --git a/doc/user/project/deploy_tokens/index.md b/doc/user/project/deploy_tokens/index.md
index 84d504a994a..04a2eeacffb 100644
--- a/doc/user/project/deploy_tokens/index.md
+++ b/doc/user/project/deploy_tokens/index.md
@@ -31,7 +31,7 @@ You can create as many deploy tokens as you need from the settings of your
project. Alternatively, you can also create [group-scoped deploy tokens](#group-deploy-token).
1. Sign in to your GitLab account.
-1. On the top bar, select **Menu > Projects** or **Menu > Groups** to find your project or group.
+1. On the top bar, select **Main menu > Projects** or **Main menu > Groups** to find your project or group.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Deploy tokens**.
1. Choose a name, and optionally, an expiration date and username for the token.
@@ -51,7 +51,7 @@ Deploy tokens expire at midnight UTC on the date you define.
To revoke a deploy token:
-1. On the top bar, select **Menu > Projects** or **Menu > Groups** to find your project or group.
+1. On the top bar, select **Main menu > Projects** or **Main menu > Groups** to find your project or group.
1. On the left sidebar, select **Settings > Repository**.
1. Expand **Deploy tokens**.
1. In the **Active Deploy Tokens** section, by the token you want to revoke, select **Revoke**.
diff --git a/doc/user/project/import/gitlab_com.md b/doc/user/project/import/gitlab_com.md
index 1bf98988481..6c0f18dbdd3 100644
--- a/doc/user/project/import/gitlab_com.md
+++ b/doc/user/project/import/gitlab_com.md
@@ -16,7 +16,7 @@ Prerequisite:
To import a GitLab.com project to your self-managed GitLab instance:
-1. On the top bar, select **Menu > Projects > Create new project**.
+1. On the top bar, select **Main menu > Projects > Create new project**.
1. Select **Import project**.
1. Select **GitLab.com**.
1. Give GitLab.com permission to access your projects.
diff --git a/doc/user/project/import/repo_by_url.md b/doc/user/project/import/repo_by_url.md
index 5163f957171..f288a4891bb 100644
--- a/doc/user/project/import/repo_by_url.md
+++ b/doc/user/project/import/repo_by_url.md
@@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
You can import your existing repositories by providing the Git URL:
-1. On the top bar, select **Menu > Create new project**.
+1. On the top bar, select **Main menu > Create new project**.
1. Select the **Import project** tab.
1. Select **Repository by URL**.
1. Enter a **Git repository URL**.
diff --git a/doc/user/project/members/index.md b/doc/user/project/members/index.md
index 4a6272a0ca3..8d8169e8454 100644
--- a/doc/user/project/members/index.md
+++ b/doc/user/project/members/index.md
@@ -68,7 +68,7 @@ Prerequisite:
To add a user to a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Members**.
1. Select **Invite members**.
1. Enter an email address and select a [role](../../permissions.md).
@@ -106,7 +106,7 @@ Prerequisite:
To add groups to a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Members**.
1. Select **Invite a group**.
1. Select a group.
@@ -131,7 +131,7 @@ Prerequisite:
To import users:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Members**.
1. Select **Import from a project**.
1. Select the project. You can view only the projects for which you're a maintainer.
@@ -175,7 +175,7 @@ Prerequisites:
To remove a member from a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Members**.
1. Next to the project member you want to remove, select **Remove member**.
1. Optional. In the confirmation box, select the
@@ -197,7 +197,7 @@ You can filter and sort members in a project.
### Display inherited members
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Members**.
1. In the **Filter members** box, select `Membership` `=` `Inherited`.
1. Press <kbd>Enter</kbd>.
@@ -206,7 +206,7 @@ You can filter and sort members in a project.
### Display direct members
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Project information > Members**.
1. In the **Filter members** box, select `Membership` `=` `Direct`.
1. Press <kbd>Enter</kbd>.
@@ -229,7 +229,7 @@ You can sort members by **Account**, **Access granted**, **Max role**, or **Last
GitLab users can request to become a member of a project.
-1. On the top bar, select **Menu > Projects** and find the project you want to be a member of.
+1. On the top bar, select **Main menu > Projects** and find the project you want to be a member of.
1. By the project name, select **Request Access**.
![Request access button](img/request_access_button.png)
@@ -253,7 +253,7 @@ Prerequisite:
- You must be the project owner.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Visibility, project features, permissions**.
1. Under **Project visibility**, select **Users can request access**.
diff --git a/doc/user/project/merge_requests/cherry_pick_changes.md b/doc/user/project/merge_requests/cherry_pick_changes.md
index 7d12fdac87a..2040995280e 100644
--- a/doc/user/project/merge_requests/cherry_pick_changes.md
+++ b/doc/user/project/merge_requests/cherry_pick_changes.md
@@ -23,6 +23,7 @@ is tracked [in this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/202215)
## Cherry-pick example
In this example of cherry-picking, a Git repository has two branches: `develop` and `main`.
+This example shows a cherry-picked commit from one branch being added to another:
```mermaid
gitGraph
@@ -41,7 +42,7 @@ gitGraph
commit id:"H"
```
-The example shows a cherry-pick of commit `B` from the `develop` branch, which is added
+In this example, a cherry-pick of commit `B` from the `develop` branch is added
after commit `E` in the `main` branch.
Commit `G` is added after the cherry-pick.
diff --git a/doc/user/project/pages/getting_started/pages_ci_cd_template.md b/doc/user/project/pages/getting_started/pages_ci_cd_template.md
index 510f9332e7b..58e15104815 100644
--- a/doc/user/project/pages/getting_started/pages_ci_cd_template.md
+++ b/doc/user/project/pages/getting_started/pages_ci_cd_template.md
@@ -16,7 +16,7 @@ Use a `.gitlab-ci.yml` template when you have an existing project that you want
Your GitLab repository should contain files specific to an SSG, or plain HTML. After you complete
these steps, you may have to do additional configuration for the Pages site to generate properly.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select the project's name.
1. From the **Add** (**{plus}**) dropdown, select **New file**.
1. From the **Select a template type** dropdown, select `.gitlab-ci.yml`.
diff --git a/doc/user/project/pages/getting_started/pages_ui.md b/doc/user/project/pages/getting_started/pages_ui.md
index 66b4f43b6b2..7e618fbaec8 100644
--- a/doc/user/project/pages/getting_started/pages_ui.md
+++ b/doc/user/project/pages/getting_started/pages_ui.md
@@ -29,7 +29,7 @@ provide the build commands, and creates the necessary boilerplate for you.
To build your YAML file from the GitLab UI:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Pages** to display the friendly
interface **Get Started With Pages**.
1. If your framework's build process does not need one of the provided build
diff --git a/doc/user/project/protected_tags.md b/doc/user/project/protected_tags.md
index 870c544cf4c..9b1e862af58 100644
--- a/doc/user/project/protected_tags.md
+++ b/doc/user/project/protected_tags.md
@@ -97,7 +97,7 @@ Prerequisite:
To do this:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Repository > Tags**.
1. Next to the tag you want to delete, select **Delete** (**{remove}**).
1. On the confirmation dialog, enter the tag name and select **Yes, delete protected tag**.
diff --git a/doc/user/project/releases/index.md b/doc/user/project/releases/index.md
index bf4575c9492..87ea95524fe 100644
--- a/doc/user/project/releases/index.md
+++ b/doc/user/project/releases/index.md
@@ -75,7 +75,7 @@ Prerequisites:
To create a release in the Releases page:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Releases** and select **New release**.
1. From the [**Tag name**](release_fields.md#tag-name) dropdown, either:
- Select an existing Git tag. Selecting an existing tag that is already associated with a release
@@ -97,7 +97,7 @@ To create a release in the Tags page, add release notes to either an existing or
To add release notes to a new Git tag:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Repository > Tags**.
1. Select **New tag**.
1. Optional. Enter a tag message in the **Message** text box.
@@ -107,7 +107,7 @@ To add release notes to a new Git tag:
To edit release notes of an existing Git tag:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Repository > Tags**.
1. Select **Edit release notes** (**{pencil}**).
1. In the **Release notes** text box, enter the release's description.
@@ -233,7 +233,7 @@ Prerequisites:
To delete a release in the UI:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Deployments > Releases**.
1. In the top-right corner of the release you want to delete, select **Edit this release** (**{pencil}**).
1. On the **Edit Release** page, select **Delete**.
diff --git a/doc/user/project/service_desk.md b/doc/user/project/service_desk.md
index 544ee2d8617..cfd5c0a9cf5 100644
--- a/doc/user/project/service_desk.md
+++ b/doc/user/project/service_desk.md
@@ -150,7 +150,7 @@ The templates are inherited. For example, in a project, you can also access temp
To use a custom description template with Service Desk:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. [Create a description template](description_templates.md#create-an-issue-template).
1. On the left sidebar, select **Settings > General > Service Desk**.
1. From the dropdown list **Template to append to all Service Desk issues**, search or select your template.
@@ -164,7 +164,7 @@ this name in the `From` header. The default display name is `GitLab Support Bot`
To edit the custom email display name:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General > Service Desk**.
1. Enter a new name in **Email display name**.
1. Select **Save Changes**.
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index 5aa8cecc886..b415f836795 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -43,7 +43,7 @@ Prerequisites:
To export a project and its data, follow these steps:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Advanced**.
1. Select **Export project**.
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index dd5cc6b89ff..3cff66e01c5 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -51,7 +51,7 @@ requirements or needs additional oversight. The label can optionally apply
Group owners can create, edit, and delete compliance frameworks:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings** > **General**.
1. Expand the **Compliance frameworks** section.
diff --git a/doc/user/project/settings/project_access_tokens.md b/doc/user/project/settings/project_access_tokens.md
index d653320db5f..c9c5efce9b1 100644
--- a/doc/user/project/settings/project_access_tokens.md
+++ b/doc/user/project/settings/project_access_tokens.md
@@ -55,7 +55,7 @@ configured for personal access tokens.
To create a project access token:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Access Tokens**.
1. Enter a name. The token name is visible to any user with permissions to view the project.
1. Optional. Enter an expiry date for the token. The token expires on that date at midnight UTC. An instance-wide [maximum lifetime](../../admin_area/settings/account_and_limit_settings.md#limit-the-lifetime-of-access-tokens) setting can limit the maximum allowable lifetime in self-managed instances.
@@ -70,7 +70,7 @@ A project access token is displayed. Save the project access token somewhere saf
To revoke a project access token:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > Access Tokens**.
1. Next to the project access token to revoke, select **Revoke**.
@@ -93,7 +93,7 @@ The scope determines the actions you can perform when you authenticate with a pr
To enable or disable project access token creation for all projects in a top-level group:
-1. On the top bar, select **Menu > Groups** and find your group.
+1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Permissions and group features**.
1. Under **Permissions**, turn on or off **Allow project and group access token creation**.
diff --git a/doc/user/project/time_tracking.md b/doc/user/project/time_tracking.md
index 971ecf66a3c..522ec962e53 100644
--- a/doc/user/project/time_tracking.md
+++ b/doc/user/project/time_tracking.md
@@ -170,7 +170,7 @@ The following time units are available:
In GitLab self-managed instances, you can limit the display of time units to hours.
To do so:
-1. On the top bar, select **Menu > Admin**.
+1. On the top bar, select **Main menu > Admin**.
1. On the left sidebar, select **Settings > Preferences**.
1. Expand **Localization**.
1. Under **Time tracking**, select the **Limit display of time tracking units to hours** checkbox.
diff --git a/doc/user/public_access.md b/doc/user/public_access.md
index 595997ed1c2..3accb0f5e3d 100644
--- a/doc/user/public_access.md
+++ b/doc/user/public_access.md
@@ -65,7 +65,7 @@ Prerequisite:
- You must have the Owner role for a project.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Visibility, project features, permissions**.
1. Change **Project visibility** to either **Private**, **Internal**, or **Public**.
@@ -79,7 +79,7 @@ Prerequisite:
- Subgroups and projects must already have visibility settings that are at least as
restrictive as the new setting of the parent group.
-1. On the top bar, select **Menu > Groups** and find your project.
+1. On the top bar, select **Main menu > Groups** and find your project.
1. On the left sidebar, select **Settings > General**.
1. Expand **Naming, visibility**.
1. Under **Visibility level** select either **Private**, **Internal**, or **Public**.
diff --git a/doc/user/search/index.md b/doc/user/search/index.md
index 76a85b55585..41eff28b088 100644
--- a/doc/user/search/index.md
+++ b/doc/user/search/index.md
@@ -44,7 +44,7 @@ search, or choose a specific group or project.
To search through code or other documents in a project:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the top bar, in the search field, type the string you want to search for.
1. Press **Enter**.
@@ -68,7 +68,7 @@ where the results were found.
You can search for a commit SHA.
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the top bar, in the search field, type the SHA.
If a single result is returned, GitLab redirects to the commit result
diff --git a/doc/user/snippets.md b/doc/user/snippets.md
index 325c98355c2..f4683414dea 100644
--- a/doc/user/snippets.md
+++ b/doc/user/snippets.md
@@ -23,10 +23,16 @@ GitLab provides two types of snippets:
- **Personal snippets**: Created independent of any project.
You can set a [visibility level](public_access.md)
- for your snippet: public, internal, or private.
+ for your snippet: public or private.
- **Project snippets**: Always related to a specific project.
Project snippets can be visible publicly, or to only project members.
+NOTE:
+From July 2019, the `Internal` visibility setting is disabled for new projects, groups,
+and snippets on GitLab.com. Existing snippets using the `Internal`
+visibility setting keep this setting. You can read more about the change in the
+[relevant issue](https://gitlab.com/gitlab-org/gitlab/-/issues/12388).
+
## Create snippets
You can create snippets in multiple ways, depending on whether you want to create a personal or project snippet:
@@ -61,10 +67,10 @@ In GitLab versions 13.0 and later, snippets are [versioned by default](#versione
To discover all snippets visible to you in GitLab, you can:
- **View all snippets visible to you**: On the top bar of your GitLab
- instance, select **Menu > Snippets** to view your snippets dashboard.
+ instance, select **Main menu > Snippets** to view your snippets dashboard.
- **Visit [GitLab snippets](https://gitlab.com/dashboard/snippets)** for your snippets on GitLab.com.
- **Explore all public snippets**: On the top bar of your GitLab
- instance, select **Menu > Snippets** and select **Explore snippets** to view
+ instance, select **Main menu > Snippets** and select **Explore snippets** to view
[all public snippets](https://gitlab.com/explore/snippets).
- **View a project's snippets**: In your project,
go to **Snippets**.
@@ -219,7 +225,7 @@ Prerequisites:
To do this task:
-1. On the top bar, select **Menu > Projects** and find your project.
+1. On the top bar, select **Main menu > Projects** and find your project.
1. On the left sidebar, select **Snippets**.
1. Select the snippet you want to report as spam.
1. Select **Submit as spam**.
diff --git a/lib/api/composer_packages.rb b/lib/api/composer_packages.rb
index de59cb4a7c3..d9806fa37d1 100644
--- a/lib/api/composer_packages.rb
+++ b/lib/api/composer_packages.rb
@@ -150,17 +150,18 @@ module API
get 'archives/*package_name', urgency: :default do
authorize_read_package!(authorized_user_project)
- metadata = authorized_user_project
+ package = authorized_user_project
.packages
.composer
.with_name(params[:package_name])
.with_composer_target(params[:sha])
.first
- &.composer_metadatum
+ metadata = package&.composer_metadatum
not_found! unless metadata
track_package_event('pull_package', :composer, project: authorized_user_project, namespace: authorized_user_project.namespace)
+ package.touch_last_downloaded_at
send_git_archive authorized_user_project.repository, ref: metadata.target_sha, format: 'zip', append_sha: true
end
diff --git a/lib/api/concerns/packages/debian_package_endpoints.rb b/lib/api/concerns/packages/debian_package_endpoints.rb
index 6b234b86154..2cde70ce8fb 100644
--- a/lib/api/concerns/packages/debian_package_endpoints.rb
+++ b/lib/api/concerns/packages/debian_package_endpoints.rb
@@ -35,12 +35,12 @@ module API
::Packages::Debian::DistributionsFinder.new(container, codename_or_suite: params[:distribution]).execute.last!
end
- def present_package_file!
+ def present_distribution_package_file!
not_found! unless params[:package_name].start_with?(params[:letter])
package_file = distribution_from!(user_project).package_files.with_file_name(params[:file_name]).last!
- present_carrierwave_file!(package_file.file)
+ present_package_file!(package_file)
end
def present_index_file!(file_type)
diff --git a/lib/api/debian_group_packages.rb b/lib/api/debian_group_packages.rb
index 8bf4ac22802..0962d749558 100644
--- a/lib/api/debian_group_packages.rb
+++ b/lib/api/debian_group_packages.rb
@@ -48,7 +48,7 @@ module API
route_setting :authentication, authenticate_non_public: true
get 'pool/:distribution/:project_id/:letter/:package_name/:package_version/:file_name', requirements: PACKAGE_FILE_REQUIREMENTS do
- present_package_file!
+ present_distribution_package_file!
end
end
end
diff --git a/lib/api/debian_project_packages.rb b/lib/api/debian_project_packages.rb
index 06846d8f36e..9dedc4390f7 100644
--- a/lib/api/debian_project_packages.rb
+++ b/lib/api/debian_project_packages.rb
@@ -51,7 +51,7 @@ module API
route_setting :authentication, authenticate_non_public: true
get 'pool/:distribution/:letter/:package_name/:package_version/:file_name', requirements: PACKAGE_FILE_REQUIREMENTS do
- present_package_file!
+ present_distribution_package_file!
end
params do
diff --git a/lib/api/entities/package.rb b/lib/api/entities/package.rb
index 1efd457aa5f..18fc0576dd4 100644
--- a/lib/api/entities/package.rb
+++ b/lib/api/entities/package.rb
@@ -39,6 +39,7 @@ module API
end
expose :created_at
+ expose :last_downloaded_at
expose :project_id, if: ->(_, opts) { opts[:group] }
expose :project_path, if: ->(obj, opts) { opts[:group] && Ability.allowed?(opts[:user], :read_project, obj.project) }
expose :tags
diff --git a/lib/api/generic_packages.rb b/lib/api/generic_packages.rb
index 0b1c06b3c26..ad5455c5de6 100644
--- a/lib/api/generic_packages.rb
+++ b/lib/api/generic_packages.rb
@@ -102,7 +102,7 @@ module API
track_package_event('pull_package', :generic, project: project, user: current_user, namespace: project.namespace)
- present_carrierwave_file!(package_file.file)
+ present_package_file!(package_file)
end
end
end
diff --git a/lib/api/helm_packages.rb b/lib/api/helm_packages.rb
index a1b265bc8f3..f90084a7e57 100644
--- a/lib/api/helm_packages.rb
+++ b/lib/api/helm_packages.rb
@@ -67,7 +67,7 @@ module API
track_package_event('pull_package', :helm, project: authorized_user_project, namespace: authorized_user_project.namespace)
- present_carrierwave_file!(package_file.file)
+ present_package_file!(package_file)
end
desc 'Authorize a chart upload from workhorse' do
diff --git a/lib/api/helpers/packages/conan/api_helpers.rb b/lib/api/helpers/packages/conan/api_helpers.rb
index ba1c409317e..a9d91895cfe 100644
--- a/lib/api/helpers/packages/conan/api_helpers.rb
+++ b/lib/api/helpers/packages/conan/api_helpers.rb
@@ -173,7 +173,7 @@ module API
track_package_event('pull_package', :conan, category: 'API::ConanPackages', user: current_user, project: project, namespace: project.namespace) if params[:file_name] == ::Packages::Conan::FileMetadatum::PACKAGE_BINARY
- present_carrierwave_file!(package_file.file)
+ present_package_file!(package_file)
end
def find_or_create_package
diff --git a/lib/api/helpers/packages_helpers.rb b/lib/api/helpers/packages_helpers.rb
index 66c4c661454..687c8330cc8 100644
--- a/lib/api/helpers/packages_helpers.rb
+++ b/lib/api/helpers/packages_helpers.rb
@@ -53,6 +53,11 @@ module API
category = args.delete(:category) || self.options[:for].name
::Gitlab::Tracking.event(category, event_name.to_s, **args)
end
+
+ def present_package_file!(package_file, supports_direct_download: true)
+ package_file.package.touch_last_downloaded_at
+ present_carrierwave_file!(package_file.file, supports_direct_download: supports_direct_download)
+ end
end
end
end
diff --git a/lib/api/maven_packages.rb b/lib/api/maven_packages.rb
index fb0221ee907..e38c88d7424 100644
--- a/lib/api/maven_packages.rb
+++ b/lib/api/maven_packages.rb
@@ -76,7 +76,10 @@ module API
format == 'jar'
end
- def present_carrierwave_file_with_head_support!(file, supports_direct_download: true)
+ def present_carrierwave_file_with_head_support!(package_file, supports_direct_download: true)
+ package_file.package.touch_last_downloaded_at
+ file = package_file.file
+
if head_request_on_aws_file?(file, supports_direct_download) && !file.file_storage?
return redirect(signed_head_url(file))
end
@@ -148,7 +151,7 @@ module API
package_file.file_sha1
else
track_package_event('pull_package', :maven, project: project, namespace: project.namespace) if jar_file?(format)
- present_carrierwave_file_with_head_support!(package_file.file)
+ present_carrierwave_file_with_head_support!(package_file)
end
end
@@ -189,7 +192,7 @@ module API
else
track_package_event('pull_package', :maven, project: package.project, namespace: package.project.namespace) if jar_file?(format)
- present_carrierwave_file_with_head_support!(package_file.file)
+ present_carrierwave_file_with_head_support!(package_file)
end
end
end
@@ -227,7 +230,7 @@ module API
else
track_package_event('pull_package', :maven, project: user_project, namespace: user_project.namespace) if jar_file?(format)
- present_carrierwave_file_with_head_support!(package_file.file)
+ present_carrierwave_file_with_head_support!(package_file)
end
end
diff --git a/lib/api/npm_project_packages.rb b/lib/api/npm_project_packages.rb
index 21bb2e69799..166c0b755fe 100644
--- a/lib/api/npm_project_packages.rb
+++ b/lib/api/npm_project_packages.rb
@@ -35,7 +35,7 @@ module API
track_package_event('pull_package', package, category: 'API::NpmPackages', project: project, namespace: project.namespace)
- present_carrierwave_file!(package_file.file)
+ present_package_file!(package_file)
end
desc 'Create NPM package' do
diff --git a/lib/api/nuget_project_packages.rb b/lib/api/nuget_project_packages.rb
index 1e630cffea1..3e05ea13311 100644
--- a/lib/api/nuget_project_packages.rb
+++ b/lib/api/nuget_project_packages.rb
@@ -193,7 +193,7 @@ module API
)
# nuget and dotnet don't support 302 Moved status codes, supports_direct_download has to be set to false
- present_carrierwave_file!(package_file.file, supports_direct_download: false)
+ present_package_file!(package_file, supports_direct_download: false)
end
end
end
diff --git a/lib/api/pypi_packages.rb b/lib/api/pypi_packages.rb
index f8a7a3c0ecc..ae583ca968a 100644
--- a/lib/api/pypi_packages.rb
+++ b/lib/api/pypi_packages.rb
@@ -120,7 +120,7 @@ module API
track_package_event('pull_package', :pypi)
- present_carrierwave_file!(package_file.file, supports_direct_download: true)
+ present_package_file!(package_file, supports_direct_download: true)
end
desc 'The PyPi Simple Group Index Endpoint' do
@@ -180,7 +180,7 @@ module API
track_package_event('pull_package', :pypi, project: project, namespace: project.namespace)
- present_carrierwave_file!(package_file.file, supports_direct_download: true)
+ present_package_file!(package_file, supports_direct_download: true)
end
desc 'The PyPi Simple Project Index Endpoint' do
diff --git a/lib/api/rubygem_packages.rb b/lib/api/rubygem_packages.rb
index 85d6f0c5a9b..b4d02613e4c 100644
--- a/lib/api/rubygem_packages.rb
+++ b/lib/api/rubygem_packages.rb
@@ -74,7 +74,7 @@ module API
track_package_event('pull_package', :rubygems, project: user_project, namespace: user_project.namespace)
- present_carrierwave_file!(package_file.file)
+ present_package_file!(package_file)
end
namespace 'api/v1' do
diff --git a/lib/gitlab/application_rate_limiter.rb b/lib/gitlab/application_rate_limiter.rb
index 70dacb4d188..507f94d87a5 100644
--- a/lib/gitlab/application_rate_limiter.rb
+++ b/lib/gitlab/application_rate_limiter.rb
@@ -51,7 +51,8 @@ module Gitlab
project_testing_integration: { threshold: 5, interval: 1.minute },
email_verification: { threshold: 10, interval: 10.minutes },
email_verification_code_send: { threshold: 10, interval: 1.hour },
- namespace_exists: { threshold: 20, interval: 1.minute }
+ namespace_exists: { threshold: 20, interval: 1.minute },
+ fetch_google_ip_list: { threshold: 10, interval: 1.minute }
}.freeze
end
diff --git a/lib/gitlab/ci/pipeline/chain/assign_partition.rb b/lib/gitlab/ci/pipeline/chain/assign_partition.rb
index 672d95fe747..f946041db31 100644
--- a/lib/gitlab/ci/pipeline/chain/assign_partition.rb
+++ b/lib/gitlab/ci/pipeline/chain/assign_partition.rb
@@ -7,8 +7,6 @@ module Gitlab
class AssignPartition < Chain::Base
include Chain::Helpers
- DEFAULT_PARTITION_ID = 100
-
def perform!
@pipeline.partition_id = find_partition_id
end
@@ -21,7 +19,7 @@ module Gitlab
# TODO handle parent-child pipelines
def find_partition_id
- DEFAULT_PARTITION_ID
+ ::Ci::Pipeline.current_partition_value
end
end
end
diff --git a/lib/gitlab/ci/processable_object_hierarchy.rb b/lib/gitlab/ci/processable_object_hierarchy.rb
new file mode 100644
index 00000000000..1122361e27e
--- /dev/null
+++ b/lib/gitlab/ci/processable_object_hierarchy.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class ProcessableObjectHierarchy < ::Gitlab::ObjectHierarchy
+ private
+
+ def middle_table
+ ::Ci::BuildNeed.arel_table
+ end
+
+ def from_tables(cte)
+ [objects_table, cte.table, middle_table]
+ end
+
+ def parent_id_column(_cte)
+ middle_table[:name]
+ end
+
+ def ancestor_conditions(cte)
+ middle_table[:name].eq(objects_table[:name]).and(
+ middle_table[:build_id].eq(cte.table[:id])
+ )
+ end
+
+ def descendant_conditions(cte)
+ middle_table[:build_id].eq(objects_table[:id]).and(
+ middle_table[:name].eq(cte.table[:name])
+ )
+ end
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index dbeb1447940..dc9a7536ee0 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -3114,6 +3114,9 @@ msgstr ""
msgid "AdminUsers|Blocking user has the following effects:"
msgstr ""
+msgid "AdminUsers|Bot"
+msgstr ""
+
msgid "AdminUsers|Can create group"
msgstr ""
@@ -27048,7 +27051,10 @@ msgstr ""
msgid "Notify|%{member_link} requested %{member_role} access to the %{target_source_link} %{target_type}."
msgstr ""
-msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{approved_highlight}was approved by%{highlight_end} %{approver_avatar} %{approver_link}"
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{reviewer_highlight}was approved by%{highlight_end} %{reviewer_avatar} %{reviewer_link}"
+msgstr ""
+
+msgid "Notify|%{mr_highlight}Merge request%{highlight_end} %{mr_link} %{reviewer_highlight}was unapproved by%{highlight_end} %{reviewer_avatar} %{reviewer_link}"
msgstr ""
msgid "Notify|%{paragraph_start}Hi %{name}!%{paragraph_end} %{paragraph_start}A new public key was added to your account:%{paragraph_end} %{paragraph_start}title: %{key_title}%{paragraph_end} %{paragraph_start}If this key was added in error, you can remove it under %{removal_link}%{paragraph_end}"
@@ -27144,6 +27150,12 @@ msgstr ""
msgid "Notify|Merge request was approved (%{approvals}/%{required_approvals})"
msgstr ""
+msgid "Notify|Merge request was unapproved"
+msgstr ""
+
+msgid "Notify|Merge request was unapproved (%{approvals_count}/%{approvals_required})"
+msgstr ""
+
msgid "Notify|Milestone changed to %{milestone}"
msgstr ""
diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb
index bcc483e9e0e..a60f0a3879a 100644
--- a/spec/factories/commit_statuses.rb
+++ b/spec/factories/commit_statuses.rb
@@ -56,7 +56,7 @@ FactoryBot.define do
end
after(:build) do |build, evaluator|
- build.project = build.pipeline.project
+ build.project ||= build.pipeline.project
end
end
end
diff --git a/spec/factories/packages/packages.rb b/spec/factories/packages/packages.rb
index 8a3fe4bd948..8074e505243 100644
--- a/spec/factories/packages/packages.rb
+++ b/spec/factories/packages/packages.rb
@@ -24,6 +24,10 @@ FactoryBot.define do
status { :pending_destruction }
end
+ trait :last_downloaded_at do
+ last_downloaded_at { 2.days.ago }
+ end
+
factory :maven_package do
maven_metadatum
diff --git a/spec/fixtures/api/schemas/graphql/packages/package_details.json b/spec/fixtures/api/schemas/graphql/packages/package_details.json
index 50e52a7bb87..33eb67a0280 100644
--- a/spec/fixtures/api/schemas/graphql/packages/package_details.json
+++ b/spec/fixtures/api/schemas/graphql/packages/package_details.json
@@ -13,7 +13,8 @@
"pipelines",
"versions",
"status",
- "canDestroy"
+ "canDestroy",
+ "lastDownloadedAt"
],
"properties": {
"id": {
@@ -173,6 +174,9 @@
},
"composerConfigRepositoryUrl": {
"type": "string"
+ },
+ "lastDownloadedAt": {
+ "type": ["string", "null"]
}
}
}
diff --git a/spec/fixtures/api/schemas/public_api/v4/packages/package.json b/spec/fixtures/api/schemas/public_api/v4/packages/package.json
index 607e0df1886..5d0d5f63aa9 100644
--- a/spec/fixtures/api/schemas/public_api/v4/packages/package.json
+++ b/spec/fixtures/api/schemas/public_api/v4/packages/package.json
@@ -6,7 +6,9 @@
"package_type",
"status",
"_links",
- "versions"
+ "versions",
+ "created_at",
+ "last_downloaded_at"
],
"properties": {
"name": {
@@ -38,6 +40,9 @@
"created_at": {
"type": "string"
},
+ "last_downloaded_at": {
+ "type": ["string", "null"]
+ },
"versions": {
"type": "array",
"items": {
diff --git a/spec/frontend/repository/components/table/index_spec.js b/spec/frontend/repository/components/table/index_spec.js
index 57b481634ca..697d2dcc7f5 100644
--- a/spec/frontend/repository/components/table/index_spec.js
+++ b/spec/frontend/repository/components/table/index_spec.js
@@ -38,6 +38,7 @@ const MOCK_BLOBS = [
const MOCK_COMMITS = [
{
fileName: 'blob.md',
+ filePath: 'test_dir/blob.md',
type: 'blob',
commit: {
message: 'Updated blob.md',
@@ -45,6 +46,7 @@ const MOCK_COMMITS = [
},
{
fileName: 'blob2.md',
+ filePath: 'test_dir/blob2.md',
type: 'blob',
commit: {
message: 'Updated blob2.md',
@@ -52,11 +54,20 @@ const MOCK_COMMITS = [
},
{
fileName: 'blob3.md',
+ filePath: 'test_dir/blob3.md',
type: 'blob',
commit: {
message: 'Updated blob3.md',
},
},
+ {
+ fileName: 'root_blob.md',
+ filePath: '/root_blob.md',
+ type: 'blob',
+ commit: {
+ message: 'Updated root_blob.md',
+ },
+ },
];
function factory({ path, isLoading = false, hasMore = true, entries = {}, commits = [] }) {
@@ -77,6 +88,8 @@ function factory({ path, isLoading = false, hasMore = true, entries = {}, commit
});
}
+const findTableRows = () => vm.findAllComponents(TableRow);
+
describe('Repository table component', () => {
afterEach(() => {
vm.destroy();
@@ -108,14 +121,14 @@ describe('Repository table component', () => {
it('renders table rows', () => {
factory({
- path: '/',
+ path: 'test_dir',
entries: {
blobs: MOCK_BLOBS,
},
commits: MOCK_COMMITS,
});
- const rows = vm.findAllComponents(TableRow);
+ const rows = findTableRows();
expect(rows.length).toEqual(3);
expect(rows.at(2).attributes().mode).toEqual('120000');
@@ -123,6 +136,28 @@ describe('Repository table component', () => {
expect(rows.at(2).props().commitInfo).toEqual(MOCK_COMMITS[2]);
});
+ it('renders correct commit info for blobs in the root', () => {
+ factory({
+ path: '/',
+ entries: {
+ blobs: [
+ {
+ id: '126abc',
+ sha: '126abc',
+ flatPath: 'root_blob.md',
+ name: 'root_blob.md',
+ type: 'blob',
+ webUrl: 'http://test.com',
+ mode: '120000',
+ },
+ ],
+ },
+ commits: MOCK_COMMITS,
+ });
+
+ expect(findTableRows().at(0).props().commitInfo).toEqual(MOCK_COMMITS[3]);
+ });
+
describe('Show more button', () => {
const showMoreButton = () => vm.find(GlButton);
diff --git a/spec/frontend/repository/log_tree_spec.js b/spec/frontend/repository/log_tree_spec.js
index e3b4dcb8acc..c1309539b6d 100644
--- a/spec/frontend/repository/log_tree_spec.js
+++ b/spec/frontend/repository/log_tree_spec.js
@@ -30,7 +30,7 @@ describe('resolveCommit', () => {
{ fileName: 'index.js', filePath: '/app/assets/index.js' },
];
- resolveCommit(commits, '', resolver);
+ resolveCommit(commits, '/', resolver);
expect(resolver.resolve).toHaveBeenCalledWith({
fileName: 'index.js',
@@ -107,14 +107,14 @@ describe('fetchLogsTree', () => {
}));
it('calls entry resolver', () =>
- fetchLogsTree(client, '', '0', resolver).then(() => {
+ fetchLogsTree(client, 'test', '0', resolver).then(() => {
expect(resolver.resolve).toHaveBeenCalledWith(
expect.objectContaining({
__typename: 'LogTreeCommit',
commitPath: 'https://test.com',
committedDate: '2019-01-01',
fileName: 'index.js',
- filePath: '/index.js',
+ filePath: 'test/index.js',
message: 'testing message',
sha: '123',
}),
@@ -122,7 +122,7 @@ describe('fetchLogsTree', () => {
}));
it('writes query to client', async () => {
- await fetchLogsTree(client, '', '0', resolver);
+ await fetchLogsTree(client, '/', '0', resolver);
expect(client.readQuery({ query: commitsQuery })).toEqual({
commits: [
expect.objectContaining({
diff --git a/spec/frontend/repository/utils/commit_spec.js b/spec/frontend/repository/utils/commit_spec.js
index b3dd5118308..65728e9cb24 100644
--- a/spec/frontend/repository/utils/commit_spec.js
+++ b/spec/frontend/repository/utils/commit_spec.js
@@ -15,7 +15,7 @@ const mockData = [
describe('normalizeData', () => {
it('normalizes data into LogTreeCommit object', () => {
- expect(normalizeData(mockData, '')).toEqual([
+ expect(normalizeData(mockData, '/')).toEqual([
{
sha: '123',
message: 'testing message',
diff --git a/spec/helpers/notify_helper_spec.rb b/spec/helpers/notify_helper_spec.rb
index 77298d7e205..09da2b89dff 100644
--- a/spec/helpers/notify_helper_spec.rb
+++ b/spec/helpers/notify_helper_spec.rb
@@ -51,9 +51,9 @@ RSpec.describe NotifyHelper do
end
end
- describe '#merge_request_approved_description' do
+ describe '#merge_request_hash_param' do
let(:merge_request) { create(:merge_request) }
- let(:user) { create(:user) }
+ let(:reviewer) { create(:user) }
let(:avatar_icon_for_user) { 'avatar_icon_for_user' }
before do
@@ -61,20 +61,20 @@ RSpec.describe NotifyHelper do
end
it 'returns MR approved description' do
- result = helper.merge_request_approved_description(merge_request, user)
- expect(result).to eq "<span style=\"font-weight: 600;color:#333333;\">Merge request</span> " \
- "#{
- link_to(merge_request.to_reference, merge_request_url(merge_request),
- style: "font-weight: 600;color:#3777b0;text-decoration:none")
- } " \
- "<span>was approved by</span> " \
- "#{
- content_tag(:img, nil, height: "24", src: avatar_icon_for_user,
- style: "border-radius:12px;margin:-7px 0 -7px 3px;",
- width: "24", alt: "Avatar", class: "avatar"
- )
- } " \
- "#{link_to(user.name, user_url(user), style: "color:#333333;text-decoration:none;", class: "muted")}"
+ mr_link_style = "font-weight: 600;color:#3777b0;text-decoration:none"
+ reviewer_avatar_style = "border-radius:12px;margin:-7px 0 -7px 3px;"
+ mr_link = link_to(merge_request.to_reference, merge_request_url(merge_request), style: mr_link_style).html_safe
+ reviewer_avatar = content_tag(:img, nil, height: "24", src: avatar_icon_for_user, style: reviewer_avatar_style, \
+ width: "24", alt: "Avatar", class: "avatar").html_safe
+ reviewer_link = link_to(reviewer.name, user_url(reviewer), style: "color:#333333;text-decoration:none;", \
+ class: "muted").html_safe
+ result = helper.merge_request_hash_param(merge_request, reviewer)
+ expect(result[:mr_highlight]).to eq '<span style="font-weight: 600;color:#333333;">'.html_safe
+ expect(result[:highlight_end]).to eq '</span>'.html_safe
+ expect(result[:mr_link]).to eq mr_link
+ expect(result[:reviewer_highlight]).to eq '<span>'.html_safe
+ expect(result[:reviewer_avatar]).to eq reviewer_avatar
+ expect(result[:reviewer_link]).to eq reviewer_link
end
end
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
index 91e6305ea99..617a796781e 100644
--- a/spec/helpers/users_helper_spec.rb
+++ b/spec/helpers/users_helper_spec.rb
@@ -136,7 +136,7 @@ RSpec.describe UsersHelper do
badges = helper.user_badges_in_admin_section(blocked_user)
- expect(filter_ee_badges(badges)).to match_array([text: "Blocked", variant: "danger"])
+ expect(filter_ee_badges(badges)).to match_array([text: s_("AdminUsers|Blocked"), variant: "danger"])
end
end
@@ -146,7 +146,7 @@ RSpec.describe UsersHelper do
badges = helper.user_badges_in_admin_section(blocked_pending_approval_user)
- expect(filter_ee_badges(badges)).to match_array([text: 'Pending approval', variant: 'info'])
+ expect(filter_ee_badges(badges)).to match_array([text: s_('AdminUsers|Pending approval'), variant: 'info'])
end
end
@@ -156,7 +156,7 @@ RSpec.describe UsersHelper do
badges = helper.user_badges_in_admin_section(banned_user)
- expect(filter_ee_badges(badges)).to match_array([text: 'Banned', variant: 'danger'])
+ expect(filter_ee_badges(badges)).to match_array([text: s_('AdminUsers|Banned'), variant: 'danger'])
end
end
@@ -166,7 +166,17 @@ RSpec.describe UsersHelper do
badges = helper.user_badges_in_admin_section(admin_user)
- expect(filter_ee_badges(badges)).to match_array([text: "Admin", variant: "success"])
+ expect(filter_ee_badges(badges)).to match_array([text: s_("AdminUsers|Admin"), variant: "success"])
+ end
+ end
+
+ context 'with a bot' do
+ it "returns the bot badge" do
+ bot = create(:user, :bot)
+
+ badges = helper.user_badges_in_admin_section(bot)
+
+ expect(filter_ee_badges(badges)).to match_array([text: s_('AdminUsers|Bot'), variant: "muted"])
end
end
@@ -176,7 +186,7 @@ RSpec.describe UsersHelper do
badges = helper.user_badges_in_admin_section(external_user)
- expect(filter_ee_badges(badges)).to match_array([text: "External", variant: "secondary"])
+ expect(filter_ee_badges(badges)).to match_array([text: s_("AdminUsers|External"), variant: "secondary"])
end
end
@@ -184,7 +194,7 @@ RSpec.describe UsersHelper do
it 'returns the "It\'s You" badge' do
badges = helper.user_badges_in_admin_section(user)
- expect(filter_ee_badges(badges)).to match_array([text: "It's you!", variant: "muted"])
+ expect(filter_ee_badges(badges)).to match_array([text: s_("AdminUsers|It's you!"), variant: "muted"])
end
end
@@ -195,9 +205,9 @@ RSpec.describe UsersHelper do
badges = helper.user_badges_in_admin_section(user)
expect(badges).to match_array([
- { text: "Blocked", variant: "danger" },
- { text: "Admin", variant: "success" },
- { text: "External", variant: "secondary" }
+ { text: s_("AdminUsers|Blocked"), variant: "danger" },
+ { text: s_("AdminUsers|Admin"), variant: "success" },
+ { text: s_("AdminUsers|External"), variant: "secondary" }
])
end
end
@@ -208,7 +218,7 @@ RSpec.describe UsersHelper do
badges = helper.user_badges_in_admin_section(locked_user)
- expect(filter_ee_badges(badges)).to match_array([text: "Locked", variant: "warning"])
+ expect(filter_ee_badges(badges)).to match_array([text: s_("AdminUsers|Locked"), variant: "warning"])
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb
index 3a8abb69850..7215c65d41b 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/assign_partition_spec.rb
@@ -16,7 +16,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::AssignPartition do
describe '#perform!' do
before do
- stub_const("#{described_class}::DEFAULT_PARTITION_ID", current_partition_id)
+ allow(Ci::Pipeline).to receive(:current_partition_value) { current_partition_id }
end
subject { step.perform! }
diff --git a/spec/lib/gitlab/ci/processable_object_hierarchy_spec.rb b/spec/lib/gitlab/ci/processable_object_hierarchy_spec.rb
new file mode 100644
index 00000000000..a844ce6486b
--- /dev/null
+++ b/spec/lib/gitlab/ci/processable_object_hierarchy_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::ProcessableObjectHierarchy do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { project.owner }
+
+ let_it_be(:pipeline) { create(:ci_empty_pipeline, project: project, ref: 'master') }
+
+ let_it_be(:job1) { create(:ci_build, :created, pipeline: pipeline, name: 'job1') }
+ let_it_be(:job2) { create(:ci_build, :created, :dependent, pipeline: pipeline, name: 'job2', needed: job1) }
+ let_it_be(:job3) { create(:ci_build, :created, :dependent, pipeline: pipeline, name: 'job3', needed: job1) }
+ let_it_be(:job4) { create(:ci_build, :created, :dependent, pipeline: pipeline, name: 'job4', needed: job2) }
+ let_it_be(:job5) { create(:ci_build, :created, :dependent, pipeline: pipeline, name: 'job5', needed: job3) }
+ let_it_be(:job6) { create(:ci_build, :created, :dependent, pipeline: pipeline, name: 'job6', needed: job4) }
+
+ describe '#base_and_ancestors' do
+ it 'includes the base and its ancestors' do
+ relation = described_class.new(::Ci::Processable.where(id: job2.id)).base_and_ancestors
+
+ expect(relation).to eq([job2, job1])
+ end
+
+ it 'can find ancestors upto a certain level' do
+ relation = described_class.new(::Ci::Processable.where(id: job4.id)).base_and_ancestors(upto: job1.name)
+
+ expect(relation).to eq([job4, job2])
+ end
+
+ describe 'hierarchy_order option' do
+ let(:relation) do
+ described_class.new(::Ci::Processable.where(id: job4.id)).base_and_ancestors(hierarchy_order: hierarchy_order)
+ end
+
+ context 'for :asc' do
+ let(:hierarchy_order) { :asc }
+
+ it 'orders by child to ancestor' do
+ expect(relation).to eq([job4, job2, job1])
+ end
+ end
+
+ context 'for :desc' do
+ let(:hierarchy_order) { :desc }
+
+ it 'orders by ancestor to child' do
+ expect(relation).to eq([job1, job2, job4])
+ end
+ end
+ end
+ end
+
+ describe '#base_and_descendants' do
+ it 'includes the base and its descendants' do
+ relation = described_class.new(::Ci::Processable.where(id: job2.id)).base_and_descendants
+
+ expect(relation).to contain_exactly(job2, job4, job6)
+ end
+
+ context 'when with_depth is true' do
+ let(:relation) do
+ described_class.new(::Ci::Processable.where(id: job1.id)).base_and_descendants(with_depth: true)
+ end
+
+ it 'includes depth in the results' do
+ object_depths = {
+ job1.id => 1,
+ job2.id => 2,
+ job3.id => 2,
+ job4.id => 3,
+ job5.id => 3,
+ job6.id => 4
+ }
+
+ relation.each do |object|
+ expect(object.depth).to eq(object_depths[object.id])
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 9fd2980bc7b..7ee381b29ea 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1599,32 +1599,6 @@ RSpec.describe Ci::Build do
end
end
- describe '#count_user_verification?' do
- subject { build.count_user_verification? }
-
- context 'when build is the verify action for the environment' do
- let(:build) do
- create(:ci_build,
- ref: 'master',
- environment: 'staging',
- options: { environment: { action: 'verify' } })
- end
-
- it { is_expected.to be_truthy }
- end
-
- context 'when build is not the verify action for the environment' do
- let(:build) do
- create(:ci_build,
- ref: 'master',
- environment: 'staging',
- options: { environment: { action: 'start' } })
- end
-
- it { is_expected.to be_falsey }
- end
- end
-
describe '#expanded_environment_name' do
subject { build.expanded_environment_name }
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index 1962d232f3d..098f8bd4514 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -761,7 +761,7 @@ RSpec.describe Ci::JobArtifact do
end
end
- context 'with partition_id' do
+ describe 'partitioning' do
let(:job) { build(:ci_build, partition_id: 123) }
let(:artifact) { build(:ci_job_artifact, job: job, partition_id: nil) }
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index f93aeef4f23..bc4ba33067f 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -5484,4 +5484,36 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
end
end
end
+
+ describe 'partitioning' do
+ let(:pipeline) { build(:ci_pipeline) }
+
+ before do
+ allow(described_class).to receive(:current_partition_value) { 123 }
+ end
+
+ it 'sets partition_id to the current partition value' do
+ expect { pipeline.valid? }.to change(pipeline, :partition_id).to(123)
+ end
+
+ context 'when it is already set' do
+ let(:pipeline) { build(:ci_pipeline, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { pipeline.valid? }.not_to change(pipeline, :partition_id)
+ end
+ end
+
+ context 'without current partition value' do
+ before do
+ allow(described_class).to receive(:current_partition_value) {}
+ end
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { pipeline.valid? }.not_to change(pipeline, :partition_id)
+ end
+ end
+ end
end
diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb
index d55a8509a98..dd9af33a562 100644
--- a/spec/models/ci/stage_spec.rb
+++ b/spec/models/ci/stage_spec.rb
@@ -369,4 +369,33 @@ RSpec.describe Ci::Stage, :models do
let!(:model) { create(:ci_stage, project: parent) }
end
end
+
+ describe 'partitioning' do
+ context 'with pipeline' do
+ let(:pipeline) { build(:ci_pipeline, partition_id: 123) }
+ let(:stage) { build(:ci_stage, pipeline: pipeline) }
+
+ it 'copies the partition_id from pipeline' do
+ expect { stage.valid? }.to change(stage, :partition_id).to(123)
+ end
+
+ context 'when it is already set' do
+ let(:stage) { build(:ci_stage, pipeline: pipeline, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { stage.valid? }.not_to change(stage, :partition_id)
+ end
+ end
+ end
+
+ context 'without pipeline' do
+ subject(:stage) { build(:ci_stage, pipeline: nil) }
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { stage.valid? }.not_to change(stage, :partition_id)
+ end
+ end
+ end
end
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index ec6f2bda79a..adbd20b6730 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -1018,4 +1018,37 @@ RSpec.describe CommitStatus do
it { is_expected.to be_nil }
end
end
+
+ describe 'partitioning' do
+ context 'with pipeline' do
+ let(:pipeline) { build(:ci_pipeline, partition_id: 123) }
+ let(:status) { build(:commit_status, pipeline: pipeline) }
+
+ it 'copies the partition_id from pipeline' do
+ expect { status.valid? }.to change(status, :partition_id).to(123)
+ end
+
+ context 'when it is already set' do
+ let(:status) { build(:commit_status, pipeline: pipeline, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { status.valid? }.not_to change(status, :partition_id)
+ end
+ end
+ end
+
+ context 'without pipeline' do
+ subject(:status) do
+ build(:commit_status,
+ project: build_stubbed(:project),
+ pipeline: nil)
+ end
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { status.valid? }.not_to change(status, :partition_id)
+ end
+ end
+ end
end
diff --git a/spec/models/concerns/ci/has_deployment_name_spec.rb b/spec/models/concerns/ci/has_deployment_name_spec.rb
deleted file mode 100644
index 8c7338638b1..00000000000
--- a/spec/models/concerns/ci/has_deployment_name_spec.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe Ci::HasDeploymentName do
- describe 'deployment_name?' do
- let(:build) { create(:ci_build) }
-
- subject { build.branch? }
-
- it 'does detect deployment names' do
- build.name = 'deployment'
-
- expect(build.deployment_name?).to be_truthy
- end
-
- it 'does detect partial deployment names' do
- build.name = 'do a really cool deploy'
-
- expect(build.deployment_name?).to be_truthy
- end
-
- it 'does not detect non-deployment names' do
- build.name = 'testing'
-
- expect(build.deployment_name?).to be_falsy
- end
-
- it 'is case insensitive' do
- build.name = 'DEPLOY'
- expect(build.deployment_name?).to be_truthy
- end
- end
-end
diff --git a/spec/models/concerns/ci/track_environment_usage_spec.rb b/spec/models/concerns/ci/track_environment_usage_spec.rb
new file mode 100644
index 00000000000..d75972c49b5
--- /dev/null
+++ b/spec/models/concerns/ci/track_environment_usage_spec.rb
@@ -0,0 +1,61 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::TrackEnvironmentUsage do
+ describe '#verifies_environment?' do
+ subject { build.verifies_environment? }
+
+ context 'when build is the verify action for the environment' do
+ let(:build) do
+ build_stubbed(:ci_build,
+ ref: 'master',
+ environment: 'staging',
+ options: { environment: { action: 'verify' } })
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when build is not the verify action for the environment' do
+ let(:build) do
+ build_stubbed(:ci_build,
+ ref: 'master',
+ environment: 'staging',
+ options: { environment: { action: 'start' } })
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ describe 'deployment_name?' do
+ let(:build) { create(:ci_build) }
+
+ subject { build.branch? }
+
+ it 'does detect deployment names' do
+ build.name = 'deployment'
+
+ expect(build).to be_deployment_name
+ end
+
+ it 'does detect partial deployment names' do
+ build.name = 'do a really cool deploy'
+
+ expect(build).to be_deployment_name
+ end
+
+ it 'does not detect non-deployment names' do
+ build.name = 'testing'
+
+ expect(build).not_to be_deployment_name
+ end
+
+ it 'is case insensitive' do
+ build.name = 'DEPLOY'
+
+ expect(build).to be_deployment_name
+ end
+ end
+end
diff --git a/spec/models/packages/package_spec.rb b/spec/models/packages/package_spec.rb
index e44883e7b0e..fb88dbb4212 100644
--- a/spec/models/packages/package_spec.rb
+++ b/spec/models/packages/package_spec.rb
@@ -1357,4 +1357,16 @@ RSpec.describe Packages::Package, type: :model do
it { is_expected.to eq(normalized_name) }
end
end
+
+ describe '#touch_last_downloaded_at' do
+ let_it_be(:package) { create(:package) }
+
+ subject { package.touch_last_downloaded_at }
+
+ it 'updates the downloaded_at' do
+ expect(::Gitlab::Database::LoadBalancing::Session).to receive(:without_sticky_writes).and_call_original
+ expect { subject }
+ .to change(package, :last_downloaded_at).from(nil).to(instance_of(ActiveSupport::TimeWithZone))
+ end
+ end
end
diff --git a/spec/requests/api/commit_statuses_spec.rb b/spec/requests/api/commit_statuses_spec.rb
index 39be28d7427..dc5d9620dc4 100644
--- a/spec/requests/api/commit_statuses_spec.rb
+++ b/spec/requests/api/commit_statuses_spec.rb
@@ -478,6 +478,26 @@ RSpec.describe API::CommitStatuses do
.to include 'has already been taken'
end
end
+
+ context 'with partitions' do
+ let(:current_partition_id) { 123 }
+
+ before do
+ allow(Ci::Pipeline)
+ .to receive(:current_partition_value) { current_partition_id }
+ end
+
+ it 'creates records in the current partition' do
+ expect { post api(post_url, developer), params: { state: 'running' } }
+ .to change(CommitStatus, :count).by(1)
+ .and change(Ci::Pipeline, :count).by(1)
+
+ status = CommitStatus.find(json_response['id'])
+
+ expect(status.partition_id).to eq(current_partition_id)
+ expect(status.pipeline.partition_id).to eq(current_partition_id)
+ end
+ end
end
context 'reporter user' do
diff --git a/spec/requests/api/conan_instance_packages_spec.rb b/spec/requests/api/conan_instance_packages_spec.rb
index e4747e0eb99..b343e0cfc97 100644
--- a/spec/requests/api/conan_instance_packages_spec.rb
+++ b/spec/requests/api/conan_instance_packages_spec.rb
@@ -103,8 +103,7 @@ RSpec.describe API::ConanInstancePackages do
context 'file download endpoints' do
include_context 'conan file download endpoints'
- describe 'GET /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/
-:recipe_revision/export/:file_name' do
+ describe 'GET /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/export/:file_name' do
subject do
get api("/packages/conan/v1/files/#{recipe_path}/#{metadata.recipe_revision}/export/#{recipe_file.file_name}"),
headers: headers
@@ -114,8 +113,7 @@ RSpec.describe API::ConanInstancePackages do
it_behaves_like 'project not found by recipe'
end
- describe 'GET /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/
-:recipe_revision/package/:conan_package_reference/:package_revision/:file_name' do
+ describe 'GET /api/v4/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name' do
subject do
get api("/packages/conan/v1/files/#{recipe_path}/#{metadata.recipe_revision}/package/#{metadata.conan_package_reference}/#{metadata.package_revision}/#{package_file.file_name}"),
headers: headers
diff --git a/spec/requests/api/conan_project_packages_spec.rb b/spec/requests/api/conan_project_packages_spec.rb
index 48e36b55a68..4e6af9942ef 100644
--- a/spec/requests/api/conan_project_packages_spec.rb
+++ b/spec/requests/api/conan_project_packages_spec.rb
@@ -102,8 +102,7 @@ RSpec.describe API::ConanProjectPackages do
context 'file download endpoints', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/326194' do
include_context 'conan file download endpoints'
- describe 'GET /api/v4/projects/:id/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/
-:recipe_revision/export/:file_name' do
+ describe 'GET /api/v4/projects/:id/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/export/:file_name' do
subject do
get api("/projects/#{project_id}/packages/conan/v1/files/#{recipe_path}/#{metadata.recipe_revision}/export/#{recipe_file.file_name}"),
headers: headers
@@ -113,8 +112,7 @@ RSpec.describe API::ConanProjectPackages do
it_behaves_like 'project not found by project id'
end
- describe 'GET /api/v4/projects/:id/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/
-:recipe_revision/package/:conan_package_reference/:package_revision/:file_name' do
+ describe 'GET /api/v4/projects/:id/packages/conan/v1/files/:package_name/package_version/:package_username/:package_channel/:recipe_revision/package/:conan_package_reference/:package_revision/:file_name' do
subject do
get api("/projects/#{project_id}/packages/conan/v1/files/#{recipe_path}/#{metadata.recipe_revision}/package/#{metadata.conan_package_reference}/#{metadata.package_revision}/#{package_file.file_name}"),
headers: headers
diff --git a/spec/requests/api/debian_group_packages_spec.rb b/spec/requests/api/debian_group_packages_spec.rb
index 956de61bf07..542ae05f996 100644
--- a/spec/requests/api/debian_group_packages_spec.rb
+++ b/spec/requests/api/debian_group_packages_spec.rb
@@ -49,11 +49,11 @@ RSpec.describe API::DebianGroupPackages do
end
describe 'GET groups/:id/-/packages/debian/pool/:codename/:project_id/:letter/:package_name/:package_version/:file_name' do
+ using RSpec::Parameterized::TableSyntax
+
let(:url) { "/groups/#{container.id}/-/packages/debian/pool/#{package.debian_distribution.codename}/#{project.id}/#{letter}/#{package.name}/#{package.version}/#{file_name}" }
let(:file_name) { params[:file_name] }
- using RSpec::Parameterized::TableSyntax
-
where(:file_name, :success_body) do
'sample_1.2.3~alpha2.tar.xz' | /^.7zXZ/
'sample_1.2.3~alpha2.dsc' | /^Format: 3.0 \(native\)/
@@ -65,6 +65,12 @@ RSpec.describe API::DebianGroupPackages do
with_them do
it_behaves_like 'Debian packages read endpoint', 'GET', :success, params[:success_body]
+
+ context 'for bumping last downloaded at' do
+ include_context 'Debian repository access', :public, :developer, :basic do
+ it_behaves_like 'bumping the package last downloaded at field'
+ end
+ end
end
end
end
diff --git a/spec/requests/api/debian_project_packages_spec.rb b/spec/requests/api/debian_project_packages_spec.rb
index 122a3df5e67..f0cd0b6d5ed 100644
--- a/spec/requests/api/debian_project_packages_spec.rb
+++ b/spec/requests/api/debian_project_packages_spec.rb
@@ -49,11 +49,11 @@ RSpec.describe API::DebianProjectPackages do
end
describe 'GET projects/:id/packages/debian/pool/:codename/:letter/:package_name/:package_version/:file_name' do
+ using RSpec::Parameterized::TableSyntax
+
let(:url) { "/projects/#{container.id}/packages/debian/pool/#{package.debian_distribution.codename}/#{letter}/#{package.name}/#{package.version}/#{file_name}" }
let(:file_name) { params[:file_name] }
- using RSpec::Parameterized::TableSyntax
-
where(:file_name, :success_body) do
'sample_1.2.3~alpha2.tar.xz' | /^.7zXZ/
'sample_1.2.3~alpha2.dsc' | /^Format: 3.0 \(native\)/
@@ -65,6 +65,12 @@ RSpec.describe API::DebianProjectPackages do
with_them do
it_behaves_like 'Debian packages read endpoint', 'GET', :success, params[:success_body]
+
+ context 'for bumping last downloaded at' do
+ include_context 'Debian repository access', :public, :developer, :basic do
+ it_behaves_like 'bumping the package last downloaded at field'
+ end
+ end
end
end
diff --git a/spec/requests/api/generic_packages_spec.rb b/spec/requests/api/generic_packages_spec.rb
index 3a5c6103781..823eafab734 100644
--- a/spec/requests/api/generic_packages_spec.rb
+++ b/spec/requests/api/generic_packages_spec.rb
@@ -572,6 +572,12 @@ RSpec.describe API::GenericPackages do
expect(response).to have_gitlab_http_status(expected_status)
end
+
+ if params[:expected_status] == :success
+ it_behaves_like 'bumping the package last downloaded at field' do
+ subject { download_file(auth_header) }
+ end
+ end
end
where(:authenticate_with, :expected_status) do
@@ -587,6 +593,12 @@ RSpec.describe API::GenericPackages do
expect(response).to have_gitlab_http_status(expected_status)
end
+
+ if params[:expected_status] == :success
+ it_behaves_like 'bumping the package last downloaded at field' do
+ subject { download_file(deploy_token_auth_header) }
+ end
+ end
end
end
@@ -608,6 +620,12 @@ RSpec.describe API::GenericPackages do
expect(response).to have_gitlab_http_status(expected_status)
end
+
+ if params[:expected_status] == :success
+ it_behaves_like 'bumping the package last downloaded at field' do
+ subject { download_file(personal_access_token_header) }
+ end
+ end
end
end
diff --git a/spec/requests/api/graphql/packages/composer_spec.rb b/spec/requests/api/graphql/packages/composer_spec.rb
index 9830623ede8..89c01d44771 100644
--- a/spec/requests/api/graphql/packages/composer_spec.rb
+++ b/spec/requests/api/graphql/packages/composer_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'package details' do
include GraphqlHelpers
include_context 'package details setup'
- let_it_be(:package) { create(:composer_package, project: project) }
+ let_it_be(:package) { create(:composer_package, :last_downloaded_at, project: project) }
let_it_be(:composer_json) { { name: 'name', type: 'type', license: 'license', version: 1 } }
let_it_be(:composer_metadatum) do
# we are forced to manually create the metadatum, without using the factory to force the sha to be a string
diff --git a/spec/requests/api/graphql/packages/conan_spec.rb b/spec/requests/api/graphql/packages/conan_spec.rb
index 5bd5a71bbeb..7ad85edecef 100644
--- a/spec/requests/api/graphql/packages/conan_spec.rb
+++ b/spec/requests/api/graphql/packages/conan_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'conan package details' do
include GraphqlHelpers
include_context 'package details setup'
- let_it_be(:package) { create(:conan_package, project: project) }
+ let_it_be(:package) { create(:conan_package, :last_downloaded_at, project: project) }
let(:metadata) { query_graphql_fragment('ConanMetadata') }
let(:package_files_metadata) { query_graphql_fragment('ConanFileMetadata') }
diff --git a/spec/requests/api/graphql/packages/helm_spec.rb b/spec/requests/api/graphql/packages/helm_spec.rb
index 1675b8faa23..79a589e2dc2 100644
--- a/spec/requests/api/graphql/packages/helm_spec.rb
+++ b/spec/requests/api/graphql/packages/helm_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'helm package details' do
include GraphqlHelpers
include_context 'package details setup'
- let_it_be(:package) { create(:helm_package, project: project) }
+ let_it_be(:package) { create(:helm_package, :last_downloaded_at, project: project) }
let(:package_files_metadata) { query_graphql_fragment('HelmFileMetadata') }
diff --git a/spec/requests/api/graphql/packages/maven_spec.rb b/spec/requests/api/graphql/packages/maven_spec.rb
index 9d59a922660..b7f39efcf73 100644
--- a/spec/requests/api/graphql/packages/maven_spec.rb
+++ b/spec/requests/api/graphql/packages/maven_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'maven package details' do
include GraphqlHelpers
include_context 'package details setup'
- let_it_be(:package) { create(:maven_package, project: project) }
+ let_it_be(:package) { create(:maven_package, :last_downloaded_at, project: project) }
let(:metadata) { query_graphql_fragment('MavenMetadata') }
@@ -31,7 +31,9 @@ RSpec.describe 'maven package details' do
context 'a versionless maven package' do
let_it_be(:maven_metadatum) { create(:maven_metadatum, app_version: nil) }
- let_it_be(:package) { create(:maven_package, project: project, version: nil, maven_metadatum: maven_metadatum) }
+ let_it_be(:package) do
+ create(:maven_package, :last_downloaded_at, project: project, version: nil, maven_metadatum: maven_metadatum)
+ end
subject { post_graphql(query, current_user: user) }
diff --git a/spec/requests/api/graphql/packages/nuget_spec.rb b/spec/requests/api/graphql/packages/nuget_spec.rb
index 87cffc67ce5..7de132d1574 100644
--- a/spec/requests/api/graphql/packages/nuget_spec.rb
+++ b/spec/requests/api/graphql/packages/nuget_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'nuget package details' do
include GraphqlHelpers
include_context 'package details setup'
- let_it_be(:package) { create(:nuget_package, :with_metadatum, project: project) }
+ let_it_be(:package) { create(:nuget_package, :last_downloaded_at, :with_metadatum, project: project) }
let_it_be(:dependency_link) { create(:packages_dependency_link, :with_nuget_metadatum, package: package) }
let(:metadata) { query_graphql_fragment('NugetMetadata') }
diff --git a/spec/requests/api/graphql/packages/package_spec.rb b/spec/requests/api/graphql/packages/package_spec.rb
index b9bb57c7df7..e9f82d66775 100644
--- a/spec/requests/api/graphql/packages/package_spec.rb
+++ b/spec/requests/api/graphql/packages/package_spec.rb
@@ -6,8 +6,8 @@ RSpec.describe 'package details' do
let_it_be_with_reload(:group) { create(:group) }
let_it_be_with_reload(:project) { create(:project, group: group) }
+ let_it_be_with_reload(:composer_package) { create(:composer_package, :last_downloaded_at, project: project) }
let_it_be(:user) { create(:user) }
- let_it_be(:composer_package) { create(:composer_package, project: project) }
let_it_be(:composer_json) { { name: 'name', type: 'type', license: 'license', version: 1 } }
let_it_be(:composer_metadatum) do
# we are forced to manually create the metadatum, without using the factory to force the sha to be a string
@@ -65,6 +65,17 @@ RSpec.describe 'package details' do
end
end
+ context 'with package without last_downloaded_at' do
+ before do
+ composer_package.update!(last_downloaded_at: nil)
+ subject
+ end
+
+ it 'matches the JSON schema' do
+ expect(package_details).to match_schema('graphql/packages/package_details')
+ end
+ end
+
context 'with package files pending destruction' do
let_it_be(:package_file) { create(:package_file, package: composer_package) }
let_it_be(:package_file_pending_destruction) { create(:package_file, :pending_destruction, package: composer_package) }
diff --git a/spec/requests/api/graphql/packages/pypi_spec.rb b/spec/requests/api/graphql/packages/pypi_spec.rb
index 0cc5bd2e3b2..c0e589f3597 100644
--- a/spec/requests/api/graphql/packages/pypi_spec.rb
+++ b/spec/requests/api/graphql/packages/pypi_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'pypi package details' do
include GraphqlHelpers
include_context 'package details setup'
- let_it_be(:package) { create(:pypi_package, project: project) }
+ let_it_be(:package) { create(:pypi_package, :last_downloaded_at, project: project) }
let(:metadata) { query_graphql_fragment('PypiMetadata') }
diff --git a/spec/requests/api/maven_packages_spec.rb b/spec/requests/api/maven_packages_spec.rb
index 1b378788b6a..33a722793e3 100644
--- a/spec/requests/api/maven_packages_spec.rb
+++ b/spec/requests/api/maven_packages_spec.rb
@@ -244,6 +244,7 @@ RSpec.describe API::MavenPackages do
shared_examples 'getting a file' do
it_behaves_like 'tracking the file download event'
+ it_behaves_like 'bumping the package last downloaded at field'
it_behaves_like 'successfully returning the file'
it_behaves_like 'file download in FIPS mode'
@@ -275,7 +276,7 @@ RSpec.describe API::MavenPackages do
shared_examples 'getting a file' do
it_behaves_like 'tracking the file download event'
-
+ it_behaves_like 'bumping the package last downloaded at field'
it_behaves_like 'successfully returning the file'
it 'denies download when no private token' do
@@ -285,7 +286,6 @@ RSpec.describe API::MavenPackages do
end
it_behaves_like 'downloads with a job token'
-
it_behaves_like 'downloads with a deploy token'
context 'with a non existing maven path' do
@@ -307,7 +307,7 @@ RSpec.describe API::MavenPackages do
shared_examples 'getting a file' do
it_behaves_like 'tracking the file download event'
-
+ it_behaves_like 'bumping the package last downloaded at field'
it_behaves_like 'successfully returning the file'
it 'denies download when not enough permissions' do
@@ -327,7 +327,6 @@ RSpec.describe API::MavenPackages do
end
it_behaves_like 'downloads with a job token'
-
it_behaves_like 'downloads with a deploy token'
it 'does not allow download by a unauthorized deploy token with same id as a user with access' do
@@ -414,6 +413,7 @@ RSpec.describe API::MavenPackages do
shared_examples 'getting a file for a group' do
it_behaves_like 'tracking the file download event'
+ it_behaves_like 'bumping the package last downloaded at field'
it_behaves_like 'successfully returning the file'
it_behaves_like 'file download in FIPS mode'
@@ -445,7 +445,7 @@ RSpec.describe API::MavenPackages do
shared_examples 'getting a file for a group' do
it_behaves_like 'tracking the file download event'
-
+ it_behaves_like 'bumping the package last downloaded at field'
it_behaves_like 'successfully returning the file'
it 'denies download when no private token' do
@@ -455,7 +455,6 @@ RSpec.describe API::MavenPackages do
end
it_behaves_like 'downloads with a job token'
-
it_behaves_like 'downloads with a deploy token'
context 'with a non existing maven path' do
@@ -477,7 +476,7 @@ RSpec.describe API::MavenPackages do
shared_examples 'getting a file for a group' do
it_behaves_like 'tracking the file download event'
-
+ it_behaves_like 'bumping the package last downloaded at field'
it_behaves_like 'successfully returning the file'
it 'denies download when not enough permissions' do
@@ -495,7 +494,6 @@ RSpec.describe API::MavenPackages do
end
it_behaves_like 'downloads with a job token'
-
it_behaves_like 'downloads with a deploy token'
context 'with a non existing maven path' do
@@ -676,7 +674,7 @@ RSpec.describe API::MavenPackages do
subject { download_file_with_token(file_name: package_file.file_name) }
it_behaves_like 'tracking the file download event'
-
+ it_behaves_like 'bumping the package last downloaded at field'
it_behaves_like 'successfully returning the file'
it 'denies download when not enough permissions' do
@@ -694,7 +692,6 @@ RSpec.describe API::MavenPackages do
end
it_behaves_like 'downloads with a job token'
-
it_behaves_like 'downloads with a deploy token'
context 'with a non existing maven path' do
diff --git a/spec/requests/api/npm_project_packages_spec.rb b/spec/requests/api/npm_project_packages_spec.rb
index 3bcffac2760..bdcd6e7278d 100644
--- a/spec/requests/api/npm_project_packages_spec.rb
+++ b/spec/requests/api/npm_project_packages_spec.rb
@@ -63,6 +63,7 @@ RSpec.describe API::NpmProjectPackages do
it_behaves_like 'successfully downloads the file'
it_behaves_like 'a package tracking event', 'API::NpmPackages', 'pull_package'
+ it_behaves_like 'bumping the package last downloaded at field'
end
context 'with job token' do
@@ -70,12 +71,14 @@ RSpec.describe API::NpmProjectPackages do
it_behaves_like 'successfully downloads the file'
it_behaves_like 'a package tracking event', 'API::NpmPackages', 'pull_package'
+ it_behaves_like 'bumping the package last downloaded at field'
end
end
context 'a public project' do
it_behaves_like 'successfully downloads the file'
it_behaves_like 'a package tracking event', 'API::NpmPackages', 'pull_package'
+ it_behaves_like 'bumping the package last downloaded at field'
context 'with a job token for a different user' do
let_it_be(:other_user) { create(:user) }
diff --git a/spec/requests/api/project_packages_spec.rb b/spec/requests/api/project_packages_spec.rb
index 7a05da8e13f..00d295b3490 100644
--- a/spec/requests/api/project_packages_spec.rb
+++ b/spec/requests/api/project_packages_spec.rb
@@ -6,7 +6,7 @@ RSpec.describe API::ProjectPackages do
let_it_be(:project) { create(:project, :public) }
let(:user) { create(:user) }
- let!(:package1) { create(:npm_package, project: project, version: '3.1.0', name: "@#{project.root_namespace.path}/foo1") }
+ let!(:package1) { create(:npm_package, :last_downloaded_at, project: project, version: '3.1.0', name: "@#{project.root_namespace.path}/foo1") }
let(:package_url) { "/projects/#{project.id}/packages/#{package1.id}" }
let!(:package2) { create(:nuget_package, project: project, version: '2.0.4') }
let!(:another_package) { create(:npm_package) }
@@ -272,6 +272,17 @@ RSpec.describe API::ProjectPackages do
it_behaves_like 'returns package', :project, :no_type
it_behaves_like 'returns package', :project, :guest
end
+
+ context 'with a package without last_downloaded_at' do
+ let(:package_url) { "/projects/#{project.id}/packages/#{package2.id}" }
+
+ it 'returns 200 and the package information' do
+ subject
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(response).to match_response_schema(single_package_schema)
+ end
+ end
end
context 'project is private' do
diff --git a/spec/requests/projects/packages/package_files_controller_spec.rb b/spec/requests/projects/packages/package_files_controller_spec.rb
new file mode 100644
index 00000000000..a6daf57f0fa
--- /dev/null
+++ b/spec/requests/projects/packages/package_files_controller_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::Packages::PackageFilesController do
+ let_it_be(:project) { create(:project, :public) }
+ let_it_be(:package) { create(:package, project: project) }
+ let_it_be(:package_file) { create(:package_file, package: package) }
+
+ let(:filename) { package_file.file_name }
+
+ describe 'GET download' do
+ subject do
+ get download_namespace_project_package_file_url(
+ id: package_file.id,
+ namespace_id: project.namespace,
+ project_id: project
+ )
+ end
+
+ it 'sends the package file' do
+ subject
+
+ expect(response.headers['Content-Disposition'])
+ .to eq(%Q(attachment; filename="#{filename}"; filename*=UTF-8''#{filename}))
+ end
+
+ it_behaves_like 'bumping the package last downloaded at field'
+ end
+end
diff --git a/spec/serializers/pipeline_details_entity_spec.rb b/spec/serializers/pipeline_details_entity_spec.rb
index 67f8860ed4a..ca38721cc7f 100644
--- a/spec/serializers/pipeline_details_entity_spec.rb
+++ b/spec/serializers/pipeline_details_entity_spec.rb
@@ -104,7 +104,7 @@ RSpec.describe PipelineDetailsEntity do
let(:pipeline) { create(:ci_empty_pipeline) }
before do
- create(:generic_commit_status, pipeline: pipeline)
+ create(:commit_status, pipeline: pipeline)
end
it 'contains stages' do
diff --git a/spec/services/ci/after_requeue_job_service_spec.rb b/spec/services/ci/after_requeue_job_service_spec.rb
index fb67ee18fb2..1f692bdb71a 100644
--- a/spec/services/ci/after_requeue_job_service_spec.rb
+++ b/spec/services/ci/after_requeue_job_service_spec.rb
@@ -112,7 +112,7 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
check_jobs_statuses(
a1: 'pending',
a2: 'created',
- a3: 'skipped',
+ a3: 'created',
b1: 'success',
b2: 'created',
c1: 'created',
@@ -120,6 +120,26 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
)
end
+ context 'when the FF ci_requeue_with_dag_object_hierarchy is disabled' do
+ before do
+ stub_feature_flags(ci_requeue_with_dag_object_hierarchy: false)
+ end
+
+ it 'marks subsequent skipped jobs as processable but leaves a3 created' do
+ execute_after_requeue_service(a1)
+
+ check_jobs_statuses(
+ a1: 'pending',
+ a2: 'created',
+ a3: 'skipped',
+ b1: 'success',
+ b2: 'created',
+ c1: 'created',
+ c2: 'created'
+ )
+ end
+ end
+
context 'when executed by a different user than the original owner' do
let(:retryer) { create(:user).tap { |u| project.add_maintainer(u) } }
let(:service) { described_class.new(project, retryer) }
@@ -140,7 +160,7 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
expect(jobs_name_status_owner_needs).to contain_exactly(
{ 'name' => 'a1', 'status' => 'pending', 'user_id' => user.id, 'needs' => [] },
{ 'name' => 'a2', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['a1'] },
- { 'name' => 'a3', 'status' => 'skipped', 'user_id' => user.id, 'needs' => ['a2'] },
+ { 'name' => 'a3', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['a2'] },
{ 'name' => 'b1', 'status' => 'success', 'user_id' => user.id, 'needs' => [] },
{ 'name' => 'b2', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['a2'] },
{ 'name' => 'c1', 'status' => 'created', 'user_id' => retryer.id, 'needs' => ['b2'] },
@@ -237,6 +257,79 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
end
end
+ context 'with same-stage needs' do
+ let(:config) do
+ <<-EOY
+ a:
+ script: exit $(($RANDOM % 2))
+
+ b:
+ script: exit 0
+ needs: [a]
+
+ c:
+ script: exit 0
+ needs: [b]
+ EOY
+ end
+
+ let(:pipeline) do
+ Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload
+ end
+
+ let(:a) { find_job('a') }
+
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ check_jobs_statuses(
+ a: 'pending',
+ b: 'created',
+ c: 'created'
+ )
+
+ a.drop!
+ check_jobs_statuses(
+ a: 'failed',
+ b: 'skipped',
+ c: 'skipped'
+ )
+
+ new_a = Ci::RetryJobService.new(project, user).clone!(a)
+ new_a.enqueue!
+ check_jobs_statuses(
+ a: 'pending',
+ b: 'skipped',
+ c: 'skipped'
+ )
+ end
+
+ it 'marks subsequent skipped jobs as processable' do
+ execute_after_requeue_service(a)
+
+ check_jobs_statuses(
+ a: 'pending',
+ b: 'created',
+ c: 'created'
+ )
+ end
+
+ context 'when the FF ci_requeue_with_dag_object_hierarchy is disabled' do
+ before do
+ stub_feature_flags(ci_requeue_with_dag_object_hierarchy: false)
+ end
+
+ it 'marks the next subsequent skipped job as processable but leaves c skipped' do
+ execute_after_requeue_service(a)
+
+ check_jobs_statuses(
+ a: 'pending',
+ b: 'created',
+ c: 'skipped'
+ )
+ end
+ end
+ end
+
private
def find_job(name)
diff --git a/spec/services/ci/create_pipeline_service/partitioning_spec.rb b/spec/services/ci/create_pipeline_service/partitioning_spec.rb
index 4c362de07c6..cb4f32f591f 100644
--- a/spec/services/ci/create_pipeline_service/partitioning_spec.rb
+++ b/spec/services/ci/create_pipeline_service/partitioning_spec.rb
@@ -35,9 +35,7 @@ RSpec.describe Ci::CreatePipelineService, :yaml_processor_feature_flag_corectnes
before do
stub_ci_pipeline_yaml_file(config)
- stub_const(
- 'Gitlab::Ci::Pipeline::Chain::AssignPartition::DEFAULT_PARTITION_ID',
- current_partition_id)
+ allow(Ci::Pipeline).to receive(:current_partition_value) { current_partition_id }
end
it 'assigns partition_id to pipeline' do
diff --git a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb
index 5bc508447c1..06bb6d39fe5 100644
--- a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb
+++ b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb
@@ -55,6 +55,8 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService do
statuses.each do |status|
if event == 'play'
status.play(user)
+ elsif event == 'retry'
+ ::Ci::RetryJobService.new(project, user).execute(status)
else
status.public_send("#{event}!")
end
diff --git a/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_fail_and_retry_1.yml b/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_fail_and_retry_1.yml
new file mode 100644
index 00000000000..b9b8eb2f532
--- /dev/null
+++ b/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_fail_and_retry_1.yml
@@ -0,0 +1,55 @@
+config:
+ build:
+ script: exit $(($RANDOM % 2))
+
+ test:
+ script: exit 0
+ needs: [build]
+
+ deploy:
+ script: exit 0
+ needs: [test]
+
+init:
+ expect:
+ pipeline: pending
+ stages:
+ test: pending
+ jobs:
+ build: pending
+ test: created
+ deploy: created
+
+transitions:
+ - event: drop
+ jobs: [build]
+ expect:
+ pipeline: failed
+ stages:
+ test: failed
+ jobs:
+ build: failed
+ test: skipped
+ deploy: skipped
+
+ - event: retry
+ jobs: [build]
+ expect:
+ pipeline: running
+ stages:
+ test: pending
+ jobs:
+ build: pending
+ test: created
+ deploy: created
+
+ - event: success
+ jobs: [build]
+ expect:
+ pipeline: running
+ stages:
+ test: running
+ jobs:
+ build: success
+ test: pending
+ deploy: created
diff --git a/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_fail_and_retry_2.yml b/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_fail_and_retry_2.yml
new file mode 100644
index 00000000000..c875ebab3c9
--- /dev/null
+++ b/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_fail_and_retry_2.yml
@@ -0,0 +1,63 @@
+config:
+ build:
+ script: exit $(($RANDOM % 2))
+
+ test1:
+ script: exit 0
+ needs: [build]
+
+ test2:
+ script: exit 0
+ when: manual
+
+ deploy:
+ script: exit 0
+ needs: [test1, test2]
+
+init:
+ expect:
+ pipeline: pending
+ stages:
+ test: pending
+ jobs:
+ build: pending
+ test1: created
+ test2: manual
+ deploy: skipped
+
+transitions:
+ - event: drop
+ jobs: [build]
+ expect:
+ pipeline: failed
+ stages:
+ test: failed
+ jobs:
+ build: failed
+ test1: skipped
+ test2: manual
+ deploy: skipped
+
+ - event: retry
+ jobs: [build]
+ expect:
+ pipeline: running
+ stages:
+ test: pending
+ jobs:
+ build: pending
+ test1: created
+ test2: manual
+ deploy: skipped
+
+ - event: success
+ jobs: [build]
+ expect:
+ pipeline: running
+ stages:
+ test: running
+ jobs:
+ build: success
+ test1: pending
+ test2: manual
+ deploy: skipped
diff --git a/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_subsequent_manual_jobs.yml b/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_subsequent_manual_jobs.yml
new file mode 100644
index 00000000000..03ffda1caab
--- /dev/null
+++ b/spec/services/ci/pipeline_processing/test_cases/dag_same_stage_with_subsequent_manual_jobs.yml
@@ -0,0 +1,65 @@
+config:
+ job1:
+ script: exit 0
+ when: manual
+
+ job2:
+ script: exit 0
+ needs: [job1]
+
+ job3:
+ script: exit 0
+ when: manual
+ needs: [job2]
+
+ job4:
+ script: exit 0
+ needs: [job3]
+
+init:
+ expect:
+ pipeline: skipped
+ stages:
+ test: skipped
+ jobs:
+ job1: manual
+ job2: skipped
+ job3: skipped
+ job4: skipped
+
+transitions:
+ - event: play
+ jobs: [job1]
+ expect:
+ pipeline: pending
+ stages:
+ test: pending
+ jobs:
+ job1: pending
+ job2: created
+ job3: created
+ job4: created
+
+ - event: success
+ jobs: [job1]
+ expect:
+ pipeline: running
+ stages:
+ test: running
+ jobs:
+ job1: success
+ job2: pending
+ job3: created
+ job4: created
+
+ - event: success
+ jobs: [job2]
+ expect:
+ pipeline: success
+ stages:
+ test: success
+ jobs:
+ job1: success
+ job2: success
+ job3: manual
+ job4: skipped
diff --git a/spec/services/google_cloud/fetch_google_ip_list_service_spec.rb b/spec/services/google_cloud/fetch_google_ip_list_service_spec.rb
new file mode 100644
index 00000000000..b83037f80cd
--- /dev/null
+++ b/spec/services/google_cloud/fetch_google_ip_list_service_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GoogleCloud::FetchGoogleIpListService,
+ :use_clean_rails_memory_store_caching, :clean_gitlab_redis_rate_limiting do
+ include StubRequests
+
+ let(:google_cloud_ips) { File.read(Rails.root.join('spec/fixtures/cdn/google_cloud.json')) }
+ let(:headers) { { 'Content-Type' => 'application/json' } }
+
+ subject { described_class.new.execute }
+
+ before do
+ WebMock.stub_request(:get, described_class::GOOGLE_IP_RANGES_URL)
+ .to_return(status: 200, body: google_cloud_ips, headers: headers)
+ end
+
+ describe '#execute' do
+ it 'returns a list of IPAddr subnets and caches the result' do
+ expect(::ObjectStorage::CDN::GoogleIpCache).to receive(:update!).and_call_original
+ expect(subject[:subnets]).to be_an(Array)
+ expect(subject[:subnets]).to all(be_an(IPAddr))
+ end
+
+ shared_examples 'IP range retrieval failure' do
+ it 'does not cache the result and logs an error' do
+ expect(Gitlab::ErrorTracking).to receive(:log_exception).and_call_original
+ expect(::ObjectStorage::CDN::GoogleIpCache).not_to receive(:update!)
+ expect(subject[:subnets]).to be_nil
+ end
+ end
+
+ context 'with rate limit in effect' do
+ before do
+ 10.times { described_class.new.execute }
+ end
+
+ it 'returns rate limit error' do
+ expect(subject[:status]).to eq(:error)
+ expect(subject[:message]).to eq("#{described_class} was rate limited")
+ end
+ end
+
+ context 'when the URL returns a 404' do
+ before do
+ WebMock.stub_request(:get, described_class::GOOGLE_IP_RANGES_URL).to_return(status: 404)
+ end
+
+ it_behaves_like 'IP range retrieval failure'
+ end
+
+ context 'when the URL returns too large of a payload' do
+ before do
+ stub_const("#{described_class}::RESPONSE_BODY_LIMIT", 300)
+ end
+
+ it_behaves_like 'IP range retrieval failure'
+ end
+
+ context 'when the URL returns HTML' do
+ let(:headers) { { 'Content-Type' => 'text/html' } }
+
+ it_behaves_like 'IP range retrieval failure'
+ end
+
+ context 'when the URL returns empty results' do
+ let(:google_cloud_ips) { '{}' }
+
+ it_behaves_like 'IP range retrieval failure'
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb
index dc2c4f890b1..6a77de4266f 100644
--- a/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/composer_packages_shared_examples.rb
@@ -108,6 +108,7 @@ RSpec.shared_examples 'process Composer api request' do |user_type, status, add_
end
it_behaves_like 'returning response status', status
+ it_behaves_like 'bumping the package last downloaded at field' if status == :success
end
end
diff --git a/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb
index cd8f2d48d8f..629d93676eb 100644
--- a/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/conan_packages_shared_examples.rb
@@ -631,6 +631,7 @@ RSpec.shared_examples 'a public project with packages' do
end
it_behaves_like 'allows download with no token'
+ it_behaves_like 'bumping the package last downloaded at field'
it 'returns the file' do
subject
@@ -647,6 +648,7 @@ RSpec.shared_examples 'an internal project with packages' do
end
it_behaves_like 'denies download with no token'
+ it_behaves_like 'bumping the package last downloaded at field'
it 'returns the file' do
subject
@@ -662,6 +664,7 @@ RSpec.shared_examples 'a private project with packages' do
end
it_behaves_like 'denies download with no token'
+ it_behaves_like 'bumping the package last downloaded at field'
it 'returns the file' do
subject
diff --git a/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb
index acbcf4f7f3d..06ed0448b50 100644
--- a/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/helm_packages_shared_examples.rb
@@ -191,14 +191,15 @@ RSpec.shared_examples 'process helm download content request' do |user_type, sta
end
end
- it_behaves_like 'a package tracking event', 'API::HelmPackages', 'pull_package'
-
it 'returns expected status and a valid package archive' do
subject
expect(response).to have_gitlab_http_status(status)
expect(response.media_type).to eq('application/octet-stream')
end
+
+ it_behaves_like 'a package tracking event', 'API::HelmPackages', 'pull_package'
+ it_behaves_like 'bumping the package last downloaded at field'
end
end
@@ -278,7 +279,6 @@ RSpec.shared_examples 'handling helm chart index requests' do
end
it_behaves_like 'deploy token for package GET requests'
-
it_behaves_like 'rejects helm access with unknown project id' do
subject { get api(url) }
end
diff --git a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
index 6568d51b90e..fdd55893deb 100644
--- a/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/nuget_packages_shared_examples.rb
@@ -293,6 +293,8 @@ RSpec.shared_examples 'process nuget download content request' do |user_type, st
it_behaves_like 'a package tracking event', 'API::NugetPackages', 'pull_package'
+ it_behaves_like 'bumping the package last downloaded at field'
+
it 'returns a valid package archive' do
subject
@@ -315,6 +317,8 @@ RSpec.shared_examples 'process nuget download content request' do |user_type, st
end
it_behaves_like 'a package tracking event', 'API::NugetPackages', 'pull_symbol_package'
+
+ it_behaves_like 'bumping the package last downloaded at field'
end
context 'with lower case package name' do
diff --git a/spec/support/shared_examples/requests/api/packages_shared_examples.rb b/spec/support/shared_examples/requests/api/packages_shared_examples.rb
index eb650b7a09f..860cb1b1d86 100644
--- a/spec/support/shared_examples/requests/api/packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/packages_shared_examples.rb
@@ -165,3 +165,10 @@ RSpec.shared_examples 'not a package tracking event' do
expect_no_snowplow_event
end
end
+
+RSpec.shared_examples 'bumping the package last downloaded at field' do
+ it 'bumps last_downloaded_at' do
+ expect { subject }
+ .to change { package.reload.last_downloaded_at }.from(nil).to(instance_of(ActiveSupport::TimeWithZone))
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
index ba8311bf0be..f411b5699a9 100644
--- a/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/pypi_packages_shared_examples.rb
@@ -167,6 +167,7 @@ RSpec.shared_examples 'PyPI package download' do |user_type, status, add_member
it_behaves_like 'returning response status', status
it_behaves_like 'a package tracking event', described_class.name, 'pull_package'
+ it_behaves_like 'bumping the package last downloaded at field'
end
end
diff --git a/spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb b/spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb
index abdb468353a..f075927e7bf 100644
--- a/spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/rubygems_packages_shared_examples.rb
@@ -203,5 +203,6 @@ RSpec.shared_examples 'Rubygems gem download' do |user_type, status, add_member
end
it_behaves_like 'a package tracking event', described_class.name, 'pull_package'
+ it_behaves_like 'bumping the package last downloaded at field'
end
end
diff --git a/spec/uploaders/object_storage/cdn/google_cdn_spec.rb b/spec/uploaders/object_storage/cdn/google_cdn_spec.rb
index 6e57995f59f..b72f6d66d69 100644
--- a/spec/uploaders/object_storage/cdn/google_cdn_spec.rb
+++ b/spec/uploaders/object_storage/cdn/google_cdn_spec.rb
@@ -2,7 +2,8 @@
require 'spec_helper'
-RSpec.describe ObjectStorage::CDN::GoogleCDN, :use_clean_rails_memory_store_caching do
+RSpec.describe ObjectStorage::CDN::GoogleCDN,
+ :use_clean_rails_memory_store_caching, :use_clean_rails_redis_caching, :sidekiq_inline do
include StubRequests
let(:key) { SecureRandom.hex }
@@ -15,7 +16,7 @@ RSpec.describe ObjectStorage::CDN::GoogleCDN, :use_clean_rails_memory_store_cach
subject { described_class.new(options) }
before do
- WebMock.stub_request(:get, described_class::GOOGLE_IP_RANGES_URL)
+ WebMock.stub_request(:get, GoogleCloud::FetchGoogleIpListService::GOOGLE_IP_RANGES_URL)
.to_return(status: 200, body: google_cloud_ips, headers: headers)
end
@@ -35,12 +36,6 @@ RSpec.describe ObjectStorage::CDN::GoogleCDN, :use_clean_rails_memory_store_cach
it { expect(subject.use_cdn?(ip_address)).to eq(expected) }
end
- it 'caches the value' do
- expect(subject.use_cdn?(public_ip)).to be true
- expect(Rails.cache.fetch(described_class::GOOGLE_CDN_LIST_KEY)).to be_present
- expect(Gitlab::ProcessMemoryCache.cache_backend.fetch(described_class::GOOGLE_CDN_LIST_KEY)).to be_present
- end
-
context 'when the key name is missing' do
let(:options) { { url: 'https://cdn.gitlab.example.com', key: Base64.urlsafe_encode64(SecureRandom.hex) } }
@@ -73,43 +68,6 @@ RSpec.describe ObjectStorage::CDN::GoogleCDN, :use_clean_rails_memory_store_cach
expect(subject.use_cdn?(public_ip)).to be false
end
end
-
- shared_examples 'IP range retrieval failure' do
- it 'does not cache the result and logs an error' do
- expect(Gitlab::ErrorTracking).to receive(:log_exception).and_call_original
- expect(subject.use_cdn?(public_ip)).to be false
- expect(Rails.cache.fetch(described_class::GOOGLE_CDN_LIST_KEY)).to be_nil
- expect(Gitlab::ProcessMemoryCache.cache_backend.fetch(described_class::GOOGLE_CDN_LIST_KEY)).to be_nil
- end
- end
-
- context 'when the URL returns a 404' do
- before do
- WebMock.stub_request(:get, described_class::GOOGLE_IP_RANGES_URL).to_return(status: 404)
- end
-
- it_behaves_like 'IP range retrieval failure'
- end
-
- context 'when the URL returns too large of a payload' do
- before do
- stub_const("#{described_class}::RESPONSE_BODY_LIMIT", 300)
- end
-
- it_behaves_like 'IP range retrieval failure'
- end
-
- context 'when the URL returns HTML' do
- let(:headers) { { 'Content-Type' => 'text/html' } }
-
- it_behaves_like 'IP range retrieval failure'
- end
-
- context 'when the URL returns empty results' do
- let(:google_cloud_ips) { '{}' }
-
- it_behaves_like 'IP range retrieval failure'
- end
end
describe '#signed_url' do
diff --git a/spec/uploaders/object_storage/cdn/google_ip_cache_spec.rb b/spec/uploaders/object_storage/cdn/google_ip_cache_spec.rb
new file mode 100644
index 00000000000..d6568636bc0
--- /dev/null
+++ b/spec/uploaders/object_storage/cdn/google_ip_cache_spec.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe ObjectStorage::CDN::GoogleIpCache,
+ :use_clean_rails_memory_store_caching, :use_clean_rails_redis_caching do
+ include StubRequests
+
+ let(:subnets) { [IPAddr.new("34.80.0.0/15"), IPAddr.new("2600:1900:4180::/44")] }
+ let(:public_ip) { '18.245.0.42' }
+
+ describe '.update!' do
+ it 'caches to both L1 and L2 caches' do
+ expect(Gitlab::ProcessMemoryCache.cache_backend.exist?(described_class::GOOGLE_CDN_LIST_KEY)).to be false
+ expect(Rails.cache.exist?(described_class::GOOGLE_CDN_LIST_KEY)).to be false
+
+ described_class.update!(subnets)
+
+ expect(Gitlab::ProcessMemoryCache.cache_backend.fetch(described_class::GOOGLE_CDN_LIST_KEY)).to eq(subnets)
+ expect(Rails.cache.fetch(described_class::GOOGLE_CDN_LIST_KEY)).to eq(subnets)
+ end
+ end
+
+ describe '.ready?' do
+ it 'returns false' do
+ expect(described_class.ready?).to be false
+ end
+
+ it 'returns true' do
+ described_class.update!(subnets)
+
+ expect(described_class.ready?).to be true
+ end
+ end
+
+ describe '.google_ip?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:ip_address, :expected) do
+ '34.80.0.1' | true
+ '18.245.0.42' | false
+ '2500:1900:4180:0000:0000:0000:0000:0000' | false
+ '2600:1900:4180:0000:0000:0000:0000:0000' | true
+ '10.10.1.5' | false
+ 'fc00:0000:0000:0000:0000:0000:0000:0000' | false
+ end
+
+ before do
+ described_class.update!(subnets)
+ end
+
+ with_them do
+ it { expect(described_class.google_ip?(ip_address)).to eq(expected) }
+ end
+
+ it 'uses the L2 cache and updates the L1 cache when L1 is missing' do
+ Gitlab::ProcessMemoryCache.cache_backend.delete(described_class::GOOGLE_CDN_LIST_KEY)
+ expect(Rails.cache.fetch(described_class::GOOGLE_CDN_LIST_KEY)).to eq(subnets)
+
+ expect(described_class.google_ip?(public_ip)).to be false
+
+ expect(Gitlab::ProcessMemoryCache.cache_backend.fetch(described_class::GOOGLE_CDN_LIST_KEY)).to eq(subnets)
+ expect(Rails.cache.fetch(described_class::GOOGLE_CDN_LIST_KEY)).to eq(subnets)
+ end
+
+ it 'avoids populating L1 cache if L2 is missing' do
+ Gitlab::ProcessMemoryCache.cache_backend.delete(described_class::GOOGLE_CDN_LIST_KEY)
+ Rails.cache.delete(described_class::GOOGLE_CDN_LIST_KEY)
+
+ expect(described_class.google_ip?(public_ip)).to be false
+
+ expect(Gitlab::ProcessMemoryCache.cache_backend.exist?(described_class::GOOGLE_CDN_LIST_KEY)).to be false
+ expect(Rails.cache.exist?(described_class::GOOGLE_CDN_LIST_KEY)).to be false
+ end
+ end
+
+ describe '.async_refresh' do
+ it 'schedules the worker' do
+ expect(::GoogleCloud::FetchGoogleIpListWorker).to receive(:perform_async)
+
+ described_class.async_refresh
+ end
+ end
+end
diff --git a/spec/workers/google_cloud/fetch_google_ip_list_worker_spec.rb b/spec/workers/google_cloud/fetch_google_ip_list_worker_spec.rb
new file mode 100644
index 00000000000..c0b32515d15
--- /dev/null
+++ b/spec/workers/google_cloud/fetch_google_ip_list_worker_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe GoogleCloud::FetchGoogleIpListWorker do
+ describe '#perform' do
+ it 'returns success' do
+ allow_next_instance_of(GoogleCloud::FetchGoogleIpListService) do |service|
+ expect(service).to receive(:execute).and_return({ status: :success })
+ end
+
+ expect(described_class.new.perform).to eq({ status: :success })
+ end
+ end
+end