summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml2
-rw-r--r--.gitlab/CODEOWNERS2
-rw-r--r--.gitlab/ci/frontend.gitlab-ci.yml11
-rw-r--r--.gitlab/ci/global.gitlab-ci.yml9
-rw-r--r--.gitlab/ci/review-apps/qa.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml20
-rw-r--r--.rubocop_todo/rails/lexically_scoped_action_filter.yml11
-rw-r--r--app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue1
-rw-r--r--app/assets/javascripts/ci_variable_list/components/legacy_ci_variable_modal.vue1
-rw-r--r--app/assets/javascripts/diffs/components/app.vue10
-rw-r--r--app/assets/javascripts/diffs/store/actions.js14
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/delete_modal.vue61
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue26
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue26
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/constants.js7
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/graphql/mutations/destroy_packages.mutation.graphql5
-rw-r--r--app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue71
-rw-r--r--app/assets/javascripts/packages_and_registries/settings/group/graphql/mutations/update_package_forwarding_settings.mutation.graphql16
-rw-r--r--app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue2
-rw-r--r--app/assets/javascripts/search/sidebar/components/scope_navigation.vue2
-rw-r--r--config/initializers/memory_watchdog.rb2
-rw-r--r--config/initializers_before_autoloader/000_inflections.rb1
-rw-r--r--db/docs/dependency_proxy_blob_states.yml9
-rw-r--r--db/migrate/20221018202524_create_dependency_proxy_blob_states.rb49
-rw-r--r--db/schema_migrations/202210182025241
-rw-r--r--db/structure.sql30
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md10
-rw-r--r--doc/api/geo_nodes.md39
-rw-r--r--doc/api/graphql/reference/index.md64
-rw-r--r--doc/api/suggestions.md4
-rw-r--r--doc/architecture/blueprints/_template.md5
-rw-r--r--doc/architecture/blueprints/ci_data_decay/index.md8
-rw-r--r--doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md8
-rw-r--r--doc/architecture/blueprints/ci_pipeline_components/index.md8
-rw-r--r--doc/architecture/blueprints/ci_scale/index.md8
-rw-r--r--doc/architecture/blueprints/cloud_native_build_logs/index.md8
-rw-r--r--doc/architecture/blueprints/cloud_native_gitlab_pages/index.md8
-rw-r--r--doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md8
-rw-r--r--doc/architecture/blueprints/consolidating_groups_and_projects/index.md8
-rw-r--r--doc/architecture/blueprints/container_registry_metadata_database/index.md8
-rw-r--r--doc/architecture/blueprints/database/scalability/patterns/read_mostly.md8
-rw-r--r--doc/architecture/blueprints/database/scalability/patterns/time_decay.md8
-rw-r--r--doc/architecture/blueprints/database_scaling/size-limits.md8
-rw-r--r--doc/architecture/blueprints/database_testing/index.md8
-rw-r--r--doc/architecture/blueprints/feature_flags_development/index.md8
-rw-r--r--doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md8
-rw-r--r--doc/architecture/blueprints/graphql_api/index.md8
-rw-r--r--doc/architecture/blueprints/image_resizing/index.md8
-rw-r--r--doc/architecture/blueprints/object_storage/index.md8
-rw-r--r--doc/architecture/blueprints/pods/index.md8
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-git-access.md8
-rw-r--r--doc/architecture/blueprints/pods/pods-feature-template.md8
-rw-r--r--doc/architecture/blueprints/pods/proposal-stateless-router-with-buffering-requests.md8
-rw-r--r--doc/architecture/blueprints/pods/proposal-stateless-router-with-routes-learning.md8
-rw-r--r--doc/architecture/blueprints/rate_limiting/index.md8
-rw-r--r--doc/architecture/blueprints/runner_scaling/index.md8
-rw-r--r--doc/architecture/blueprints/runner_tokens/index.md8
-rw-r--r--doc/architecture/blueprints/work_items/index.md8
-rw-r--r--doc/development/pipelines/index.md2
-rw-r--r--doc/development/pipelines/internals.md2
-rw-r--r--doc/user/infrastructure/iac/index.md4
-rw-r--r--lib/api/entities/metadata.rb8
-rw-r--r--lib/api/metadata.rb56
-rw-r--r--lib/api/v3/github.rb2
-rw-r--r--lib/gitlab/cluster/puma_worker_killer_initializer.rb4
-rw-r--r--lib/gitlab/database/gitlab_schemas.yml1
-rw-r--r--lib/gitlab/database/load_balancing/load_balancer.rb7
-rw-r--r--lib/gitlab/github_import/importer/events/changed_label.rb1
-rw-r--r--lib/gitlab/i18n.rb20
-rw-r--r--locale/gitlab.pot22
-rw-r--r--package.json2
-rw-r--r--qa/Dockerfile4
-rw-r--r--spec/factories/dependency_proxy.rb9
-rw-r--r--spec/features/global_search_spec.rb15
-rw-r--r--spec/features/merge_request/user_sees_diff_spec.rb14
-rw-r--r--spec/features/snippets/search_snippets_spec.rb6
-rw-r--r--spec/frontend/diffs/store/actions_spec.js38
-rw-r--r--spec/frontend/editor/schema/ci/ci_schema_spec.js60
-rw-r--r--spec/frontend/editor/schema/ci/json_tests/negative_tests/default_no_additional_properties.json2
-rw-r--r--spec/frontend/editor/schema/ci/json_tests/negative_tests/inherit_default_no_additional_properties.json6
-rw-r--r--spec/frontend/editor/schema/ci/json_tests/negative_tests/job_variables_must_not_contain_objects.json6
-rw-r--r--spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links.json (renamed from spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_invalid_link_type.json)22
-rw-r--r--spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_empty.json13
-rw-r--r--spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_missing.json11
-rw-r--r--spec/frontend/editor/schema/ci/json_tests/negative_tests/retry_unknown_when.json2
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml62
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_files_not_an_array.yml8
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_prefix_array.yml10
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_with_dot.yml6
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_with_multiple_dots.yml7
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_with_slash.yml6
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/paths_not_an_array.yml5
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/untracked_string.yml4
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/when_integer.yml4
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/when_not_reserved_keyword.yml4
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/empty.yml2
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/invalid_variable.yml2
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/leading_slash.yml2
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/no_slash.yml2
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/tailing_slash.yml2
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/empty.yml3
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/invalid_variable.yml3
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/leading_slash.yml3
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/no_slash.yml3
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/tailing_slash.yml3
-rw-r--r--spec/frontend/editor/schema/ci/yaml_tests/negative_tests/trigger.yml (renamed from spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/trigger_include.yml)35
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/delete_modal_spec.js71
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap9
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/list/package_list_row_spec.js43
-rw-r--r--spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js27
-rw-r--r--spec/frontend/packages_and_registries/package_registry/mock_data.js28
-rw-r--r--spec/frontend/packages_and_registries/package_registry/pages/__snapshots__/list_spec.js.snap8
-rw-r--r--spec/frontend/packages_and_registries/package_registry/pages/list_spec.js88
-rw-r--r--spec/graphql/mutations/commits/create_spec.rb2
-rw-r--r--spec/initializers/memory_watchdog_spec.rb57
-rw-r--r--spec/lib/gitlab/cluster/puma_worker_killer_initializer_spec.rb30
-rw-r--r--spec/lib/gitlab/database/load_balancing/transaction_leaking_spec.rb5
-rw-r--r--spec/lib/gitlab/github_import/importer/events/changed_label_spec.rb75
-rw-r--r--spec/lib/gitlab/memory/watchdog/configurator_spec.rb12
-rw-r--r--spec/models/factories_spec.rb2
-rw-r--r--spec/services/search_service_spec.rb154
-rw-r--r--spec/support/helpers/stub_configuration.rb4
-rw-r--r--spec/support/shared_examples/features/search/redacted_search_results_shared_examples.rb304
-rw-r--r--spec/views/search/show.html.haml_spec.rb10
-rw-r--r--yarn.lock8
125 files changed, 1445 insertions, 736 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index bbf8265da85..936b9410a60 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -99,7 +99,7 @@ variables:
GIT_SUBMODULE_STRATEGY: "none"
GET_SOURCES_ATTEMPTS: "3"
DEBIAN_VERSION: "bullseye"
- CHROME_VERSION: "103"
+ CHROME_VERSION: "106"
DOCKER_VERSION: "20.10.14"
RUBY_VERSION: "2.7"
GO_VERSION: "1.18"
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS
index 3c9a4b09b9b..2bb47c77ba5 100644
--- a/.gitlab/CODEOWNERS
+++ b/.gitlab/CODEOWNERS
@@ -2,7 +2,7 @@
# project here: https://gitlab.com/gitlab-org/gitlab/-/project_members
# As described in https://docs.gitlab.com/ee/user/project/code_owners.html
-* @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 @nolith @jacobvosmaer-gitlab @gitlab-org/tw-leadership
+* @gitlab-org/maintainers/rails-backend @gitlab-org/maintainers/frontend @gitlab-org/maintainers/database @gl-quality/qe-maintainers @gl-quality/tooling-maintainers @gitlab-org/delivery @gitlab-org/maintainers/cicd-templates @nolith @jacobvosmaer-gitlab @gitlab-org/tw-leadership
CODEOWNERS @gitlab-org/development-leaders @gitlab-org/tw-leadership
docs/CODEOWNERS @gitlab-org/development-leaders @gitlab-org/tw-leadership
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index 0dbe58d1e10..4d120de277a 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -88,17 +88,6 @@ update-assets-compile-test-cache:
- echo -n "${GITLAB_ASSETS_HASH}" > "cached-assets-hash.txt"
artifacts: {} # This job's purpose is only to update the cache.
-# TODO: Remove this as it's duplicating update-assets-compile-*-cache
-update-yarn-cache:
- extends:
- - .default-retry
- - .default-utils-before_script
- - .yarn-cache-push
- - .shared:rules:update-cache
- stage: prepare
- script:
- - yarn_install_script
-
update-storybook-yarn-cache:
extends:
- .default-retry
diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml
index 2c75c9a5a0f..d461183a2ca 100644
--- a/.gitlab/ci/global.gitlab-ci.yml
+++ b/.gitlab/ci/global.gitlab-ci.yml
@@ -219,23 +219,16 @@
- *node-modules-cache
- *assets-tmp-cache
-# TODO: Remove this as it's duplicating .assets-compile-cache-push
-.yarn-cache-push:
- cache:
- - *node-modules-cache-push
-
.assets-compile-cache:
cache:
- *ruby-gems-cache
- *node-modules-cache
- - *assets-cache
- *assets-tmp-cache
.assets-compile-cache-push:
cache:
- *ruby-gems-cache # We don't push this cache as it's already rebuilt by `update-setup-test-env-cache`
- *node-modules-cache-push
- - *assets-cache-push
- *assets-tmp-cache-push
.storybook-yarn-cache:
@@ -245,7 +238,7 @@
.storybook-yarn-cache-push:
cache:
- - *node-modules-cache # We don't push this cache as it's already rebuilt by `update-yarn-cache`
+ - *node-modules-cache # We don't push this cache as it's already rebuilt by `update-assets-compile-*-cache`
- *storybook-node-modules-cache-push
.use-pg11:
diff --git a/.gitlab/ci/review-apps/qa.gitlab-ci.yml b/.gitlab/ci/review-apps/qa.gitlab-ci.yml
index c631161aa08..69ce028987a 100644
--- a/.gitlab/ci/review-apps/qa.gitlab-ci.yml
+++ b/.gitlab/ci/review-apps/qa.gitlab-ci.yml
@@ -25,7 +25,7 @@ include:
- cd qa && bundle install
.review-qa-base:
- image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3-git-2.33-lfs-2.9-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}-gcloud-383-kubectl-1.23
+ image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3-git-2.36-lfs-2.9-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}-gcloud-383-kubectl-1.23
extends:
- .use-docker-in-docker
- .bundle-base
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index db0ceae2868..41d61f16d4c 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -856,6 +856,8 @@
.frontend:rules:minimal-default-rules:
rules:
+ - <<: *if-merge-request-approved
+ when: never
- <<: *if-automated-merge-request
when: never
- <<: *if-security-merge-request
@@ -930,14 +932,16 @@
- <<: *if-merge-request-labels-run-all-jest
- <<: *if-merge-request-labels-frontend-and-feature-flag
- <<: *if-merge-request
- changes: ["{package.json,yarn.lock}"]
+ changes: *frontend-dependency-patterns
- <<: *if-merge-request
changes: [".gitlab/ci/rules.gitlab-ci.yml", ".gitlab/ci/frontend.gitlab-ci.yml"]
- <<: *if-automated-merge-request
changes: *code-backstage-patterns
- <<: *if-security-merge-request
changes: *code-backstage-patterns
- - <<: *if-default-branch-refs
+ - <<: *if-merge-request-not-approved
+ when: never
+ - <<: *if-default-refs
changes: *code-backstage-patterns
.frontend:rules:jest:minimal:
@@ -950,7 +954,7 @@
- <<: *if-merge-request-labels-frontend-and-feature-flag
when: never
- <<: *if-merge-request
- changes: ["{package.json,yarn.lock}"]
+ changes: *frontend-dependency-patterns
when: never
- <<: *if-merge-request
changes: [".gitlab/ci/rules.gitlab-ci.yml", ".gitlab/ci/frontend.gitlab-ci.yml"]
@@ -964,18 +968,24 @@
- <<: *if-merge-request-labels-as-if-foss
- <<: *if-merge-request-labels-run-all-jest
- <<: *if-merge-request
- changes: ["{package.json,yarn.lock}"]
+ changes: *frontend-dependency-patterns
- <<: *if-security-merge-request
changes: *code-backstage-patterns
+ - <<: *if-merge-request-not-approved
+ when: never
+ - <<: *if-merge-request
+ changes: *frontend-patterns-for-as-if-foss
.frontend:rules:jest:minimal:as-if-foss:
rules:
- !reference [".strict-ee-only-rules", rules]
- !reference [".frontend:rules:minimal-default-rules", rules]
+ - <<: *if-merge-request-labels-as-if-foss
+ when: never
- <<: *if-merge-request-labels-run-all-jest
when: never
- <<: *if-merge-request
- changes: ["{package.json,yarn.lock}"]
+ changes: *frontend-dependency-patterns
when: never
- <<: *if-fork-merge-request
when: never
diff --git a/.rubocop_todo/rails/lexically_scoped_action_filter.yml b/.rubocop_todo/rails/lexically_scoped_action_filter.yml
index 9edc8f7ce58..dde0da13d3c 100644
--- a/.rubocop_todo/rails/lexically_scoped_action_filter.yml
+++ b/.rubocop_todo/rails/lexically_scoped_action_filter.yml
@@ -1,15 +1,13 @@
---
Rails/LexicallyScopedActionFilter:
- # Offense count: 73
- # Temporarily disabled due to too many offenses
- Enabled: false
+ Details: grace period
Exclude:
- 'app/controllers/admin/groups_controller.rb'
+ - 'app/controllers/admin/hooks_controller.rb'
- 'app/controllers/clusters/base_controller.rb'
- 'app/controllers/clusters/clusters_controller.rb'
- 'app/controllers/concerns/enforces_two_factor_authentication.rb'
- 'app/controllers/concerns/integrations/actions.rb'
- - 'app/controllers/concerns/multiple_boards_actions.rb'
- 'app/controllers/concerns/oauth_applications.rb'
- 'app/controllers/concerns/spammable_actions/captcha_check/html_format_actions_support.rb'
- 'app/controllers/confirmations_controller.rb'
@@ -17,14 +15,17 @@ Rails/LexicallyScopedActionFilter:
- 'app/controllers/groups/group_members_controller.rb'
- 'app/controllers/groups/milestones_controller.rb'
- 'app/controllers/groups/runners_controller.rb'
+ - 'app/controllers/groups/settings/repository_controller.rb'
- 'app/controllers/groups/uploads_controller.rb'
- 'app/controllers/groups_controller.rb'
- 'app/controllers/import/base_controller.rb'
- 'app/controllers/oauth/applications_controller.rb'
- 'app/controllers/passwords_controller.rb'
+ - 'app/controllers/projects/analytics/cycle_analytics/stages_controller.rb'
- 'app/controllers/projects/badges_controller.rb'
- 'app/controllers/projects/branches_controller.rb'
- 'app/controllers/projects/environments_controller.rb'
+ - 'app/controllers/projects/hooks_controller.rb'
- 'app/controllers/projects/incidents_controller.rb'
- 'app/controllers/projects/issue_links_controller.rb'
- 'app/controllers/projects/issues_controller.rb'
@@ -36,6 +37,8 @@ Rails/LexicallyScopedActionFilter:
- 'app/controllers/projects/project_members_controller.rb'
- 'app/controllers/projects/prometheus/alerts_controller.rb'
- 'app/controllers/projects/releases_controller.rb'
+ - 'app/controllers/projects/settings/integration_hook_logs_controller.rb'
+ - 'app/controllers/projects/settings/merge_requests_controller.rb'
- 'app/controllers/projects/snippets_controller.rb'
- 'app/controllers/projects/tags_controller.rb'
- 'app/controllers/projects/todos_controller.rb'
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
index 449b9d31dcb..346a038000e 100644
--- a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
+++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue
@@ -297,6 +297,7 @@ export default {
data-testid="pipeline-form-ci-variable-value"
data-qa-selector="ci_variable_value_field"
class="gl-font-monospace!"
+ spellcheck="false"
/>
</gl-form-group>
diff --git a/app/assets/javascripts/ci_variable_list/components/legacy_ci_variable_modal.vue b/app/assets/javascripts/ci_variable_list/components/legacy_ci_variable_modal.vue
index f49cd476cb2..fa90e0e3e6c 100644
--- a/app/assets/javascripts/ci_variable_list/components/legacy_ci_variable_modal.vue
+++ b/app/assets/javascripts/ci_variable_list/components/legacy_ci_variable_modal.vue
@@ -275,6 +275,7 @@ export default {
data-testid="pipeline-form-ci-variable-value"
data-qa-selector="ci_variable_value_field"
class="gl-font-monospace!"
+ spellcheck="false"
/>
</gl-form-group>
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index bc49464a560..7bc75127876 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -471,8 +471,14 @@ export default {
},
fetchData(toggleTree = true) {
this.fetchDiffFilesMeta()
- .then(({ real_size = 0 }) => {
- this.diffFilesLength = parseInt(real_size, 10) || 0;
+ .then((data) => {
+ let realSize = 0;
+
+ if (data) {
+ realSize = data.real_size;
+ }
+
+ this.diffFilesLength = parseInt(realSize, 10) || 0;
if (toggleTree) {
this.setTreeDisplay();
}
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 5234be44b05..c73012527a2 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -5,7 +5,7 @@ import {
historyPushState,
scrollToElement,
} from '~/lib/utils/common_utils';
-import { createAlert } from '~/flash';
+import { createAlert, VARIANT_WARNING } from '~/flash';
import { diffViewerModes } from '~/ide/constants';
import axios from '~/lib/utils/axios_utils';
@@ -229,9 +229,17 @@ export const fetchDiffFilesMeta = ({ commit, state }) => {
return data;
})
- .catch(() => worker.terminate());
-};
+ .catch((error) => {
+ worker.terminate();
+ if (error.response.status === httpStatusCodes.NOT_FOUND) {
+ createAlert({
+ message: __('Building your merge request. Wait a few moments, then refresh this page.'),
+ variant: VARIANT_WARNING,
+ });
+ }
+ });
+};
export const fetchCoverageFiles = ({ commit, state }) => {
const coveragePoll = new Poll({
resource: {
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/delete_modal.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/delete_modal.vue
new file mode 100644
index 00000000000..2a1de2ae4a7
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/delete_modal.vue
@@ -0,0 +1,61 @@
+<script>
+import { GlModal } from '@gitlab/ui';
+import { __, n__ } from '~/locale';
+import {
+ DELETE_PACKAGES_MODAL_TITLE,
+ DELETE_PACKAGE_MODAL_PRIMARY_ACTION,
+} from '~/packages_and_registries/package_registry/constants';
+
+export default {
+ name: 'DeleteModal',
+ i18n: {
+ DELETE_PACKAGES_MODAL_TITLE,
+ },
+ components: {
+ GlModal,
+ },
+ props: {
+ itemsToBeDeleted: {
+ type: Array,
+ required: true,
+ },
+ },
+ computed: {
+ description() {
+ return n__(
+ 'PackageRegistry|You are about to delete 1 package. This operation is irreversible.',
+ `PackageRegistry|You are about to delete %d packages. This operation is irreversible.`,
+ this.itemsToBeDeleted.length,
+ );
+ },
+ },
+ modal: {
+ packagesDeletePrimaryAction: {
+ text: DELETE_PACKAGE_MODAL_PRIMARY_ACTION,
+ attributes: [{ variant: 'danger' }, { category: 'primary' }],
+ },
+ cancelAction: {
+ text: __('Cancel'),
+ },
+ },
+ methods: {
+ show() {
+ this.$refs.deleteModal.show();
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-modal
+ ref="deleteModal"
+ size="sm"
+ modal-id="delete-packages-modal"
+ :action-primary="$options.modal.packagesDeletePrimaryAction"
+ :action-cancel="$options.modal.cancelAction"
+ :title="$options.i18n.DELETE_PACKAGES_MODAL_TITLE"
+ @primary="$emit('confirm')"
+ >
+ <span>{{ description }}</span>
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue
index 7a000aca0f2..4553dd3421b 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/list/package_list_row.vue
@@ -2,6 +2,7 @@
import {
GlDropdown,
GlDropdownItem,
+ GlFormCheckbox,
GlIcon,
GlSprintf,
GlTooltipDirective,
@@ -26,6 +27,7 @@ export default {
components: {
GlDropdown,
GlDropdownItem,
+ GlFormCheckbox,
GlIcon,
GlSprintf,
GlTruncate,
@@ -45,6 +47,11 @@ export default {
type: Object,
required: true,
},
+ selected: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
},
computed: {
packageType() {
@@ -90,7 +97,15 @@ export default {
</script>
<template>
- <list-item data-testid="package-row">
+ <list-item data-testid="package-row" v-bind="$attrs">
+ <template #left-action>
+ <gl-form-checkbox
+ v-if="packageEntity.canDestroy"
+ class="gl-m-0"
+ :checked="selected"
+ @change="$emit('select')"
+ />
+ </template>
<template #left-primary>
<div class="gl-display-flex gl-align-items-center gl-mr-3 gl-min-w-0">
<router-link
@@ -168,12 +183,9 @@ export default {
category="tertiary"
no-caret
>
- <gl-dropdown-item
- data-testid="action-delete"
- variant="danger"
- @click="$emit('packageToDelete', packageEntity)"
- >{{ $options.i18n.deletePackage }}</gl-dropdown-item
- >
+ <gl-dropdown-item data-testid="action-delete" variant="danger" @click="$emit('delete')">{{
+ $options.i18n.deletePackage
+ }}</gl-dropdown-item>
</gl-dropdown>
</template>
</list-item>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue b/app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue
index 0043d778c60..ddcddf80c15 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/components/list/packages_list.vue
@@ -1,6 +1,6 @@
<script>
import { GlAlert } from '@gitlab/ui';
-import { s__, sprintf } from '~/locale';
+import { s__, sprintf, n__ } from '~/locale';
import DeletePackageModal from '~/packages_and_registries/shared/components/delete_package_modal.vue';
import PackagesListRow from '~/packages_and_registries/package_registry/components/list/package_list_row.vue';
import PackagesListLoader from '~/packages_and_registries/shared/components/packages_list_loader.vue';
@@ -48,6 +48,9 @@ export default {
};
},
computed: {
+ listTitle() {
+ return n__('%d package', '%d packages', this.list.length);
+ },
isListEmpty() {
return !this.list || this.list.length === 0;
},
@@ -83,6 +86,14 @@ export default {
this.itemToBeDeleted = { ...item };
this.track(REQUEST_DELETE_PACKAGE_TRACKING_ACTION);
},
+ setItemsToBeDeleted(items) {
+ if (items.length === 1) {
+ const [item] = items;
+ this.setItemToBeDeleted(item);
+ return;
+ }
+ this.$emit('delete', items);
+ },
deleteItemConfirmation() {
this.$emit('package:delete', this.itemToBeDeleted);
this.track(DELETE_PACKAGE_TRACKING_ACTION);
@@ -124,15 +135,22 @@ export default {
>
<registry-list
data-testid="packages-table"
- :hidden-delete="true"
:is-loading="isLoading"
:items="list"
:pagination="pageInfo"
+ :title="listTitle"
+ @delete="setItemsToBeDeleted"
@prev-page="$emit('prev-page')"
@next-page="$emit('next-page')"
>
- <template #default="{ item }">
- <packages-list-row :package-entity="item" @packageToDelete="setItemToBeDeleted(item)" />
+ <template #default="{ selectItem, isSelected, item, first }">
+ <packages-list-row
+ :first="first"
+ :package-entity="item"
+ :selected="isSelected(item)"
+ @delete="setItemToBeDeleted(item)"
+ @select="selectItem(item)"
+ />
</template>
</registry-list>
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/constants.js b/app/assets/javascripts/packages_and_registries/package_registry/constants.js
index 006164fd462..b731cd77e66 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/constants.js
+++ b/app/assets/javascripts/packages_and_registries/package_registry/constants.js
@@ -110,6 +110,13 @@ export const FETCH_PACKAGE_PIPELINES_ERROR_MESSAGE = s__(
export const FETCH_PACKAGE_METADATA_ERROR_MESSAGE = s__(
'PackageRegistry|Something went wrong while fetching the package metadata.',
);
+export const DELETE_PACKAGES_ERROR_MESSAGE = s__(
+ 'PackageRegistry|Something went wrong while deleting packages.',
+);
+export const DELETE_PACKAGES_SUCCESS_MESSAGE = s__('PackageRegistry|Packages deleted successfully');
+
+export const DELETE_PACKAGES_MODAL_TITLE = s__('PackageRegistry|Delete packages');
+export const DELETE_PACKAGE_MODAL_PRIMARY_ACTION = s__('PackageRegistry|Permanently delete');
export const DELETE_PACKAGE_SUCCESS_MESSAGE = s__('PackageRegistry|Package deleted successfully');
export const PACKAGE_REGISTRY_TITLE = __('Package Registry');
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/graphql/mutations/destroy_packages.mutation.graphql b/app/assets/javascripts/packages_and_registries/package_registry/graphql/mutations/destroy_packages.mutation.graphql
new file mode 100644
index 00000000000..e1ff5518bf8
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/package_registry/graphql/mutations/destroy_packages.mutation.graphql
@@ -0,0 +1,5 @@
+mutation destroyPackages($ids: [PackagesPackageID!]!) {
+ destroyPackages(input: { ids: $ids }) {
+ errors
+ }
+}
diff --git a/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue b/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue
index ea1432299ce..8b5d51cb856 100644
--- a/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue
+++ b/app/assets/javascripts/packages_and_registries/package_registry/pages/list.vue
@@ -1,5 +1,5 @@
<script>
-import { GlBanner, GlEmptyState, GlLink, GlSprintf } from '@gitlab/ui';
+import { GlAlert, GlBanner, GlEmptyState, GlLink, GlSprintf } from '@gitlab/ui';
import { createAlert, VARIANT_INFO } from '~/flash';
import { getCookie, historyReplaceState, parseBoolean, setCookie } from '~/lib/utils/common_utils';
import { s__ } from '~/locale';
@@ -10,19 +10,23 @@ import {
GRAPHQL_PAGE_SIZE,
HIDE_PACKAGE_MIGRATION_SURVEY_COOKIE,
DELETE_PACKAGE_SUCCESS_MESSAGE,
+ DELETE_PACKAGES_ERROR_MESSAGE,
+ DELETE_PACKAGES_SUCCESS_MESSAGE,
EMPTY_LIST_HELP_URL,
PACKAGE_HELP_URL,
SURVEY_LINK,
} from '~/packages_and_registries/package_registry/constants';
import getPackagesQuery from '~/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql';
-
+import destroyPackagesMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_packages.mutation.graphql';
import DeletePackage from '~/packages_and_registries/package_registry/components/functional/delete_package.vue';
import PackageTitle from '~/packages_and_registries/package_registry/components/list/package_title.vue';
import PackageSearch from '~/packages_and_registries/package_registry/components/list/package_search.vue';
import PackageList from '~/packages_and_registries/package_registry/components/list/packages_list.vue';
+import DeleteModal from '~/packages_and_registries/package_registry/components/delete_modal.vue';
export default {
components: {
+ GlAlert,
GlBanner,
GlEmptyState,
GlLink,
@@ -30,11 +34,14 @@ export default {
PackageList,
PackageTitle,
PackageSearch,
+ DeleteModal,
DeletePackage,
},
inject: ['emptyListIllustration', 'isGroupPage', 'fullPath'],
data() {
return {
+ alertVariables: null,
+ itemsToBeDeleted: [],
packages: {},
sort: '',
filters: {},
@@ -114,6 +121,45 @@ export default {
historyReplaceState(cleanUrl);
}
},
+ async confirmDelete() {
+ const { itemsToBeDeleted } = this;
+ this.itemsToBeDeleted = [];
+ this.mutationLoading = true;
+ try {
+ const { data } = await this.$apollo.mutate({
+ mutation: destroyPackagesMutation,
+ variables: {
+ ids: itemsToBeDeleted.map((i) => i.id),
+ },
+ awaitRefetchQueries: true,
+ refetchQueries: [
+ {
+ query: getPackagesQuery,
+ variables: { ...this.queryVariables, first: GRAPHQL_PAGE_SIZE },
+ },
+ ],
+ });
+
+ if (data?.destroyPackages?.errors[0]) {
+ throw new Error(data.destroyPackages.errors[0]);
+ }
+ this.showAlert({
+ variant: 'success',
+ message: DELETE_PACKAGES_SUCCESS_MESSAGE,
+ });
+ } catch {
+ this.showAlert({
+ variant: 'danger',
+ message: DELETE_PACKAGES_ERROR_MESSAGE,
+ });
+ } finally {
+ this.mutationLoading = false;
+ }
+ },
+ showDeletePackagesModal(toBeDeleted) {
+ this.itemsToBeDeleted = toBeDeleted;
+ this.$refs.deletePackagesModal.show();
+ },
handleSearchUpdate({ sort, filters }) {
this.sort = sort;
this.filters = { ...filters };
@@ -151,6 +197,9 @@ export default {
updateQuery: this.updateQuery,
});
},
+ showAlert(obj) {
+ this.alertVariables = { ...obj };
+ },
},
i18n: {
widenFilters: s__('PackageRegistry|To widen your search, change or remove the filters above.'),
@@ -175,6 +224,15 @@ export default {
<template>
<div>
+ <gl-alert
+ v-if="alertVariables"
+ :variant="alertVariables.variant"
+ class="gl-mt-5"
+ dismissible
+ @dismiss="alertVariables = null"
+ >
+ {{ alertVariables.message }}
+ </gl-alert>
<gl-banner
v-if="showSurveyBanner"
:title="$options.i18n.surveyBannerTitle"
@@ -187,7 +245,7 @@ export default {
<p>{{ $options.i18n.surveyBannerDescription }}</p>
</gl-banner>
<package-title :help-url="$options.links.PACKAGE_HELP_URL" :count="packagesCount" />
- <package-search @update="handleSearchUpdate" />
+ <package-search class="gl-mb-5" @update="handleSearchUpdate" />
<delete-package
:refetch-queries="refetchQueriesData"
@@ -203,6 +261,7 @@ export default {
@prev-page="fetchPreviousPage"
@next-page="fetchNextPage"
@package:delete="deletePackage"
+ @delete="showDeletePackagesModal"
>
<template #empty-state>
<gl-empty-state :title="emptyStateTitle" :svg-path="emptyListIllustration">
@@ -221,5 +280,11 @@ export default {
</package-list>
</template>
</delete-package>
+
+ <delete-modal
+ ref="deletePackagesModal"
+ :items-to-be-deleted="itemsToBeDeleted"
+ @confirm="confirmDelete"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/packages_and_registries/settings/group/graphql/mutations/update_package_forwarding_settings.mutation.graphql b/app/assets/javascripts/packages_and_registries/settings/group/graphql/mutations/update_package_forwarding_settings.mutation.graphql
new file mode 100644
index 00000000000..e5e31f03a7d
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/settings/group/graphql/mutations/update_package_forwarding_settings.mutation.graphql
@@ -0,0 +1,16 @@
+mutation updatePackageForwardingSettings($input: UpdateNamespacePackageSettingsInput!) {
+ updateNamespacePackageSettings(input: $input) {
+ packageSettings {
+ mavenPackageRequestsForwarding
+ lockMavenPackageRequestsForwarding
+ mavenPackageRequestsForwardingLocked
+ npmPackageRequestsForwarding
+ lockNpmPackageRequestsForwarding
+ npmPackageRequestsForwardingLocked
+ pypiPackageRequestsForwarding
+ lockPypiPackageRequestsForwarding
+ pypiPackageRequestsForwardingLocked
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
index 2802e4a90b9..0256eec6d56 100644
--- a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
+++ b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
@@ -124,7 +124,7 @@ export default {
</script>
<template>
- <div class="gl-pt-2">
+ <div>
<gl-loading-icon v-if="$apollo.queries.pipeline.loading" />
<pipeline-mini-graph
v-else
diff --git a/app/assets/javascripts/search/sidebar/components/scope_navigation.vue b/app/assets/javascripts/search/sidebar/components/scope_navigation.vue
index 37138955415..f2782f96da1 100644
--- a/app/assets/javascripts/search/sidebar/components/scope_navigation.vue
+++ b/app/assets/javascripts/search/sidebar/components/scope_navigation.vue
@@ -45,7 +45,7 @@ export default {
</script>
<template>
- <nav>
+ <nav class="search-filter">
<gl-nav vertical pills>
<gl-nav-item
v-for="(item, scope, index) in navigation"
diff --git a/config/initializers/memory_watchdog.rb b/config/initializers/memory_watchdog.rb
index 8a540414378..99c5d61293f 100644
--- a/config/initializers/memory_watchdog.rb
+++ b/config/initializers/memory_watchdog.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
return unless Gitlab::Runtime.application?
-return unless Gitlab::Utils.to_boolean(ENV['GITLAB_MEMORY_WATCHDOG_ENABLED'])
+return unless Gitlab::Utils.to_boolean(ENV['GITLAB_MEMORY_WATCHDOG_ENABLED'], default: Gitlab::Runtime.puma?)
Gitlab::Cluster::LifecycleEvents.on_worker_start do
watchdog = Gitlab::Memory::Watchdog.new
diff --git a/config/initializers_before_autoloader/000_inflections.rb b/config/initializers_before_autoloader/000_inflections.rb
index cb4ea8d64e4..795b0f20128 100644
--- a/config/initializers_before_autoloader/000_inflections.rb
+++ b/config/initializers_before_autoloader/000_inflections.rb
@@ -17,6 +17,7 @@ ActiveSupport::Inflector.inflections do |inflect|
award_emoji
ci_secure_file_registry
container_repository_registry
+ dependency_proxy_blob_registry
design_registry
event_log
file_registry
diff --git a/db/docs/dependency_proxy_blob_states.yml b/db/docs/dependency_proxy_blob_states.yml
new file mode 100644
index 00000000000..ddb9414b5f8
--- /dev/null
+++ b/db/docs/dependency_proxy_blob_states.yml
@@ -0,0 +1,9 @@
+---
+table_name: dependency_proxy_blob_states
+classes:
+ - Geo::DependencyProxyBlobState
+feature_categories:
+ - geo_replication
+description: Separate table for dependency proxy blob verification states
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/101429
+milestone: '15.6'
diff --git a/db/migrate/20221018202524_create_dependency_proxy_blob_states.rb b/db/migrate/20221018202524_create_dependency_proxy_blob_states.rb
new file mode 100644
index 00000000000..b042df43f04
--- /dev/null
+++ b/db/migrate/20221018202524_create_dependency_proxy_blob_states.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+class CreateDependencyProxyBlobStates < Gitlab::Database::Migration[2.0]
+ VERIFICATION_STATE_INDEX_NAME = "index_dependency_proxy_blob_states_on_verification_state"
+ PENDING_VERIFICATION_INDEX_NAME = "index_dependency_proxy_blob_states_pending_verification"
+ FAILED_VERIFICATION_INDEX_NAME = "index_dependency_proxy_blob_states_failed_verification"
+ NEEDS_VERIFICATION_INDEX_NAME = "index_dependency_proxy_blob_states_needs_verification"
+
+ enable_lock_retries!
+
+ def up
+ table_comment = {
+ owner: 'group::geo',
+ description: 'Geo-specific table to store the verification state of DependencyProxy::Blob objects'
+ }
+
+ create_table :dependency_proxy_blob_states, id: false, comment: Gitlab::Json.dump(table_comment) do |t|
+ t.datetime_with_timezone :verification_started_at
+ t.datetime_with_timezone :verification_retry_at
+ t.datetime_with_timezone :verified_at
+ t.references :dependency_proxy_blob,
+ primary_key: true,
+ default: nil,
+ index: true,
+ foreign_key: { on_delete: :cascade }
+ t.integer :verification_state, default: 0, limit: 2, null: false
+ t.integer :verification_retry_count, default: 0, limit: 2, null: false
+ t.binary :verification_checksum, using: 'verification_checksum::bytea'
+ t.text :verification_failure, limit: 255
+
+ t.index :verification_state, name: VERIFICATION_STATE_INDEX_NAME
+ t.index :verified_at,
+ where: "(verification_state = 0)",
+ order: { verified_at: 'ASC NULLS FIRST' },
+ name: PENDING_VERIFICATION_INDEX_NAME
+ t.index :verification_retry_at,
+ where: "(verification_state = 3)",
+ order: { verification_retry_at: 'ASC NULLS FIRST' },
+ name: FAILED_VERIFICATION_INDEX_NAME
+ t.index :verification_state,
+ where: "(verification_state = 0 OR verification_state = 3)",
+ name: NEEDS_VERIFICATION_INDEX_NAME
+ end
+ end
+
+ def down
+ drop_table :dependency_proxy_blob_states
+ end
+end
diff --git a/db/schema_migrations/20221018202524 b/db/schema_migrations/20221018202524
new file mode 100644
index 00000000000..ee738f3608c
--- /dev/null
+++ b/db/schema_migrations/20221018202524
@@ -0,0 +1 @@
+a3266078f4760f0f5a4c7a43669cea1170924f29d6867e712620c2234dbf13c6 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 7c3182ba0b2..f863dbd2cec 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -14586,6 +14586,20 @@ CREATE SEQUENCE dast_sites_id_seq
ALTER SEQUENCE dast_sites_id_seq OWNED BY dast_sites.id;
+CREATE TABLE dependency_proxy_blob_states (
+ verification_started_at timestamp with time zone,
+ verification_retry_at timestamp with time zone,
+ verified_at timestamp with time zone,
+ dependency_proxy_blob_id bigint NOT NULL,
+ verification_state smallint DEFAULT 0 NOT NULL,
+ verification_retry_count smallint DEFAULT 0 NOT NULL,
+ verification_checksum bytea,
+ verification_failure text,
+ CONSTRAINT check_8e4f76fffe CHECK ((char_length(verification_failure) <= 255))
+);
+
+COMMENT ON TABLE dependency_proxy_blob_states IS '{"owner":"group::geo","description":"Geo-specific table to store the verification state of DependencyProxy::Blob objects"}';
+
CREATE TABLE dependency_proxy_blobs (
id integer NOT NULL,
group_id integer NOT NULL,
@@ -25558,6 +25572,9 @@ ALTER TABLE ONLY dast_site_validations
ALTER TABLE ONLY dast_sites
ADD CONSTRAINT dast_sites_pkey PRIMARY KEY (id);
+ALTER TABLE ONLY dependency_proxy_blob_states
+ ADD CONSTRAINT dependency_proxy_blob_states_pkey PRIMARY KEY (dependency_proxy_blob_id);
+
ALTER TABLE ONLY dependency_proxy_blobs
ADD CONSTRAINT dependency_proxy_blobs_pkey PRIMARY KEY (id);
@@ -28785,6 +28802,16 @@ CREATE UNIQUE INDEX index_dast_sites_on_project_id_and_url ON dast_sites USING b
CREATE UNIQUE INDEX index_dep_prox_manifests_on_group_id_file_name_and_status ON dependency_proxy_manifests USING btree (group_id, file_name, status);
+CREATE INDEX index_dependency_proxy_blob_states_failed_verification ON dependency_proxy_blob_states USING btree (verification_retry_at NULLS FIRST) WHERE (verification_state = 3);
+
+CREATE INDEX index_dependency_proxy_blob_states_needs_verification ON dependency_proxy_blob_states USING btree (verification_state) WHERE ((verification_state = 0) OR (verification_state = 3));
+
+CREATE INDEX index_dependency_proxy_blob_states_on_dependency_proxy_blob_id ON dependency_proxy_blob_states USING btree (dependency_proxy_blob_id);
+
+CREATE INDEX index_dependency_proxy_blob_states_on_verification_state ON dependency_proxy_blob_states USING btree (verification_state);
+
+CREATE INDEX index_dependency_proxy_blob_states_pending_verification ON dependency_proxy_blob_states USING btree (verified_at NULLS FIRST) WHERE (verification_state = 0);
+
CREATE INDEX index_dependency_proxy_blobs_on_group_id_and_file_name ON dependency_proxy_blobs USING btree (group_id, file_name);
CREATE INDEX index_dependency_proxy_blobs_on_group_id_status_read_at_id ON dependency_proxy_blobs USING btree (group_id, status, read_at, id);
@@ -34099,6 +34126,9 @@ ALTER TABLE ONLY project_metrics_settings
ALTER TABLE ONLY prometheus_metrics
ADD CONSTRAINT fk_rails_4c8957a707 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY dependency_proxy_blob_states
+ ADD CONSTRAINT fk_rails_4cdbb92cbd FOREIGN KEY (dependency_proxy_blob_id) REFERENCES dependency_proxy_blobs(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY scim_identities
ADD CONSTRAINT fk_rails_4d2056ebd9 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 9adfabb0ab2..d3b28fd15bc 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -321,6 +321,16 @@ configuration option in `gitlab.yml`. These metrics are served from the
| `geo_ci_secure_files_verification_total` | Gauge | 15.3 | Number of secure files verifications tried on secondary | `url` |
| `geo_ci_secure_files_verified` | Gauge | 15.3 | Number of secure files verified on secondary | `url` |
| `geo_ci_secure_files_verification_failed` | Gauge | 15.3 | Number of secure files verifications failed on secondary | `url` |
+| `geo_dependency_proxy_blob` | Gauge | 15.6 | Number of dependency proxy blobs on primary | |
+| `geo_dependency_proxy_blob_checksum_total` | Gauge | 15.6 | Number of dependency proxy blobs tried to checksum on primary | |
+| `geo_dependency_proxy_blob_checksummed` | Gauge | 15.6 | Number of dependency proxy blobs successfully checksummed on primary | |
+| `geo_dependency_proxy_blob_checksum_failed` | Gauge | 15.6 | Number of dependency proxy blobs failed to calculate the checksum on primary | |
+| `geo_dependency_proxy_blob_synced` | Gauge | 15.6 | Number of dependency proxy blobs synced on secondary | |
+| `geo_dependency_proxy_blob_failed` | Gauge | 15.6 | Number of dependency proxy blobs failed to sync on secondary | |
+| `geo_dependency_proxy_blob_registry` | Gauge | 15.6 | Number of dependency proxy blobs in the registry | |
+| `geo_dependency_proxy_blob_verification_total` | Gauge | 15.6 | Number of dependency proxy blobs verifications tried on secondary | |
+| `geo_dependency_proxy_blob_verified` | Gauge | 15.6 | Number of dependency proxy blobs verified on secondary | |
+| `geo_dependency_proxy_blob_verification_failed` | Gauge | 15.6 | Number of dependency proxy blobs verifications failed on secondary | |
## Database load balancing metrics **(PREMIUM SELF)**
diff --git a/doc/api/geo_nodes.md b/doc/api/geo_nodes.md
index fe25b6661a0..00380e1624b 100644
--- a/doc/api/geo_nodes.md
+++ b/doc/api/geo_nodes.md
@@ -504,6 +504,19 @@ Example response:
"ci_secure_files_synced_in_percentage": "100.00%",
"ci_secure_files_verified_in_percentage": "100.00%",
"ci_secure_files_synced_missing_on_primary_count": 0,
+ "dependency_proxy_blobs_count": 5,
+ "dependency_proxy_blobs_checksum_total_count": 5,
+ "dependency_proxy_blobs_checksummed_count": 5,
+ "dependency_proxy_blobs_checksum_failed_count": 0,
+ "dependency_proxy_blobs_synced_count": 5,
+ "dependency_proxy_blobs_failed_count": 0,
+ "dependency_proxy_blobs_registry_count": 5,
+ "dependency_proxy_blobs_verification_total_count": 5,
+ "dependency_proxy_blobs_verified_count": 5,
+ "dependency_proxy_blobs_verification_failed_count": 0,
+ "dependency_proxy_blobs_synced_in_percentage": "100.00%",
+ "dependency_proxy_blobs_verified_in_percentage": "100.00%",
+ "dependency_proxy_blobs_synced_missing_on_primary_count": 0,
"container_repositories_count": 5,
"container_repositories_synced_count": 5,
"container_repositories_failed_count": 0,
@@ -675,6 +688,19 @@ Example response:
"job_artifacts_synced_in_percentage": "100.00%",
"job_artifacts_verified_in_percentage": "100.00%",
"job_artifacts_synced_missing_on_primary_count": 0,
+ "dependency_proxy_blobs_count": 5,
+ "dependency_proxy_blobs_checksum_total_count": 5,
+ "dependency_proxy_blobs_checksummed_count": 5,
+ "dependency_proxy_blobs_checksum_failed_count": 0,
+ "dependency_proxy_blobs_synced_count": 5,
+ "dependency_proxy_blobs_failed_count": 0,
+ "dependency_proxy_blobs_registry_count": 5,
+ "dependency_proxy_blobs_verification_total_count": 5,
+ "dependency_proxy_blobs_verified_count": 5,
+ "dependency_proxy_blobs_verification_failed_count": 0,
+ "dependency_proxy_blobs_synced_in_percentage": "100.00%",
+ "dependency_proxy_blobs_verified_in_percentage": "100.00%",
+ "dependency_proxy_blobs_synced_missing_on_primary_count": 0,
"container_repositories_count": 5,
"container_repositories_synced_count": 5,
"container_repositories_failed_count": 0,
@@ -856,6 +882,19 @@ Example response:
"ci_secure_files_synced_in_percentage": "100.00%",
"ci_secure_files_verified_in_percentage": "100.00%",
"ci_secure_files_synced_missing_on_primary_count": 0,
+ "dependency_proxy_blobs_count": 5,
+ "dependency_proxy_blobs_checksum_total_count": 5,
+ "dependency_proxy_blobs_checksummed_count": 5,
+ "dependency_proxy_blobs_checksum_failed_count": 0,
+ "dependency_proxy_blobs_synced_count": 5,
+ "dependency_proxy_blobs_failed_count": 0,
+ "dependency_proxy_blobs_registry_count": 5,
+ "dependency_proxy_blobs_verification_total_count": 5,
+ "dependency_proxy_blobs_verified_count": 5,
+ "dependency_proxy_blobs_verification_failed_count": 0,
+ "dependency_proxy_blobs_synced_in_percentage": "100.00%",
+ "dependency_proxy_blobs_verified_in_percentage": "100.00%",
+ "dependency_proxy_blobs_synced_missing_on_primary_count": 0,
"container_repositories_count": 5,
"container_repositories_synced_count": 5,
"container_repositories_failed_count": 0,
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index a71790d4c9a..2eb935b229e 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -7283,6 +7283,29 @@ The edge type for [`DependencyProxyBlob`](#dependencyproxyblob).
| <a id="dependencyproxyblobedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="dependencyproxyblobedgenode"></a>`node` | [`DependencyProxyBlob`](#dependencyproxyblob) | The item at the end of the edge. |
+#### `DependencyProxyBlobRegistryConnection`
+
+The connection type for [`DependencyProxyBlobRegistry`](#dependencyproxyblobregistry).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="dependencyproxyblobregistryconnectionedges"></a>`edges` | [`[DependencyProxyBlobRegistryEdge]`](#dependencyproxyblobregistryedge) | A list of edges. |
+| <a id="dependencyproxyblobregistryconnectionnodes"></a>`nodes` | [`[DependencyProxyBlobRegistry]`](#dependencyproxyblobregistry) | A list of nodes. |
+| <a id="dependencyproxyblobregistryconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `DependencyProxyBlobRegistryEdge`
+
+The edge type for [`DependencyProxyBlobRegistry`](#dependencyproxyblobregistry).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="dependencyproxyblobregistryedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| <a id="dependencyproxyblobregistryedgenode"></a>`node` | [`DependencyProxyBlobRegistry`](#dependencyproxyblobregistry) | The item at the end of the edge. |
+
#### `DependencyProxyManifestConnection`
The connection type for [`DependencyProxyManifest`](#dependencyproxymanifest).
@@ -11693,6 +11716,25 @@ Dependency proxy blob.
| <a id="dependencyproxyblobsize"></a>`size` | [`String!`](#string) | Size of the blob file. |
| <a id="dependencyproxyblobupdatedat"></a>`updatedAt` | [`Time!`](#time) | Date of most recent update. |
+### `DependencyProxyBlobRegistry`
+
+Represents the Geo replication and verification state of a dependency_proxy_blob.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="dependencyproxyblobregistrycreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp when the DependencyProxyBlobRegistry was created. |
+| <a id="dependencyproxyblobregistrydependencyproxyblobid"></a>`dependencyProxyBlobId` | [`ID!`](#id) | ID of the Dependency Proxy Blob. |
+| <a id="dependencyproxyblobregistryid"></a>`id` | [`ID!`](#id) | ID of the DependencyProxyBlobRegistry. |
+| <a id="dependencyproxyblobregistrylastsyncfailure"></a>`lastSyncFailure` | [`String`](#string) | Error message during sync of the DependencyProxyBlobRegistry. |
+| <a id="dependencyproxyblobregistrylastsyncedat"></a>`lastSyncedAt` | [`Time`](#time) | Timestamp of the most recent successful sync of the DependencyProxyBlobRegistry. |
+| <a id="dependencyproxyblobregistryretryat"></a>`retryAt` | [`Time`](#time) | Timestamp after which the DependencyProxyBlobRegistry is resynced. |
+| <a id="dependencyproxyblobregistryretrycount"></a>`retryCount` | [`Int`](#int) | Number of consecutive failed sync attempts of the DependencyProxyBlobRegistry. |
+| <a id="dependencyproxyblobregistrystate"></a>`state` | [`RegistryState`](#registrystate) | Sync state of the DependencyProxyBlobRegistry. |
+| <a id="dependencyproxyblobregistryverificationretryat"></a>`verificationRetryAt` | [`Time`](#time) | Timestamp after which the DependencyProxyBlobRegistry is reverified. |
+| <a id="dependencyproxyblobregistryverifiedat"></a>`verifiedAt` | [`Time`](#time) | Timestamp of the most recent successful verification of the DependencyProxyBlobRegistry. |
+
### `DependencyProxyImageTtlGroupPolicy`
Group-level Dependency Proxy TTL policy settings.
@@ -12898,6 +12940,28 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="geonodecontainerrepositoryregistriesreplicationstate"></a>`replicationState` | [`ReplicationStateEnum`](#replicationstateenum) | Filters registries by their replication state. |
| <a id="geonodecontainerrepositoryregistriesverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Filters registries by their verification state. |
+##### `GeoNode.dependencyProxyBlobRegistries`
+
+Find Dependency Proxy Blob registries on this Geo node. Ignored if `geo_dependency_proxy_blob_replication` feature flag is disabled.
+
+WARNING:
+**Introduced** in 15.6.
+This feature is in Alpha. It can be changed or removed at any time.
+
+Returns [`DependencyProxyBlobRegistryConnection`](#dependencyproxyblobregistryconnection).
+
+This field returns a [connection](#connections). It accepts the
+four standard [pagination arguments](#connection-pagination-arguments):
+`before: String`, `after: String`, `first: Int`, `last: Int`.
+
+###### Arguments
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="geonodedependencyproxyblobregistriesids"></a>`ids` | [`[ID!]`](#id) | Filters registries by their ID. |
+| <a id="geonodedependencyproxyblobregistriesreplicationstate"></a>`replicationState` | [`ReplicationStateEnum`](#replicationstateenum) | Filters registries by their replication state. |
+| <a id="geonodedependencyproxyblobregistriesverificationstate"></a>`verificationState` | [`VerificationStateEnum`](#verificationstateenum) | Filters registries by their verification state. |
+
##### `GeoNode.groupWikiRepositoryRegistries`
Find group wiki repository registries on this Geo node.
diff --git a/doc/api/suggestions.md b/doc/api/suggestions.md
index b3c18b82211..0b6fa25c5c7 100644
--- a/doc/api/suggestions.md
+++ b/doc/api/suggestions.md
@@ -38,7 +38,7 @@ Example response:
"to_line": 10,
"applicable": true,
"applied": false,
- "from_content": "This is an example\n",
+ "from_content": "This is an eaxmple\n",
"to_content": "This is an example\n"
}
```
@@ -68,7 +68,7 @@ Example response:
"to_line": 10,
"applicable": true,
"applied": false,
- "from_content": "This is an example\n",
+ "from_content": "This is an eaxmple\n",
"to_content": "This is an example\n"
}
{
diff --git a/doc/architecture/blueprints/_template.md b/doc/architecture/blueprints/_template.md
index 01c2ab3595e..798d51a97ad 100644
--- a/doc/architecture/blueprints/_template.md
+++ b/doc/architecture/blueprints/_template.md
@@ -53,11 +53,6 @@ as part of any review.
-->
<!--
-Start the document by pasting an exact copy of the disclaimer found at:
-https://docs.gitlab.com/ee/development/documentation/versions.html#legal-disclaimer-for-future-features
--->
-
-<!--
For long pages, consider creating a table of contents.
The `[_TOC_]` function is not supported on docs.gitlab.com.
-->
diff --git a/doc/architecture/blueprints/ci_data_decay/index.md b/doc/architecture/blueprints/ci_data_decay/index.md
index 951598afd69..b7c3bdde2f8 100644
--- a/doc/architecture/blueprints/ci_data_decay/index.md
+++ b/doc/architecture/blueprints/ci_data_decay/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# CI/CD data time decay
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
## Summary
GitLab CI/CD is one of the most data and compute intensive components of GitLab.
diff --git a/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md b/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
index 3ee7640940e..d61412ae1ed 100644
--- a/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
+++ b/doc/architecture/blueprints/ci_data_decay/pipeline_partitioning.md
@@ -7,14 +7,6 @@ description: 'Pipeline data partitioning design'
# Pipeline data partitioning design
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
## What problem are we trying to solve?
We want to partition the CI/CD dataset, because some of the database tables are
diff --git a/doc/architecture/blueprints/ci_pipeline_components/index.md b/doc/architecture/blueprints/ci_pipeline_components/index.md
index 1a909913e49..a3c72227f3e 100644
--- a/doc/architecture/blueprints/ci_pipeline_components/index.md
+++ b/doc/architecture/blueprints/ci_pipeline_components/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# CI/CD pipeline components catalog
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
## Summary
## Goals
diff --git a/doc/architecture/blueprints/ci_scale/index.md b/doc/architecture/blueprints/ci_scale/index.md
index 6922934eb4b..c02fb35974b 100644
--- a/doc/architecture/blueprints/ci_scale/index.md
+++ b/doc/architecture/blueprints/ci_scale/index.md
@@ -7,14 +7,6 @@ description: 'Improve scalability of GitLab CI/CD'
# CI/CD Scaling
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
## Summary
GitLab CI/CD is one of the most data and compute intensive components of GitLab.
diff --git a/doc/architecture/blueprints/cloud_native_build_logs/index.md b/doc/architecture/blueprints/cloud_native_build_logs/index.md
index 17eace53d2f..20cfb46abc4 100644
--- a/doc/architecture/blueprints/cloud_native_build_logs/index.md
+++ b/doc/architecture/blueprints/cloud_native_build_logs/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# Cloud Native Build Logs
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
Cloud native and the adoption of Kubernetes has been recognised by GitLab to be
one of the top two biggest tailwinds that are helping us grow faster as a
company behind the project.
diff --git a/doc/architecture/blueprints/cloud_native_gitlab_pages/index.md b/doc/architecture/blueprints/cloud_native_gitlab_pages/index.md
index bf4d0974bdd..b6f3a59dc0b 100644
--- a/doc/architecture/blueprints/cloud_native_gitlab_pages/index.md
+++ b/doc/architecture/blueprints/cloud_native_gitlab_pages/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# GitLab Pages New Architecture
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
GitLab Pages is an important component of the GitLab product. It is mostly
being used to serve static content, and has a limited set of well defined
responsibilities. That being said, unfortunately it has become a blocker for
diff --git a/doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md b/doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md
index cbce34a7f17..53f38fa85fd 100644
--- a/doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md
+++ b/doc/architecture/blueprints/composable_codebase_using_rails_engines/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# Composable GitLab codebase - using Rails Engines
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
NOTE:
Due to our focus on improving the overall availability of GitLab.com and reducing tech debt, we do not have capacity to act on this blueprint. We will re-evaluate in Q1-FY23.
diff --git a/doc/architecture/blueprints/consolidating_groups_and_projects/index.md b/doc/architecture/blueprints/consolidating_groups_and_projects/index.md
index a1236bd5452..0818d9b973d 100644
--- a/doc/architecture/blueprints/consolidating_groups_and_projects/index.md
+++ b/doc/architecture/blueprints/consolidating_groups_and_projects/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# Consolidating Groups and Projects
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
There are numerous features that exist exclusively within groups or
projects. The boundary between group and project features used to be clear.
However, there is growing demand to have group features within projects, and
diff --git a/doc/architecture/blueprints/container_registry_metadata_database/index.md b/doc/architecture/blueprints/container_registry_metadata_database/index.md
index ccdde84fc68..63e27286756 100644
--- a/doc/architecture/blueprints/container_registry_metadata_database/index.md
+++ b/doc/architecture/blueprints/container_registry_metadata_database/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# Container Registry Metadata Database
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
## Usage of the GitLab Container Registry
With the [Container Registry](https://gitlab.com/gitlab-org/container-registry) integrated into GitLab, every GitLab project can have its own space to store its Docker images. You can use the registry to build, push and share images using the Docker client, CI/CD or the GitLab API.
diff --git a/doc/architecture/blueprints/database/scalability/patterns/read_mostly.md b/doc/architecture/blueprints/database/scalability/patterns/read_mostly.md
index 09b7d3f5f1c..ec236c9bfe3 100644
--- a/doc/architecture/blueprints/database/scalability/patterns/read_mostly.md
+++ b/doc/architecture/blueprints/database/scalability/patterns/read_mostly.md
@@ -10,14 +10,6 @@ description: 'Learn how to scale operating on read-mostly data at scale'
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/326037) in GitLab 14.0.
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
This document describes the *read-mostly* pattern introduced in the
[Database Scalability Working Group](https://about.gitlab.com/company/team/structure/working-groups/database-scalability/#read-mostly-data).
We discuss the characteristics of *read-mostly* data and propose best practices for GitLab development
diff --git a/doc/architecture/blueprints/database/scalability/patterns/time_decay.md b/doc/architecture/blueprints/database/scalability/patterns/time_decay.md
index fd5612e27bd..93f5dffd3f5 100644
--- a/doc/architecture/blueprints/database/scalability/patterns/time_decay.md
+++ b/doc/architecture/blueprints/database/scalability/patterns/time_decay.md
@@ -10,14 +10,6 @@ description: 'Learn how to operate on large time-decay data'
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/326035) in GitLab 14.0.
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
This document describes the *time-decay pattern* introduced in the
[Database Scalability Working Group](https://about.gitlab.com/company/team/structure/working-groups/database-scalability/#time-decay-data).
We discuss the characteristics of time-decay data, and propose best practices for GitLab development
diff --git a/doc/architecture/blueprints/database_scaling/size-limits.md b/doc/architecture/blueprints/database_scaling/size-limits.md
index 42654a9979f..e530bd6eff0 100644
--- a/doc/architecture/blueprints/database_scaling/size-limits.md
+++ b/doc/architecture/blueprints/database_scaling/size-limits.md
@@ -7,14 +7,6 @@ description: 'Database Scalability / Limit table sizes'
# Database Scalability: Limit on-disk table size to < 100 GB for GitLab.com
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
This document is a proposal to work towards reducing and limiting table sizes on GitLab.com. We establish a **measurable target** by limiting table size to a certain threshold. This is used as an indicator to drive database focus and decision making. With GitLab.com growing, we continuously re-evaluate which tables need to be worked on to prevent or otherwise fix violations.
This is not meant to be a hard rule but rather a strong indication that work needs to be done to break a table apart or otherwise reduce its size.
diff --git a/doc/architecture/blueprints/database_testing/index.md b/doc/architecture/blueprints/database_testing/index.md
index e50190666b6..fe6dcf1723d 100644
--- a/doc/architecture/blueprints/database_testing/index.md
+++ b/doc/architecture/blueprints/database_testing/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# Database Testing
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
We have identified [common themes of reverted migrations](https://gitlab.com/gitlab-org/gitlab/-/issues/233391) and discovered failed migrations breaking in both production and staging even when successfully tested in a developer environment. We have also experienced production incidents even with successful testing in staging. These failures are quite expensive: they can have a significant effect on availability, block deployments, and generate incident escalations. These escalations must be triaged and either reverted or fixed forward. Often, this can take place without the original author's involvement due to time zones and/or the criticality of the escalation. With our increased deployment speeds and stricter uptime requirements, the need for improving database testing is critical, particularly earlier in the development process (shift left).
From a developer's perspective, it is hard, if not unfeasible, to validate a migration on a large enough dataset before it goes into production.
diff --git a/doc/architecture/blueprints/feature_flags_development/index.md b/doc/architecture/blueprints/feature_flags_development/index.md
index 10ab83de3d1..730daf56f0d 100644
--- a/doc/architecture/blueprints/feature_flags_development/index.md
+++ b/doc/architecture/blueprints/feature_flags_development/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# Architectural discussion of feature flags
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
Usage of feature flags become crucial for the development of GitLab. The
feature flags are a convenient way to ship changes early, and safely rollout
them to wide audience ensuring that feature is stable and performant.
diff --git a/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md b/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md
index c0030e63dc9..6ac67dd0f18 100644
--- a/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md
+++ b/doc/architecture/blueprints/gitlab_to_kubernetes_communication/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# GitLab to Kubernetes communication **(FREE)**
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
The goal of this document is to define how GitLab can communicate with Kubernetes
and in-cluster services through the GitLab agent.
diff --git a/doc/architecture/blueprints/graphql_api/index.md b/doc/architecture/blueprints/graphql_api/index.md
index cc08f12f549..4b446a78541 100644
--- a/doc/architecture/blueprints/graphql_api/index.md
+++ b/doc/architecture/blueprints/graphql_api/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# GraphQL API
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
[GraphQL](https://graphql.org/) is a data query and manipulation language for
APIs, and a runtime for fulfilling queries with existing data.
diff --git a/doc/architecture/blueprints/image_resizing/index.md b/doc/architecture/blueprints/image_resizing/index.md
index e96fd67008e..948378d8834 100644
--- a/doc/architecture/blueprints/image_resizing/index.md
+++ b/doc/architecture/blueprints/image_resizing/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# Image resizing for avatars and content images
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
Currently, we are showing all uploaded images 1:1, which is of course not ideal. To improve performance greatly, add image resizing to the backend. There are two main areas of image resizing to consider; avatars and content images. The MVC for this implementation focuses on Avatars. Avatars requests consist of approximately 70% of total image requests. There is an identified set of sizes we intend to support which makes the scope of this first MVC very narrow. Content image resizing has many more considerations for size and features. It is entirely possible that we have two separate development efforts with the same goal of increasing performance via image resizing.
## MVC Avatar Resizing
diff --git a/doc/architecture/blueprints/object_storage/index.md b/doc/architecture/blueprints/object_storage/index.md
index d1593fb4eb0..61dc37d7706 100644
--- a/doc/architecture/blueprints/object_storage/index.md
+++ b/doc/architecture/blueprints/object_storage/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# Object storage: `direct_upload` consolidation
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
## Abstract
GitLab stores three classes of user data: database records, Git
diff --git a/doc/architecture/blueprints/pods/index.md b/doc/architecture/blueprints/pods/index.md
index dfd046a40e8..527720d003a 100644
--- a/doc/architecture/blueprints/pods/index.md
+++ b/doc/architecture/blueprints/pods/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# Pods
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
This document is a work-in-progress and represents a very early state of the Pods design. Significant aspects are not documented, though we expect to add them in the future.
## Summary
diff --git a/doc/architecture/blueprints/pods/pods-feature-git-access.md b/doc/architecture/blueprints/pods/pods-feature-git-access.md
index 4f4d7b91420..ae996281d46 100644
--- a/doc/architecture/blueprints/pods/pods-feature-git-access.md
+++ b/doc/architecture/blueprints/pods/pods-feature-git-access.md
@@ -5,14 +5,6 @@ comments: false
description: 'Pods: Git Access'
---
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
This document is a work-in-progress and represents a very early state of the
Pods design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Pods, and we intend to
diff --git a/doc/architecture/blueprints/pods/pods-feature-template.md b/doc/architecture/blueprints/pods/pods-feature-template.md
index 8971822ebee..fd9813710f2 100644
--- a/doc/architecture/blueprints/pods/pods-feature-template.md
+++ b/doc/architecture/blueprints/pods/pods-feature-template.md
@@ -5,14 +5,6 @@ comments: false
description: 'Pods architecture: Problem A'
---
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
This document is a work-in-progress and represents a very early state of the
Pods design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Pods, and we intend to
diff --git a/doc/architecture/blueprints/pods/proposal-stateless-router-with-buffering-requests.md b/doc/architecture/blueprints/pods/proposal-stateless-router-with-buffering-requests.md
index 03bb7a255c0..21aa72273fe 100644
--- a/doc/architecture/blueprints/pods/proposal-stateless-router-with-buffering-requests.md
+++ b/doc/architecture/blueprints/pods/proposal-stateless-router-with-buffering-requests.md
@@ -5,14 +5,6 @@ comments: false
description: 'Pods Stateless Router Proposal'
---
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
This document is a work-in-progress and represents a very early state of the
Pods design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Pods, and we intend to
diff --git a/doc/architecture/blueprints/pods/proposal-stateless-router-with-routes-learning.md b/doc/architecture/blueprints/pods/proposal-stateless-router-with-routes-learning.md
index 6d3f27d9ae2..e7520f3d6a8 100644
--- a/doc/architecture/blueprints/pods/proposal-stateless-router-with-routes-learning.md
+++ b/doc/architecture/blueprints/pods/proposal-stateless-router-with-routes-learning.md
@@ -5,14 +5,6 @@ comments: false
description: 'Pods Stateless Router Proposal'
---
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
This document is a work-in-progress and represents a very early state of the
Pods design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Pods, and we intend to
diff --git a/doc/architecture/blueprints/rate_limiting/index.md b/doc/architecture/blueprints/rate_limiting/index.md
index eac0fbb05f8..ffe0712d69b 100644
--- a/doc/architecture/blueprints/rate_limiting/index.md
+++ b/doc/architecture/blueprints/rate_limiting/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# Next Rate Limiting Architecture
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
## Summary
Introducing reasonable application limits is a very important step in any SaaS
diff --git a/doc/architecture/blueprints/runner_scaling/index.md b/doc/architecture/blueprints/runner_scaling/index.md
index f7c477b4154..24c6820f94a 100644
--- a/doc/architecture/blueprints/runner_scaling/index.md
+++ b/doc/architecture/blueprints/runner_scaling/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# Next Runner Auto-scaling Architecture
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
## Summary
GitLab Runner is a core component of GitLab CI/CD. It makes it possible to run
diff --git a/doc/architecture/blueprints/runner_tokens/index.md b/doc/architecture/blueprints/runner_tokens/index.md
index f9f53d0a03e..3f8a27e503d 100644
--- a/doc/architecture/blueprints/runner_tokens/index.md
+++ b/doc/architecture/blueprints/runner_tokens/index.md
@@ -7,14 +7,6 @@ description: 'Next Runner Token Architecture'
# Next GitLab Runner Token Architecture
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
## Summary
GitLab Runner is a core component of GitLab CI/CD that runs
diff --git a/doc/architecture/blueprints/work_items/index.md b/doc/architecture/blueprints/work_items/index.md
index edc092f12b0..75a9d8d76ad 100644
--- a/doc/architecture/blueprints/work_items/index.md
+++ b/doc/architecture/blueprints/work_items/index.md
@@ -10,14 +10,6 @@ participating-stages: []
# Work Items
-DISCLAIMER:
-This page contains information related to upcoming products, features, and functionality.
-It is important to note that the information presented is for informational purposes only.
-Please do not rely on this information for purchasing or planning purposes.
-As with all projects, the items mentioned on this page are subject to change or delay.
-The development, release, and timing of any products, features, or functionality remain at the
-sole discretion of GitLab Inc.
-
This document is a work-in-progress. Some aspects are not documented, though we expect to add them in the future.
## Summary
diff --git a/doc/development/pipelines/index.md b/doc/development/pipelines/index.md
index caffd121ca4..e29d38bb22f 100644
--- a/doc/development/pipelines/index.md
+++ b/doc/development/pipelines/index.md
@@ -85,7 +85,7 @@ In this mode, `jest` would resolve all the dependencies of related to the change
In addition, there are a few circumstances where we would always run the full Jest tests:
-- when the `pipeline:run-all-jest` label is set on the merge request or is set by triage automation when the merge request is approved by any reviewer
+- when the `pipeline:run-all-jest` label is set on the merge request
- when the merge request is created by an automation (for example, Gitaly update or MR targeting a stable branch)
- when the merge request is created in a security mirror
- when any CI configuration file is changed (for example, `.gitlab-ci.yml` or `.gitlab/ci/**/*`)
diff --git a/doc/development/pipelines/internals.md b/doc/development/pipelines/internals.md
index a3774e20e96..2861e2f266b 100644
--- a/doc/development/pipelines/internals.md
+++ b/doc/development/pipelines/internals.md
@@ -204,7 +204,7 @@ and included in `rules` definitions via [YAML anchors](../../ci/yaml/yaml_optimi
| `yaml-lint-patterns` | Only create job for YAML-related changes. |
| `docs-patterns` | Only create job for docs-related changes. |
| `frontend-dependency-patterns` | Only create job when frontend dependencies are updated (that is, `package.json`, and `yarn.lock`). changes. |
-| `frontend-patterns` | Only create job for frontend-related changes. |
+| `frontend-patterns-for-as-if-foss` | Only create job for frontend-related changes that have impact on FOSS. |
| `backend-patterns` | Only create job for backend-related changes. |
| `db-patterns` | Only create job for DB-related changes. |
| `backstage-patterns` | Only create job for backstage-related changes (that is, Danger, fixtures, RuboCop, specs). |
diff --git a/doc/user/infrastructure/iac/index.md b/doc/user/infrastructure/iac/index.md
index c2285ecc773..866b16652fa 100644
--- a/doc/user/infrastructure/iac/index.md
+++ b/doc/user/infrastructure/iac/index.md
@@ -99,8 +99,8 @@ in the template you fetched to customize your configuration.
- To collaborate on Terraform code changes and Infrastructure-as-Code workflows, use the
[Terraform integration in merge requests](mr_integration.md).
- To manage GitLab resources like users, groups, and projects, use the
- [GitLab Terraform provider](https://github.com/gitlabhq/terraform-provider-gitlab). It is released separately from GitLab
- and its documentation is available on [the Terraform docs site](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs).
+ [GitLab Terraform provider](https://gitlab.com/gitlab-org/terraform-provider-gitlab).
+ The GitLab Terraform provider documentation is available on [the Terraform docs site](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs).
- [Create a new cluster on Amazon Elastic Kubernetes Service (EKS)](../clusters/connect/new_eks_cluster.md).
- [Create a new cluster on Google Kubernetes Engine (GKE)](../clusters/connect/new_gke_cluster.md).
- [Troubleshoot](troubleshooting.md) issues with GitLab and Terraform.
diff --git a/lib/api/entities/metadata.rb b/lib/api/entities/metadata.rb
index daa491ec42a..1e04b5c5982 100644
--- a/lib/api/entities/metadata.rb
+++ b/lib/api/entities/metadata.rb
@@ -3,12 +3,12 @@
module API
module Entities
class Metadata < Grape::Entity
- expose :version
- expose :revision
+ expose :version, documentation: { type: 'string', example: '15.2-pre' }
+ expose :revision, documentation: { type: 'string', example: 'c401a659d0c' }
expose :kas do
expose :enabled, documentation: { type: 'boolean' }
- expose :externalUrl
- expose :version
+ expose :externalUrl, documentation: { type: 'string', example: 'grpc://gitlab.example.com:8150' }
+ expose :version, documentation: { type: 'string', example: '15.0.0' }
end
end
end
diff --git a/lib/api/metadata.rb b/lib/api/metadata.rb
index 3e42ffe336a..2fdb97f98ef 100644
--- a/lib/api/metadata.rb
+++ b/lib/api/metadata.rb
@@ -9,6 +9,8 @@ module API
before { authenticate! }
+ METADATA_TAGS = %w[metadata].freeze
+
feature_category :not_owned # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned
METADATA_QUERY = <<~EOF
@@ -35,30 +37,13 @@ module API
end
end
- desc 'Retrieve metadata information for this GitLab instance.' do
+ desc 'Retrieve metadata information for this GitLab instance' do
detail 'This feature was introduced in GitLab 15.2.'
- success [
- {
- code: 200,
- model: Entities::Metadata,
- message: 'successful operation',
- examples: {
- successful_response: {
- 'value' => {
- version: "15.0-pre",
- revision: "c401a659d0c",
- kas: {
- enabled: true,
- externalUrl: "grpc://gitlab.example.com:8150",
- version: "15.0.0"
- }
- }
- }
- }
- }
+ success Entities::Metadata
+ failure [
+ { code: 401, message: 'Unauthorized' }
]
- failure [{ code: 401, message: 'unauthorized operation' }]
- tags %w[metadata]
+ tags METADATA_TAGS
end
get '/metadata' do
run_metadata_query
@@ -66,31 +51,14 @@ module API
# Support the deprecated `/version` route.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/366287
- desc 'Get the version information of the GitLab instance.' do
+ desc 'Retrieves version information for the GitLab instance' do
detail 'This feature was introduced in GitLab 8.13 and deprecated in 15.5. ' \
'We recommend you instead use the Metadata API.'
- success [
- {
- code: 200,
- model: Entities::Metadata,
- message: 'successful operation',
- examples: {
- 'Example' => {
- 'value' => {
- version: "15.0-pre",
- revision: "c401a659d0c",
- kas: {
- enabled: true,
- externalUrl: "grpc://gitlab.example.com:8150",
- version: "15.0.0"
- }
- }
- }
- }
- }
+ success Entities::Metadata
+ failure [
+ { code: 401, message: 'Unauthorized' }
]
- failure [{ code: 401, message: 'unauthorized operation' }]
- tags %w[metadata]
+ tags METADATA_TAGS
end
get '/version' do
diff --git a/lib/api/v3/github.rb b/lib/api/v3/github.rb
index c86b7785ce2..e4a26838746 100644
--- a/lib/api/v3/github.rb
+++ b/lib/api/v3/github.rb
@@ -58,7 +58,7 @@ module API
project = find_project!(
::Gitlab::Jira::Dvcs.restore_full_path(**params.slice(:namespace, :project).symbolize_keys)
)
- not_found! unless can?(current_user, :download_code, project)
+ not_found! unless can?(current_user, :read_code, project)
project
end
diff --git a/lib/gitlab/cluster/puma_worker_killer_initializer.rb b/lib/gitlab/cluster/puma_worker_killer_initializer.rb
index 5908de68687..957faf797b5 100644
--- a/lib/gitlab/cluster/puma_worker_killer_initializer.rb
+++ b/lib/gitlab/cluster/puma_worker_killer_initializer.rb
@@ -9,6 +9,10 @@ module Gitlab
puma_master_max_memory_mb: 950,
additional_puma_dev_max_memory_mb: 200)
+ # We are replacing PWK with Watchdog by using backward compatible RssMemoryLimit monitor by default.
+ # https://gitlab.com/groups/gitlab-org/-/epics/9119
+ return if Gitlab::Utils.to_boolean(ENV.fetch('GITLAB_MEMORY_WATCHDOG_ENABLED', true))
+
require 'puma_worker_killer'
PumaWorkerKiller.config do |config|
diff --git a/lib/gitlab/database/gitlab_schemas.yml b/lib/gitlab/database/gitlab_schemas.yml
index f9a7ac017ce..bf6ebb21f7d 100644
--- a/lib/gitlab/database/gitlab_schemas.yml
+++ b/lib/gitlab/database/gitlab_schemas.yml
@@ -168,6 +168,7 @@ dast_site_profiles_pipelines: :gitlab_main
dast_sites: :gitlab_main
dast_site_tokens: :gitlab_main
dast_site_validations: :gitlab_main
+dependency_proxy_blob_states: :gitlab_main
dependency_proxy_blobs: :gitlab_main
dependency_proxy_group_settings: :gitlab_main
dependency_proxy_image_ttl_group_policies: :gitlab_main
diff --git a/lib/gitlab/database/load_balancing/load_balancer.rb b/lib/gitlab/database/load_balancing/load_balancer.rb
index 0881025b425..cb3a378ad64 100644
--- a/lib/gitlab/database/load_balancing/load_balancer.rb
+++ b/lib/gitlab/database/load_balancing/load_balancer.rb
@@ -119,6 +119,13 @@ module Gitlab
connection = pool.connection
transaction_open = connection.transaction_open?
+ if attempt && attempt > 1
+ ::Gitlab::Database::LoadBalancing::Logger.warn(
+ event: :read_write_retry,
+ message: 'A read_write block was retried because of connection error'
+ )
+ end
+
yield connection
rescue StandardError => e
# No leaking will happen on the final attempt. Leaks are caused by subsequent retries
diff --git a/lib/gitlab/github_import/importer/events/changed_label.rb b/lib/gitlab/github_import/importer/events/changed_label.rb
index 83130d18db9..553ef0886e8 100644
--- a/lib/gitlab/github_import/importer/events/changed_label.rb
+++ b/lib/gitlab/github_import/importer/events/changed_label.rb
@@ -13,6 +13,7 @@ module Gitlab
def create_event(issue_event)
attrs = {
+ importing: true,
user_id: author_id(issue_event),
label_id: label_finder.id_for(issue_event.label_title),
action: action(issue_event.event),
diff --git a/lib/gitlab/i18n.rb b/lib/gitlab/i18n.rb
index a2d06b7f5b3..a42cac61a55 100644
--- a/lib/gitlab/i18n.rb
+++ b/lib/gitlab/i18n.rb
@@ -44,30 +44,30 @@ module Gitlab
TRANSLATION_LEVELS = {
'bg' => 0,
'cs_CZ' => 0,
- 'da_DK' => 37,
+ 'da_DK' => 36,
'de' => 17,
'en' => 100,
'eo' => 0,
- 'es' => 36,
+ 'es' => 35,
'fil_PH' => 0,
- 'fr' => 72,
+ 'fr' => 85,
'gl_ES' => 0,
'id_ID' => 0,
'it' => 1,
- 'ja' => 31,
- 'ko' => 20,
+ 'ja' => 30,
+ 'ko' => 21,
'nb_NO' => 25,
'nl_NL' => 0,
'pl_PL' => 3,
- 'pt_BR' => 57,
- 'ro_RO' => 99,
- 'ru' => 26,
+ 'pt_BR' => 58,
+ 'ro_RO' => 98,
+ 'ru' => 25,
'si_LK' => 11,
'tr_TR' => 11,
- 'uk' => 49,
+ 'uk' => 52,
'zh_CN' => 98,
'zh_HK' => 1,
- 'zh_TW' => 99
+ 'zh_TW' => 100
}.freeze
private_constant :TRANSLATION_LEVELS
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 964fba75001..64fe7b182e2 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -343,6 +343,11 @@ msgid_plural "%d more comments"
msgstr[0] ""
msgstr[1] ""
+msgid "%d package"
+msgid_plural "%d packages"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d pending comment"
msgid_plural "%d pending comments"
msgstr[0] ""
@@ -7198,6 +7203,9 @@ msgstr ""
msgid "BuildArtifacts|Loading artifacts"
msgstr ""
+msgid "Building your merge request. Wait a few moments, then refresh this page."
+msgstr ""
+
msgid "Built-in"
msgstr ""
@@ -28913,6 +28921,9 @@ msgstr ""
msgid "PackageRegistry|Delete package version"
msgstr ""
+msgid "PackageRegistry|Delete packages"
+msgstr ""
+
msgid "PackageRegistry|Delete selected"
msgstr ""
@@ -29047,6 +29058,9 @@ msgstr[1] ""
msgid "PackageRegistry|Package updated by commit %{link} on branch %{branch}, built by pipeline %{pipeline}, and published to the registry %{datetime}"
msgstr ""
+msgid "PackageRegistry|Packages deleted successfully"
+msgstr ""
+
msgid "PackageRegistry|Permanently delete"
msgstr ""
@@ -29101,6 +29115,9 @@ msgstr ""
msgid "PackageRegistry|Show Yarn commands"
msgstr ""
+msgid "PackageRegistry|Something went wrong while deleting packages."
+msgstr ""
+
msgid "PackageRegistry|Something went wrong while deleting the package asset."
msgstr ""
@@ -29172,6 +29189,11 @@ msgid_plural "PackageRegistry|You are about to delete %d assets. This operation
msgstr[0] ""
msgstr[1] ""
+msgid "PackageRegistry|You are about to delete 1 package. This operation is irreversible."
+msgid_plural "PackageRegistry|You are about to delete %d packages. This operation is irreversible."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "PackageRegistry|You are about to delete version %{version} of %{name}. Are you sure?"
msgstr ""
diff --git a/package.json b/package.json
index a4aba6591f2..0fe20b96301 100644
--- a/package.json
+++ b/package.json
@@ -118,7 +118,7 @@
"dateformat": "^5.0.1",
"deckar01-task_list": "^2.3.1",
"diff": "^3.4.0",
- "dompurify": "^2.4.0",
+ "dompurify": "^2.4.1",
"dropzone": "^4.2.0",
"editorconfig": "^0.15.3",
"emoji-regex": "^10.0.0",
diff --git a/qa/Dockerfile b/qa/Dockerfile
index 58839dbff79..7f236a25288 100644
--- a/qa/Dockerfile
+++ b/qa/Dockerfile
@@ -1,9 +1,9 @@
ARG DOCKER_VERSION=20.10.14
-ARG CHROME_VERSION=103
+ARG CHROME_VERSION=106
ARG QA_BUILD_TARGET=qa
ARG RUBY_VERSION=2.7
-FROM registry.gitlab.com/gitlab-org/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3-git-2.33-lfs-2.9-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}-gcloud-383-kubectl-1.23 AS qa
+FROM registry.gitlab.com/gitlab-org/gitlab-build-images/debian-bullseye-ruby-${RUBY_VERSION}:bundler-2.3-git-2.36-lfs-2.9-chrome-${CHROME_VERSION}-docker-${DOCKER_VERSION}-gcloud-383-kubectl-1.23 AS qa
LABEL maintainer="GitLab Quality Department <quality@gitlab.com>"
ENV DEBIAN_FRONTEND="noninteractive"
diff --git a/spec/factories/dependency_proxy.rb b/spec/factories/dependency_proxy.rb
index afa6c61116a..33356a701df 100644
--- a/spec/factories/dependency_proxy.rb
+++ b/spec/factories/dependency_proxy.rb
@@ -4,13 +4,20 @@ FactoryBot.define do
factory :dependency_proxy_blob, class: 'DependencyProxy::Blob' do
group
size { 1234 }
- file { fixture_file_upload('spec/fixtures/dependency_proxy/a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4.gz') }
file_name { 'a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4.gz' }
status { :default }
+ after(:build) do |blob, _evaluator|
+ blob.file = fixture_file_upload('spec/fixtures/dependency_proxy/a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4.gz')
+ end
+
trait :pending_destruction do
status { :pending_destruction }
end
+
+ trait :remote_store do
+ file_store { DependencyProxy::FileUploader::Store::REMOTE }
+ end
end
factory :dependency_proxy_manifest, class: 'DependencyProxy::Manifest' do
diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb
index 666bf3594de..2e63ec2d4f2 100644
--- a/spec/features/global_search_spec.rb
+++ b/spec/features/global_search_spec.rb
@@ -2,14 +2,13 @@
require 'spec_helper'
-RSpec.describe 'Global search' do
+RSpec.describe 'Global search', :js do
include AfterNextHelpers
- let(:user) { create(:user) }
- let(:project) { create(:project, namespace: user.namespace) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:project) { create(:project, namespace: user.namespace) }
before do
- stub_feature_flags(search_page_vertical_nav: false)
project.add_maintainer(user)
sign_in(user)
end
@@ -42,7 +41,7 @@ RSpec.describe 'Global search' do
end
end
- it 'closes the dropdown on blur', :js do
+ it 'closes the dropdown on blur' do
find('#search').click
fill_in 'search', with: "a"
@@ -59,7 +58,7 @@ RSpec.describe 'Global search' do
expect(page).to have_no_selector('#js-header-search')
end
- it 'focuses search input when shortcut "s" is pressed', :js do
+ it 'focuses search input when shortcut "s" is pressed' do
expect(page).not_to have_selector('#search:focus')
find('body').native.send_key('s')
@@ -74,7 +73,7 @@ RSpec.describe 'Global search' do
stub_feature_flags(new_header_search: true)
visit dashboard_projects_path
- # intialize javascript loaded input search input field
+ # initialize javascript loaded input search input field
find('#search').click
find('body').click
end
@@ -84,7 +83,7 @@ RSpec.describe 'Global search' do
expect(page).to have_selector('#js-header-search')
end
- it 'focuses search input when shortcut "s" is pressed', :js do
+ it 'focuses search input when shortcut "s" is pressed' do
expect(page).not_to have_selector('#search:focus')
find('body').native.send_key('s')
diff --git a/spec/features/merge_request/user_sees_diff_spec.rb b/spec/features/merge_request/user_sees_diff_spec.rb
index 2e65183d26f..0bae019793c 100644
--- a/spec/features/merge_request/user_sees_diff_spec.rb
+++ b/spec/features/merge_request/user_sees_diff_spec.rb
@@ -38,6 +38,20 @@ RSpec.describe 'Merge request > User sees diff', :js do
end
end
+ context 'when linking to a line' do
+ let(:note) { create :diff_note_on_merge_request, project: project, noteable: merge_request }
+ let(:line) { note.diff_file.highlighted_diff_lines.last }
+ let(:line_code) { line.line_code }
+
+ before do
+ visit "#{diffs_project_merge_request_path(project, merge_request)}##{line_code}"
+ end
+
+ it 'shows the linked line' do
+ expect(page).to have_selector("[id='#{line_code}']", visible: true, obscured: false)
+ end
+ end
+
context 'when merge request has overflow' do
it 'displays warning' do
allow(Commit).to receive(:max_diff_options).and_return(max_files: 3)
diff --git a/spec/features/snippets/search_snippets_spec.rb b/spec/features/snippets/search_snippets_spec.rb
index 69b9a0aa64d..d18729d080a 100644
--- a/spec/features/snippets/search_snippets_spec.rb
+++ b/spec/features/snippets/search_snippets_spec.rb
@@ -2,11 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Search Snippets' do
- before do
- stub_feature_flags(search_page_vertical_nav: false)
- end
-
+RSpec.describe 'Search Snippets', :js do
it 'user searches for snippets by title' do
public_snippet = create(:personal_snippet, :public, title: 'Beginning and Middle')
private_snippet = create(:personal_snippet, :private, title: 'Middle and End')
diff --git a/spec/frontend/diffs/store/actions_spec.js b/spec/frontend/diffs/store/actions_spec.js
index bf75f956d7f..87366cdbfc5 100644
--- a/spec/frontend/diffs/store/actions_spec.js
+++ b/spec/frontend/diffs/store/actions_spec.js
@@ -183,11 +183,11 @@ describe('DiffsStoreActions', () => {
beforeEach(() => {
delete noFilesData.diff_files;
-
- mock.onGet(endpointMetadata).reply(200, diffMetadata);
});
it('should fetch diff meta information', () => {
+ mock.onGet(endpointMetadata).reply(200, diffMetadata);
+
return testAction(
diffActions.fetchDiffFilesMeta,
{},
@@ -206,6 +206,40 @@ describe('DiffsStoreActions', () => {
[],
);
});
+
+ it('should show a warning on 404 reponse', async () => {
+ mock.onGet(endpointMetadata).reply(404);
+
+ await testAction(
+ diffActions.fetchDiffFilesMeta,
+ {},
+ { endpointMetadata, diffViewType: 'inline', showWhitespace: true },
+ [{ type: types.SET_LOADING, payload: true }],
+ [],
+ );
+
+ expect(createAlert).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalledWith({
+ message: expect.stringMatching(
+ 'Building your merge request. Wait a few moments, then refresh this page.',
+ ),
+ variant: 'warning',
+ });
+ });
+
+ it('should show no warning on any other status code', async () => {
+ mock.onGet(endpointMetadata).reply(500);
+
+ await testAction(
+ diffActions.fetchDiffFilesMeta,
+ {},
+ { endpointMetadata, diffViewType: 'inline', showWhitespace: true },
+ [{ type: types.SET_LOADING, payload: true }],
+ [],
+ );
+
+ expect(createAlert).not.toHaveBeenCalled();
+ });
});
describe('fetchCoverageFiles', () => {
diff --git a/spec/frontend/editor/schema/ci/ci_schema_spec.js b/spec/frontend/editor/schema/ci/ci_schema_spec.js
index 0eb08f0cf55..32126a5fd9a 100644
--- a/spec/frontend/editor/schema/ci/ci_schema_spec.js
+++ b/spec/frontend/editor/schema/ci/ci_schema_spec.js
@@ -18,9 +18,7 @@ import VariablesJson from './json_tests/positive_tests/variables.json';
import DefaultNoAdditionalPropertiesJson from './json_tests/negative_tests/default_no_additional_properties.json';
import InheritDefaultNoAdditionalPropertiesJson from './json_tests/negative_tests/inherit_default_no_additional_properties.json';
import JobVariablesMustNotContainObjectsJson from './json_tests/negative_tests/job_variables_must_not_contain_objects.json';
-import ReleaseAssetsLinksEmptyJson from './json_tests/negative_tests/release_assets_links_empty.json';
-import ReleaseAssetsLinksInvalidLinkTypeJson from './json_tests/negative_tests/release_assets_links_invalid_link_type.json';
-import ReleaseAssetsLinksMissingJson from './json_tests/negative_tests/release_assets_links_missing.json';
+import ReleaseAssetsLinksJson from './json_tests/negative_tests/release_assets_links.json';
import RetryUnknownWhenJson from './json_tests/negative_tests/retry_unknown_when.json';
// YAML POSITIVE TEST
@@ -35,38 +33,18 @@ import JobWhenYaml from './yaml_tests/positive_tests/job_when.yml';
// YAML NEGATIVE TEST
import ArtifactsNegativeYaml from './yaml_tests/negative_tests/artifacts.yml';
+import CacheKeyNeative from './yaml_tests/negative_tests/cache.yml';
import IncludeNegativeYaml from './yaml_tests/negative_tests/include.yml';
-import RulesNegativeYaml from './yaml_tests/negative_tests/rules.yml';
-import VariablesInvalidSyntaxDescYaml from './yaml_tests/negative_tests/variables/invalid_syntax_desc.yml';
-import VariablesWrongSyntaxUsageExpand from './yaml_tests/negative_tests/variables/wrong_syntax_usage_expand.yml';
import JobWhenNegativeYaml from './yaml_tests/negative_tests/job_when.yml';
-
import ProjectPathIncludeEmptyYaml from './yaml_tests/negative_tests/project_path/include/empty.yml';
import ProjectPathIncludeInvalidVariableYaml from './yaml_tests/negative_tests/project_path/include/invalid_variable.yml';
import ProjectPathIncludeLeadSlashYaml from './yaml_tests/negative_tests/project_path/include/leading_slash.yml';
import ProjectPathIncludeNoSlashYaml from './yaml_tests/negative_tests/project_path/include/no_slash.yml';
import ProjectPathIncludeTailSlashYaml from './yaml_tests/negative_tests/project_path/include/tailing_slash.yml';
-import ProjectPathTriggerIncludeYaml from './yaml_tests/negative_tests/project_path/trigger/trigger_include.yml';
-import ProjectPathTriggerMinimalEmptyYaml from './yaml_tests/negative_tests/project_path/trigger/minimal/empty.yml';
-import ProjectPathTriggerMinimalInvalidVariableYaml from './yaml_tests/negative_tests/project_path/trigger/minimal/invalid_variable.yml';
-import ProjectPathTriggerMinimalLeadSlashYaml from './yaml_tests/negative_tests/project_path/trigger/minimal/leading_slash.yml';
-import ProjectPathTriggerMinimalNoSlashYaml from './yaml_tests/negative_tests/project_path/trigger/minimal/no_slash.yml';
-import ProjectPathTriggerMinimalTailSlashYaml from './yaml_tests/negative_tests/project_path/trigger/minimal/tailing_slash.yml';
-import ProjectPathTriggerProjectEmptyYaml from './yaml_tests/negative_tests/project_path/trigger/project/empty.yml';
-import ProjectPathTriggerProjectInvalidVariableYaml from './yaml_tests/negative_tests/project_path/trigger/project/invalid_variable.yml';
-import ProjectPathTriggerProjectLeadSlashYaml from './yaml_tests/negative_tests/project_path/trigger/project/leading_slash.yml';
-import ProjectPathTriggerProjectNoSlashYaml from './yaml_tests/negative_tests/project_path/trigger/project/no_slash.yml';
-import ProjectPathTriggerProjectTailSlashYaml from './yaml_tests/negative_tests/project_path/trigger/project/tailing_slash.yml';
-
-import CacheKeyFilesNotArray from './yaml_tests/negative_tests/cache/key_files_not_an_array.yml';
-import CacheKeyPrefixArray from './yaml_tests/negative_tests/cache/key_prefix_array.yml';
-import CacheKeyWithDot from './yaml_tests/negative_tests/cache/key_with_dot.yml';
-import CacheKeyWithMultipleDots from './yaml_tests/negative_tests/cache/key_with_multiple_dots.yml';
-import CacheKeyWithSlash from './yaml_tests/negative_tests/cache/key_with_slash.yml';
-import CachePathsNotAnArray from './yaml_tests/negative_tests/cache/paths_not_an_array.yml';
-import CacheUntrackedString from './yaml_tests/negative_tests/cache/untracked_string.yml';
-import CacheWhenInteger from './yaml_tests/negative_tests/cache/when_integer.yml';
-import CacheWhenNotReservedKeyword from './yaml_tests/negative_tests/cache/when_not_reserved_keyword.yml';
+import RulesNegativeYaml from './yaml_tests/negative_tests/rules.yml';
+import TriggerNegative from './yaml_tests/negative_tests/trigger.yml';
+import VariablesInvalidSyntaxDescYaml from './yaml_tests/negative_tests/variables/invalid_syntax_desc.yml';
+import VariablesWrongSyntaxUsageExpand from './yaml_tests/negative_tests/variables/wrong_syntax_usage_expand.yml';
const ajv = new Ajv({
strictTypes: false,
@@ -119,22 +97,12 @@ describe('negative tests', () => {
DefaultNoAdditionalPropertiesJson,
JobVariablesMustNotContainObjectsJson,
InheritDefaultNoAdditionalPropertiesJson,
- ReleaseAssetsLinksEmptyJson,
- ReleaseAssetsLinksInvalidLinkTypeJson,
- ReleaseAssetsLinksMissingJson,
+ ReleaseAssetsLinksJson,
RetryUnknownWhenJson,
// YAML
ArtifactsNegativeYaml,
- CacheKeyFilesNotArray,
- CacheKeyPrefixArray,
- CacheKeyWithDot,
- CacheKeyWithMultipleDots,
- CacheKeyWithSlash,
- CachePathsNotAnArray,
- CacheUntrackedString,
- CacheWhenInteger,
- CacheWhenNotReservedKeyword,
+ CacheKeyNeative,
IncludeNegativeYaml,
JobWhenNegativeYaml,
RulesNegativeYaml,
@@ -145,17 +113,7 @@ describe('negative tests', () => {
ProjectPathIncludeLeadSlashYaml,
ProjectPathIncludeNoSlashYaml,
ProjectPathIncludeTailSlashYaml,
- ProjectPathTriggerIncludeYaml,
- ProjectPathTriggerMinimalEmptyYaml,
- ProjectPathTriggerMinimalInvalidVariableYaml,
- ProjectPathTriggerMinimalLeadSlashYaml,
- ProjectPathTriggerMinimalNoSlashYaml,
- ProjectPathTriggerMinimalTailSlashYaml,
- ProjectPathTriggerProjectEmptyYaml,
- ProjectPathTriggerProjectInvalidVariableYaml,
- ProjectPathTriggerProjectLeadSlashYaml,
- ProjectPathTriggerProjectNoSlashYaml,
- ProjectPathTriggerProjectTailSlashYaml,
+ TriggerNegative,
}),
)('schema validates %s', (_, input) => {
// We construct a new "JSON" from each main key that is inside a
diff --git a/spec/frontend/editor/schema/ci/json_tests/negative_tests/default_no_additional_properties.json b/spec/frontend/editor/schema/ci/json_tests/negative_tests/default_no_additional_properties.json
index 955c19ef1ab..d30bc4649ab 100644
--- a/spec/frontend/editor/schema/ci/json_tests/negative_tests/default_no_additional_properties.json
+++ b/spec/frontend/editor/schema/ci/json_tests/negative_tests/default_no_additional_properties.json
@@ -9,4 +9,4 @@
"name": "test"
}
}
-}
+} \ No newline at end of file
diff --git a/spec/frontend/editor/schema/ci/json_tests/negative_tests/inherit_default_no_additional_properties.json b/spec/frontend/editor/schema/ci/json_tests/negative_tests/inherit_default_no_additional_properties.json
index 7411e4c2434..1a31467f9ae 100644
--- a/spec/frontend/editor/schema/ci/json_tests/negative_tests/inherit_default_no_additional_properties.json
+++ b/spec/frontend/editor/schema/ci/json_tests/negative_tests/inherit_default_no_additional_properties.json
@@ -1,8 +1,10 @@
{
"karma": {
"inherit": {
- "default": ["secrets"]
+ "default": [
+ "secrets"
+ ]
},
"script": "karma"
}
-}
+} \ No newline at end of file
diff --git a/spec/frontend/editor/schema/ci/json_tests/negative_tests/job_variables_must_not_contain_objects.json b/spec/frontend/editor/schema/ci/json_tests/negative_tests/job_variables_must_not_contain_objects.json
index bfdbf26ee70..68dd57824ab 100644
--- a/spec/frontend/editor/schema/ci/json_tests/negative_tests/job_variables_must_not_contain_objects.json
+++ b/spec/frontend/editor/schema/ci/json_tests/negative_tests/job_variables_must_not_contain_objects.json
@@ -1,7 +1,9 @@
{
"gitlab-ci-variables-object": {
"stage": "test",
- "script": ["true"],
+ "script": [
+ "true"
+ ],
"variables": {
"DEPLOY_ENVIRONMENT": {
"value": "staging",
@@ -9,4 +11,4 @@
}
}
}
-}
+} \ No newline at end of file
diff --git a/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_invalid_link_type.json b/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links.json
index 048911aefa3..00b5b54c7e2 100644
--- a/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_invalid_link_type.json
+++ b/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links.json
@@ -1,4 +1,24 @@
{
+ "gitlab-ci-release-assets-links-missing": {
+ "script": "dostuff",
+ "stage": "deploy",
+ "release": {
+ "description": "Created using the release-cli $EXTRA_DESCRIPTION",
+ "tag_name": "$CI_COMMIT_TAG",
+ "assets": {}
+ }
+ },
+ "gitlab-ci-release-assets-links-empty": {
+ "script": "dostuff",
+ "stage": "deploy",
+ "release": {
+ "description": "Created using the release-cli $EXTRA_DESCRIPTION",
+ "tag_name": "$CI_COMMIT_TAG",
+ "assets": {
+ "links": []
+ }
+ }
+ },
"gitlab-ci-release-assets-links-invalid-link-type": {
"script": "dostuff",
"stage": "deploy",
@@ -21,4 +41,4 @@
}
}
}
-}
+} \ No newline at end of file
diff --git a/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_empty.json b/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_empty.json
deleted file mode 100644
index 84a1aa14698..00000000000
--- a/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_empty.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "gitlab-ci-release-assets-links-empty": {
- "script": "dostuff",
- "stage": "deploy",
- "release": {
- "description": "Created using the release-cli $EXTRA_DESCRIPTION",
- "tag_name": "$CI_COMMIT_TAG",
- "assets": {
- "links": []
- }
- }
- }
-}
diff --git a/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_missing.json b/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_missing.json
deleted file mode 100644
index 6f0b5a3bff8..00000000000
--- a/spec/frontend/editor/schema/ci/json_tests/negative_tests/release_assets_links_missing.json
+++ /dev/null
@@ -1,11 +0,0 @@
-{
- "gitlab-ci-release-assets-links-missing": {
- "script": "dostuff",
- "stage": "deploy",
- "release": {
- "description": "Created using the release-cli $EXTRA_DESCRIPTION",
- "tag_name": "$CI_COMMIT_TAG",
- "assets": {}
- }
- }
-}
diff --git a/spec/frontend/editor/schema/ci/json_tests/negative_tests/retry_unknown_when.json b/spec/frontend/editor/schema/ci/json_tests/negative_tests/retry_unknown_when.json
index 433504f52c6..2c53ce07109 100644
--- a/spec/frontend/editor/schema/ci/json_tests/negative_tests/retry_unknown_when.json
+++ b/spec/frontend/editor/schema/ci/json_tests/negative_tests/retry_unknown_when.json
@@ -6,4 +6,4 @@
"when": "gitlab-ci-retry-object-unknown-when"
}
}
-}
+} \ No newline at end of file
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml
new file mode 100644
index 00000000000..3979c9ae2ac
--- /dev/null
+++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache.yml
@@ -0,0 +1,62 @@
+cache-key-files-not-an-array:
+ script: echo "This job uses a cache."
+ cache:
+ key:
+ files: package.json
+ paths:
+ - vendor/ruby
+ - node_modules
+
+cache-key-prefix-array:
+ script: echo "This job uses a cache."
+ cache:
+ key:
+ files:
+ - Gemfile.lock
+ prefix:
+ - binaries-cache-$CI_JOB_NAME
+ paths:
+ - binaries/
+
+cache-key-with-.:
+ script: echo "This job uses a cache."
+ cache:
+ key: .
+ paths:
+ - binaries/
+
+cache-key-with-multiple-.:
+ stage: test
+ script: echo "This job uses a cache."
+ cache:
+ key: ..
+ paths:
+ - binaries/
+
+cache-key-with-/:
+ script: echo "This job uses a cache."
+ cache:
+ key: binaries-ca/che
+ paths:
+ - binaries/
+
+cache-path-not-an-array:
+ script: echo "This job uses a cache."
+ cache:
+ key: binaries-cache
+ paths: binaries/*.apk
+
+cache-untracked-string:
+ script: echo "This job uses a cache."
+ cache:
+ untracked: 'true'
+
+when_integer:
+ script: echo "This job uses a cache."
+ cache:
+ when: 0
+
+when_not_reserved_keyword:
+ script: echo "This job uses a cache."
+ cache:
+ when: 'never'
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_files_not_an_array.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_files_not_an_array.yml
deleted file mode 100644
index 64b927a9940..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_files_not_an_array.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-cache-key-files-not-an-array:
- script: echo "This job uses a cache."
- cache:
- key:
- files: package.json
- paths:
- - vendor/ruby
- - node_modules
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_prefix_array.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_prefix_array.yml
deleted file mode 100644
index 9024dfe6441..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_prefix_array.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-cache-key-prefix-array:
- script: echo "This job uses a cache."
- cache:
- key:
- files:
- - Gemfile.lock
- prefix:
- - binaries-cache-$CI_JOB_NAME
- paths:
- - binaries/
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_with_dot.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_with_dot.yml
deleted file mode 100644
index 7d21e5f4111..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_with_dot.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-cache-key-with-.:
- script: echo "This job uses a cache."
- cache:
- key: .
- paths:
- - binaries/
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_with_multiple_dots.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_with_multiple_dots.yml
deleted file mode 100644
index 1256be628d1..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_with_multiple_dots.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-cache-key-with-multiple-.:
- stage: test
- script: echo "This job uses a cache."
- cache:
- key: ..
- paths:
- - binaries/
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_with_slash.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_with_slash.yml
deleted file mode 100644
index ea6c0345bd4..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/key_with_slash.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-cache-key-with-/:
- script: echo "This job uses a cache."
- cache:
- key: binaries-ca/che
- paths:
- - binaries/
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/paths_not_an_array.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/paths_not_an_array.yml
deleted file mode 100644
index 26cc8d1935e..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/paths_not_an_array.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-cache-path-not-an-array:
- script: echo "This job uses a cache."
- cache:
- key: binaries-cache
- paths: binaries/*.apk
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/untracked_string.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/untracked_string.yml
deleted file mode 100644
index ed21e87f009..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/untracked_string.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-cache-untracked-string:
- script: echo "This job uses a cache."
- cache:
- untracked: 'true'
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/when_integer.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/when_integer.yml
deleted file mode 100644
index 5420bd9d0dd..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/when_integer.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-when_integer:
- script: echo "This job uses a cache."
- cache:
- when: 0
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/when_not_reserved_keyword.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/when_not_reserved_keyword.yml
deleted file mode 100644
index 2a6e204a6db..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/cache/when_not_reserved_keyword.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-when_not_reserved_keyword:
- script: echo "This job uses a cache."
- cache:
- when: 'never'
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/empty.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/empty.yml
deleted file mode 100644
index cad8dbbf430..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/empty.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-trigger-minimal:
- trigger: ''
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/invalid_variable.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/invalid_variable.yml
deleted file mode 100644
index 6ca37666d09..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/invalid_variable.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-trigger-minimal:
- trigger: 'slug#'
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/leading_slash.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/leading_slash.yml
deleted file mode 100644
index 9d7c6b44125..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/leading_slash.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-trigger-minimal:
- trigger: '/slug'
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/no_slash.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/no_slash.yml
deleted file mode 100644
index acd047477c8..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/no_slash.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-trigger-minimal:
- trigger: 'slug'
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/tailing_slash.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/tailing_slash.yml
deleted file mode 100644
index 0fdd00da3de..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/minimal/tailing_slash.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-trigger-minimal:
- trigger: 'slug/'
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/empty.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/empty.yml
deleted file mode 100644
index 0aa2330cecb..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/empty.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-trigger-project:
- trigger:
- project: ''
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/invalid_variable.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/invalid_variable.yml
deleted file mode 100644
index 3c17ec62039..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/invalid_variable.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-trigger-project:
- trigger:
- project: 'slug#'
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/leading_slash.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/leading_slash.yml
deleted file mode 100644
index f9884603171..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/leading_slash.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-trigger-project:
- trigger:
- project: '/slug'
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/no_slash.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/no_slash.yml
deleted file mode 100644
index d89e09756eb..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/no_slash.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-trigger-project:
- trigger:
- project: 'slug'
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/tailing_slash.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/tailing_slash.yml
deleted file mode 100644
index 3c39d6be4cb..00000000000
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/project/tailing_slash.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-trigger-project:
- trigger:
- project: 'slug/'
diff --git a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/trigger_include.yml b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/trigger.yml
index 6527db04a62..73cc82f2f1c 100644
--- a/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/project_path/trigger/trigger_include.yml
+++ b/spec/frontend/editor/schema/ci/yaml_tests/negative_tests/trigger.yml
@@ -1,3 +1,38 @@
+trigger-minimal-empty:
+ trigger: ''
+
+trigger-minimal-invalid-variable:
+ trigger: 'slug#'
+
+trigger-minimal-leading-slash:
+ trigger: '/slug'
+
+trigger-minimal-no-slash:
+ trigger: 'slug'
+
+trigger-minimal-trailing-slash:
+ trigger: 'slug/'
+
+trigger-project-empty:
+ trigger:
+ project: ''
+
+trigger-project-invalid-variable:
+ trigger:
+ project: 'slug#'
+
+trigger-project-leading-slash:
+ trigger:
+ project: '/slug'
+
+trigger-project-no-slash:
+ trigger:
+ project: 'slug'
+
+trigger-project-trailing-slash:
+ trigger:
+ project: 'slug/'
+
trigger-include-empty:
trigger:
include:
diff --git a/spec/frontend/packages_and_registries/package_registry/components/delete_modal_spec.js b/spec/frontend/packages_and_registries/package_registry/components/delete_modal_spec.js
new file mode 100644
index 00000000000..e0e26434680
--- /dev/null
+++ b/spec/frontend/packages_and_registries/package_registry/components/delete_modal_spec.js
@@ -0,0 +1,71 @@
+import { GlModal as RealGlModal } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { stubComponent } from 'helpers/stub_component';
+import DeleteModal from '~/packages_and_registries/package_registry/components/delete_modal.vue';
+
+const GlModal = stubComponent(RealGlModal, {
+ methods: {
+ show: jest.fn(),
+ },
+});
+
+describe('DeleteModal', () => {
+ let wrapper;
+
+ const defaultItemsToBeDeleted = [
+ {
+ name: 'package 01',
+ },
+ {
+ name: 'package 02',
+ },
+ ];
+
+ const findModal = () => wrapper.findComponent(GlModal);
+
+ const mountComponent = ({ itemsToBeDeleted = defaultItemsToBeDeleted } = {}) => {
+ wrapper = shallowMountExtended(DeleteModal, {
+ propsData: {
+ itemsToBeDeleted,
+ },
+ stubs: {
+ GlModal,
+ },
+ });
+ };
+
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ it('passes title prop', () => {
+ expect(findModal().props('title')).toMatchInterpolatedText('Delete packages');
+ });
+
+ it('passes actionPrimary prop', () => {
+ expect(findModal().props('actionPrimary')).toStrictEqual({
+ text: 'Permanently delete',
+ attributes: [{ variant: 'danger' }, { category: 'primary' }],
+ });
+ });
+
+ it('renders description', () => {
+ expect(findModal().text()).toContain(
+ 'You are about to delete 2 packages. This operation is irreversible.',
+ );
+ });
+
+ it('emits confirm when primary event is emitted', () => {
+ expect(wrapper.emitted('confirm')).toBeUndefined();
+
+ findModal().vm.$emit('primary');
+
+ expect(wrapper.emitted('confirm')).toHaveLength(1);
+ });
+
+ it('show calls gl-modal show', () => {
+ findModal().vm.show();
+
+ expect(GlModal.methods.show).toHaveBeenCalled();
+ });
+});
diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap b/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap
index 5be05ddf629..a7de751aadd 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap
+++ b/spec/frontend/packages_and_registries/package_registry/components/list/__snapshots__/package_list_row_spec.js.snap
@@ -8,7 +8,14 @@ exports[`packages_list_row renders 1`] = `
<div
class="gl-display-flex gl-align-items-center gl-py-3"
>
- <!---->
+ <div
+ class="gl-w-7 gl-display-flex gl-justify-content-start gl-pl-2"
+ >
+ <gl-form-checkbox-stub
+ class="gl-m-0"
+ id="2"
+ />
+ </div>
<div
class="gl-display-flex gl-xs-flex-direction-column gl-justify-content-space-between gl-align-items-stretch gl-flex-grow-1"
diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/package_list_row_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/package_list_row_spec.js
index b5a512b8806..913b4f5926f 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/list/package_list_row_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/list/package_list_row_spec.js
@@ -1,4 +1,4 @@
-import { GlSprintf } from '@gitlab/ui';
+import { GlFormCheckbox, GlSprintf } from '@gitlab/ui';
import Vue, { nextTick } from 'vue';
import VueRouter from 'vue-router';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
@@ -40,9 +40,11 @@ describe('packages_list_row', () => {
const findPublishMethod = () => wrapper.findComponent(PublishMethod);
const findCreatedDateText = () => wrapper.findByTestId('created-date');
const findTimeAgoTooltip = () => wrapper.findComponent(TimeagoTooltip);
+ const findBulkDeleteAction = () => wrapper.findComponent(GlFormCheckbox);
const mountComponent = ({
packageEntity = packageWithoutTags,
+ selected = false,
provide = defaultProvide,
} = {}) => {
wrapper = shallowMountExtended(PackagesListRow, {
@@ -53,6 +55,7 @@ describe('packages_list_row', () => {
},
propsData: {
packageEntity,
+ selected,
},
directives: {
GlTooltip: createMockDirective(),
@@ -117,14 +120,13 @@ describe('packages_list_row', () => {
});
});
- it('emits the packageToDelete event when the delete button is clicked', async () => {
+ it('emits the delete event when the delete button is clicked', async () => {
mountComponent({ packageEntity: packageWithoutTags });
findDeleteDropdown().vm.$emit('click');
await nextTick();
- expect(wrapper.emitted('packageToDelete')).toHaveLength(1);
- expect(wrapper.emitted('packageToDelete')[0]).toEqual([packageWithoutTags]);
+ expect(wrapper.emitted('delete')).toHaveLength(1);
});
});
@@ -151,6 +153,39 @@ describe('packages_list_row', () => {
});
});
+ describe('left action template', () => {
+ it('does not render checkbox if not permitted', () => {
+ mountComponent({
+ packageEntity: { ...packageWithoutTags, canDestroy: false },
+ });
+
+ expect(findBulkDeleteAction().exists()).toBe(false);
+ });
+
+ it('renders checkbox', () => {
+ mountComponent();
+
+ expect(findBulkDeleteAction().exists()).toBe(true);
+ expect(findBulkDeleteAction().attributes('checked')).toBeUndefined();
+ });
+
+ it('emits select when checked', () => {
+ mountComponent();
+
+ findBulkDeleteAction().vm.$emit('change');
+
+ expect(wrapper.emitted('select')).toHaveLength(1);
+ });
+
+ it('renders checkbox in selected state if selected', () => {
+ mountComponent({
+ selected: true,
+ });
+
+ expect(findBulkDeleteAction().attributes('checked')).toBe('true');
+ });
+ });
+
describe('secondary left info', () => {
it('has the package version', () => {
mountComponent();
diff --git a/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js b/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js
index c5b6b7da65b..7cc5bea0f7a 100644
--- a/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/components/list/packages_list_spec.js
@@ -99,10 +99,10 @@ describe('packages_list', () => {
it('shows the registry list with the right props', () => {
expect(findRegistryList().props()).toMatchObject({
+ title: '2 packages',
items: defaultProps.list,
pagination: defaultProps.pageInfo,
isLoading: false,
- hiddenDelete: true,
});
});
@@ -128,7 +128,7 @@ describe('packages_list', () => {
describe('when the user can destroy the package', () => {
beforeEach(async () => {
mountComponent();
- await findPackagesListRow().vm.$emit('packageToDelete', firstPackage);
+ await findPackagesListRow().vm.$emit('delete', firstPackage);
});
it('passes itemToBeDeleted to the modal', () => {
@@ -148,6 +148,27 @@ describe('packages_list', () => {
});
});
+ describe('when the user can bulk destroy packages', () => {
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ it('passes itemToBeDeleted to the modal when there is only one package', async () => {
+ await findRegistryList().vm.$emit('delete', [firstPackage]);
+
+ expect(findPackageListDeleteModal().props('itemToBeDeleted')).toStrictEqual(firstPackage);
+ expect(wrapper.emitted('delete')).toBeUndefined();
+ });
+
+ it('emits delete when there is more than one package', () => {
+ const items = [firstPackage, secondPackage];
+ findRegistryList().vm.$emit('delete', items);
+
+ expect(wrapper.emitted('delete')).toHaveLength(1);
+ expect(wrapper.emitted('delete')[0]).toEqual([items]);
+ });
+ });
+
describe('when an error package is present', () => {
beforeEach(() => {
mountComponent({ list: [firstPackage, errorPackage] });
@@ -210,7 +231,7 @@ describe('packages_list', () => {
beforeEach(() => {
eventSpy = jest.spyOn(Tracking, 'event');
mountComponent();
- findPackagesListRow().vm.$emit('packageToDelete', firstPackage);
+ findPackagesListRow().vm.$emit('delete', firstPackage);
return nextTick();
});
diff --git a/spec/frontend/packages_and_registries/package_registry/mock_data.js b/spec/frontend/packages_and_registries/package_registry/mock_data.js
index f247c83c85f..f36c5923532 100644
--- a/spec/frontend/packages_and_registries/package_registry/mock_data.js
+++ b/spec/frontend/packages_and_registries/package_registry/mock_data.js
@@ -294,6 +294,33 @@ export const packageDestroyMutation = () => ({
},
});
+export const packagesDestroyMutation = () => ({
+ data: {
+ destroyPackages: {
+ errors: [],
+ },
+ },
+});
+
+export const packagesDestroyMutationError = () => ({
+ data: {
+ destroyPackages: null,
+ },
+ errors: [
+ {
+ message:
+ "The resource that you are attempting to access does not exist or you don't have permission to perform this action",
+ locations: [
+ {
+ line: 2,
+ column: 3,
+ },
+ ],
+ path: ['destroyPackages'],
+ },
+ ],
+});
+
export const packageDestroyMutationError = () => ({
data: {
destroyPackage: null,
@@ -320,6 +347,7 @@ export const packageDestroyFilesMutation = () => ({
},
},
});
+
export const packageDestroyFilesMutationError = () => ({
data: {
destroyPackageFiles: null,
diff --git a/spec/frontend/packages_and_registries/package_registry/pages/__snapshots__/list_spec.js.snap b/spec/frontend/packages_and_registries/package_registry/pages/__snapshots__/list_spec.js.snap
index 7759c366796..c2fecf87428 100644
--- a/spec/frontend/packages_and_registries/package_registry/pages/__snapshots__/list_spec.js.snap
+++ b/spec/frontend/packages_and_registries/package_registry/pages/__snapshots__/list_spec.js.snap
@@ -2,6 +2,8 @@
exports[`PackagesListApp renders 1`] = `
<div>
+ <!---->
+
<gl-card-stub
bodyclass="gl-display-flex gl-p-0!"
class="gl-px-8 gl-py-6 gl-line-height-20 gl-mt-3"
@@ -53,7 +55,9 @@ exports[`PackagesListApp renders 1`] = `
helpurl="/help/user/packages/index"
/>
- <package-search-stub />
+ <package-search-stub
+ class="gl-mb-5"
+ />
<div>
<section
@@ -115,5 +119,7 @@ exports[`PackagesListApp renders 1`] = `
</div>
</section>
</div>
+
+ <div />
</div>
`;
diff --git a/spec/frontend/packages_and_registries/package_registry/pages/list_spec.js b/spec/frontend/packages_and_registries/package_registry/pages/list_spec.js
index 942eb0b3980..abdb875e839 100644
--- a/spec/frontend/packages_and_registries/package_registry/pages/list_spec.js
+++ b/spec/frontend/packages_and_registries/package_registry/pages/list_spec.js
@@ -1,4 +1,4 @@
-import { GlBanner, GlEmptyState, GlSprintf, GlLink } from '@gitlab/ui';
+import { GlAlert, GlBanner, GlEmptyState, GlSprintf, GlLink } from '@gitlab/ui';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
@@ -6,12 +6,13 @@ import * as utils from '~/lib/utils/common_utils';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
+import { stubComponent } from 'helpers/stub_component';
import ListPage from '~/packages_and_registries/package_registry/pages/list.vue';
import PackageTitle from '~/packages_and_registries/package_registry/components/list/package_title.vue';
import PackageSearch from '~/packages_and_registries/package_registry/components/list/package_search.vue';
import OriginalPackageList from '~/packages_and_registries/package_registry/components/list/packages_list.vue';
import DeletePackage from '~/packages_and_registries/package_registry/components/functional/delete_package.vue';
-
+import DeleteModal from '~/packages_and_registries/package_registry/components/delete_modal.vue';
import {
PROJECT_RESOURCE_TYPE,
GROUP_RESOURCE_TYPE,
@@ -19,11 +20,19 @@ import {
HIDE_PACKAGE_MIGRATION_SURVEY_COOKIE,
EMPTY_LIST_HELP_URL,
PACKAGE_HELP_URL,
+ DELETE_PACKAGES_ERROR_MESSAGE,
+ DELETE_PACKAGES_SUCCESS_MESSAGE,
} from '~/packages_and_registries/package_registry/constants';
import getPackagesQuery from '~/packages_and_registries/package_registry/graphql/queries/get_packages.query.graphql';
-
-import { packagesListQuery, packageData, pagination } from '../mock_data';
+import destroyPackagesMutation from '~/packages_and_registries/package_registry/graphql/mutations/destroy_packages.mutation.graphql';
+import {
+ packagesListQuery,
+ packageData,
+ pagination,
+ packagesDestroyMutation,
+ packagesDestroyMutationError,
+} from '../mock_data';
jest.mock('~/flash');
@@ -49,20 +58,26 @@ describe('PackagesListApp', () => {
filters: { packageName: 'foo', packageType: 'CONAN' },
};
+ const findAlert = () => wrapper.findComponent(GlAlert);
const findBanner = () => wrapper.findComponent(GlBanner);
const findPackageTitle = () => wrapper.findComponent(PackageTitle);
const findSearch = () => wrapper.findComponent(PackageSearch);
const findListComponent = () => wrapper.findComponent(PackageList);
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
const findDeletePackage = () => wrapper.findComponent(DeletePackage);
+ const findDeletePackagesModal = () => wrapper.findComponent(DeleteModal);
const mountComponent = ({
resolver = jest.fn().mockResolvedValue(packagesListQuery()),
+ mutationResolver,
provide = defaultProvide,
} = {}) => {
Vue.use(VueApollo);
- const requestHandlers = [[getPackagesQuery, resolver]];
+ const requestHandlers = [
+ [getPackagesQuery, resolver],
+ [destroyPackagesMutation, mutationResolver],
+ ];
apolloProvider = createMockApollo(requestHandlers);
wrapper = shallowMountExtended(ListPage, {
@@ -76,6 +91,11 @@ describe('PackagesListApp', () => {
GlLink,
PackageList,
DeletePackage,
+ DeleteModal: stubComponent(DeleteModal, {
+ methods: {
+ show: jest.fn(),
+ },
+ }),
},
});
};
@@ -348,4 +368,62 @@ describe('PackagesListApp', () => {
expect(findListComponent().props('isLoading')).toBe(false);
});
});
+
+ describe('bulk delete package', () => {
+ const items = [{ id: '1' }, { id: '2' }];
+
+ it('deletePackage is bound to package-list package:delete event', async () => {
+ mountComponent();
+
+ await waitForFirstRequest();
+
+ findListComponent().vm.$emit('delete', [{ id: '1' }, { id: '2' }]);
+
+ await waitForPromises();
+
+ expect(findDeletePackagesModal().props('itemsToBeDeleted')).toEqual(items);
+ });
+
+ it('calls mutation with the right values and shows success alert', async () => {
+ const mutationResolver = jest.fn().mockResolvedValue(packagesDestroyMutation());
+ mountComponent({
+ mutationResolver,
+ });
+
+ await waitForFirstRequest();
+
+ findListComponent().vm.$emit('delete', items);
+
+ findDeletePackagesModal().vm.$emit('confirm');
+
+ expect(mutationResolver).toHaveBeenCalledWith({
+ ids: items.map((item) => item.id),
+ });
+
+ await waitForPromises();
+
+ expect(findAlert().exists()).toBe(true);
+ expect(findAlert().props('variant')).toEqual('success');
+ expect(findAlert().text()).toMatchInterpolatedText(DELETE_PACKAGES_SUCCESS_MESSAGE);
+ });
+
+ it('on error shows danger alert', async () => {
+ const mutationResolver = jest.fn().mockResolvedValue(packagesDestroyMutationError());
+ mountComponent({
+ mutationResolver,
+ });
+
+ await waitForFirstRequest();
+
+ findListComponent().vm.$emit('delete', items);
+
+ findDeletePackagesModal().vm.$emit('confirm');
+
+ await waitForPromises();
+
+ expect(findAlert().exists()).toBe(true);
+ expect(findAlert().props('variant')).toEqual('danger');
+ expect(findAlert().text()).toMatchInterpolatedText(DELETE_PACKAGES_ERROR_MESSAGE);
+ });
+ });
});
diff --git a/spec/graphql/mutations/commits/create_spec.rb b/spec/graphql/mutations/commits/create_spec.rb
index fd0c2c46b2e..2c452410cca 100644
--- a/spec/graphql/mutations/commits/create_spec.rb
+++ b/spec/graphql/mutations/commits/create_spec.rb
@@ -179,7 +179,7 @@ RSpec.describe Mutations::Commits::Create do
it 'returns errors' do
expect(mutated_commit).to be_nil
- expect(subject[:errors].to_s).to match(/3:UserCommitFiles: empty CommitMessage/)
+ expect(subject[:errors].to_s).to match(/empty CommitMessage/)
end
end
diff --git a/spec/initializers/memory_watchdog_spec.rb b/spec/initializers/memory_watchdog_spec.rb
index 5c3d020016a..92834c889c2 100644
--- a/spec/initializers/memory_watchdog_spec.rb
+++ b/spec/initializers/memory_watchdog_spec.rb
@@ -3,6 +3,24 @@
require 'fast_spec_helper'
RSpec.describe 'memory watchdog' do
+ shared_examples 'starts configured watchdog' do |configure_monitor_method|
+ shared_examples 'configures and starts watchdog' do
+ it "correctly configures and starts watchdog", :aggregate_failures do
+ expect(Gitlab::Memory::Watchdog::Configurator).to receive(configure_monitor_method)
+
+ expect(Gitlab::Memory::Watchdog).to receive(:new).and_return(watchdog)
+ expect(Gitlab::BackgroundTask).to receive(:new).with(watchdog).and_return(background_task)
+ expect(background_task).to receive(:start)
+ expect(Gitlab::Cluster::LifecycleEvents).to receive(:on_worker_start).and_yield
+
+ run_initializer
+ end
+ end
+ end
+
+ let(:watchdog) { instance_double(Gitlab::Memory::Watchdog) }
+ let(:background_task) { instance_double(Gitlab::BackgroundTask) }
+
subject(:run_initializer) do
load rails_root_join('config/initializers/memory_watchdog.rb')
end
@@ -15,10 +33,6 @@ RSpec.describe 'memory watchdog' do
end
context 'when runtime is an application' do
- let(:watchdog) { instance_double(Gitlab::Memory::Watchdog) }
- let(:background_task) { instance_double(Gitlab::BackgroundTask) }
- let(:logger) { Gitlab::AppLogger }
-
before do
allow(Gitlab::Runtime).to receive(:application?).and_return(true)
end
@@ -29,21 +43,6 @@ RSpec.describe 'memory watchdog' do
run_initializer
end
- shared_examples 'starts configured watchdog' do |configure_monitor_method|
- shared_examples 'configures and starts watchdog' do
- it "correctly configures and starts watchdog", :aggregate_failures do
- expect(Gitlab::Memory::Watchdog::Configurator).to receive(configure_monitor_method)
-
- expect(Gitlab::Memory::Watchdog).to receive(:new).and_return(watchdog)
- expect(Gitlab::BackgroundTask).to receive(:new).with(watchdog).and_return(background_task)
- expect(background_task).to receive(:start)
- expect(Gitlab::Cluster::LifecycleEvents).to receive(:on_worker_start).and_yield
-
- run_initializer
- end
- end
- end
-
context 'when puma' do
before do
allow(Gitlab::Runtime).to receive(:puma?).and_return(true)
@@ -92,10 +91,24 @@ RSpec.describe 'memory watchdog' do
allow(Gitlab::Runtime).to receive(:application?).and_return(true)
end
- it 'does not register life-cycle hook' do
- expect(Gitlab::Cluster::LifecycleEvents).not_to receive(:on_worker_start)
+ context 'when puma' do
+ before do
+ allow(Gitlab::Runtime).to receive(:puma?).and_return(true)
+ end
- run_initializer
+ it_behaves_like 'starts configured watchdog', :configure_for_puma
+ end
+
+ context 'when sidekiq' do
+ before do
+ allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true)
+ end
+
+ it 'does not register life-cycle hook' do
+ expect(Gitlab::Cluster::LifecycleEvents).not_to receive(:on_worker_start)
+
+ run_initializer
+ end
end
end
end
diff --git a/spec/lib/gitlab/cluster/puma_worker_killer_initializer_spec.rb b/spec/lib/gitlab/cluster/puma_worker_killer_initializer_spec.rb
new file mode 100644
index 00000000000..cb13a711857
--- /dev/null
+++ b/spec/lib/gitlab/cluster/puma_worker_killer_initializer_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'puma_worker_killer'
+
+RSpec.describe Gitlab::Cluster::PumaWorkerKillerInitializer do
+ describe '.start' do
+ context 'when GITLAB_MEMORY_WATCHDOG_ENABLED is false' do
+ before do
+ stub_env('GITLAB_MEMORY_WATCHDOG_ENABLED', 'false')
+ end
+
+ it 'configures and start PumaWorkerKiller' do
+ expect(PumaWorkerKiller).to receive(:config)
+ expect(PumaWorkerKiller).to receive(:start)
+
+ described_class.start({})
+ end
+ end
+
+ context 'when GITLAB_MEMORY_WATCHDOG_ENABLED is not set' do
+ it 'configures and start PumaWorkerKiller' do
+ expect(PumaWorkerKiller).not_to receive(:config)
+ expect(PumaWorkerKiller).not_to receive(:start)
+
+ described_class.start({})
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/load_balancing/transaction_leaking_spec.rb b/spec/lib/gitlab/database/load_balancing/transaction_leaking_spec.rb
index cd49544125d..1eb077fe6ca 100644
--- a/spec/lib/gitlab/database/load_balancing/transaction_leaking_spec.rb
+++ b/spec/lib/gitlab/database/load_balancing/transaction_leaking_spec.rb
@@ -60,6 +60,7 @@ RSpec.describe 'Load balancer behavior with errors inside a transaction', :redis
conn = model.connection
expect(::Gitlab::Database::LoadBalancing::Logger).to receive(:warn).with(hash_including(event: :transaction_leak))
+ expect(::Gitlab::Database::LoadBalancing::Logger).to receive(:warn).with(hash_including(event: :read_write_retry))
conn.transaction do
expect(conn).to be_transaction_open
@@ -78,6 +79,8 @@ RSpec.describe 'Load balancer behavior with errors inside a transaction', :redis
expect(::Gitlab::Database::LoadBalancing::Logger)
.not_to receive(:warn).with(hash_including(event: :transaction_leak))
+ expect(::Gitlab::Database::LoadBalancing::Logger)
+ .to receive(:warn).with(hash_including(event: :read_write_retry))
expect(conn).not_to be_transaction_open
@@ -109,6 +112,8 @@ RSpec.describe 'Load balancer behavior with errors inside a transaction', :redis
it 'retries when not in a transaction' do
expect(::Gitlab::Database::LoadBalancing::Logger)
.not_to receive(:warn).with(hash_including(event: :transaction_leak))
+ expect(::Gitlab::Database::LoadBalancing::Logger)
+ .to receive(:warn).with(hash_including(event: :read_write_retry))
expect { execute(model.connection) }.not_to raise_error
end
diff --git a/spec/lib/gitlab/github_import/importer/events/changed_label_spec.rb b/spec/lib/gitlab/github_import/importer/events/changed_label_spec.rb
index 4476b4123ee..6a409762599 100644
--- a/spec/lib/gitlab/github_import/importer/events/changed_label_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/events/changed_label_spec.rb
@@ -10,7 +10,9 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedLabel do
let(:client) { instance_double('Gitlab::GithubImport::Client') }
let(:issuable) { create(:issue, project: project) }
- let!(:label) { create(:label, project: project) }
+ let(:label) { create(:label, project: project) }
+ let(:label_title) { label.title }
+ let(:label_id) { label.id }
let(:issue_event) do
Gitlab::GithubImport::Representation::IssueEvent.from_json_hash(
@@ -18,7 +20,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedLabel do
'actor' => { 'id' => user.id, 'login' => user.username },
'event' => event_type,
'commit_id' => nil,
- 'label_title' => label.title,
+ 'label_title' => label_title,
'created_at' => '2022-04-26 18:30:53 UTC',
'issue' => { 'number' => issuable.iid, pull_request: issuable.is_a?(MergeRequest) }
)
@@ -27,7 +29,7 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedLabel do
let(:event_attrs) do
{
user_id: user.id,
- label_id: label.id,
+ label_id: label_id,
created_at: issue_event.created_at
}.stringify_keys
end
@@ -42,7 +44,6 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedLabel do
end
before do
- allow(Gitlab::Cache::Import::Caching).to receive(:read_integer).and_return(label.id)
allow_next_instance_of(Gitlab::GithubImport::IssuableFinder) do |finder|
allow(finder).to receive(:database_id).and_return(issuable.id)
end
@@ -52,16 +53,35 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedLabel do
end
context 'with Issue' do
- context 'when importing a labeled event' do
- let(:event_type) { 'labeled' }
- let(:expected_event_attrs) { event_attrs.merge(issue_id: issuable.id, action: 'add') }
+ context 'when importing event with associated label' do
+ before do
+ allow(Gitlab::Cache::Import::Caching).to receive(:read_integer).and_return(label.id)
+ end
- it_behaves_like 'new event'
+ context 'when importing a labeled event' do
+ let(:event_type) { 'labeled' }
+ let(:expected_event_attrs) { event_attrs.merge(issue_id: issuable.id, action: 'add') }
+
+ it_behaves_like 'new event'
+ end
+
+ context 'when importing an unlabeled event' do
+ let(:event_type) { 'unlabeled' }
+ let(:expected_event_attrs) { event_attrs.merge(issue_id: issuable.id, action: 'remove') }
+
+ it_behaves_like 'new event'
+ end
end
- context 'when importing an unlabeled event' do
- let(:event_type) { 'unlabeled' }
- let(:expected_event_attrs) { event_attrs.merge(issue_id: issuable.id, action: 'remove') }
+ context 'when importing event without associated label' do
+ before do
+ allow(Gitlab::Cache::Import::Caching).to receive(:read_integer).and_return(nil)
+ end
+
+ let(:label_title) { 'deleted_label' }
+ let(:label_id) { nil }
+ let(:event_type) { 'labeled' }
+ let(:expected_event_attrs) { event_attrs.merge(issue_id: issuable.id, action: 'add') }
it_behaves_like 'new event'
end
@@ -70,16 +90,35 @@ RSpec.describe Gitlab::GithubImport::Importer::Events::ChangedLabel do
context 'with MergeRequest' do
let(:issuable) { create(:merge_request, source_project: project, target_project: project) }
- context 'when importing a labeled event' do
- let(:event_type) { 'labeled' }
- let(:expected_event_attrs) { event_attrs.merge(merge_request_id: issuable.id, action: 'add') }
+ context 'when importing event with associated label' do
+ before do
+ allow(Gitlab::Cache::Import::Caching).to receive(:read_integer).and_return(label.id)
+ end
- it_behaves_like 'new event'
+ context 'when importing a labeled event' do
+ let(:event_type) { 'labeled' }
+ let(:expected_event_attrs) { event_attrs.merge(merge_request_id: issuable.id, action: 'add') }
+
+ it_behaves_like 'new event'
+ end
+
+ context 'when importing an unlabeled event' do
+ let(:event_type) { 'unlabeled' }
+ let(:expected_event_attrs) { event_attrs.merge(merge_request_id: issuable.id, action: 'remove') }
+
+ it_behaves_like 'new event'
+ end
end
- context 'when importing an unlabeled event' do
- let(:event_type) { 'unlabeled' }
- let(:expected_event_attrs) { event_attrs.merge(merge_request_id: issuable.id, action: 'remove') }
+ context 'when importing event without associated label' do
+ before do
+ allow(Gitlab::Cache::Import::Caching).to receive(:read_integer).and_return(nil)
+ end
+
+ let(:label_title) { 'deleted_label' }
+ let(:label_id) { nil }
+ let(:event_type) { 'labeled' }
+ let(:expected_event_attrs) { event_attrs.merge(merge_request_id: issuable.id, action: 'add') }
it_behaves_like 'new event'
end
diff --git a/spec/lib/gitlab/memory/watchdog/configurator_spec.rb b/spec/lib/gitlab/memory/watchdog/configurator_spec.rb
index 2c5fae5736d..e6f2d57e9e6 100644
--- a/spec/lib/gitlab/memory/watchdog/configurator_spec.rb
+++ b/spec/lib/gitlab/memory/watchdog/configurator_spec.rb
@@ -2,6 +2,7 @@
require 'fast_spec_helper'
require 'prometheus/client'
+require 'sidekiq'
require_dependency 'gitlab/cluster/lifecycle_events'
RSpec.describe Gitlab::Memory::Watchdog::Configurator do
@@ -184,4 +185,15 @@ RSpec.describe Gitlab::Memory::Watchdog::Configurator do
end
end
# rubocop: enable RSpec/VerifiedDoubles
+
+ describe '.configure_for_sidekiq' do
+ let(:logger) { ::Sidekiq.logger }
+
+ subject(:configurator) { described_class.configure_for_sidekiq }
+
+ it_behaves_like 'as configurator',
+ Gitlab::Memory::Watchdog::TermProcessHandler,
+ 'SIDEKIQ_MEMORY_KILLER_CHECK_INTERVAL',
+ 3
+ end
end
diff --git a/spec/models/factories_spec.rb b/spec/models/factories_spec.rb
index 072f5496bca..65b993cca7f 100644
--- a/spec/models/factories_spec.rb
+++ b/spec/models/factories_spec.rb
@@ -22,6 +22,7 @@ RSpec.describe 'factories', :saas do
[:ci_job_artifact, :raw],
[:ci_job_artifact, :gzip],
[:ci_job_artifact, :correct_checksum],
+ [:dependency_proxy_blob, :remote_store],
[:environment, :non_playable],
[:composer_cache_file, :object_storage],
[:debian_project_component_file, :object_storage],
@@ -50,6 +51,7 @@ RSpec.describe 'factories', :saas do
[:ee_ci_job_artifact, :v2],
[:ee_ci_job_artifact, :v2_1],
[:geo_ci_secure_file_state, any],
+ [:geo_dependency_proxy_blob_state, any],
[:geo_event_log, :geo_event],
[:geo_job_artifact_state, any],
[:geo_lfs_object_state, any],
diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb
index 5edea13afa4..26def474b88 100644
--- a/spec/services/search_service_spec.rb
+++ b/spec/services/search_service_spec.rb
@@ -399,159 +399,7 @@ RSpec.describe SearchService do
end
end
- context 'redacting search results' do
- let(:search) { 'anything' }
-
- subject(:result) { search_service.search_objects }
-
- shared_examples "redaction limits N+1 queries" do |limit:|
- it 'does not exceed the query limit' do
- # issuing the query to remove the data loading call
- unredacted_results.to_a
-
- # only the calls from the redaction are left
- query = ActiveRecord::QueryRecorder.new { result }
-
- # these are the project authorization calls, which are not preloaded
- expect(query.count).to be <= limit
- end
- end
-
- def found_blob(project)
- Gitlab::Search::FoundBlob.new(project: project)
- end
-
- def found_wiki_page(project)
- Gitlab::Search::FoundWikiPage.new(found_blob(project))
- end
-
- before do
- expect(search_service)
- .to receive(:search_results)
- .and_return(double('search results', objects: unredacted_results))
- end
-
- def ar_relation(klass, *objects)
- klass.id_in(objects.map(&:id))
- end
-
- def kaminari_array(*objects)
- Kaminari.paginate_array(objects).page(1).per(20)
- end
-
- context 'issues' do
- let(:readable) { create(:issue, project: accessible_project) }
- let(:unreadable) { create(:issue, project: inaccessible_project) }
- let(:unredacted_results) { ar_relation(Issue, readable, unreadable) }
- let(:scope) { 'issues' }
-
- it 'redacts the inaccessible issue' do
- expect(result).to contain_exactly(readable)
- end
- end
-
- context 'notes' do
- let(:readable) { create(:note_on_commit, project: accessible_project) }
- let(:unreadable) { create(:note_on_commit, project: inaccessible_project) }
- let(:unredacted_results) { ar_relation(Note, readable, unreadable) }
- let(:scope) { 'notes' }
-
- it 'redacts the inaccessible note' do
- expect(result).to contain_exactly(readable)
- end
- end
-
- context 'merge_requests' do
- let(:readable) { create(:merge_request, source_project: accessible_project, author: user) }
- let(:unreadable) { create(:merge_request, source_project: inaccessible_project) }
- let(:unredacted_results) { ar_relation(MergeRequest, readable, unreadable) }
- let(:scope) { 'merge_requests' }
-
- it 'redacts the inaccessible merge request' do
- expect(result).to contain_exactly(readable)
- end
-
- context 'with :with_api_entity_associations' do
- let(:unredacted_results) { ar_relation(MergeRequest.with_api_entity_associations, readable, unreadable) }
-
- it_behaves_like "redaction limits N+1 queries", limit: 8
- end
- end
-
- context 'project repository blobs' do
- let(:readable) { found_blob(accessible_project) }
- let(:unreadable) { found_blob(inaccessible_project) }
- let(:unredacted_results) { kaminari_array(readable, unreadable) }
- let(:scope) { 'blobs' }
-
- it 'redacts the inaccessible blob' do
- expect(result).to contain_exactly(readable)
- end
- end
-
- context 'project wiki blobs' do
- let(:readable) { found_wiki_page(accessible_project) }
- let(:unreadable) { found_wiki_page(inaccessible_project) }
- let(:unredacted_results) { kaminari_array(readable, unreadable) }
- let(:scope) { 'wiki_blobs' }
-
- it 'redacts the inaccessible blob' do
- expect(result).to contain_exactly(readable)
- end
- end
-
- context 'project snippets' do
- let(:readable) { create(:project_snippet, project: accessible_project) }
- let(:unreadable) { create(:project_snippet, project: inaccessible_project) }
- let(:unredacted_results) { ar_relation(ProjectSnippet, readable, unreadable) }
- let(:scope) { 'snippet_titles' }
-
- it 'redacts the inaccessible snippet' do
- expect(result).to contain_exactly(readable)
- end
-
- context 'with :with_api_entity_associations' do
- it_behaves_like "redaction limits N+1 queries", limit: 14
- end
- end
-
- context 'personal snippets' do
- let(:readable) { create(:personal_snippet, :private, author: user) }
- let(:unreadable) { create(:personal_snippet, :private) }
- let(:unredacted_results) { ar_relation(PersonalSnippet, readable, unreadable) }
- let(:scope) { 'snippet_titles' }
-
- it 'redacts the inaccessible snippet' do
- expect(result).to contain_exactly(readable)
- end
-
- context 'with :with_api_entity_associations' do
- it_behaves_like "redaction limits N+1 queries", limit: 4
- end
- end
-
- context 'commits' do
- let(:readable) { accessible_project.commit }
- let(:unreadable) { inaccessible_project.commit }
- let(:unredacted_results) { kaminari_array(readable, unreadable) }
- let(:scope) { 'commits' }
-
- it 'redacts the inaccessible commit' do
- expect(result).to contain_exactly(readable)
- end
- end
-
- context 'users' do
- let(:other_user) { create(:user) }
- let(:unredacted_results) { ar_relation(User, user, other_user) }
- let(:scope) { 'users' }
-
- it 'passes the users through' do
- # Users are always visible to everyone
- expect(result).to contain_exactly(user, other_user)
- end
- end
- end
+ it_behaves_like 'a redacted search results'
end
describe '#valid_request?' do
diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb
index f41457d2420..24c768258a1 100644
--- a/spec/support/helpers/stub_configuration.rb
+++ b/spec/support/helpers/stub_configuration.rb
@@ -38,6 +38,10 @@ module StubConfiguration
allow(Rails.application.routes).to receive(:default_url_options).and_return(url_options)
end
+ def stub_dependency_proxy_setting(messages)
+ allow(Gitlab.config.dependency_proxy).to receive_messages(to_settings(messages))
+ end
+
def stub_gravatar_setting(messages)
allow(Gitlab.config.gravatar).to receive_messages(to_settings(messages))
end
diff --git a/spec/support/shared_examples/features/search/redacted_search_results_shared_examples.rb b/spec/support/shared_examples/features/search/redacted_search_results_shared_examples.rb
new file mode 100644
index 00000000000..4d242d0e719
--- /dev/null
+++ b/spec/support/shared_examples/features/search/redacted_search_results_shared_examples.rb
@@ -0,0 +1,304 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'a redacted search results' do
+ let_it_be(:user) { create(:user) }
+
+ let_it_be(:accessible_group) { create(:group, :private) }
+ let_it_be(:accessible_project) { create(:project, :repository, :private, name: 'accessible_project') }
+
+ let_it_be(:group_member) { create(:group_member, group: accessible_group, user: user) }
+
+ let_it_be(:inaccessible_group) { create(:group, :private) }
+ let_it_be(:inaccessible_project) { create(:project, :repository, :private, name: 'inaccessible_project') }
+
+ let(:search) { 'anything' }
+
+ subject(:result) { search_service.search_objects }
+
+ def found_blob(project)
+ Gitlab::Search::FoundBlob.new(project: project)
+ end
+
+ def found_wiki_page(project)
+ Gitlab::Search::FoundWikiPage.new(found_blob(project))
+ end
+
+ def ar_relation(klass, *objects)
+ klass.id_in(objects.map(&:id))
+ end
+
+ def kaminari_array(*objects)
+ Kaminari.paginate_array(objects).page(1).per(20)
+ end
+
+ before do
+ accessible_project.add_maintainer(user)
+
+ allow(search_service)
+ .to receive_message_chain(:search_results, :objects)
+ .and_return(unredacted_results)
+ end
+
+ context 'for issues' do
+ let(:readable) { create(:issue, project: accessible_project) }
+ let(:unreadable) { create(:issue, project: inaccessible_project) }
+ let(:unredacted_results) { ar_relation(Issue, readable, unreadable) }
+ let(:scope) { 'issues' }
+
+ it 'redacts the inaccessible issue' do
+ expect(search_service.send(:logger))
+ .to receive(:error)
+ .with(hash_including(
+ message: "redacted_search_results",
+ current_user_id: user.id,
+ query: search,
+ filtered: array_including(
+ [
+ { class_name: 'Issue', id: unreadable.id, ability: :read_issue }
+ ])))
+
+ expect(result).to contain_exactly(readable)
+ end
+ end
+
+ context 'for notes' do
+ let(:readable_merge_request) do
+ create(:merge_request_with_diffs, target_project: accessible_project, source_project: accessible_project)
+ end
+
+ let(:readable_note_on_commit) { create(:note_on_commit, project: accessible_project) }
+ let(:readable_diff_note) { create(:diff_note_on_commit, project: accessible_project) }
+ let(:readable_note_on_mr) do
+ create(:discussion_note_on_merge_request, noteable: readable_merge_request, project: accessible_project)
+ end
+
+ let(:readable_diff_note_on_mr) do
+ create(:diff_note_on_merge_request, noteable: readable_merge_request, project: accessible_project)
+ end
+
+ let(:readable_note_on_project_snippet) do
+ create(:note_on_project_snippet, noteable: readable_merge_request, project: accessible_project)
+ end
+
+ let(:unreadable_merge_request) do
+ create(:merge_request_with_diffs, target_project: inaccessible_project, source_project: inaccessible_project)
+ end
+
+ let(:unreadable_note_on_commit) { create(:note_on_commit, project: inaccessible_project) }
+ let(:unreadable_diff_note) { create(:diff_note_on_commit, project: inaccessible_project) }
+ let(:unreadable_note_on_mr) do
+ create(:discussion_note_on_merge_request, noteable: unreadable_merge_request, project: inaccessible_project)
+ end
+
+ let(:unreadable_note_on_project_snippet) do
+ create(:note_on_project_snippet, noteable: unreadable_merge_request, project: inaccessible_project)
+ end
+
+ let(:unredacted_results) do
+ ar_relation(Note,
+ readable_note_on_commit,
+ readable_diff_note,
+ readable_note_on_mr,
+ readable_diff_note_on_mr,
+ readable_note_on_project_snippet,
+ unreadable_note_on_commit,
+ unreadable_diff_note,
+ unreadable_note_on_mr,
+ unreadable_note_on_project_snippet)
+ end
+
+ let(:scope) { 'notes' }
+
+ it 'redacts the inaccessible notes' do
+ expect(search_service.send(:logger))
+ .to receive(:error)
+ .with(hash_including(
+ message: "redacted_search_results",
+ current_user_id: user.id,
+ query: search,
+ filtered: array_including(
+ [
+ { class_name: 'Note', id: unreadable_note_on_commit.id, ability: :read_note },
+ { class_name: 'DiffNote', id: unreadable_diff_note.id, ability: :read_note },
+ { class_name: 'DiscussionNote', id: unreadable_note_on_mr.id, ability: :read_note },
+ { class_name: 'Note', id: unreadable_note_on_project_snippet.id, ability: :read_note }
+ ])))
+
+ expect(result).to contain_exactly(readable_note_on_commit,
+ readable_diff_note,
+ readable_note_on_mr,
+ readable_diff_note_on_mr,
+ readable_note_on_project_snippet)
+ end
+ end
+
+ context 'for merge_requests' do
+ let(:readable) { create(:merge_request, source_project: accessible_project) }
+ let(:unreadable) { create(:merge_request, source_project: inaccessible_project) }
+ let(:unredacted_results) { ar_relation(MergeRequest, readable, unreadable) }
+ let(:scope) { 'merge_requests' }
+
+ it 'redacts the inaccessible merge request' do
+ expect(search_service.send(:logger))
+ .to receive(:error)
+ .with(hash_including(
+ message: "redacted_search_results",
+ current_user_id: user.id,
+ query: search,
+ filtered: array_including(
+ [
+ { class_name: 'MergeRequest', id: unreadable.id, ability: :read_merge_request }
+ ])))
+
+ expect(result).to contain_exactly(readable)
+ end
+
+ context 'with :with_api_entity_associations' do
+ let(:unredacted_results) { ar_relation(MergeRequest.with_api_entity_associations, readable, unreadable) }
+
+ it_behaves_like "redaction limits N+1 queries", limit: 8
+ end
+ end
+
+ context 'for blobs' do
+ let(:readable) { found_blob(accessible_project) }
+ let(:unreadable) { found_blob(inaccessible_project) }
+ let(:unredacted_results) { kaminari_array(readable, unreadable) }
+ let(:scope) { 'blobs' }
+
+ it 'redacts the inaccessible blob' do
+ expect(search_service.send(:logger))
+ .to receive(:error)
+ .with(hash_including(
+ message: "redacted_search_results",
+ current_user_id: user.id,
+ query: search,
+ filtered: array_including(
+ [
+ { class_name: 'Gitlab::Search::FoundBlob', id: unreadable.id, ability: :read_blob }
+ ])))
+
+ expect(result).to contain_exactly(readable)
+ end
+ end
+
+ context 'for wiki blobs' do
+ let(:readable) { found_wiki_page(accessible_project) }
+ let(:unreadable) { found_wiki_page(inaccessible_project) }
+ let(:unredacted_results) { kaminari_array(readable, unreadable) }
+ let(:scope) { 'wiki_blobs' }
+
+ it 'redacts the inaccessible blob' do
+ expect(search_service.send(:logger))
+ .to receive(:error)
+ .with(hash_including(
+ message: "redacted_search_results",
+ current_user_id: user.id,
+ query: search,
+ filtered: array_including(
+ [
+ { class_name: 'Gitlab::Search::FoundWikiPage', id: unreadable.id, ability: :read_wiki_page }
+ ])))
+
+ expect(result).to contain_exactly(readable)
+ end
+ end
+
+ context 'for project snippets' do
+ let(:readable) { create(:project_snippet, project: accessible_project) }
+ let(:unreadable) { create(:project_snippet, project: inaccessible_project) }
+ let(:unredacted_results) { ar_relation(ProjectSnippet, readable, unreadable) }
+ let(:scope) { 'snippet_titles' }
+
+ it 'redacts the inaccessible snippet' do
+ expect(search_service.send(:logger))
+ .to receive(:error)
+ .with(hash_including(
+ message: "redacted_search_results",
+ current_user_id: user.id,
+ query: search,
+ filtered: array_including(
+ [
+ { class_name: 'ProjectSnippet', id: unreadable.id, ability: :read_snippet }
+ ])))
+
+ expect(result).to contain_exactly(readable)
+ end
+
+ context 'with :with_api_entity_associations' do
+ it_behaves_like "redaction limits N+1 queries", limit: 14
+ end
+ end
+
+ context 'for personal snippets' do
+ let(:readable) { create(:personal_snippet, :private, author: user) }
+ let(:unreadable) { create(:personal_snippet, :private) }
+ let(:unredacted_results) { ar_relation(PersonalSnippet, readable, unreadable) }
+ let(:scope) { 'snippet_titles' }
+
+ it 'redacts the inaccessible snippet' do
+ expect(search_service.send(:logger))
+ .to receive(:error)
+ .with(hash_including(
+ message: "redacted_search_results",
+ current_user_id: user.id,
+ query: search,
+ filtered: array_including(
+ [
+ { class_name: 'PersonalSnippet', id: unreadable.id, ability: :read_snippet }
+ ])))
+
+ expect(result).to contain_exactly(readable)
+ end
+
+ context 'with :with_api_entity_associations' do
+ it_behaves_like "redaction limits N+1 queries", limit: 4
+ end
+ end
+
+ context 'for commits' do
+ let(:readable) { accessible_project.commit }
+ let(:unreadable) { inaccessible_project.commit }
+ let(:unredacted_results) { kaminari_array(readable, unreadable) }
+ let(:scope) { 'commits' }
+
+ it 'redacts the inaccessible commit' do
+ expect(search_service.send(:logger))
+ .to receive(:error)
+ .with(hash_including(
+ message: "redacted_search_results",
+ current_user_id: user.id,
+ query: search,
+ filtered: array_including(
+ [
+ { class_name: 'Commit', id: unreadable.id, ability: :read_commit }
+ ])))
+
+ expect(result).to contain_exactly(readable)
+ end
+ end
+
+ context 'for users' do
+ let(:other_user) { create(:user) }
+ let(:unredacted_results) { ar_relation(User, user, other_user) }
+ let(:scope) { 'users' }
+
+ it 'passes the users through' do
+ # Users are always visible to everyone
+ expect(result).to contain_exactly(user, other_user)
+ end
+ end
+end
+
+RSpec.shared_examples "redaction limits N+1 queries" do |limit:|
+ it 'does not exceed the query limit' do
+ # issuing the query to remove the data loading call
+ unredacted_results.to_a
+
+ # only the calls from the redaction are left
+ query = ActiveRecord::QueryRecorder.new { result }
+
+ # these are the project authorization calls, which are not preloaded
+ expect(query.count).to be <= limit
+ end
+end
diff --git a/spec/views/search/show.html.haml_spec.rb b/spec/views/search/show.html.haml_spec.rb
index 565dadd64fe..5f9c6c65a08 100644
--- a/spec/views/search/show.html.haml_spec.rb
+++ b/spec/views/search/show.html.haml_spec.rb
@@ -11,10 +11,10 @@ RSpec.describe 'search/show' do
stub_template "search/_results.html.haml" => 'Results Partial'
end
- context 'feature flag enabled' do
+ context 'search_page_vertical_nav feature flag enabled' do
before do
- allow(self).to receive(:current_user).and_return(user)
- @search_term = search_term
+ allow(view).to receive(:current_user) { user }
+ assign(:search_term, search_term)
render
end
@@ -29,11 +29,11 @@ RSpec.describe 'search/show' do
end
end
- context 'feature flag disabled' do
+ context 'search_page_vertical_nav feature flag disabled' do
before do
stub_feature_flags(search_page_vertical_nav: false)
- @search_term = search_term
+ assign(:search_term, search_term)
render
end
diff --git a/yarn.lock b/yarn.lock
index 27a3f834065..3e1e1b88281 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4992,10 +4992,10 @@ dompurify@2.3.8:
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.8.tgz#224fe9ae57d7ebd9a1ae1ac18c1c1ca3f532226f"
integrity sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw==
-dompurify@^2.4.0:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.0.tgz#c9c88390f024c2823332615c9e20a453cf3825dd"
- integrity sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA==
+dompurify@^2.4.0, dompurify@^2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.1.tgz#f9cb1a275fde9af6f2d0a2644ef648dd6847b631"
+ integrity sha512-ewwFzHzrrneRjxzmK6oVz/rZn9VWspGFRDb4/rRtIsM1n36t9AKma/ye8syCpcw+XJ25kOK/hOG7t1j2I2yBqA==
domutils@^2.5.2, domutils@^2.6.0:
version "2.6.0"