summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-05-12 09:08:08 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-05-12 09:08:08 +0000
commit71d34aac9a0fae0507c265929767422391816b01 (patch)
treededb769442c9576e2f44bf3c500b013beb9604d9
parent90726a8ccc9df6d9b5ff4f5e1eb31d015c1db8e2 (diff)
downloadgitlab-ce-71d34aac9a0fae0507c265929767422391816b01.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.eslintrc.yml2
-rw-r--r--.gitlab/ci/qa.gitlab-ci.yml2
-rw-r--r--.gitlab/ci/review-apps/qa.gitlab-ci.yml3
-rw-r--r--.rubocop_todo/layout/space_inside_parens.yml1
-rw-r--r--GITLAB_KAS_VERSION2
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_kroki.js4
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_math.js4
-rw-r--r--app/assets/javascripts/blob/blob_line_permalink_updater.js5
-rw-r--r--app/assets/javascripts/blob/pipeline_tour_success_modal.vue2
-rw-r--r--app/assets/javascripts/blob/viewer/index.js17
-rw-r--r--app/assets/javascripts/breadcrumb.js2
-rw-r--r--app/assets/javascripts/code_navigation/utils/index.js4
-rw-r--r--app/assets/javascripts/deprecated_jquery_dropdown/render.js8
-rw-r--r--app/assets/javascripts/diff.js2
-rw-r--r--app/assets/javascripts/filtered_search/available_dropdown_mappings.js4
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_hint.js4
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_operator.js2
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_user.js4
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_utils.js2
-rw-r--r--app/assets/javascripts/filtered_search/droplab/drop_down.js4
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js2
-rw-r--r--app/assets/javascripts/image_diff/helpers/dom_helper.js2
-rw-r--r--app/assets/javascripts/issues/create_merge_request_dropdown.js5
-rw-r--r--app/assets/javascripts/issues/show/components/description.vue2
-rw-r--r--app/assets/javascripts/lazy_loader.js9
-rw-r--r--app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js2
-rw-r--r--app/assets/javascripts/members/components/table/role_dropdown.vue2
-rw-r--r--app/assets/javascripts/pages/shared/nav/sidebar_tracking.js10
-rw-r--r--app/assets/javascripts/pages/users/activity_calendar.js2
-rw-r--r--app/assets/javascripts/pipeline_editor/components/file_tree/container.vue11
-rw-r--r--app/assets/javascripts/pipeline_editor/components/popovers/file_tree_popover.vue18
-rw-r--r--app/assets/javascripts/pipeline_editor/index.js2
-rw-r--r--app/assets/javascripts/projects/commits/components/author_select.vue2
-rw-r--r--app/assets/javascripts/sidebar/mount_sidebar.js4
-rw-r--r--app/assets/javascripts/terraform/index.js2
-rw-r--r--app/assets/javascripts/whats_new/components/app.vue2
-rw-r--r--app/assets/javascripts/whats_new/utils/notification.js2
-rw-r--r--app/controllers/admin/sessions_controller.rb2
-rw-r--r--app/controllers/profiles/two_factor_auths_controller.rb2
-rw-r--r--app/controllers/sessions_controller.rb2
-rw-r--r--app/helpers/ci/pipeline_editor_helper.rb1
-rw-r--r--app/models/alert_management/alert.rb2
-rw-r--r--app/models/users/callout.rb3
-rw-r--r--app/services/alert_management/alerts/update_service.rb20
-rw-r--r--app/services/concerns/alert_management/alert_processing.rb2
-rw-r--r--app/services/users/validate_manual_otp_service.rb (renamed from app/services/users/validate_otp_service.rb)6
-rw-r--r--app/services/users/validate_push_otp_service.rb25
-rw-r--r--config/feature_flags/development/namespace_storage_limit_bypass_date_check.yml8
-rw-r--r--config/metrics/counts_28d/20210901221659_p_ci_templates_security_cluster_image_scanning_monthly.yml4
-rw-r--r--config/metrics/counts_28d/20210901223916_p_ci_templates_implicit_security_cluster_image_scanning_monthly.yml4
-rw-r--r--config/metrics/counts_7d/20210901221654_p_ci_templates_security_cluster_image_scanning_weekly.yml4
-rw-r--r--config/metrics/counts_7d/20210901223912_p_ci_templates_implicit_security_cluster_image_scanning_weekly.yml4
-rw-r--r--db/post_migrate/20220428133724_schedule_expire_o_auth_tokens.rb25
-rw-r--r--db/post_migrate/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints.rb28
-rw-r--r--db/schema_migrations/202204281337241
-rw-r--r--db/schema_migrations/202205051746581
-rw-r--r--db/structure.sql4
-rw-r--r--doc/.vale/gitlab/BadPlurals.yml2
-rw-r--r--doc/administration/configure.md4
-rw-r--r--doc/administration/geo/disaster_recovery/index.md2
-rw-r--r--doc/administration/geo/replication/multiple_servers.md4
-rw-r--r--doc/administration/gitaly/configure_gitaly.md4
-rw-r--r--doc/administration/package_information/signed_packages.md2
-rw-r--r--doc/administration/postgresql/pgbouncer.md6
-rw-r--r--doc/administration/reference_architectures/10k_users.md2
-rw-r--r--doc/administration/reference_architectures/1k_users.md2
-rw-r--r--doc/administration/reference_architectures/25k_users.md2
-rw-r--r--doc/administration/reference_architectures/2k_users.md2
-rw-r--r--doc/administration/reference_architectures/3k_users.md2
-rw-r--r--doc/administration/reference_architectures/50k_users.md2
-rw-r--r--doc/administration/reference_architectures/5k_users.md2
-rw-r--r--doc/api/graphql/reference/index.md1
-rw-r--r--doc/api/wikis.md2
-rw-r--r--doc/architecture/blueprints/database_testing/index.md2
-rw-r--r--doc/ci/git_submodules.md2
-rw-r--r--doc/ci/quick_start/index.md2
-rw-r--r--doc/development/database/migrations_for_multiple_databases.md4
-rw-r--r--doc/development/database/multiple_databases.md2
-rw-r--r--doc/development/experiment_guide/experiment_rollout.md2
-rw-r--r--doc/development/fe_guide/vue3_migration.md2
-rw-r--r--doc/development/migration_style_guide.md2
-rw-r--r--doc/development/pipelines.md2
-rw-r--r--doc/development/product_qualified_lead_guide/index.md4
-rw-r--r--doc/development/secure_coding_guidelines.md8
-rw-r--r--doc/development/snowplow/implementation.md4
-rw-r--r--doc/install/aws/gitlab_hybrid_on_aws.md16
-rw-r--r--doc/subscriptions/self_managed/index.md2
-rw-r--r--doc/user/admin_area/settings/files_api_rate_limits.md2
-rw-r--r--doc/user/admin_area/settings/sign_up_restrictions.md2
-rw-r--r--doc/user/application_security/cluster_image_scanning/index.md15
-rw-r--r--doc/user/application_security/dast/checks/16.3.md2
-rw-r--r--doc/user/application_security/dependency_scanning/index.md2
-rw-r--r--doc/user/application_security/sast/index.md2
-rw-r--r--doc/user/clusters/management_project_template.md2
-rw-r--r--doc/user/infrastructure/clusters/index.md2
-rw-r--r--doc/user/project/merge_requests/test_coverage_visualization.md2
-rw-r--r--doc/user/search/global_search/advanced_search_syntax.md2
-rw-r--r--lib/api/internal/base.rb24
-rw-r--r--lib/bulk_imports/groups/transformers/group_attributes_transformer.rb6
-rw-r--r--lib/bulk_imports/groups/transformers/subgroup_to_entity_transformer.rb2
-rw-r--r--lib/gitlab/auth/otp/strategies/forti_authenticator.rb44
-rw-r--r--lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp.rb50
-rw-r--r--lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp.rb47
-rw-r--r--lib/gitlab/background_migration/expire_o_auth_tokens.rb23
-rw-r--r--lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml1
-rw-r--r--lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml29
-rw-r--r--lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml4
-rw-r--r--lib/gitlab/ci/templates/Security/Cluster-Image-Scanning.gitlab-ci.yml34
-rw-r--r--lib/gitlab/ci/templates/Security/DAST-API.gitlab-ci.yml13
-rw-r--r--lib/gitlab/ci/templates/Security/DAST-API.latest.gitlab-ci.yml6
-rw-r--r--lib/gitlab/usage_data_counters/known_events/ci_templates.yml8
-rw-r--r--locale/gitlab.pot11
-rw-r--r--qa/Dockerfile62
-rw-r--r--qa/qa/git/repository.rb34
-rw-r--r--qa/spec/git/repository_spec.rb6
-rw-r--r--spec/frontend/__helpers__/init_vue_mr_page_helper.js18
-rw-r--r--spec/frontend/__helpers__/matchers/to_have_sprite_icon.js2
-rw-r--r--spec/frontend/access_tokens/index_spec.js2
-rw-r--r--spec/frontend/admin/users/index_spec.js8
-rw-r--r--spec/frontend/authentication/two_factor_auth/index_spec.js4
-rw-r--r--spec/frontend/blob/components/table_contents_spec.js4
-rw-r--r--spec/frontend/blob/viewer/index_spec.js6
-rw-r--r--spec/frontend/cascading_settings/components/lock_popovers_spec.js10
-rw-r--r--spec/frontend/code_navigation/store/actions_spec.js12
-rw-r--r--spec/frontend/confirm_modal_spec.js6
-rw-r--r--spec/frontend/helpers/startup_css_helper_spec.js7
-rw-r--r--spec/frontend/issues/create_merge_request_dropdown_spec.js4
-rw-r--r--spec/frontend/labels/delete_label_modal_spec.js6
-rw-r--r--spec/frontend/lazy_loader_spec.js4
-rw-r--r--spec/frontend/members/index_spec.js2
-rw-r--r--spec/frontend/members/utils_spec.js2
-rw-r--r--spec/frontend/notebook/cells/markdown_spec.js4
-rw-r--r--spec/frontend/notes/stores/actions_spec.js10
-rw-r--r--spec/frontend/performance_bar/index_spec.js10
-rw-r--r--spec/frontend/pipeline_editor/components/file-tree/container_spec.js7
-rw-r--r--spec/frontend/pipeline_editor/components/popovers/file_tree_popover_spec.js22
-rw-r--r--spec/frontend/pipeline_editor/mock_data.js1
-rw-r--r--spec/frontend/search_autocomplete_spec.js2
-rw-r--r--spec/frontend/user_popovers_spec.js2
-rw-r--r--spec/frontend/users_select/test_helper.js8
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_merged_spec.js4
-rw-r--r--spec/frontend/vue_mr_widget/mr_widget_options_spec.js2
-rw-r--r--spec/frontend_integration/ide/helpers/ide_helper.js4
-rw-r--r--spec/helpers/ci/pipeline_editor_helper_spec.rb2
-rw-r--r--spec/lib/bulk_imports/groups/pipelines/group_pipeline_spec.rb2
-rw-r--r--spec/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline_spec.rb12
-rw-r--r--spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb26
-rw-r--r--spec/lib/bulk_imports/groups/transformers/subgroup_to_entity_transformer_spec.rb8
-rw-r--r--spec/lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp_spec.rb (renamed from spec/lib/gitlab/auth/otp/strategies/forti_authenticator_spec.rb)2
-rw-r--r--spec/lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp_spec.rb65
-rw-r--r--spec/lib/gitlab/background_migration/expire_o_auth_tokens_spec.rb35
-rw-r--r--spec/migrations/20220428133724_schedule_expire_o_auth_tokens_spec.rb31
-rw-r--r--spec/migrations/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints_spec.rb24
-rw-r--r--spec/models/alert_management/alert_spec.rb4
-rw-r--r--spec/requests/api/internal/base_spec.rb83
-rw-r--r--spec/services/alert_management/alerts/update_service_spec.rb45
-rw-r--r--spec/services/users/validate_manual_otp_service_spec.rb (renamed from spec/services/users/validate_otp_service_spec.rb)27
-rw-r--r--spec/services/users/validate_push_otp_service_spec.rb45
159 files changed, 966 insertions, 457 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
index e02d2c02d79..d29af93319e 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -108,8 +108,6 @@ rules:
message: 'Migrate to GlSkeletonLoader, or import GlDeprecatedSkeletonLoading.'
# See https://gitlab.com/gitlab-org/gitlab/-/issues/360551
vue/multi-word-component-names: off
- unicorn/prefer-dom-node-dataset:
- - error
overrides:
- files:
- '{,ee/,jh/}spec/frontend*/**/*'
diff --git a/.gitlab/ci/qa.gitlab-ci.yml b/.gitlab/ci/qa.gitlab-ci.yml
index 93cd43fbdee..676e895035b 100644
--- a/.gitlab/ci/qa.gitlab-ci.yml
+++ b/.gitlab/ci/qa.gitlab-ci.yml
@@ -1,5 +1,5 @@
.qa-job-base:
- image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:debian-bullseye-ruby-2.7-bundler-2.3-git-2.33-lfs-2.9-chrome-99-docker-20.10.14-gcloud-383-kubectl-1.23
+ image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-2.7:bundler-2.3-git-2.33-chrome-99
extends:
- .default-retry
- .qa-cache
diff --git a/.gitlab/ci/review-apps/qa.gitlab-ci.yml b/.gitlab/ci/review-apps/qa.gitlab-ci.yml
index 98928ce4715..47e756eb230 100644
--- a/.gitlab/ci/review-apps/qa.gitlab-ci.yml
+++ b/.gitlab/ci/review-apps/qa.gitlab-ci.yml
@@ -28,7 +28,7 @@ include:
- .qa-cache
- .test_variables
- .bundler_variables
- image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images:debian-bullseye-ruby-2.7-bundler-2.3-git-2.33-lfs-2.9-chrome-99-docker-20.10.14-gcloud-383-kubectl-1.23
+ image: ${REGISTRY_HOST}/${REGISTRY_GROUP}/gitlab-build-images/debian-bullseye-ruby-2.7:bundler-2.3-git-2.33-lfs-2.9-chrome-99-docker-20.10.14-gcloud-383-kubectl-1.23
stage: qa
needs:
- review-deploy
@@ -50,7 +50,6 @@ include:
--tag ~orchestrated \
--tag ~transient \
--tag ~skip_signup_disabled \
- --tag ~requires_git_protocol_v2 \
--force-color \
--order random \
--format documentation \
diff --git a/.rubocop_todo/layout/space_inside_parens.yml b/.rubocop_todo/layout/space_inside_parens.yml
index 62d33391a25..9022d41aeeb 100644
--- a/.rubocop_todo/layout/space_inside_parens.yml
+++ b/.rubocop_todo/layout/space_inside_parens.yml
@@ -90,7 +90,6 @@ Layout/SpaceInsideParens:
- 'ee/spec/lib/gitlab/auth/otp/session_enforcer_spec.rb'
- 'ee/spec/lib/gitlab/auth/smartcard/ldap_certificate_spec.rb'
- 'ee/spec/lib/gitlab/auth_spec.rb'
- - 'ee/spec/lib/gitlab/ci/templates/cluster_image_scanning_gitlab_ci_yaml_spec.rb'
- 'ee/spec/lib/gitlab/ci/templates/coverage_fuzzing_gitlab_ci_yaml_spec.rb'
- 'ee/spec/lib/gitlab/ci/templates/dast_api_gitlab_ci_yaml_spec.rb'
- 'ee/spec/lib/gitlab/ci/templates/dast_api_latest_gitlab_ci_yaml_spec.rb'
diff --git a/GITLAB_KAS_VERSION b/GITLAB_KAS_VERSION
index 6242bb49da0..94188a74831 100644
--- a/GITLAB_KAS_VERSION
+++ b/GITLAB_KAS_VERSION
@@ -1 +1 @@
-14.10.0
+15.0.0
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 26f2bbc1975..07ea9fa4381 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-14.1.1
+14.2.0
diff --git a/app/assets/javascripts/behaviors/markdown/render_kroki.js b/app/assets/javascripts/behaviors/markdown/render_kroki.js
index 241585c30f1..abe71694d73 100644
--- a/app/assets/javascripts/behaviors/markdown/render_kroki.js
+++ b/app/assets/javascripts/behaviors/markdown/render_kroki.js
@@ -55,8 +55,8 @@ export function renderKroki(krokiImages) {
// A single Kroki image is processed multiple times for some reason,
// so this condition ensures we only create one alert per Kroki image
- if (!Object.hasOwn(parent.dataset, 'krokiProcessed')) {
- parent.dataset.krokiProcessed = 'true';
+ if (!parent.hasAttribute('data-kroki-processed')) {
+ parent.setAttribute('data-kroki-processed', 'true');
parent.after(createAlert(krokiImage));
}
});
diff --git a/app/assets/javascripts/behaviors/markdown/render_math.js b/app/assets/javascripts/behaviors/markdown/render_math.js
index 7e1bf83f8a1..12f47255bdf 100644
--- a/app/assets/javascripts/behaviors/markdown/render_math.js
+++ b/app/assets/javascripts/behaviors/markdown/render_math.js
@@ -110,7 +110,7 @@ class SafeMathRenderer {
try {
displayContainer.innerHTML = this.katex.renderToString(text, {
- displayMode: el.dataset.mathStyle === 'display',
+ displayMode: el.getAttribute('data-math-style') === 'display',
throwOnError: true,
maxSize: 20,
maxExpand: 20,
@@ -143,7 +143,7 @@ class SafeMathRenderer {
this.elements.forEach((el) => {
const placeholder = document.createElement('span');
placeholder.style.display = 'none';
- placeholder.dataset.mathStyle = el.dataset.mathStyle;
+ placeholder.setAttribute('data-math-style', el.getAttribute('data-math-style'));
placeholder.textContent = el.textContent;
el.parentNode.replaceChild(placeholder, el);
this.queue.push(placeholder);
diff --git a/app/assets/javascripts/blob/blob_line_permalink_updater.js b/app/assets/javascripts/blob/blob_line_permalink_updater.js
index 0a5bcf326a1..a3dd241604d 100644
--- a/app/assets/javascripts/blob/blob_line_permalink_updater.js
+++ b/app/assets/javascripts/blob/blob_line_permalink_updater.js
@@ -9,11 +9,10 @@ const updateLineNumbersOnBlobPermalinks = (linksToUpdate) => {
[].concat(Array.prototype.slice.call(linksToUpdate)).forEach((permalinkButton) => {
const baseHref =
- permalinkButton.dataset.originalHref ||
+ permalinkButton.getAttribute('data-original-href') ||
(() => {
const href = permalinkButton.getAttribute('href');
- // eslint-disable-next-line no-param-reassign
- permalinkButton.dataset.originalHref = href;
+ permalinkButton.setAttribute('data-original-href', href);
return href;
})();
permalinkButton.setAttribute('href', `${baseHref}${hashUrlString}`);
diff --git a/app/assets/javascripts/blob/pipeline_tour_success_modal.vue b/app/assets/javascripts/blob/pipeline_tour_success_modal.vue
index b4ca29114cb..f3c542c467a 100644
--- a/app/assets/javascripts/blob/pipeline_tour_success_modal.vue
+++ b/app/assets/javascripts/blob/pipeline_tour_success_modal.vue
@@ -132,7 +132,7 @@ export default {
<gl-button
ref="goToPipelines"
:href="goToPipelinesPath"
- variant="success"
+ variant="confirm"
:data-track-property="humanAccess"
:data-track-value="$options.goToTrackValuePipelines"
:data-track-action="$options.trackEvent"
diff --git a/app/assets/javascripts/blob/viewer/index.js b/app/assets/javascripts/blob/viewer/index.js
index a0d4f7ef4f2..a6eed4ecae3 100644
--- a/app/assets/javascripts/blob/viewer/index.js
+++ b/app/assets/javascripts/blob/viewer/index.js
@@ -36,19 +36,19 @@ const loadRichBlobViewer = (type) => {
const loadViewer = (viewerParam) => {
const viewer = viewerParam;
- const { url } = viewer.dataset;
+ const url = viewer.getAttribute('data-url');
- if (!url || viewer.dataset.loaded || viewer.dataset.loading) {
+ if (!url || viewer.getAttribute('data-loaded') || viewer.getAttribute('data-loading')) {
return Promise.resolve(viewer);
}
- viewer.dataset.loading = 'true';
+ viewer.setAttribute('data-loading', 'true');
return axios.get(url).then(({ data }) => {
viewer.innerHTML = data.html;
window.requestIdleCallback(() => {
- delete viewer.dataset.loading;
+ viewer.removeAttribute('data-loading');
});
return viewer;
@@ -108,7 +108,7 @@ export class BlobViewer {
switchToInitialViewer() {
const initialViewer = this.$fileHolder[0].querySelector('.blob-viewer:not(.hidden)');
- let initialViewerName = initialViewer.dataset.type;
+ let initialViewerName = initialViewer.getAttribute('data-type');
if (this.switcher && window.location.hash.indexOf('#L') === 0) {
initialViewerName = 'simple';
@@ -138,12 +138,12 @@ export class BlobViewer {
e.preventDefault();
- this.switchToViewer(target.dataset.viewer);
+ this.switchToViewer(target.getAttribute('data-viewer'));
}
toggleCopyButtonState() {
if (!this.copySourceBtn) return;
- if (this.simpleViewer.dataset.loaded) {
+ if (this.simpleViewer.getAttribute('data-loaded')) {
this.copySourceBtnTooltip.setAttribute('title', __('Copy file contents'));
this.copySourceBtn.classList.remove('disabled');
} else if (this.activeViewer === this.simpleViewer) {
@@ -199,8 +199,7 @@ export class BlobViewer {
this.$fileHolder.trigger('highlight:line');
handleLocationHash();
- // eslint-disable-next-line no-param-reassign
- viewer.dataset.loaded = 'true';
+ viewer.setAttribute('data-loaded', 'true');
this.toggleCopyButtonState();
eventHub.$emit('showBlobInteractionZones', viewer.dataset.path);
});
diff --git a/app/assets/javascripts/breadcrumb.js b/app/assets/javascripts/breadcrumb.js
index 113840dbc52..b9d3742974c 100644
--- a/app/assets/javascripts/breadcrumb.js
+++ b/app/assets/javascripts/breadcrumb.js
@@ -5,7 +5,7 @@ export const addTooltipToEl = (el) => {
if (textEl && textEl.scrollWidth > textEl.offsetWidth) {
el.setAttribute('title', el.textContent);
- el.dataset.container = 'body';
+ el.setAttribute('data-container', 'body');
el.classList.add('has-tooltip');
}
};
diff --git a/app/assets/javascripts/code_navigation/utils/index.js b/app/assets/javascripts/code_navigation/utils/index.js
index 46038df2f86..0d72153d8fe 100644
--- a/app/assets/javascripts/code_navigation/utils/index.js
+++ b/app/assets/javascripts/code_navigation/utils/index.js
@@ -32,8 +32,8 @@ export const addInteractionClass = ({ path, d, wrapTextNodes }) => {
});
if (el && !isTextNode(el)) {
- el.dataset.charIndex = d.start_char;
- el.dataset.lineIndex = d.start_line;
+ el.setAttribute('data-char-index', d.start_char);
+ el.setAttribute('data-line-index', d.start_line);
el.classList.add('cursor-pointer', 'code-navigation', 'js-code-navigation');
el.closest('.line').classList.add('code-navigation-line');
}
diff --git a/app/assets/javascripts/deprecated_jquery_dropdown/render.js b/app/assets/javascripts/deprecated_jquery_dropdown/render.js
index f10c2d82b61..37287b9d981 100644
--- a/app/assets/javascripts/deprecated_jquery_dropdown/render.js
+++ b/app/assets/javascripts/deprecated_jquery_dropdown/render.js
@@ -107,10 +107,10 @@ function createLink(data, selected, options, index) {
}
if (options.trackSuggestionClickedLabel) {
- link.dataset.trackAction = 'click_text';
- link.dataset.trackLabel = options.trackSuggestionClickedLabel;
- link.dataset.trackValue = index;
- link.dataset.trackProperty = slugify(data.category || 'no-category');
+ link.setAttribute('data-track-action', 'click_text');
+ link.setAttribute('data-track-label', options.trackSuggestionClickedLabel);
+ link.setAttribute('data-track-value', index);
+ link.setAttribute('data-track-property', slugify(data.category || 'no-category'));
}
link.classList.toggle('is-active', selected);
diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js
index 47de7a76899..a12829f8420 100644
--- a/app/assets/javascripts/diff.js
+++ b/app/assets/javascripts/diff.js
@@ -26,7 +26,7 @@ export default class Diff {
FilesCommentButton.init($diffFile);
const firstFile = $('.files').first().get(0);
- const canCreateNote = firstFile && Object.hasOwn(firstFile.dataset, 'canCreateNote');
+ const canCreateNote = firstFile && firstFile.hasAttribute('data-can-create-note');
$diffFile.each((index, file) => initImageDiffHelper.initImageDiff(file, canCreateNote));
if (!isBound) {
diff --git a/app/assets/javascripts/filtered_search/available_dropdown_mappings.js b/app/assets/javascripts/filtered_search/available_dropdown_mappings.js
index 3913e4e8d81..b57db73a86e 100644
--- a/app/assets/javascripts/filtered_search/available_dropdown_mappings.js
+++ b/app/assets/javascripts/filtered_search/available_dropdown_mappings.js
@@ -197,10 +197,10 @@ export default class AvailableDropdownMappings {
}
getGroupId() {
- return this.filteredSearchInput.dataset.groupId || '';
+ return this.filteredSearchInput.getAttribute('data-group-id') || '';
}
getProjectId() {
- return this.filteredSearchInput.dataset.projectId || '';
+ return this.filteredSearchInput.getAttribute('data-project-id') || '';
}
}
diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js
index 93897b4ed6c..9d29782c9a7 100644
--- a/app/assets/javascripts/filtered_search/dropdown_hint.js
+++ b/app/assets/javascripts/filtered_search/dropdown_hint.js
@@ -25,9 +25,9 @@ export default class DropdownHint extends FilteredSearchDropdown {
const { selected } = e.detail;
if (selected.tagName === 'LI') {
- if (Object.hasOwn(selected.dataset, 'value')) {
+ if (selected.hasAttribute('data-value')) {
this.dismissDropdown();
- } else if (selected.dataset.action === 'submit') {
+ } else if (selected.getAttribute('data-action') === 'submit') {
this.dismissDropdown();
this.dispatchFormSubmitEvent();
} else {
diff --git a/app/assets/javascripts/filtered_search/dropdown_operator.js b/app/assets/javascripts/filtered_search/dropdown_operator.js
index cd0f541b4fb..fb9f25a8c45 100644
--- a/app/assets/javascripts/filtered_search/dropdown_operator.js
+++ b/app/assets/javascripts/filtered_search/dropdown_operator.js
@@ -23,7 +23,7 @@ export default class DropdownOperator extends FilteredSearchDropdown {
const { selected } = e.detail;
if (selected.tagName === 'LI') {
- if (Object.hasOwn(selected.dataset, 'value')) {
+ if (selected.hasAttribute('data-value')) {
const name = FilteredSearchVisualTokens.getLastTokenPartial();
const operator = selected.dataset.value;
diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js
index 26507a85fa8..9a23ff25eac 100644
--- a/app/assets/javascripts/filtered_search/dropdown_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_user.js
@@ -31,11 +31,11 @@ export default class DropdownUser extends DropdownAjaxFilter {
}
getGroupId() {
- return this.input.dataset.groupId;
+ return this.input.getAttribute('data-group-id');
}
getProjectId() {
- return this.input.dataset.projectId;
+ return this.input.getAttribute('data-project-id');
}
projectOrGroupId() {
diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js
index 22e1604871a..c98d1f8e064 100644
--- a/app/assets/javascripts/filtered_search/dropdown_utils.js
+++ b/app/assets/javascripts/filtered_search/dropdown_utils.js
@@ -87,7 +87,6 @@ export default class DropdownUtils {
}
static setDataValueIfSelected(filter, operator, selected) {
- // eslint-disable-next-line unicorn/prefer-dom-node-dataset
const dataValue = selected.getAttribute('data-value');
if (dataValue) {
@@ -97,7 +96,6 @@ export default class DropdownUtils {
tokenValue: dataValue,
clicked: true,
options: {
- // eslint-disable-next-line unicorn/prefer-dom-node-dataset
capitalizeTokenValue: selected.hasAttribute('data-capitalize'),
},
});
diff --git a/app/assets/javascripts/filtered_search/droplab/drop_down.js b/app/assets/javascripts/filtered_search/droplab/drop_down.js
index 398a7b26677..05b741af191 100644
--- a/app/assets/javascripts/filtered_search/droplab/drop_down.js
+++ b/app/assets/javascripts/filtered_search/droplab/drop_down.js
@@ -165,8 +165,8 @@ class DropDown {
images.forEach((image) => {
const img = image;
- img.src = img.dataset.src;
- delete img.dataset.src;
+ img.src = img.getAttribute('data-src');
+ img.removeAttribute('data-src');
});
}
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index ac2cf27e873..07f2c75f00a 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -814,7 +814,7 @@ export default class FilteredSearchManager {
getUsernameParams() {
const usernamesById = {};
try {
- const attribute = this.filteredSearchInput.dataset.usernameParams;
+ const attribute = this.filteredSearchInput.getAttribute('data-username-params');
JSON.parse(attribute).forEach((user) => {
usernamesById[user.id] = user.username;
});
diff --git a/app/assets/javascripts/image_diff/helpers/dom_helper.js b/app/assets/javascripts/image_diff/helpers/dom_helper.js
index 180e927a3e7..3468a629f5a 100644
--- a/app/assets/javascripts/image_diff/helpers/dom_helper.js
+++ b/app/assets/javascripts/image_diff/helpers/dom_helper.js
@@ -6,7 +6,7 @@ export function setPositionDataAttribute(el, options) {
const positionObject = { ...JSON.parse(position), x, y, width, height };
- el.dataset.position = JSON.stringify(positionObject);
+ el.setAttribute('data-position', JSON.stringify(positionObject));
}
export function updateDiscussionAvatarBadgeNumber(discussionEl, newBadgeNumber) {
diff --git a/app/assets/javascripts/issues/create_merge_request_dropdown.js b/app/assets/javascripts/issues/create_merge_request_dropdown.js
index 7c611dbf39e..3ea70c07058 100644
--- a/app/assets/javascripts/issues/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/issues/create_merge_request_dropdown.js
@@ -81,7 +81,10 @@ export default class CreateMergeRequestDropdown {
this.init();
if (isConfidentialIssue()) {
- this.createMergeRequestButton.dataset.dropdownTrigger = '#create-merge-request-dropdown';
+ this.createMergeRequestButton.setAttribute(
+ 'data-dropdown-trigger',
+ '#create-merge-request-dropdown',
+ );
initConfidentialMergeRequest();
}
}
diff --git a/app/assets/javascripts/issues/show/components/description.vue b/app/assets/javascripts/issues/show/components/description.vue
index 7594676489f..831cef66836 100644
--- a/app/assets/javascripts/issues/show/components/description.vue
+++ b/app/assets/javascripts/issues/show/components/description.vue
@@ -270,7 +270,7 @@ export default {
},
setActiveTask(el) {
const { parentElement } = el;
- const lineNumbers = parentElement.dataset.sourcepos.match(/\b\d+(?=:)/g);
+ const lineNumbers = parentElement.getAttribute('data-sourcepos').match(/\b\d+(?=:)/g);
this.activeTask = {
title: parentElement.innerText,
lineNumberStart: lineNumbers[0],
diff --git a/app/assets/javascripts/lazy_loader.js b/app/assets/javascripts/lazy_loader.js
index ba801082377..2b4dd205cf1 100644
--- a/app/assets/javascripts/lazy_loader.js
+++ b/app/assets/javascripts/lazy_loader.js
@@ -127,7 +127,7 @@ export default class LazyLoader {
// Loading Images which are in the current viewport or close to them
this.lazyImages = this.lazyImages.filter((selectedImage) => {
- if (selectedImage.dataset.src) {
+ if (selectedImage.getAttribute('data-src')) {
const imgBoundRect = selectedImage.getBoundingClientRect();
const imgTop = scrollTop + imgBoundRect.top;
const imgBound = imgTop + imgBoundRect.height;
@@ -156,17 +156,16 @@ export default class LazyLoader {
}
static loadImage(img) {
- if (img.dataset.src) {
+ if (img.getAttribute('data-src')) {
img.setAttribute('loading', 'lazy');
- let imgUrl = img.dataset.src;
+ let imgUrl = img.getAttribute('data-src');
// Only adding width + height for avatars for now
if (imgUrl.indexOf('/avatar/') > -1 && imgUrl.indexOf('?') === -1) {
const targetWidth = img.getAttribute('width') || img.width;
imgUrl += `?width=${targetWidth}`;
}
img.setAttribute('src', imgUrl);
- // eslint-disable-next-line no-param-reassign
- delete img.dataset.src;
+ img.removeAttribute('data-src');
img.classList.remove('lazy');
img.classList.add('js-lazy-loaded');
img.classList.add('qa-js-lazy-loaded');
diff --git a/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js b/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js
index a0e730e9722..1adb6f9c26f 100644
--- a/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js
+++ b/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js
@@ -52,7 +52,7 @@ export function confirmAction(
export function confirmViaGlModal(message, element) {
const primaryBtnConfig = {};
- const { confirmBtnVariant } = element.dataset;
+ const confirmBtnVariant = element.getAttribute('data-confirm-btn-variant');
if (confirmBtnVariant) {
primaryBtnConfig.primaryBtnVariant = confirmBtnVariant;
diff --git a/app/assets/javascripts/members/components/table/role_dropdown.vue b/app/assets/javascripts/members/components/table/role_dropdown.vue
index 6cd8bf57313..fa895cf24c4 100644
--- a/app/assets/javascripts/members/components/table/role_dropdown.vue
+++ b/app/assets/javascripts/members/components/table/role_dropdown.vue
@@ -41,7 +41,7 @@ export default {
const dropdownToggle = this.$refs.glDropdown.$el.querySelector('.dropdown-toggle');
if (dropdownToggle) {
- dropdownToggle.dataset.qaSelector = 'access_level_dropdown';
+ dropdownToggle.setAttribute('data-qa-selector', 'access_level_dropdown');
}
},
methods: {
diff --git a/app/assets/javascripts/pages/shared/nav/sidebar_tracking.js b/app/assets/javascripts/pages/shared/nav/sidebar_tracking.js
index 47aae36ecbb..79ce1a37d21 100644
--- a/app/assets/javascripts/pages/shared/nav/sidebar_tracking.js
+++ b/app/assets/javascripts/pages/shared/nav/sidebar_tracking.js
@@ -1,6 +1,6 @@
function onSidebarLinkClick() {
const setDataTrackAction = (element, action) => {
- element.dataset.trackAction = action;
+ element.setAttribute('data-track-action', action);
};
const setDataTrackExtra = (element, value) => {
@@ -12,10 +12,10 @@ function onSidebarLinkClick() {
? SIDEBAR_COLLAPSED
: SIDEBAR_EXPANDED;
- element.dataset.trackExtra = JSON.stringify({
- sidebar_display: sidebarCollapsed,
- menu_display: value,
- });
+ element.setAttribute(
+ 'data-track-extra',
+ JSON.stringify({ sidebar_display: sidebarCollapsed, menu_display: value }),
+ );
};
const EXPANDED = 'Expanded';
diff --git a/app/assets/javascripts/pages/users/activity_calendar.js b/app/assets/javascripts/pages/users/activity_calendar.js
index 94506d33b33..996e12bc105 100644
--- a/app/assets/javascripts/pages/users/activity_calendar.js
+++ b/app/assets/javascripts/pages/users/activity_calendar.js
@@ -298,7 +298,7 @@ export default class ActivityCalendar {
.querySelector(this.activitiesContainer)
.querySelectorAll('.js-localtime')
.forEach((el) => {
- el.setAttribute('title', formatDate(el.dataset.datetime));
+ el.setAttribute('title', formatDate(el.getAttribute('data-datetime')));
});
})
.catch(() =>
diff --git a/app/assets/javascripts/pipeline_editor/components/file_tree/container.vue b/app/assets/javascripts/pipeline_editor/components/file_tree/container.vue
index 8bffb281211..280cd729a43 100644
--- a/app/assets/javascripts/pipeline_editor/components/file_tree/container.vue
+++ b/app/assets/javascripts/pipeline_editor/components/file_tree/container.vue
@@ -24,7 +24,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
- inject: ['ciConfigPath'],
+ inject: ['ciConfigPath', 'includesHelpPagePath'],
props: {
includes: {
type: Array,
@@ -61,7 +61,14 @@ export default {
<span data-testid="current-config-filename">{{ ciConfigPath }}</span>
</span>
</div>
- <gl-alert v-if="showTip" variant="tip" :title="$options.i18n.tipTitle" @dismiss="dismissTip">
+ <gl-alert
+ v-if="showTip"
+ variant="tip"
+ :title="$options.i18n.tipTitle"
+ :secondary-button-text="$options.i18n.tipBtn"
+ :secondary-button-link="includesHelpPagePath"
+ @dismiss="dismissTip"
+ >
{{ $options.i18n.tipDescription }}
</gl-alert>
<div class="gl-overflow-y-auto">
diff --git a/app/assets/javascripts/pipeline_editor/components/popovers/file_tree_popover.vue b/app/assets/javascripts/pipeline_editor/components/popovers/file_tree_popover.vue
index 029fe2ec909..6270429535d 100644
--- a/app/assets/javascripts/pipeline_editor/components/popovers/file_tree_popover.vue
+++ b/app/assets/javascripts/pipeline_editor/components/popovers/file_tree_popover.vue
@@ -1,6 +1,6 @@
<script>
-import { GlPopover, GlOutsideDirective as Outside } from '@gitlab/ui';
-import { s__, __ } from '~/locale';
+import { GlLink, GlPopover, GlOutsideDirective as Outside, GlSprintf } from '@gitlab/ui';
+import { s__ } from '~/locale';
import { FILE_TREE_POPOVER_DISMISSED_KEY } from '../../constants';
export default {
@@ -8,13 +8,15 @@ export default {
directives: { Outside },
i18n: {
description: s__(
- 'pipelineEditorWalkthrough|You can use the file tree to view your pipeline configuration files.',
+ 'pipelineEditorWalkthrough|You can use the file tree to view your pipeline configuration files. %{linkStart}Learn more%{linkEnd}',
),
- learnMore: __('Learn more'),
},
components: {
+ GlLink,
GlPopover,
+ GlSprintf,
},
+ inject: ['includesHelpPagePath'],
data() {
return {
showPopover: false,
@@ -46,8 +48,12 @@ export default {
data-qa-selector="file_tree_popover"
@close-button-clicked="dismissPermanently"
>
- <div v-outside="closePopover" class="gl-display-flex gl-flex-direction-column">
- <p class="gl-font-base">{{ $options.i18n.description }}</p>
+ <div v-outside="closePopover" class="gl-font-base gl-mb-3">
+ <gl-sprintf :message="$options.i18n.description">
+ <template #link="{ content }">
+ <gl-link :href="includesHelpPagePath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
</div>
</gl-popover>
</template>
diff --git a/app/assets/javascripts/pipeline_editor/index.js b/app/assets/javascripts/pipeline_editor/index.js
index 732fc665c9e..7bfdddbc08a 100644
--- a/app/assets/javascripts/pipeline_editor/index.js
+++ b/app/assets/javascripts/pipeline_editor/index.js
@@ -30,6 +30,7 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => {
defaultBranch,
emptyStateIllustrationPath,
helpPaths,
+ includesHelpPagePath,
lintHelpPagePath,
lintUnavailableHelpPagePath,
needsHelpPagePath,
@@ -118,6 +119,7 @@ export const initPipelineEditor = (selector = '#js-pipeline-editor') => {
defaultBranch,
emptyStateIllustrationPath,
helpPaths,
+ includesHelpPagePath,
lintHelpPagePath,
lintUnavailableHelpPagePath,
needsHelpPagePath,
diff --git a/app/assets/javascripts/projects/commits/components/author_select.vue b/app/assets/javascripts/projects/commits/components/author_select.vue
index 884ef732144..c8a0a3417f3 100644
--- a/app/assets/javascripts/projects/commits/components/author_select.vue
+++ b/app/assets/javascripts/projects/commits/components/author_select.vue
@@ -57,7 +57,7 @@ export default {
if (authorParam) {
commitsSearchInput.setAttribute('disabled', true);
- commitsSearchInput.dataset.toggle = 'tooltip';
+ commitsSearchInput.setAttribute('data-toggle', 'tooltip');
commitsSearchInput.setAttribute('title', tooltipMessage);
this.currentAuthor = authorParam;
}
diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js
index 5e57d4c3d6e..2a7d967cb61 100644
--- a/app/assets/javascripts/sidebar/mount_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_sidebar.js
@@ -119,7 +119,7 @@ function mountAssigneesComponentDeprecated(mediator) {
issuableIid: String(iid),
projectPath: fullPath,
field: el.dataset.field,
- signedIn: Object.hasOwn(el.dataset, 'signedIn'),
+ signedIn: el.hasAttribute('data-signed-in'),
issuableType:
isInIssuePage() || isInIncidentPage() || isInDesignPage()
? IssuableType.Issue
@@ -149,7 +149,7 @@ function mountAssigneesComponent() {
},
provide: {
canUpdate: editable,
- directlyInviteMembers: Object.hasOwn(el.dataset, 'directlyInviteMembers'),
+ directlyInviteMembers: el.hasAttribute('data-directly-invite-members'),
},
render: (createElement) =>
createElement('sidebar-assignees-widget', {
diff --git a/app/assets/javascripts/terraform/index.js b/app/assets/javascripts/terraform/index.js
index 64e90f9227c..34261f3c4db 100644
--- a/app/assets/javascripts/terraform/index.js
+++ b/app/assets/javascripts/terraform/index.js
@@ -39,7 +39,7 @@ export default () => {
props: {
emptyStateImage,
projectPath,
- terraformAdmin: Object.hasOwn(el.dataset, 'terraformAdmin'),
+ terraformAdmin: el.hasAttribute('data-terraform-admin'),
},
});
},
diff --git a/app/assets/javascripts/whats_new/components/app.vue b/app/assets/javascripts/whats_new/components/app.vue
index 0c55cc2f8a6..b74dba686ad 100644
--- a/app/assets/javascripts/whats_new/components/app.vue
+++ b/app/assets/javascripts/whats_new/components/app.vue
@@ -33,7 +33,7 @@ export default {
this.fetchFreshItems();
const body = document.querySelector('body');
- const { namespaceId } = body.dataset;
+ const namespaceId = body.getAttribute('data-namespace-id');
this.track('click_whats_new_drawer', { label: 'namespace_id', value: namespaceId });
},
diff --git a/app/assets/javascripts/whats_new/utils/notification.js b/app/assets/javascripts/whats_new/utils/notification.js
index 41aff202f48..66ee3b1a971 100644
--- a/app/assets/javascripts/whats_new/utils/notification.js
+++ b/app/assets/javascripts/whats_new/utils/notification.js
@@ -1,6 +1,6 @@
export const STORAGE_KEY = 'display-whats-new-notification';
-export const getVersionDigest = (appEl) => appEl.dataset.versionDigest;
+export const getVersionDigest = (appEl) => appEl.getAttribute('data-version-digest');
export const setNotification = (appEl) => {
const versionDigest = getVersionDigest(appEl);
diff --git a/app/controllers/admin/sessions_controller.rb b/app/controllers/admin/sessions_controller.rb
index 9c378f4c883..63579421573 100644
--- a/app/controllers/admin/sessions_controller.rb
+++ b/app/controllers/admin/sessions_controller.rb
@@ -68,7 +68,7 @@ class Admin::SessionsController < ApplicationController
def valid_otp_attempt?(user)
otp_validation_result =
- ::Users::ValidateOtpService.new(user).execute(user_params[:otp_attempt])
+ ::Users::ValidateManualOtpService.new(user).execute(user_params[:otp_attempt])
valid_otp_attempt = otp_validation_result[:status] == :success
return valid_otp_attempt if Gitlab::Database.read_only?
diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb
index b1fa0502193..2e71b4801ed 100644
--- a/app/controllers/profiles/two_factor_auths_controller.rb
+++ b/app/controllers/profiles/two_factor_auths_controller.rb
@@ -44,7 +44,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def create
otp_validation_result =
- ::Users::ValidateOtpService.new(current_user).execute(params[:pin_code])
+ ::Users::ValidateManualOtpService.new(current_user).execute(params[:pin_code])
if otp_validation_result[:status] == :success
ActiveSession.destroy_all_but_current(current_user, session)
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 57444bf9890..66a531b0b3b 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -271,7 +271,7 @@ class SessionsController < Devise::SessionsController
def valid_otp_attempt?(user)
otp_validation_result =
- ::Users::ValidateOtpService.new(user).execute(user_params[:otp_attempt])
+ ::Users::ValidateManualOtpService.new(user).execute(user_params[:otp_attempt])
return true if otp_validation_result[:status] == :success
user.invalidate_otp_backup_code!(user_params[:otp_attempt])
diff --git a/app/helpers/ci/pipeline_editor_helper.rb b/app/helpers/ci/pipeline_editor_helper.rb
index 18557afcb99..da773e3e8a8 100644
--- a/app/helpers/ci/pipeline_editor_helper.rb
+++ b/app/helpers/ci/pipeline_editor_helper.rb
@@ -21,6 +21,7 @@ module Ci
"default-branch" => project.default_branch_or_main,
"empty-state-illustration-path" => image_path('illustrations/empty-state/empty-dag-md.svg'),
"initial-branch-name" => initial_branch,
+ "includes-help-page-path" => help_page_path('ci/yaml/includes'),
"lint-help-page-path" => help_page_path('ci/lint', anchor: 'check-cicd-syntax'),
"lint-unavailable-help-page-path" => help_page_path('ci/pipeline_editor/index', anchor: 'configuration-validation-currently-not-available-message'),
"needs-help-page-path" => help_page_path('ci/yaml/index', anchor: 'needs'),
diff --git a/app/models/alert_management/alert.rb b/app/models/alert_management/alert.rb
index e62bf2ba4ea..047a4d510f9 100644
--- a/app/models/alert_management/alert.rb
+++ b/app/models/alert_management/alert.rb
@@ -119,7 +119,7 @@ module AlertManagement
end
end
- def self.find_ongoing_alert(project, fingerprint)
+ def self.find_unresolved_alert(project, fingerprint)
for_fingerprint(project, fingerprint).not_resolved.take
end
diff --git a/app/models/users/callout.rb b/app/models/users/callout.rb
index a91a3406b22..62b79e02502 100644
--- a/app/models/users/callout.rb
+++ b/app/models/users/callout.rb
@@ -49,7 +49,8 @@ module Users
storage_enforcement_banner_fourth_enforcement_threshold: 46,
attention_requests_top_nav: 47,
attention_requests_side_nav: 48,
- minute_limit_banner: 49
+ minute_limit_banner: 49,
+ preview_user_over_limit_free_plan_alert: 50 # EE-only
}
validates :feature_name,
diff --git a/app/services/alert_management/alerts/update_service.rb b/app/services/alert_management/alerts/update_service.rb
index 0769adc862e..6bdceb0f27b 100644
--- a/app/services/alert_management/alerts/update_service.rb
+++ b/app/services/alert_management/alerts/update_service.rb
@@ -162,8 +162,9 @@ module AlertManagement
end
def filter_duplicate
- # Only need to check if changing to an open status
- return unless params[:status_event] && AlertManagement::Alert.open_status?(status)
+ # Only need to check if changing to a not-resolved status
+ return if params[:status_event].blank? || params[:status_event] == :resolve
+ return unless alert.resolved?
param_errors << unresolved_alert_error if duplicate_alert?
end
@@ -171,24 +172,23 @@ module AlertManagement
def duplicate_alert?
return if alert.fingerprint.blank?
- open_alerts.any? && open_alerts.exclude?(alert)
+ unresolved_alert.present?
end
- def open_alerts
- strong_memoize(:open_alerts) do
- AlertManagement::Alert.for_fingerprint(project, alert.fingerprint).open
+ def unresolved_alert
+ strong_memoize(:unresolved_alert) do
+ AlertManagement::Alert.find_unresolved_alert(project, alert.fingerprint)
end
end
def unresolved_alert_error
_('An %{link_start}alert%{link_end} with the same fingerprint is already open. ' \
'To change the status of this alert, resolve the linked alert.'
- ) % open_alert_url_params
+ ) % unresolved_alert_url_params
end
- def open_alert_url_params
- open_alert = open_alerts.first
- alert_path = Gitlab::Routing.url_helpers.details_project_alert_management_path(project, open_alert)
+ def unresolved_alert_url_params
+ alert_path = Gitlab::Routing.url_helpers.details_project_alert_management_path(project, unresolved_alert)
{
link_start: '<a href="%{url}">'.html_safe % { url: alert_path },
diff --git a/app/services/concerns/alert_management/alert_processing.rb b/app/services/concerns/alert_management/alert_processing.rb
index ce6985527ba..abbfd4d66d4 100644
--- a/app/services/concerns/alert_management/alert_processing.rb
+++ b/app/services/concerns/alert_management/alert_processing.rb
@@ -104,7 +104,7 @@ module AlertManagement
def find_existing_alert
return unless incoming_payload.gitlab_fingerprint
- AlertManagement::Alert.find_ongoing_alert(project, incoming_payload.gitlab_fingerprint)
+ AlertManagement::Alert.find_unresolved_alert(project, incoming_payload.gitlab_fingerprint)
end
def build_new_alert
diff --git a/app/services/users/validate_otp_service.rb b/app/services/users/validate_manual_otp_service.rb
index c8a9f217d22..96a827db13c 100644
--- a/app/services/users/validate_otp_service.rb
+++ b/app/services/users/validate_manual_otp_service.rb
@@ -1,13 +1,13 @@
# frozen_string_literal: true
module Users
- class ValidateOtpService < BaseService
+ class ValidateManualOtpService < BaseService
include ::Gitlab::Auth::Otp::Fortinet
def initialize(current_user)
@current_user = current_user
@strategy = if forti_authenticator_enabled?(current_user)
- ::Gitlab::Auth::Otp::Strategies::FortiAuthenticator.new(current_user)
+ ::Gitlab::Auth::Otp::Strategies::FortiAuthenticator::ManualOtp.new(current_user)
elsif forti_token_cloud_enabled?(current_user)
::Gitlab::Auth::Otp::Strategies::FortiTokenCloud.new(current_user)
else
@@ -19,7 +19,7 @@ module Users
strategy.validate(otp_code)
rescue StandardError => ex
Gitlab::ErrorTracking.log_exception(ex)
- error(message: ex.message)
+ error(ex.message)
end
private
diff --git a/app/services/users/validate_push_otp_service.rb b/app/services/users/validate_push_otp_service.rb
new file mode 100644
index 00000000000..6a914cda28c
--- /dev/null
+++ b/app/services/users/validate_push_otp_service.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Users
+ class ValidatePushOtpService < BaseService
+ include ::Gitlab::Auth::Otp::Fortinet
+
+ def initialize(current_user)
+ @current_user = current_user
+ @strategy = if forti_authenticator_enabled?(current_user)
+ ::Gitlab::Auth::Otp::Strategies::FortiAuthenticator::PushOtp.new(current_user)
+ end
+ end
+
+ def execute
+ strategy.validate
+ rescue StandardError => ex
+ Gitlab::ErrorTracking.log_exception(ex)
+ error(ex.message)
+ end
+
+ private
+
+ attr_reader :strategy
+ end
+end
diff --git a/config/feature_flags/development/namespace_storage_limit_bypass_date_check.yml b/config/feature_flags/development/namespace_storage_limit_bypass_date_check.yml
new file mode 100644
index 00000000000..f70e60d14b5
--- /dev/null
+++ b/config/feature_flags/development/namespace_storage_limit_bypass_date_check.yml
@@ -0,0 +1,8 @@
+---
+name: namespace_storage_limit_bypass_date_check
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86794
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/361785
+milestone: '15.0'
+type: development
+group: group::utilization
+default_enabled: false
diff --git a/config/metrics/counts_28d/20210901221659_p_ci_templates_security_cluster_image_scanning_monthly.yml b/config/metrics/counts_28d/20210901221659_p_ci_templates_security_cluster_image_scanning_monthly.yml
index f3a696627fe..4e1407fd8e3 100644
--- a/config/metrics/counts_28d/20210901221659_p_ci_templates_security_cluster_image_scanning_monthly.yml
+++ b/config/metrics/counts_28d/20210901221659_p_ci_templates_security_cluster_image_scanning_monthly.yml
@@ -6,7 +6,9 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
-status: active
+status: removed
+milestone_removed: '15.0'
+removed_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87000'
milestone: '14.3'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69204
time_frame: 28d
diff --git a/config/metrics/counts_28d/20210901223916_p_ci_templates_implicit_security_cluster_image_scanning_monthly.yml b/config/metrics/counts_28d/20210901223916_p_ci_templates_implicit_security_cluster_image_scanning_monthly.yml
index ddf785a71bd..25c535315d2 100644
--- a/config/metrics/counts_28d/20210901223916_p_ci_templates_implicit_security_cluster_image_scanning_monthly.yml
+++ b/config/metrics/counts_28d/20210901223916_p_ci_templates_implicit_security_cluster_image_scanning_monthly.yml
@@ -6,7 +6,9 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
-status: active
+status: removed
+milestone_removed: '15.0'
+removed_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87000'
milestone: '14.3'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69204
time_frame: 28d
diff --git a/config/metrics/counts_7d/20210901221654_p_ci_templates_security_cluster_image_scanning_weekly.yml b/config/metrics/counts_7d/20210901221654_p_ci_templates_security_cluster_image_scanning_weekly.yml
index 200f46ac22d..b7fd799f8a6 100644
--- a/config/metrics/counts_7d/20210901221654_p_ci_templates_security_cluster_image_scanning_weekly.yml
+++ b/config/metrics/counts_7d/20210901221654_p_ci_templates_security_cluster_image_scanning_weekly.yml
@@ -6,7 +6,9 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
-status: active
+status: removed
+milestone_removed: '15.0'
+removed_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87000'
milestone: '14.3'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69204
time_frame: 7d
diff --git a/config/metrics/counts_7d/20210901223912_p_ci_templates_implicit_security_cluster_image_scanning_weekly.yml b/config/metrics/counts_7d/20210901223912_p_ci_templates_implicit_security_cluster_image_scanning_weekly.yml
index e1ee3f9fd1d..04ba99db306 100644
--- a/config/metrics/counts_7d/20210901223912_p_ci_templates_implicit_security_cluster_image_scanning_weekly.yml
+++ b/config/metrics/counts_7d/20210901223912_p_ci_templates_implicit_security_cluster_image_scanning_weekly.yml
@@ -6,7 +6,9 @@ product_stage: ''
product_group: ''
product_category: ''
value_type: number
-status: active
+status: removed
+milestone_removed: '15.0'
+removed_by_url: 'https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87000'
milestone: '14.3'
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69204
time_frame: 7d
diff --git a/db/post_migrate/20220428133724_schedule_expire_o_auth_tokens.rb b/db/post_migrate/20220428133724_schedule_expire_o_auth_tokens.rb
new file mode 100644
index 00000000000..ef76a097b49
--- /dev/null
+++ b/db/post_migrate/20220428133724_schedule_expire_o_auth_tokens.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class ScheduleExpireOAuthTokens < Gitlab::Database::Migration[2.0]
+ MIGRATION = 'ExpireOAuthTokens'
+ INTERVAL = 2.minutes.freeze
+
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ queue_batched_background_migration(
+ MIGRATION,
+ :oauth_access_tokens,
+ :id,
+ job_interval: INTERVAL
+ )
+ end
+
+ def down
+ Gitlab::Database::BackgroundMigration::BatchedMigration
+ .for_configuration(MIGRATION, :oauth_access_tokens, :id, [])
+ .delete_all
+ end
+end
diff --git a/db/post_migrate/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints.rb b/db/post_migrate/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints.rb
new file mode 100644
index 00000000000..5cffdb641d6
--- /dev/null
+++ b/db/post_migrate/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class UpdateIndexOnAlertsToExcludeNullFingerprints < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ OLD_INDEX_NAME = 'index_partial_am_alerts_on_project_id_and_fingerprint'
+ NEW_INDEX_NAME = 'index_unresolved_alerts_on_project_id_and_fingerprint'
+
+ def up
+ add_concurrent_index :alert_management_alerts,
+ [:project_id, :fingerprint],
+ where: "fingerprint IS NOT NULL and status <> 2",
+ name: NEW_INDEX_NAME,
+ unique: true
+
+ remove_concurrent_index_by_name :alert_management_alerts, OLD_INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :alert_management_alerts,
+ [:project_id, :fingerprint],
+ where: "status <> 2",
+ name: OLD_INDEX_NAME,
+ unique: true
+
+ remove_concurrent_index_by_name :alert_management_alerts, NEW_INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20220428133724 b/db/schema_migrations/20220428133724
new file mode 100644
index 00000000000..9b9f36980f1
--- /dev/null
+++ b/db/schema_migrations/20220428133724
@@ -0,0 +1 @@
+9daf43ece9531540c942ade3939bf86f332701af8a9a0e50bd8ef3b90322baa2 \ No newline at end of file
diff --git a/db/schema_migrations/20220505174658 b/db/schema_migrations/20220505174658
new file mode 100644
index 00000000000..67dfd7ca827
--- /dev/null
+++ b/db/schema_migrations/20220505174658
@@ -0,0 +1 @@
+57dd9ed105c0380b660e8cc450848b8662bf6a41e47cbfac1bcc061934cbc24c \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 2fd4399ed27..19de1d7ba2f 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -28679,8 +28679,6 @@ CREATE INDEX index_pages_domains_on_verified_at_and_enabled_until ON pages_domai
CREATE INDEX index_pages_domains_on_wildcard ON pages_domains USING btree (wildcard);
-CREATE UNIQUE INDEX index_partial_am_alerts_on_project_id_and_fingerprint ON alert_management_alerts USING btree (project_id, fingerprint) WHERE (status <> 2);
-
CREATE INDEX index_partial_ci_builds_on_user_id_name_parser_features ON ci_builds USING btree (user_id, name) WHERE (((type)::text = 'Ci::Build'::text) AND ((name)::text = ANY (ARRAY[('container_scanning'::character varying)::text, ('dast'::character varying)::text, ('dependency_scanning'::character varying)::text, ('license_management'::character varying)::text, ('license_scanning'::character varying)::text, ('sast'::character varying)::text, ('coverage_fuzzing'::character varying)::text, ('secret_detection'::character varying)::text])));
CREATE INDEX index_pat_on_user_id_and_expires_at ON personal_access_tokens USING btree (user_id, expires_at);
@@ -29367,6 +29365,8 @@ CREATE INDEX index_unit_test_failures_failed_at ON ci_unit_test_failures USING b
CREATE UNIQUE INDEX index_unit_test_failures_unique_columns ON ci_unit_test_failures USING btree (unit_test_id, failed_at DESC, build_id);
+CREATE UNIQUE INDEX index_unresolved_alerts_on_project_id_and_fingerprint ON alert_management_alerts USING btree (project_id, fingerprint) WHERE ((fingerprint IS NOT NULL) AND (status <> 2));
+
CREATE UNIQUE INDEX index_upcoming_reconciliations_on_namespace_id ON upcoming_reconciliations USING btree (namespace_id);
CREATE INDEX index_upload_states_failed_verification ON upload_states USING btree (verification_retry_at NULLS FIRST) WHERE (verification_state = 3);
diff --git a/doc/.vale/gitlab/BadPlurals.yml b/doc/.vale/gitlab/BadPlurals.yml
index 81e2a0e563d..efc55ac3d56 100644
--- a/doc/.vale/gitlab/BadPlurals.yml
+++ b/doc/.vale/gitlab/BadPlurals.yml
@@ -11,4 +11,4 @@ level: warning
scope: raw
ignorecase: true
raw:
- - '\w*\(s\)(?<!http\(s\))'
+ - '\b\w+\(s\)(?<!http\(s\))'
diff --git a/doc/administration/configure.md b/doc/administration/configure.md
index e4d8aaf6b48..cba4a23d95a 100644
--- a/doc/administration/configure.md
+++ b/doc/administration/configure.md
@@ -44,7 +44,7 @@ The following table outlines failure modes and mitigation paths for the product
| ----------- | -------------------------- | ----------------------------- | ---------------------------------- | ----- |
| Single Gitaly Node | Downtime - Must restore from backup | Downtime - Must restore from Backup | Downtime - Must wait for outage to end | |
| Single Gitaly Node + Geo Secondary | Downtime - Must restore from backup, can perform a manual failover to secondary | Downtime - Must restore from Backup, errors could have propagated to secondary | Manual intervention - failover to Geo secondary | |
-| Sharded Gitaly Install | Partial Downtime - Only repos on impacted node affected, must restore from backup | Partial Downtime - Only repos on impacted node affected, must restore from backup | Downtime - Must wait for outage to end | |
-| Sharded Gitaly Install + Geo Secondary | Partial Downtime - Only repos on impacted node affected, must restore from backup, could perform manual failover to secondary for impacted repos | Partial Downtime - Only repos on impacted node affected, must restore from backup, errors could have propagated to secondary | Manual intervention - failover to Geo secondary | |
+| Sharded Gitaly Install | Partial Downtime - Only repositories on impacted node affected, must restore from backup | Partial Downtime - Only repositories on impacted node affected, must restore from backup | Downtime - Must wait for outage to end | |
+| Sharded Gitaly Install + Geo Secondary | Partial Downtime - Only repositories on impacted node affected, must restore from backup, could perform manual failover to secondary for impacted repositories | Partial Downtime - Only repositories on impacted node affected, must restore from backup, errors could have propagated to secondary | Manual intervention - failover to Geo secondary | |
| Gitaly Cluster Install* | No Downtime - will swap repository primary to another node after 10 seconds | N/A - All writes are voted on by multiple Gitaly Cluster nodes | Downtime - Must wait for outage to end | Snapshot backups for Gitaly Cluster nodes not supported at this time |
| Gitaly Cluster Install* + Geo Secondary | No Downtime - will swap repository primary to another node after 10 seconds | N/A - All writes are voted on by multiple Gitaly Cluster nodes | Manual intervention - failover to Geo secondary | Snapshot backups for Gitaly Cluster nodes not supported at this time |
diff --git a/doc/administration/geo/disaster_recovery/index.md b/doc/administration/geo/disaster_recovery/index.md
index 0f3dbee671e..baece830318 100644
--- a/doc/administration/geo/disaster_recovery/index.md
+++ b/doc/administration/geo/disaster_recovery/index.md
@@ -643,7 +643,7 @@ avoid a split-brain situation where writes can occur in two different GitLab
instances, complicating recovery efforts. So to prepare for the failover, you
must disable the **primary** site:
-- If you have access to the **primary** Kubernetes cluster, connect to it and disable the GitLab webservice and Sidekiq pods:
+- If you have access to the **primary** Kubernetes cluster, connect to it and disable the GitLab `webservice` and `Sidekiq` pods:
```shell
kubectl --namespace gitlab scale deploy gitlab-geo-webservice-default --replicas=0
diff --git a/doc/administration/geo/replication/multiple_servers.md b/doc/administration/geo/replication/multiple_servers.md
index 87b1aa7fc44..7b800817461 100644
--- a/doc/administration/geo/replication/multiple_servers.md
+++ b/doc/administration/geo/replication/multiple_servers.md
@@ -119,7 +119,7 @@ NOTE:
[NFS](../../nfs.md) can be used in place of Gitaly but is not
recommended.
-### Step 2: Configure Postgres streaming replication
+### Step 2: Configure PostgreSQL streaming replication
Follow the [Geo database replication instructions](../setup/database.md).
@@ -261,7 +261,7 @@ nodes connect to the databases.
NOTE:
Make sure that current node's IP is listed in
`postgresql['md5_auth_cidr_addresses']` setting of the read-replica database to
-allow Rails on this node to connect to Postgres.
+allow Rails on this node to connect to PostgreSQL.
After making these changes [Reconfigure GitLab](../../restart_gitlab.md#omnibus-gitlab-reconfigure) so the changes take effect.
diff --git a/doc/administration/gitaly/configure_gitaly.md b/doc/administration/gitaly/configure_gitaly.md
index e4fa0f61fee..d3b0304eeaa 100644
--- a/doc/administration/gitaly/configure_gitaly.md
+++ b/doc/administration/gitaly/configure_gitaly.md
@@ -833,7 +833,7 @@ especially for large repositories.
Control groups (cgroups) in Linux allow limits to be imposed on how much memory and CPU can be consumed.
See the [`cgroups` Linux man page](https://man7.org/linux/man-pages/man7/cgroups.7.html) for more information.
-cgroups can be useful for protecting the system against resource exhaustion because of overcomsumption of memory and CPU.
+cgroups can be useful for protecting the system against resource exhaustion because of overconsumption of memory and CPU.
Gitaly has built-in cgroups control. When configured, Gitaly assigns Git
processes to a cgroup based on the repository the Git command is operating in.
@@ -915,7 +915,7 @@ gitaly['cgroups_repositories_cpu_shares'] => 512
when Gitaly starts.
- `cgroups_memory_bytes` is the total memory limit that is imposed collectively on all
Git processes that Gitaly spawns. 0 implies no limit.
-- `cgroups_cpu_shares` is the cpu limit that is imposed collectively on all Git
+- `cgroups_cpu_shares` is the CPU limit that is imposed collectively on all Git
processes that Gitaly spawns. 0 implies no limit. The maximum is 1024 shares,
which represents 100% of CPU.
- `cgroups_repositories_count` is the number of cgroups in the cgroups pool. Each time a new Git
diff --git a/doc/administration/package_information/signed_packages.md b/doc/administration/package_information/signed_packages.md
index d7bcfa113ff..24857dcfc27 100644
--- a/doc/administration/package_information/signed_packages.md
+++ b/doc/administration/package_information/signed_packages.md
@@ -14,7 +14,7 @@ These packages are produced by the GitLab CI process, as found in the [Omnibus
## GnuPG Public Keys
-All packages are signed with [GnuPG](https://www.gnupg.org/), in a method appropriate for their format. The key used to sign these packages can be found on [pgp.mit.edu](https://pgp.mit.edu) at [0x3cfcf9baf27eab47](https://pgp.mit.edu/pks/lookup?op=vindex&search=0x3CFCF9BAF27EAB47)
+All packages are signed with [GnuPG](https://www.gnupg.org/), in a method appropriate for their format. The key used to sign these packages can be found on [MIT PGP Public Key Server](https://pgp.mit.edu) at [0x3cfcf9baf27eab47](https://pgp.mit.edu/pks/lookup?op=vindex&search=0x3CFCF9BAF27EAB47)
## Verifying Signatures
diff --git a/doc/administration/postgresql/pgbouncer.md b/doc/administration/postgresql/pgbouncer.md
index a666c1fab95..ed3c662eba3 100644
--- a/doc/administration/postgresql/pgbouncer.md
+++ b/doc/administration/postgresql/pgbouncer.md
@@ -219,12 +219,12 @@ the database. Each of the listed services below use the following formula to def
- `puma` : `max_threads + headroom` (default `14`)
- `max_threads` is configured via: `gitlab['puma']['max_threads']` (default: `4`)
- - `headroom` can be configured via `DB_POOL_HEADROOM` env variable (default to `10`)
+ - `headroom` can be configured via `DB_POOL_HEADROOM` environment variable (default to `10`)
- `sidekiq` : `max_concurrency + 1 + headroom` (default: `61`)
- `max_concurrency` is configured via: `sidekiq['max_concurrency']` (default: `50`)
- - `headroom` can be configured via `DB_POOL_HEADROOM` env variable (default to `10`)
+ - `headroom` can be configured via `DB_POOL_HEADROOM` environment variable (default to `10`)
- `geo-logcursor`: `1+headroom` (default: `11`)
- - `headroom` can be configured via `DB_POOL_HEADROOM` env variable (default to `10`)
+ - `headroom` can be configured via `DB_POOL_HEADROOM` environment variable (default to `10`)
To calculate the `default_pool_size`, multiply the number of instances of `puma`, `sidekiq` and `geo-logcursor` by the
number of connections each can consume as per listed above. The total will be the suggested `default_pool_size`.
diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md
index be9aef2fae1..f70912dbecb 100644
--- a/doc/administration/reference_architectures/10k_users.md
+++ b/doc/administration/reference_architectures/10k_users.md
@@ -2311,7 +2311,7 @@ future with further specific cloud provider details.
- For this setup, we **recommend** and regularly [test](index.md#validation-and-test-results)
[Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine) and [Amazon Elastic Kubernetes Service (EKS)](https://aws.amazon.com/eks/). Other Kubernetes services may also work, but your mileage may vary.
-- Nodes configuration is shown as it is forced to ensure pod vcpu / memory ratios and avoid scaling during **performance testing**.
+- Nodes configuration is shown as it is forced to ensure pod vCPU / memory ratios and avoid scaling during **performance testing**.
- In production deployments, there is no need to assign pods to nodes. A minimum of three nodes in three different availability zones is strongly recommended to align with resilient cloud architecture practices.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
diff --git a/doc/administration/reference_architectures/1k_users.md b/doc/administration/reference_architectures/1k_users.md
index 7213a1eb92b..8d5afd732d9 100644
--- a/doc/administration/reference_architectures/1k_users.md
+++ b/doc/administration/reference_architectures/1k_users.md
@@ -62,7 +62,7 @@ monitor .[#7FFFD4,norank]--> redis
@enduml
```
-The diagram above shows that while GitLab can be installed on a single server, it is internally composed of multiple services. As a GitLab instance is scaled, each of these services are broken out and independently scaled according to the demands placed on them. In some cases PaaS can be leveraged for some services (e.g. Cloud Object Storage for some file systems). For the sake of redundancy some of the services become clusters of nodes storing the same data. In a horizontal configuration of GitLab there are various ancillary services required to coordinate clusters or discover of resources (e.g. PgBouncer for Postgres connection management, Consul for Prometheus end point discovery).
+The diagram above shows that while GitLab can be installed on a single server, it is internally composed of multiple services. As a GitLab instance is scaled, each of these services are broken out and independently scaled according to the demands placed on them. In some cases PaaS can be leveraged for some services (e.g. Cloud Object Storage for some file systems). For the sake of redundancy some of the services become clusters of nodes storing the same data. In a horizontal configuration of GitLab there are various ancillary services required to coordinate clusters or discover of resources (e.g. PgBouncer for PostgreSQL connection management, Consul for Prometheus end point discovery).
## Requirements
diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md
index 89f403c9bb1..d9f349b59c7 100644
--- a/doc/administration/reference_architectures/25k_users.md
+++ b/doc/administration/reference_architectures/25k_users.md
@@ -2309,7 +2309,7 @@ future with further specific cloud provider details.
- For this setup, we **recommend** and regularly [test](index.md#validation-and-test-results)
[Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine) and [Amazon Elastic Kubernetes Service (EKS)](https://aws.amazon.com/eks/). Other Kubernetes services may also work, but your mileage may vary.
-- Nodes configuration is shown as it is forced to ensure pod vcpu / memory ratios and avoid scaling during **performance testing**.
+- Nodes configuration is shown as it is forced to ensure pod vCPU / memory ratios and avoid scaling during **performance testing**.
- In production deployments, there is no need to assign pods to nodes. A minimum of three nodes in three different availability zones is strongly recommended to align with resilient cloud architecture practices.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md
index c1f53c5ebad..d029f356612 100644
--- a/doc/administration/reference_architectures/2k_users.md
+++ b/doc/administration/reference_architectures/2k_users.md
@@ -1014,7 +1014,7 @@ future with further specific cloud provider details.
- For this setup, we **recommend** and regularly [test](index.md#validation-and-test-results)
[Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine) and [Amazon Elastic Kubernetes Service (EKS)](https://aws.amazon.com/eks/). Other Kubernetes services may also work, but your mileage may vary.
-- Nodes configuration is shown as it is forced to ensure pod vcpu / memory ratios and avoid scaling during **performance testing**.
+- Nodes configuration is shown as it is forced to ensure pod vCPU / memory ratios and avoid scaling during **performance testing**.
- In production deployments, there is no need to assign pods to nodes. A minimum of three nodes in three different availability zones is strongly recommended to align with resilient cloud architecture practices.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md
index a3a2bedc034..14b8982766c 100644
--- a/doc/administration/reference_architectures/3k_users.md
+++ b/doc/administration/reference_architectures/3k_users.md
@@ -2269,7 +2269,7 @@ future with further specific cloud provider details.
- For this setup, we **recommend** and regularly [test](index.md#validation-and-test-results)
[Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine) and [Amazon Elastic Kubernetes Service (EKS)](https://aws.amazon.com/eks/). Other Kubernetes services may also work, but your mileage may vary.
-- Nodes configuration is shown as it is forced to ensure pod vcpu / memory ratios and avoid scaling during **performance testing**.
+- Nodes configuration is shown as it is forced to ensure pod vCPU / memory ratios and avoid scaling during **performance testing**.
- In production deployments, there is no need to assign pods to nodes. A minimum of three nodes in three different availability zones is strongly recommended to align with resilient cloud architecture practices.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md
index 27685925128..078e3a289cc 100644
--- a/doc/administration/reference_architectures/50k_users.md
+++ b/doc/administration/reference_architectures/50k_users.md
@@ -2325,7 +2325,7 @@ future with further specific cloud provider details.
- For this setup, we **recommend** and regularly [test](index.md#validation-and-test-results)
[Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine) and [Amazon Elastic Kubernetes Service (EKS)](https://aws.amazon.com/eks/). Other Kubernetes services may also work, but your mileage may vary.
-- Nodes configuration is shown as it is forced to ensure pod vcpu / memory ratios and avoid scaling during **performance testing**.
+- Nodes configuration is shown as it is forced to ensure pod vCPU / memory ratios and avoid scaling during **performance testing**.
- In production deployments, there is no need to assign pods to nodes. A minimum of three nodes in three different availability zones is strongly recommended to align with resilient cloud architecture practices.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md
index 6c0b66ab31e..d18c77902f6 100644
--- a/doc/administration/reference_architectures/5k_users.md
+++ b/doc/administration/reference_architectures/5k_users.md
@@ -2244,7 +2244,7 @@ future with further specific cloud provider details.
- For this setup, we **recommend** and regularly [test](index.md#validation-and-test-results)
[Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine) and [Amazon Elastic Kubernetes Service (EKS)](https://aws.amazon.com/eks/). Other Kubernetes services may also work, but your mileage may vary.
-- Nodes configuration is shown as it is forced to ensure pod vcpu / memory ratios and avoid scaling during **performance testing**.
+- Nodes configuration is shown as it is forced to ensure pod vCPU / memory ratios and avoid scaling during **performance testing**.
- In production deployments, there is no need to assign pods to nodes. A minimum of three nodes in three different availability zones is strongly recommended to align with resilient cloud architecture practices.
Next are the backend components that run on static compute VMs via Omnibus (or External PaaS
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 1ee08281db4..90ec7e35495 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -19322,6 +19322,7 @@ Name of the feature that the callout is for.
| <a id="usercalloutfeaturenameenumpersonal_access_token_expiry"></a>`PERSONAL_ACCESS_TOKEN_EXPIRY` | Callout feature name for personal_access_token_expiry. |
| <a id="usercalloutfeaturenameenumpipeline_needs_banner"></a>`PIPELINE_NEEDS_BANNER` | Callout feature name for pipeline_needs_banner. |
| <a id="usercalloutfeaturenameenumpipeline_needs_hover_tip"></a>`PIPELINE_NEEDS_HOVER_TIP` | Callout feature name for pipeline_needs_hover_tip. |
+| <a id="usercalloutfeaturenameenumpreview_user_over_limit_free_plan_alert"></a>`PREVIEW_USER_OVER_LIMIT_FREE_PLAN_ALERT` | Callout feature name for preview_user_over_limit_free_plan_alert. |
| <a id="usercalloutfeaturenameenumprofile_personal_access_token_expiry"></a>`PROFILE_PERSONAL_ACCESS_TOKEN_EXPIRY` | Callout feature name for profile_personal_access_token_expiry. |
| <a id="usercalloutfeaturenameenumregistration_enabled_callout"></a>`REGISTRATION_ENABLED_CALLOUT` | Callout feature name for registration_enabled_callout. |
| <a id="usercalloutfeaturenameenumsecurity_configuration_devops_alert"></a>`SECURITY_CONFIGURATION_DEVOPS_ALERT` | Callout feature name for security_configuration_devops_alert. |
diff --git a/doc/api/wikis.md b/doc/api/wikis.md
index b7c9d0d6008..0122872becf 100644
--- a/doc/api/wikis.md
+++ b/doc/api/wikis.md
@@ -68,7 +68,7 @@ GET /projects/:id/wikis/:slug
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) |
-| `slug` | string | yes | URLencoded slug (a unique string) of the wiki page, such as `dir%2Fpage_name` |
+| `slug` | string | yes | URL encoded slug (a unique string) of the wiki page, such as `dir%2Fpage_name` |
| `render_html` | boolean | no | Return the rendered HTML of the wiki page |
| `version` | string | no | Wiki page version sha |
diff --git a/doc/architecture/blueprints/database_testing/index.md b/doc/architecture/blueprints/database_testing/index.md
index 8c0cb550d61..22857abf176 100644
--- a/doc/architecture/blueprints/database_testing/index.md
+++ b/doc/architecture/blueprints/database_testing/index.md
@@ -100,7 +100,7 @@ The short-term goal is detailed in [this epic](https://gitlab.com/groups/gitlab-
### Mid-term - Improved feedback, query testing and background migration testing
-Mid-term, we plan to expand the level of detail the testing pipeline reports back to the merge requet and expand its scope to cover query testing, too. By doing so, we use our experience from database code reviews and using thin-clone technology and bring this back closer to the GitLab workflow. Instead of reaching out to different tools (`postgres.ai`, `joe`, Slack, plan visualizations, and so on) we bring this back to GitLab and working directly on the merge request.
+Mid-term, we plan to expand the level of detail the testing pipeline reports back to the merge request and expand its scope to cover query testing, too. By doing so, we use our experience from database code reviews and using thin-clone technology and bring this back closer to the GitLab workflow. Instead of reaching out to different tools (`postgres.ai`, `joe`, Slack, plan visualizations, and so on) we bring this back to GitLab and working directly on the merge request.
Secondly, we plan to cover background migrations testing, too. These are typically data migrations that are scheduled to run over a long period of time. The success of both the scheduling phase and the job execution phase typically depends a lot on data distribution - which only surfaces when running these migrations on actual production data. In order to become confident about a background migration, we plan to provide the following feedback:
diff --git a/doc/ci/git_submodules.md b/doc/ci/git_submodules.md
index 23055514839..5f22fb894e5 100644
--- a/doc/ci/git_submodules.md
+++ b/doc/ci/git_submodules.md
@@ -83,4 +83,4 @@ You can check documentation for your specific OS to learn how to find and displa
hidden files.
If there is no `.gitmodules` file, it's possible the submodule settings are in a
-[gitconfig](https://www.atlassian.com/git/tutorials/setting-up-a-repository/git-config) file.
+[`git config`](https://www.atlassian.com/git/tutorials/setting-up-a-repository/git-config) file.
diff --git a/doc/ci/quick_start/index.md b/doc/ci/quick_start/index.md
index cffe4bee3be..d340e6afb64 100644
--- a/doc/ci/quick_start/index.md
+++ b/doc/ci/quick_start/index.md
@@ -19,7 +19,7 @@ If you are migrating from another CI/CD tool, view this documentation:
- [Migrate from CircleCI](../migration/circleci.md).
- [Migrate from Jenkins](../migration/jenkins.md).
-> - <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>&nbsp;Watch [First time GitLab & CI/CD](https://www.youtube.com/watch?v=kTNfi5z6Uvk&t=553s). This includes a quick introduction to GitLab, the first steps with CI/CD, building a Go project, running tests, using the CI/CD pipeline editor, detecting secrets and security vulnerabilities and offers more exercises for async practice.
+> - <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>&nbsp;Watch [First time GitLab & CI/CD](https://www.youtube.com/watch?v=kTNfi5z6Uvk&t=553s). This includes a quick introduction to GitLab, the first steps with CI/CD, building a Go project, running tests, using the CI/CD pipeline editor, detecting secrets and security vulnerabilities and offers more exercises for asynchronous practice.
> - <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>&nbsp;Watch [Intro to GitLab CI](https://www.youtube.com/watch?v=l5705U8s_nQ&t=358s). This workshop uses the Web IDE to quickly get going with building source code using CI/CD, and run unit tests.
## CI/CD process overview
diff --git a/doc/development/database/migrations_for_multiple_databases.md b/doc/development/database/migrations_for_multiple_databases.md
index ac99818a7c7..ce326a6ce4a 100644
--- a/doc/development/database/migrations_for_multiple_databases.md
+++ b/doc/development/database/migrations_for_multiple_databases.md
@@ -43,7 +43,7 @@ The DDL migrations are all migrations that:
1. Add or remove a column with or without a default value (for example, `add_column`).
1. Create or drop trigger functions (for example, `create_trigger_function`).
1. Attach or detach triggers from tables (for example, `track_record_deletions`, `untrack_record_deletions`).
-1. Prepare or not async indexes (for example, `prepare_async_index`, `unprepare_async_index_by_name`).
+1. Prepare or not asynchronous indexes (for example, `prepare_async_index`, `unprepare_async_index_by_name`).
As such DDL migrations **CANNOT**:
@@ -159,7 +159,7 @@ end
### The special purpose of `gitlab_shared`
-As described in [gitlab_schema](multiple_databases.md#the-special-purpose-of-gitlab_shared),
+As described in [`gitlab_schema`](multiple_databases.md#the-special-purpose-of-gitlab_shared),
the `gitlab_shared` tables are allowed to contain data across all databases. This implies
that such migrations should run across all databases to modify structure (DDL) or modify data (DML).
diff --git a/doc/development/database/multiple_databases.md b/doc/development/database/multiple_databases.md
index 176e1a54ea2..c622d4f50ff 100644
--- a/doc/development/database/multiple_databases.md
+++ b/doc/development/database/multiple_databases.md
@@ -526,7 +526,7 @@ ci_build.update!(updated_at: Time.current) # CI DB
ci_build.project.update!(updated_at: Time.current) # Main DB
```
-##### Async processing
+##### Asynchronous processing
If we need more guarantee that an operation finishes the work consistently we can execute it
within a background job. A background job is scheduled asynchronously and retried several times
diff --git a/doc/development/experiment_guide/experiment_rollout.md b/doc/development/experiment_guide/experiment_rollout.md
index bc700b13600..afa32d75221 100644
--- a/doc/development/experiment_guide/experiment_rollout.md
+++ b/doc/development/experiment_guide/experiment_rollout.md
@@ -25,7 +25,7 @@ When there is a case on GitLab.com (SaaS) that necessitates turning off all expe
You can toggle experiments on SaaS on and off using the `gitlab_experiment` [feature flag](../feature_flags).
-This can be done via chatops:
+This can be done via ChatOps:
- [disable](../feature_flags/controls.md#disabling-feature-flags): `/chatops run feature set gitlab_experiment false`
- [enable](../feature_flags/controls.md#process): `/chatops run feature delete gitlab_experiment`
diff --git a/doc/development/fe_guide/vue3_migration.md b/doc/development/fe_guide/vue3_migration.md
index 8c8bb36d962..068b0c5b475 100644
--- a/doc/development/fe_guide/vue3_migration.md
+++ b/doc/development/fe_guide/vue3_migration.md
@@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
The migration from Vue 2 to 3 is tracked in epic [&6252](https://gitlab.com/groups/gitlab-org/-/epics/6252).
-To ease migration to Vue 3.x, we have added [eslint rules](https://gitlab.com/gitlab-org/frontend/eslint-plugin/-/merge_requests/50)
+To ease migration to Vue 3.x, we have added [ESLint rules](https://gitlab.com/gitlab-org/frontend/eslint-plugin/-/merge_requests/50)
that prevent us from using the following deprecated features in the codebase.
## Vue filters
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index d26e20a42b9..9274c059cb7 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -505,7 +505,7 @@ end
### When to use the helper method
You can **only** use the `with_lock_retries` helper method when the execution is not already inside
-an open transaction (using Postgres subtransactions is discouraged). It can be used with
+an open transaction (using PostgreSQL subtransactions is discouraged). It can be used with
standard Rails migration helper methods. Calling more than one migration
helper is not a problem if they're executed on the same table.
diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md
index fd5dff55642..fec3dd3e1fb 100644
--- a/doc/development/pipelines.md
+++ b/doc/development/pipelines.md
@@ -53,7 +53,7 @@ In summary:
To identify the minimal set of tests needed, we use the [`test_file_finder` gem](https://gitlab.com/gitlab-org/ci-cd/test_file_finder), with two strategies:
-- dynamic mapping from test coverage tracing (generated via the [Crystalball gem](https://github.com/toptal/crystalball))
+- dynamic mapping from test coverage tracing (generated via the [`Crystalball` gem](https://github.com/toptal/crystalball))
([see where it's used](https://gitlab.com/gitlab-org/gitlab/-/blob/47d507c93779675d73a05002e2ec9c3c467cd698/tooling/bin/find_tests#L15))
- static mapping maintained in the [`tests.yml` file](https://gitlab.com/gitlab-org/gitlab/-/blob/master/tests.yml) for special cases that cannot
be mapped via coverage tracing ([see where it's used](https://gitlab.com/gitlab-org/gitlab/-/blob/47d507c93779675d73a05002e2ec9c3c467cd698/tooling/bin/find_tests#L12))
diff --git a/doc/development/product_qualified_lead_guide/index.md b/doc/development/product_qualified_lead_guide/index.md
index 2395689ada2..dcd8b33e5c5 100644
--- a/doc/development/product_qualified_lead_guide/index.md
+++ b/doc/development/product_qualified_lead_guide/index.md
@@ -16,8 +16,8 @@ A hand-raise PQL is a user who requests to speak to sales from within the produc
1. Set up CustomersDot to talk to a staging instance of Platypus.
1. Set up CustomersDot using the [normal install instructions](https://gitlab.com/gitlab-org/customers-gitlab-com/-/blob/staging/doc/setup/installation_steps.md).
-1. Set the `CUSTOMER_PORTAL_URL` env var to your local (or ngrok) URL of your CustomersDot instance.
-1. Place `export CUSTOMER_PORTAL_URL='https://XXX.ngrok.io/'` in your shell rc script (~/.zshrc or ~/.bash_profile or ~/.bashrc) and restart GDK.
+1. Set the `CUSTOMER_PORTAL_URL` environment variable to your local (or ngrok) URL of your CustomersDot instance.
+1. Place `export CUSTOMER_PORTAL_URL='https://XXX.ngrok.io/'` in your shell rc script (`~/.zshrc` or `~/.bash_profile` or `~/.bashrc`) and restart GDK.
1. Enter the credentials on CustomersDot development to Platypus in your `/config/secrets.yml` and restart. Credentials for the Platypus Staging are in the 1Password Growth vault. The URL for staging is `https://staging.ci.nexus.gitlabenvironment.cloud`.
```yaml
diff --git a/doc/development/secure_coding_guidelines.md b/doc/development/secure_coding_guidelines.md
index 270686b3cd3..3e46891d20e 100644
--- a/doc/development/secure_coding_guidelines.md
+++ b/doc/development/secure_coding_guidelines.md
@@ -660,7 +660,7 @@ cfg := &tls.Config{
}
```
-For **Ruby**, you can use [HTTParty](https://github.com/jnunemaker/httparty) and specify TLS 1.3 version as well as ciphers:
+For **Ruby**, you can use [`HTTParty`](https://github.com/jnunemaker/httparty) and specify TLS 1.3 version as well as ciphers:
Whenever possible this example should be **avoided** for security purposes:
@@ -705,7 +705,7 @@ tls.Config{
This example was taken [here](https://gitlab.com/gitlab-org/cluster-integration/gitlab-agent/-/blob/871b52dc700f1a66f6644fbb1e78a6d463a6ff83/internal/tool/tlstool/tlstool.go#L72).
-For **Ruby**, you can use again [HTTParty](https://github.com/jnunemaker/httparty) and specify this time TLS 1.2 version alongside with the recommended ciphers:
+For **Ruby**, you can use again [`HTTParty`](https://github.com/jnunemaker/httparty) and specify this time TLS 1.2 version alongside with the recommended ciphers:
```ruby
response = GitLab::HTTP.perform_request(Net::HTTP::Get, 'https://gitlab.com', ssl_version: :TLSv1_2, ciphers: ['ECDHE-ECDSA-AES128-GCM-SHA256', 'ECDHE-RSA-AES128-GCM-SHA256', 'ECDHE-ECDSA-AES256-GCM-SHA384', 'ECDHE-RSA-AES256-GCM-SHA384', 'ECDHE-ECDSA-CHACHA20-POLY1305', 'ECDHE-RSA-CHACHA20-POLY1305'])
@@ -873,7 +873,7 @@ If a vulnerable application extracts an archive file with any of these file name
#### Ruby
-For zip files, the [rubyzip](https://rubygems.org/gems/rubyzip) Ruby gem is already patched against the Zip Slip vulnerability and will refuse to extract files that try to perform directory traversal, so for this vulnerable example we will extract a `tar.gz` file with `Gem::Package::TarReader`:
+For zip files, the [`rubyzip`](https://rubygems.org/gems/rubyzip) Ruby gem is already patched against the Zip Slip vulnerability and will refuse to extract files that try to perform directory traversal, so for this vulnerable example we will extract a `tar.gz` file with `Gem::Package::TarReader`:
```ruby
# Vulnerable tar.gz extraction example!
@@ -1072,7 +1072,7 @@ Symlink attacks makes it possible for an attacker to read the contents of arbitr
#### Ruby
-For zip files, the [rubyzip](https://rubygems.org/gems/rubyzip) Ruby gem is already patched against symlink attacks as it simply ignores symbolic links, so for this vulnerable example we will extract a `tar.gz` file with `Gem::Package::TarReader`:
+For zip files, the [`rubyzip`](https://rubygems.org/gems/rubyzip) Ruby gem is already patched against symlink attacks as it simply ignores symbolic links, so for this vulnerable example we will extract a `tar.gz` file with `Gem::Package::TarReader`:
```ruby
# Vulnerable tar.gz extraction example!
diff --git a/doc/development/snowplow/implementation.md b/doc/development/snowplow/implementation.md
index 162b77772f9..6e89ada8923 100644
--- a/doc/development/snowplow/implementation.md
+++ b/doc/development/snowplow/implementation.md
@@ -36,7 +36,7 @@ as base:
_\* Undergoes a pseudonymization process at the collector level._
-These properties [are overriden](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/tracking/get_standard_context.js)
+These properties [are overridden](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/tracking/get_standard_context.js)
with frontend-specific values, like `source` (`gitlab-javascript`), `google_analytics_id`
and the custom `extra` object. You can modify this object for any subsequent
structured event that fires, although this is not recommended.
@@ -83,7 +83,7 @@ The following example shows `data-track-*` attributes assigned to a button:
| `data-track-action` | true | Action the user is taking. Clicks must be prepended with `click` and activations must be prepended with `activate`. For example, focusing a form field is `activate_form_input` and clicking a button is `click_button`. Replaces `data-track-event`, which was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/290962) in GitLab 13.11. |
| `data-track-label` | false | The specific element or object to act on. This can be: the label of the element, for example, a tab labeled 'Create from template' for `create_from_template`; a unique identifier if no text is available, for example, `groups_dropdown_close` for closing the Groups dropdown in the top bar; or the name or title attribute of a record being created. |
| `data-track-property` | false | Any additional property of the element, or object being acted on. |
-| `data-track-value` | false | Describes a numeric value (decimal) directly related to the event. This could be the value of an input. For example, `10` when clicking `internal` visibility. If omitted, this is the element's `value` property or `undefined`. For checkboxes, the default value is the element's checked attribute or `0` when unchecked. The value is parsed as numeric before sendind the event. |
+| `data-track-value` | false | Describes a numeric value (decimal) directly related to the event. This could be the value of an input. For example, `10` when clicking `internal` visibility. If omitted, this is the element's `value` property or `undefined`. For checkboxes, the default value is the element's checked attribute or `0` when unchecked. The value is parsed as numeric before sending the event. |
| `data-track-extra` | false | A key-value pair object passed as a valid JSON string. This attribute is added to the `extra` property in our [`gitlab_standard`](schemas.md#gitlab_standard) schema. |
| `data-track-context` | false | To append a custom context object, passed as a valid JSON string. |
diff --git a/doc/install/aws/gitlab_hybrid_on_aws.md b/doc/install/aws/gitlab_hybrid_on_aws.md
index e8fbd7c0f33..055cb4c3efb 100644
--- a/doc/install/aws/gitlab_hybrid_on_aws.md
+++ b/doc/install/aws/gitlab_hybrid_on_aws.md
@@ -153,7 +153,7 @@ On Demand pricing is used in this table for comparisons, but should not be used
| Supporting services such as NGINX, Prometheus, etc | 2 vCPU, 8 GB | | |
| **GitLab Ref Arch Raw Total K8s Node Capacity** | 16 vCPU, 32 GB | | |
| One Node for Overhead and Miscellaneous (EKS Cluster AutoScaler, Grafana, Prometheus, etc) | + 8 vCPU, 16GB | | |
-| **Grand Total w/ Overheads**<br />Minimum hosts = 3 | 24 vCPU, 48 GB | **c5.2xlarge** <br />(8vcpu/16GB) x 3 nodes<br />24 vCPU, 48 GB | $1.02/hr |
+| **Grand Total w/ Overheads**<br />Minimum hosts = 3 | 24 vCPU, 48 GB | **c5.2xlarge** <br />(8vCPU/16GB) x 3 nodes<br />24 vCPU, 48 GB | $1.02/hr |
| **Idle Configuration (Scaled-In)** | 16 vCPU, 32 GB | **c5.2xlarge** x 2 | $0.68/hr |
NOTE:
@@ -206,7 +206,7 @@ On Demand pricing is used in this table for comparisons, but should not be used
| Supporting services such as NGINX, Prometheus, etc | [2 allocations](../../administration/reference_architectures/3k_users.md#cluster-topology) x ([2 vCPU and 7.5 GB](../../administration/reference_architectures/3k_users.md#cluster-topology)) = <br />4 vCPU, 15 GB | | |
| **GitLab Ref Arch Raw Total K8s Node Capacity** | 32 vCPU, 56 GB | | |
| One Node for Overhead and Miscellaneous (EKS Cluster AutoScaler, Grafana, Prometheus, etc) | + 16 vCPU, 32GB | | |
-| **Grand Total w/ Overheads Full Scale**<br />Minimum hosts = 3 | 48 vCPU, 88 GB | **c5.2xlarge** (8vcpu/16GB) x 5 nodes<br />40 vCPU, 80 GB<br />[Full Fixed Scale GPT Test Results](https://gitlab.com/guided-explorations/aws/implementation-patterns/gitlab-cloud-native-hybrid-on-eks/-/blob/master/gitlab-alliances-testing/3K/3k-QuickStart-ARM-RDS-Cache_v13-12-3-ee_2021-07-23_124216/3k-QuickStart-ARM-RDS-Cache_v13-12-3-ee_2021-07-23_124216_results.txt) | $1.70/hr |
+| **Grand Total w/ Overheads Full Scale**<br />Minimum hosts = 3 | 48 vCPU, 88 GB | **c5.2xlarge** (8vCPU/16GB) x 5 nodes<br />40 vCPU, 80 GB<br />[Full Fixed Scale GPT Test Results](https://gitlab.com/guided-explorations/aws/implementation-patterns/gitlab-cloud-native-hybrid-on-eks/-/blob/master/gitlab-alliances-testing/3K/3k-QuickStart-ARM-RDS-Cache_v13-12-3-ee_2021-07-23_124216/3k-QuickStart-ARM-RDS-Cache_v13-12-3-ee_2021-07-23_124216_results.txt) | $1.70/hr |
| **Possible Idle Configuration (Scaled-In 75% - round up)**<br />Pod autoscaling must be also adjusted to enable lower idling configuration. | 24 vCPU, 48 GB | c5.2xlarge x 4 | $1.36/hr |
Other combinations of node type and quantity can be used to meet the Grand Total. Due to the properties of pods, hosts that are overly small may have significant unused capacity.
@@ -260,10 +260,10 @@ On Demand pricing is used in this table for comparisons, but should not be used
| Supporting services such as NGINX, Prometheus, etc | [2 allocations](../../administration/reference_architectures/5k_users.md#cluster-topology) x ([2 vCPU and 7.5 GB](../../administration/reference_architectures/5k_users.md#cluster-topology)) = <br />4 vCPU, 15 GB | | |
| **GitLab Ref Arch Raw Total K8s Node Capacity** | 62 vCPU, 96.5 GB | | |
| One Node for Quick Start Overhead and Miscellaneous (EKS Cluster AutoScaler, Grafana, Prometheus, etc) | + 8 vCPU, 16GB | | |
-| **Grand Total w/ Overheads Full Scale**<br />Minimum hosts = 3 | 70 vCPU, 112.5 GB | **c5.2xlarge** (8vcpu/16GB) x 9 nodes<br />72 vCPU, 144 GB<br />[Full Fixed Scale GPT Test Results](https://gitlab.com/guided-explorations/aws/implementation-patterns/gitlab-cloud-native-hybrid-on-eks/-/blob/master/gitlab-alliances-testing/5K/5k-QuickStart-ARM-RDS-Redis_v13-12-3-ee_2021-07-23_140128/5k-QuickStart-ARM-RDS-Redis_v13-12-3-ee_2021-07-23_140128_results.txt) | $2.38/hr |
+| **Grand Total w/ Overheads Full Scale**<br />Minimum hosts = 3 | 70 vCPU, 112.5 GB | **c5.2xlarge** (8vCPU/16GB) x 9 nodes<br />72 vCPU, 144 GB<br />[Full Fixed Scale GPT Test Results](https://gitlab.com/guided-explorations/aws/implementation-patterns/gitlab-cloud-native-hybrid-on-eks/-/blob/master/gitlab-alliances-testing/5K/5k-QuickStart-ARM-RDS-Redis_v13-12-3-ee_2021-07-23_140128/5k-QuickStart-ARM-RDS-Redis_v13-12-3-ee_2021-07-23_140128_results.txt) | $2.38/hr |
| **Possible Idle Configuration (Scaled-In 75% - round up)**<br />Pod autoscaling must be also adjusted to enable lower idling configuration. | 24 vCPU, 48 GB | c5.2xlarge x 7 | $1.85/hr |
-Other combinations of node type and quantity can be used to meet the Grand Total. Due to the cpu and memory requirements of pods, hosts that are overly small may have significant unused capacity.
+Other combinations of node type and quantity can be used to meet the Grand Total. Due to the CPU and memory requirements of pods, hosts that are overly small may have significant unused capacity.
NOTE:
If EKS node autoscaling is employed, it is likely that your average loading will run lower than this, especially during non-working hours and weekends.
@@ -313,10 +313,10 @@ On Demand pricing is used in this table for comparisons, but should not be used
| Supporting services such as NGINX, Prometheus, etc | [2 allocations](../../administration/reference_architectures/10k_users.md#cluster-topology) x ([2 vCPU and 7.5 GB](../../administration/reference_architectures/10k_users.md#cluster-topology))<br />4 vCPU, 15 GB | | |
| **GitLab Ref Arch Raw Total K8s Node Capacity** | 128 vCPU, 158 GB | | |
| One Node for Overhead and Miscellaneous (EKS Cluster AutoScaler, Grafana, Prometheus, etc) | + 16 vCPU, 32GB | | |
-| **Grand Total w/ Overheads Fully Scaled**<br />Minimum hosts = 3 | 142 vCPU, 190 GB | **c5.4xlarge** (16vcpu/32GB) x 9 nodes<br />144 vCPU, 288GB<br /><br />[Full Fixed Scale GPT Test Results](https://gitlab.com/guided-explorations/aws/implementation-patterns/gitlab-cloud-native-hybrid-on-eks/-/blob/master/gitlab-alliances-testing/10K/GL-CloudNative-10k-RDS-Graviton_v13-12-3-ee_2021-07-08_194647/GL-CloudNative-10k-RDS-Graviton_v13-12-3-ee_2021-07-08_194647_results.txt) | $6.12/hr |
+| **Grand Total w/ Overheads Fully Scaled**<br />Minimum hosts = 3 | 142 vCPU, 190 GB | **c5.4xlarge** (16vCPU/32GB) x 9 nodes<br />144 vCPU, 288GB<br /><br />[Full Fixed Scale GPT Test Results](https://gitlab.com/guided-explorations/aws/implementation-patterns/gitlab-cloud-native-hybrid-on-eks/-/blob/master/gitlab-alliances-testing/10K/GL-CloudNative-10k-RDS-Graviton_v13-12-3-ee_2021-07-08_194647/GL-CloudNative-10k-RDS-Graviton_v13-12-3-ee_2021-07-08_194647_results.txt) | $6.12/hr |
| **Possible Idle Configuration (Scaled-In 75% - round up)**<br />Pod autoscaling must be also adjusted to enable lower idling configuration. | 40 vCPU, 80 GB | c5.4xlarge x 7<br /><br />[Elastic Auto Scale GPT Test Results](https://gitlab.com/guided-explorations/aws/implementation-patterns/gitlab-cloud-native-hybrid-on-eks/-/blob/master/gitlab-alliances-testing/10K/GL-CloudNative-10k-AutoScaling-Test_v13-12-3-ee_2021-07-09_115139/GL-CloudNative-10k-AutoScaling-Test_v13-12-3-ee_2021-07-09_115139_results.txt) | $4.76/hr |
-Other combinations of node type and quantity can be used to meet the Grand Total. Due to the cpu and memory requirements of pods, hosts that are overly small may have significant unused capacity.
+Other combinations of node type and quantity can be used to meet the Grand Total. Due to the CPU and memory requirements of pods, hosts that are overly small may have significant unused capacity.
NOTE:
If EKS node autoscaling is employed, it is likely that your average loading will run lower than this, especially during non-working hours and weekends.
@@ -366,10 +366,10 @@ On Demand pricing is used in this table for comparisons, but should not be used
| Supporting services such as NGINX, Prometheus, etc | [2 allocations](../../administration/reference_architectures/10k_users.md#cluster-topology) x ([2 vCPU and 7.5 GB](../../administration/reference_architectures/10k_users.md#cluster-topology))<br />4 vCPU, 15 GB | | |
| **GitLab Ref Arch Raw Total K8s Node Capacity** | 428 vCPU, 533 GB | | |
| One Node for Overhead and Miscellaneous (EKS Cluster AutoScaler, Grafana, Prometheus, etc) | + 16 vCPU, 32GB | | |
-| **Grand Total w/ Overheads Fully Scaled**<br />Minimum hosts = 3 | 444 vCPU, 565 GB | **c5.4xlarge** (16vcpu/32GB) x 28 nodes<br />448 vCPU, 896GB<br /><br />[Full Fixed Scale GPT Test Results](https://gitlab.com/guided-explorations/aws/implementation-patterns/gitlab-cloud-native-hybrid-on-eks/-/blob/master/gitlab-alliances-testing/50K/50k-Fixed-Scale-Test_v13-12-3-ee_2021-08-13_172819/50k-Fixed-Scale-Test_v13-12-3-ee_2021-08-13_172819_results.txt) | $19.04/hr |
+| **Grand Total w/ Overheads Fully Scaled**<br />Minimum hosts = 3 | 444 vCPU, 565 GB | **c5.4xlarge** (16vCPU/32GB) x 28 nodes<br />448 vCPU, 896GB<br /><br />[Full Fixed Scale GPT Test Results](https://gitlab.com/guided-explorations/aws/implementation-patterns/gitlab-cloud-native-hybrid-on-eks/-/blob/master/gitlab-alliances-testing/50K/50k-Fixed-Scale-Test_v13-12-3-ee_2021-08-13_172819/50k-Fixed-Scale-Test_v13-12-3-ee_2021-08-13_172819_results.txt) | $19.04/hr |
| **Possible Idle Configuration (Scaled-In 75% - round up)**<br />Pod autoscaling must be also adjusted to enable lower idling configuration. | 40 vCPU, 80 GB | c5.2xlarge x 10<br /><br />[Elastic Auto Scale GPT Test Results](https://gitlab.com/guided-explorations/aws/implementation-patterns/gitlab-cloud-native-hybrid-on-eks/-/blob/master/gitlab-alliances-testing/50K/50k-AutoScale-Test_v13-12-3-ee_2021-08-13_192633/50k-AutoScale-Test_v13-12-3-ee_2021-08-13_192633.txt) | $6.80/hr |
-Other combinations of node type and quantity can be used to meet the Grand Total. Due to the cpu and memory requirements of pods, hosts that are overly small may have significant unused capacity.
+Other combinations of node type and quantity can be used to meet the Grand Total. Due to the CPU and memory requirements of pods, hosts that are overly small may have significant unused capacity.
NOTE:
If EKS node autoscaling is employed, it is likely that your average loading will run lower than this, especially during non-working hours and weekends.
diff --git a/doc/subscriptions/self_managed/index.md b/doc/subscriptions/self_managed/index.md
index 6765ca19518..ce49b5a9c05 100644
--- a/doc/subscriptions/self_managed/index.md
+++ b/doc/subscriptions/self_managed/index.md
@@ -425,7 +425,7 @@ an expiration message is displayed to all administrators.
For GitLab self-managed instances, you have a 14-day grace period
before this occurs.
-- To resume functionality, acticate a new license.
+- To resume functionality, activate a new license.
- To fall back to Free features, delete the expired license.
## Contact Support
diff --git a/doc/user/admin_area/settings/files_api_rate_limits.md b/doc/user/admin_area/settings/files_api_rate_limits.md
index 7305e49b0d2..544c81e0583 100644
--- a/doc/user/admin_area/settings/files_api_rate_limits.md
+++ b/doc/user/admin_area/settings/files_api_rate_limits.md
@@ -8,7 +8,7 @@ type: reference
# Rate limits on Repository files API **(FREE SELF)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68561) in GitLab 14.3.
-> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75918) in GitLab 14.6. [Feature flag files_api_throttling](https://gitlab.com/gitlab-org/gitlab/-/issues/338903) removed.
+> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75918) in GitLab 14.6. [Feature flag `files_api_throttling`](https://gitlab.com/gitlab-org/gitlab/-/issues/338903) removed.
The [Repository files API](../../../api/repository_files.md) enables you to
fetch, create, update, and delete files in your repository. To improve the security
diff --git a/doc/user/admin_area/settings/sign_up_restrictions.md b/doc/user/admin_area/settings/sign_up_restrictions.md
index 8ce3b4f1c18..534450c1871 100644
--- a/doc/user/admin_area/settings/sign_up_restrictions.md
+++ b/doc/user/admin_area/settings/sign_up_restrictions.md
@@ -60,7 +60,7 @@ To enforce confirmation of the email address used for new sign ups:
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > General**, and expand **Sign-up restrictions**.
-1. Select the **Enable email restrictions for sign ups** checkbox, then select **Save changes**.
+1. Select the **Send confirmation email on sign-up** checkbox, then select **Save changes**.
## User cap
diff --git a/doc/user/application_security/cluster_image_scanning/index.md b/doc/user/application_security/cluster_image_scanning/index.md
index 293645b8de6..97fcd2cdcff 100644
--- a/doc/user/application_security/cluster_image_scanning/index.md
+++ b/doc/user/application_security/cluster_image_scanning/index.md
@@ -28,10 +28,17 @@ To integrate GitLab with security scanners other than those listed here, see
You can use cluster image scanning through the following methods:
-- [The cluster image scanning analyzer](#use-the-cluster-image-scanning-analyzer)
+<!--- start_remove The following content will be removed on remove_date: '2022-08-22' -->
+- [The cluster image scanning analyzer](#use-the-cluster-image-scanning-analyzer-removed) ([Removed](https://gitlab.com/gitlab-org/gitlab/-/issues/356465) in GitLab 15.0. Use [the GitLab agent](#cluster-image-scanning-with-the-gitlab-agent) instead.)
+<!--- end_remove -->
- [The GitLab agent](#cluster-image-scanning-with-the-gitlab-agent)
-## Use the cluster image scanning analyzer
+<!--- start_remove The following content will be removed on remove_date: '2022-08-22' -->
+
+## Use the cluster image scanning analyzer (removed)
+
+This feature was [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/356465) in GitLab 15.0.
+Use [the GitLab agent](#cluster-image-scanning-with-the-gitlab-agent) instead.
You can use the cluster image scanning analyzer to run cluster image scanning with [GitLab CI/CD](../../../ci/index.md).
To enable the cluster image scanning analyzer, [include the CI job](#configuration)
@@ -277,6 +284,7 @@ Here's an example cluster image scanning report:
}
```
+<!--- end_remove -->
## Cluster image scanning with the GitLab agent
You can use the [GitLab agent](../../clusters/agent/index.md) to
@@ -304,9 +312,12 @@ the security vulnerabilities in your groups, projects, and pipelines.
After you find a vulnerability, you can address it in the [vulnerability report](../vulnerabilities/index.md)
or the [GitLab agent's](../../clusters/agent/vulnerabilities.md)
details section.
+<!--- start_remove The following content will be removed on remove_date: '2022-08-22' -->
## Troubleshooting
### Getting warning message `gl-cluster-image-scanning-report.json: no matching files`
For information on this error, see the [general Application Security troubleshooting section](../../../ci/pipelines/job_artifacts.md#error-message-no-files-to-upload).
+
+<!--- end_remove -->
diff --git a/doc/user/application_security/dast/checks/16.3.md b/doc/user/application_security/dast/checks/16.3.md
index e4fc2468dae..6f80a2a32c6 100644
--- a/doc/user/application_security/dast/checks/16.3.md
+++ b/doc/user/application_security/dast/checks/16.3.md
@@ -32,4 +32,4 @@ information from the `X-Powered-By` header.
## Links
- [CWE](https://cwe.mitre.org/data/definitions/16.html)
-- [PHP expose_php](https://www.php.net/manual/en/ini.core.php#ini.expose-php)
+- [PHP `expose_php`](https://www.php.net/manual/en/ini.core.php#ini.expose-php)
diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md
index 862647cec45..0452e63db8b 100644
--- a/doc/user/application_security/dependency_scanning/index.md
+++ b/doc/user/application_security/dependency_scanning/index.md
@@ -526,7 +526,7 @@ always take the latest dependency scanning artifact available.
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4908) in GitLab 14.1 [with a flag](../../../administration/feature_flags.md) named `sec_dependency_scanning_ui_enable`. Enabled by default.
> - [Enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/282533) in GitLab 14.1.
-> - [Feature flag sec_dependency_scanning_ui_enable removed](https://gitlab.com/gitlab-org/gitlab/-/issues/326005) in GitLab 14.2.
+> - [Feature flag `sec_dependency_scanning_ui_enable` removed](https://gitlab.com/gitlab-org/gitlab/-/issues/326005) in GitLab 14.2.
To enable Dependency Scanning in a project, you can create a merge request:
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index 05418c0e57f..023bc9dd90c 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -548,7 +548,7 @@ Several passthrouh types generate a configuration for the target analyzer:
- Two `git` passthrough sections pull the head of branch
`refs/remotes/origin/test` from the `myrules` Git repository, and revision
- `97f7686` from the `sast-rules` Git repostory. From the `sast-rules` Git
+ `97f7686` from the `sast-rules` Git repository. From the `sast-rules` Git
repository, only data from the `go` subdirectory is considered.
- The `sast-rules` entry has a higher precedence because it appears later in
the configuration.
diff --git a/doc/user/clusters/management_project_template.md b/doc/user/clusters/management_project_template.md
index a5f227751bb..ba3ff49c92e 100644
--- a/doc/user/clusters/management_project_template.md
+++ b/doc/user/clusters/management_project_template.md
@@ -78,7 +78,7 @@ This image contains a set of Bash utility scripts to support [Helm v3 releases](
The template contains a [Helmfile](https://github.com/roboll/helmfile) you can use to manage
cluster applications with [Helm v3](https://helm.sh/).
-This file has a list of paths to other Helmfiles for each app. They're all commented out by default, so you must uncomment
+This file has a list of paths to other Helm files for each app. They're all commented out by default, so you must uncomment
the paths for the apps that you would like to use in your cluster.
By default, each `helmfile.yaml` in these sub-paths has the attribute `installed: true`. This means that every time
diff --git a/doc/user/infrastructure/clusters/index.md b/doc/user/infrastructure/clusters/index.md
index 167ddb5fbe2..933b310ff3f 100644
--- a/doc/user/infrastructure/clusters/index.md
+++ b/doc/user/infrastructure/clusters/index.md
@@ -18,7 +18,7 @@ as well as its related [features](#deprecated-features).
The certificate-based Kubernetes integration with GitLab is deprecated.
It had the following issues:
-- There were security issues as it required direct access to the Kube API by GitLab.
+- There were security issues as it required direct access to the Kubernetes API by GitLab.
- The configuration options weren't flexible.
- The integration was flaky.
- Users were constantly reporting issues with features based on this model.
diff --git a/doc/user/project/merge_requests/test_coverage_visualization.md b/doc/user/project/merge_requests/test_coverage_visualization.md
index 42324205033..85b5bbea284 100644
--- a/doc/user/project/merge_requests/test_coverage_visualization.md
+++ b/doc/user/project/merge_requests/test_coverage_visualization.md
@@ -270,7 +270,7 @@ to collect test coverage data and generate the report.
With a minimal [`phpunit.xml`](https://phpunit.readthedocs.io/en/9.5/configuration.html) file (you may reference
[this example repository](https://gitlab.com/yookoala/code-coverage-visualization-with-php/)), you can run the test and
-generate the coverage xml:
+generate the `coverage.xml`:
```yaml
run tests:
diff --git a/doc/user/search/global_search/advanced_search_syntax.md b/doc/user/search/global_search/advanced_search_syntax.md
index a0b2f18a683..81c21c67ef3 100644
--- a/doc/user/search/global_search/advanced_search_syntax.md
+++ b/doc/user/search/global_search/advanced_search_syntax.md
@@ -34,7 +34,7 @@ Advanced Search searches default project branches only.
| Use | Description | Example |
|--------------|---------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
| `filename:` | File name | [`filename:*spec.rb`](https://gitlab.com/search?snippets=&scope=blobs&repository_ref=&search=filename%3A*spec.rb&group_id=9970&project_id=278964) |
-| `path:` | Repo location | [`path:spec/workers/`](https://gitlab.com/search?group_id=9970&project_id=278964&repository_ref=&scope=blobs&search=path%3Aspec%2Fworkers&snippets=) |
+| `path:` | Repository location | [`path:spec/workers/`](https://gitlab.com/search?group_id=9970&project_id=278964&repository_ref=&scope=blobs&search=path%3Aspec%2Fworkers&snippets=) |
| `extension:` | File extension, without the `.` | [`extension:js`](https://gitlab.com/search?group_id=9970&project_id=278964&repository_ref=&scope=blobs&search=extension%3Ajs&snippets=) |
| `blob:` | Git object ID | [`blob:998707*`](https://gitlab.com/search?snippets=false&scope=blobs&repository_ref=&search=blob%3A998707*&group_id=9970) |
diff --git a/lib/api/internal/base.rb b/lib/api/internal/base.rb
index 634be0ac669..b53f855c3a2 100644
--- a/lib/api/internal/base.rb
+++ b/lib/api/internal/base.rb
@@ -123,10 +123,19 @@ module API
'Could not find a user for the given key' unless actor.user
end
+ # TODO: backwards compatibility; remove after https://gitlab.com/gitlab-org/gitlab-shell/-/merge_requests/454 is merged
def two_factor_otp_check
{ success: false, message: 'Feature is not available' }
end
+ def two_factor_manual_otp_check
+ { success: false, message: 'Feature is not available' }
+ end
+
+ def two_factor_push_otp_check
+ { success: false, message: 'Feature is not available' }
+ end
+
def with_admin_mode_bypass!(actor_id)
return yield unless Gitlab::CurrentSettings.admin_mode
@@ -320,10 +329,23 @@ module API
end
end
+ # TODO: backwards compatibility; remove after https://gitlab.com/gitlab-org/gitlab-shell/-/merge_requests/454 is merged
post '/two_factor_otp_check', feature_category: :authentication_and_authorization do
status 200
- two_factor_otp_check
+ two_factor_manual_otp_check
+ end
+
+ post '/two_factor_push_otp_check', feature_category: :authentication_and_authorization do
+ status 200
+
+ two_factor_push_otp_check
+ end
+
+ post '/two_factor_manual_otp_check', feature_category: :authentication_and_authorization do
+ status 200
+
+ two_factor_manual_otp_check
end
end
end
diff --git a/lib/bulk_imports/groups/transformers/group_attributes_transformer.rb b/lib/bulk_imports/groups/transformers/group_attributes_transformer.rb
index 23e898a7bb2..df27275b664 100644
--- a/lib/bulk_imports/groups/transformers/group_attributes_transformer.rb
+++ b/lib/bulk_imports/groups/transformers/group_attributes_transformer.rb
@@ -8,7 +8,6 @@ module BulkImports
import_entity = context.entity
data
- .then { |data| transform_name(import_entity, data) }
.then { |data| transform_path(import_entity, data) }
.then { |data| transform_full_path(data) }
.then { |data| transform_parent(context, import_entity, data) }
@@ -19,11 +18,6 @@ module BulkImports
private
- def transform_name(import_entity, data)
- data['name'] = import_entity.destination_name
- data
- end
-
def transform_path(import_entity, data)
data['path'] = import_entity.destination_name.parameterize
data
diff --git a/lib/bulk_imports/groups/transformers/subgroup_to_entity_transformer.rb b/lib/bulk_imports/groups/transformers/subgroup_to_entity_transformer.rb
index 676a6ca8d2a..d8fb937ecd2 100644
--- a/lib/bulk_imports/groups/transformers/subgroup_to_entity_transformer.rb
+++ b/lib/bulk_imports/groups/transformers/subgroup_to_entity_transformer.rb
@@ -8,7 +8,7 @@ module BulkImports
{
source_type: :group_entity,
source_full_path: entry['full_path'],
- destination_name: entry['name'],
+ destination_name: entry['path'],
destination_namespace: context.entity.group.full_path,
parent_id: context.entity.id
}
diff --git a/lib/gitlab/auth/otp/strategies/forti_authenticator.rb b/lib/gitlab/auth/otp/strategies/forti_authenticator.rb
deleted file mode 100644
index c1433f05db2..00000000000
--- a/lib/gitlab/auth/otp/strategies/forti_authenticator.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
- module Auth
- module Otp
- module Strategies
- class FortiAuthenticator < Base
- def validate(otp_code)
- body = { username: user.username,
- token_code: otp_code }
-
- response = Gitlab::HTTP.post(
- auth_url,
- headers: { 'Content-Type': 'application/json' },
- body: body.to_json,
- basic_auth: api_credentials)
-
- # Successful authentication results in HTTP 200: OK
- # https://docs.fortinet.com/document/fortiauthenticator/6.2.0/rest-api-solution-guide/704555/authentication-auth
- response.ok? ? success : error_from_response(response)
- rescue StandardError => ex
- Gitlab::AppLogger.error(ex)
- error(ex.message)
- end
-
- private
-
- def auth_url
- host = ::Gitlab.config.forti_authenticator.host
- port = ::Gitlab.config.forti_authenticator.port
- path = 'api/v1/auth/'
-
- "https://#{host}:#{port}/#{path}"
- end
-
- def api_credentials
- { username: ::Gitlab.config.forti_authenticator.username,
- password: ::Gitlab.config.forti_authenticator.access_token }
- end
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp.rb b/lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp.rb
new file mode 100644
index 00000000000..9cf1b2247a7
--- /dev/null
+++ b/lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Auth
+ module Otp
+ module Strategies
+ module FortiAuthenticator
+ class ManualOtp < Base
+ def validate(otp_code)
+ @otp_code = otp_code
+
+ response = Gitlab::HTTP.post(
+ auth_url,
+ headers: { 'Content-Type': 'application/json' },
+ body: body.to_json,
+ basic_auth: api_credentials)
+
+ # Successful authentication results in HTTP 200: OK
+ # Manual OTP - https://docs.fortinet.com/document/fortiauthenticator/6.2.0/rest-api-solution-guide/704555/authentication-auth
+ response.ok? ? success : error_from_response(response)
+ rescue StandardError => ex
+ Gitlab::AppLogger.error(ex)
+ error(ex.message)
+ end
+
+ private
+
+ def auth_url
+ host = ::Gitlab.config.forti_authenticator.host
+ port = ::Gitlab.config.forti_authenticator.port
+ path = 'api/v1/auth/'
+
+ "https://#{host}:#{port}/#{path}"
+ end
+
+ def body
+ { username: user.username,
+ token_code: @otp_code }
+ end
+
+ def api_credentials
+ { username: ::Gitlab.config.forti_authenticator.username,
+ password: ::Gitlab.config.forti_authenticator.access_token }
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp.rb b/lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp.rb
new file mode 100644
index 00000000000..03cc648f7b0
--- /dev/null
+++ b/lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Auth
+ module Otp
+ module Strategies
+ module FortiAuthenticator
+ class PushOtp < Base
+ def validate
+ response = Gitlab::HTTP.post(
+ auth_url,
+ headers: { 'Content-Type': 'application/json' },
+ body: body.to_json,
+ basic_auth: api_credentials)
+
+ # Successful authentication results in HTTP 200: OK
+ # Push - https://docs.fortinet.com/document/fortiauthenticator/6.2.1/rest-api-solution-guide/943094/push-authentication-pushauth
+ response.ok? ? success : error_from_response(response)
+ rescue StandardError => ex
+ Gitlab::AppLogger.error(ex)
+ error(ex.message)
+ end
+
+ private
+
+ def auth_url
+ host = ::Gitlab.config.forti_authenticator.host
+ port = ::Gitlab.config.forti_authenticator.port
+ path = 'api/v1/pushauth/'
+
+ "https://#{host}:#{port}/#{path}"
+ end
+
+ def body
+ { username: user.username }
+ end
+
+ def api_credentials
+ { username: ::Gitlab.config.forti_authenticator.username,
+ password: ::Gitlab.config.forti_authenticator.access_token }
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/expire_o_auth_tokens.rb b/lib/gitlab/background_migration/expire_o_auth_tokens.rb
new file mode 100644
index 00000000000..4ba7d919cc7
--- /dev/null
+++ b/lib/gitlab/background_migration/expire_o_auth_tokens.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Add expiry to all OAuth access tokens
+ class ExpireOAuthTokens < ::Gitlab::BackgroundMigration::BatchedMigrationJob
+ def perform(batch_size)
+ each_sub_batch(
+ operation_name: :update_oauth_tokens,
+ batching_scope: ->(relation) { relation.where(expires_in: nil) }
+ ) do |sub_batch|
+ update_oauth_tokens(sub_batch, batch_size)
+ end
+ end
+
+ private
+
+ def update_oauth_tokens(relation, batch_size)
+ relation.update_all(expires_in: 7_200)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
index 8020ffee36f..fddcc1492a8 100644
--- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
@@ -178,7 +178,6 @@ include:
- template: Jobs/Helm-2to3.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Jobs/Helm-2to3.gitlab-ci.yml
- template: Security/DAST.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml
- template: Security/Container-Scanning.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
- - template: Security/Cluster-Image-Scanning.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Cluster-Image-Scanning.gitlab-ci.yml
- template: Security/Dependency-Scanning.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
- template: Security/License-Scanning.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/License-Scanning.gitlab-ci.yml
- template: Security/SAST.gitlab-ci.yml # https://gitlab.com/gitlab-org/gitlab/blob/master/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
diff --git a/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml
index aff8b6cb7fa..2fd5b409f5e 100644
--- a/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml
@@ -3,19 +3,36 @@
# This specific template is located at:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/API-Fuzzing.gitlab-ci.yml
+# To use this template, add the following to your .gitlab-ci.yml file:
+#
+# include:
+# template: API-Fuzzing.gitlab-ci.yml
+#
+# You also need to add a `fuzz` stage to your `stages:` configuration. A sample configuration for API Fuzzing:
+#
+# stages:
+# - build
+# - test
+# - deploy
+# - fuzz
+
# Read more about this feature here: https://docs.gitlab.com/ee/user/application_security/api_fuzzing/
#
-# Configure API fuzzing with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html).
+# Configure API Fuzzing with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html).
# List of available variables: https://docs.gitlab.com/ee/user/application_security/api_fuzzing/#available-cicd-variables
variables:
- FUZZAPI_VERSION: "1"
+ # Setting this variable affects all Security templates
+ # (SAST, Dependency Scanning, ...)
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/security-products"
- FUZZAPI_IMAGE: ${SECURE_ANALYZERS_PREFIX}/api-fuzzing:${FUZZAPI_VERSION}
+ #
+ FUZZAPI_VERSION: "2"
+ FUZZAPI_IMAGE_SUFFIX: ""
+ FUZZAPI_IMAGE: api-security
apifuzzer_fuzz:
stage: fuzz
- image: $FUZZAPI_IMAGE
+ image: $SECURE_ANALYZERS_PREFIX/$FUZZAPI_IMAGE:$FUZZAPI_VERSION$FUZZAPI_IMAGE_SUFFIX
allow_failure: true
rules:
- if: $API_FUZZING_DISABLED
@@ -23,6 +40,10 @@ apifuzzer_fuzz:
- if: $API_FUZZING_DISABLED_FOR_DEFAULT_BRANCH &&
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
when: never
+ - if: $CI_COMMIT_BRANCH &&
+ $CI_GITLAB_FIPS_MODE == "true"
+ variables:
+ FUZZAPI_IMAGE_SUFFIX: "-fips"
- if: $CI_COMMIT_BRANCH
script:
- /peach/analyzer-fuzz-api
diff --git a/lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml
index b6e811aa84f..450969fcdab 100644
--- a/lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/API-Fuzzing.latest.gitlab-ci.yml
@@ -26,9 +26,9 @@ variables:
# (SAST, Dependency Scanning, ...)
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/security-products"
#
- FUZZAPI_VERSION: "1"
+ FUZZAPI_VERSION: "2"
FUZZAPI_IMAGE_SUFFIX: ""
- FUZZAPI_IMAGE: api-fuzzing
+ FUZZAPI_IMAGE: api-security
apifuzzer_fuzz:
stage: fuzz
diff --git a/lib/gitlab/ci/templates/Security/Cluster-Image-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Cluster-Image-Scanning.gitlab-ci.yml
deleted file mode 100644
index 6b861510eef..00000000000
--- a/lib/gitlab/ci/templates/Security/Cluster-Image-Scanning.gitlab-ci.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-# Use this template to enable cluster image scanning in your project.
-# You should add this template to an existing `.gitlab-ci.yml` file by using the `include:`
-# keyword.
-# The template should work without modifications but you can customize the template settings if
-# needed: https://docs.gitlab.com/ee/user/application_security/cluster_image_scanning/#customize-the-container-scanning-settings
-#
-# Requirements:
-# - A `test` stage to be present in the pipeline.
-# - You must define the `CIS_KUBECONFIG` variable to allow analyzer to connect to your Kubernetes cluster and fetch found vulnerabilities.
-#
-# Configure container scanning with CI/CD variables (https://docs.gitlab.com/ee/ci/variables/index.html).
-# List of available variables: https://docs.gitlab.com/ee/user/application_security/cluster_image_scanning/#available-variables
-
-variables:
- CIS_ANALYZER_IMAGE: registry.gitlab.com/security-products/cluster-image-scanning:0
-
-cluster_image_scanning:
- image: "$CIS_ANALYZER_IMAGE"
- stage: test
- allow_failure: true
- artifacts:
- reports:
- cluster_image_scanning: gl-cluster-image-scanning-report.json
- paths: [gl-cluster-image-scanning-report.json]
- dependencies: []
- script:
- - /analyzer run
- rules:
- - if: $CLUSTER_IMAGE_SCANNING_DISABLED
- when: never
- - if: '($KUBECONFIG == null || $KUBECONFIG == "") && ($CIS_KUBECONFIG == null || $CIS_KUBECONFIG == "")'
- when: never
- - if: $CI_COMMIT_BRANCH &&
- $GITLAB_FEATURES =~ /\bcluster_image_scanning\b/
diff --git a/lib/gitlab/ci/templates/Security/DAST-API.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/DAST-API.gitlab-ci.yml
index d82f9f06f8d..893098d33c4 100644
--- a/lib/gitlab/ci/templates/Security/DAST-API.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/DAST-API.gitlab-ci.yml
@@ -26,12 +26,13 @@ variables:
# (SAST, Dependency Scanning, ...)
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/security-products"
#
- DAST_API_VERSION: "1"
- DAST_API_IMAGE: $SECURE_ANALYZERS_PREFIX/api-fuzzing:$DAST_API_VERSION
+ DAST_API_VERSION: "2"
+ DAST_API_IMAGE_SUFFIX: ""
+ DAST_API_IMAGE: api-security
dast_api:
stage: dast
- image: $DAST_API_IMAGE
+ image: $SECURE_ANALYZERS_PREFIX/$DAST_API_IMAGE:$DAST_API_VERSION$DAST_API_IMAGE_SUFFIX
allow_failure: true
rules:
- if: $DAST_API_DISABLED
@@ -39,6 +40,10 @@ dast_api:
- if: $DAST_API_DISABLED_FOR_DEFAULT_BRANCH &&
$CI_DEFAULT_BRANCH == $CI_COMMIT_REF_NAME
when: never
+ - if: $CI_COMMIT_BRANCH &&
+ $CI_GITLAB_FIPS_MODE == "true"
+ variables:
+ DAST_API_IMAGE_SUFFIX: "-fips"
- if: $CI_COMMIT_BRANCH
script:
- /peach/analyzer-dast-api
@@ -50,3 +55,5 @@ dast_api:
- gl-*.log
reports:
dast: gl-dast-api-report.json
+
+# end
diff --git a/lib/gitlab/ci/templates/Security/DAST-API.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/DAST-API.latest.gitlab-ci.yml
index b491b3e3c0c..3acc3b06031 100644
--- a/lib/gitlab/ci/templates/Security/DAST-API.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/DAST-API.latest.gitlab-ci.yml
@@ -1,7 +1,7 @@
# To contribute improvements to CI/CD templates, please follow the Development guide at:
# https://docs.gitlab.com/ee/development/cicd/templates.html
# This specific template is located at:
-# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Dast-API.gitlab-ci.yml
+# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Security/Dast-API.latest.gitlab-ci.yml
# To use this template, add the following to your .gitlab-ci.yml file:
#
@@ -26,9 +26,9 @@ variables:
# (SAST, Dependency Scanning, ...)
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/security-products"
#
- DAST_API_VERSION: "1"
+ DAST_API_VERSION: "2"
DAST_API_IMAGE_SUFFIX: ""
- DAST_API_IMAGE: api-fuzzing
+ DAST_API_IMAGE: api-security
dast_api:
stage: dast
diff --git a/lib/gitlab/usage_data_counters/known_events/ci_templates.yml b/lib/gitlab/usage_data_counters/known_events/ci_templates.yml
index 85a484b6cd0..16a8af6b4d8 100644
--- a/lib/gitlab/usage_data_counters/known_events/ci_templates.yml
+++ b/lib/gitlab/usage_data_counters/known_events/ci_templates.yml
@@ -147,10 +147,6 @@
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
-- name: p_ci_templates_security_cluster_image_scanning
- category: ci_templates
- redis_slot: ci_templates
- aggregation: weekly
- name: p_ci_templates_qualys_iac_security
category: ci_templates
redis_slot: ci_templates
@@ -631,7 +627,3 @@
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
-- name: p_ci_templates_implicit_security_cluster_image_scanning
- category: ci_templates
- redis_slot: ci_templates
- aggregation: weekly
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 937fbff7246..aba871fe1d6 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -16189,6 +16189,9 @@ msgstr ""
msgid "From June 22, 2022 (GitLab 15.1), free personal namespaces and top-level groups will be limited to %{free_limit} members"
msgstr ""
+msgid "From June 22, 2022 (GitLab 15.1), you can have a maximum of %{free_limit} unique members across all of your personal projects"
+msgstr ""
+
msgid "From issue creation until deploy to production"
msgstr ""
@@ -41389,6 +41392,9 @@ msgstr ""
msgid "View all issues"
msgstr ""
+msgid "View all personal projects"
+msgstr ""
+
msgid "View blame"
msgstr ""
@@ -43067,6 +43073,9 @@ msgstr ""
msgid "You could not create a new trigger."
msgstr ""
+msgid "You currently have more than %{free_limit} members across all your personal projects. From June 22, 2022, the %{free_limit} most recently active members will remain active, and the remaining members will get a %{link_start}status of Over limit%{link_end} and lose access. To view and manage members, check the members page for each project in your namespace. We recommend you %{move_link_start}move your project to a group%{move_link_end} so you can easily manage users and features."
+msgstr ""
+
msgid "You do not have any Google Cloud projects. Please create a Google Cloud project and then reload this page."
msgstr ""
@@ -45289,7 +45298,7 @@ msgstr ""
msgid "pipelineEditorWalkthrough|Use the %{boldStart}commit changes%{boldEnd} button at the bottom of the page to run the pipeline."
msgstr ""
-msgid "pipelineEditorWalkthrough|You can use the file tree to view your pipeline configuration files."
+msgid "pipelineEditorWalkthrough|You can use the file tree to view your pipeline configuration files. %{linkStart}Learn more%{linkEnd}"
msgstr ""
msgid "pod_name can contain only lowercase letters, digits, '-', and '.' and must start and end with an alphanumeric character"
diff --git a/qa/Dockerfile b/qa/Dockerfile
index fa666daa927..4fd44ba02df 100644
--- a/qa/Dockerfile
+++ b/qa/Dockerfile
@@ -1,33 +1,17 @@
-FROM ruby:2.7-buster
+FROM registry.gitlab.com/gitlab-org/gitlab-build-images/debian-bullseye-ruby-2.7:bundler-2.3-git-2.33-lfs-2.9-chrome-99-docker-20.10.14-gcloud-383-kubectl-1.23
LABEL maintainer="GitLab Quality Department <quality@gitlab.com>"
-ENV DEBIAN_FRONTEND="noninteractive"
-ENV DOCKER_VERSION="17.09.0-ce"
+ENV DEBIAN_FRONTEND="noninteractive" \
+ BUNDLE_WITHOUT=development
##
-# Update APT sources and install dependencies
+# Install system libs
#
-RUN sed -i "s/httpredir.debian.org/ftp.us.debian.org/" /etc/apt/sources.list
-RUN apt-get update && apt-get install -y wget unzip xvfb lsb-release git git-lfs
-
-##
-# Install Docker
-#
-RUN wget -q "https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz" && \
- tar -zxf "docker-${DOCKER_VERSION}.tgz" && mv docker/docker /usr/local/bin/docker && \
- rm "docker-${DOCKER_VERSION}.tgz"
-
-##
-# Install client certificate - Bug in Chrome Headless: https://gitlab.com/gitlab-org/gitlab/-/issues/331492
-#
-# RUN apt install -y libnss3-tools
-# RUN mkdir -p $HOME/.pki/nssdb
-# RUN certutil -N -d sql:$HOME/.pki/nssdb
-# COPY ./qa/tls_certificates/client/client.pfx /tmp/client.pfx
-# RUN pk12util -d sql:$HOME/.pki/nssdb -i /tmp/client.pfx -W ''
-# RUN mkdir -p /etc/opt/chrome/policies/managed
-# RUN echo '{ "AutoSelectCertificateForUrls": ["{\"pattern\":\"*\",\"filter\":{}}"] }' > /etc/opt/chrome/policies/managed/policy.json
-# RUN cat /etc/opt/chrome/policies/managed/policy.json
+RUN apt-get update; \
+ apt-get install -y xvfb unzip; \
+ apt-get -yq autoremove; \
+ apt-get clean -yqq; \
+ rm -rf /var/lib/apt/lists/*
##
# Install root certificate
@@ -37,39 +21,19 @@ ADD ./qa/tls_certificates/authority/ca.crt /usr/share/ca-certificates/gitlab/
RUN echo 'gitlab/ca.crt' >> /etc/ca-certificates.conf
RUN chmod -R 644 /usr/share/ca-certificates/gitlab && update-ca-certificates
-##
-# Install gcloud and kubectl CLI used in Auto DevOps test to create K8s
-# clusters
-#
-RUN export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)" && \
- echo "deb http://packages.cloud.google.com/apt $CLOUD_SDK_REPO main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \
- curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \
- apt-get update -y && apt-get install google-cloud-sdk kubectl -y
-
-##
-# Install Google Chrome version with headless support
-# Download from our local S3 bucket, populated by https://gitlab.com/gitlab-org/gitlab-build-images/-/blob/master/scripts/cache-google-chrome
-#
-# https://s3.amazonaws.com/gitlab-google-chrome-stable
-ENV CHROME_VERSION="91.0.4472.77-1"
-ENV CHROME_DEB="google-chrome-stable_${CHROME_VERSION}_amd64.deb"
-ENV CHROME_URL="https://s3.amazonaws.com/gitlab-google-chrome-stable/${CHROME_DEB}"
-RUN curl --silent --show-error --fail -O "${CHROME_URL}" && \
- dpkg -i "./${CHROME_DEB}" || true && \
- apt-get install -f -y && \
- rm -f "./${CHROME_DEB}"
-
WORKDIR /home/gitlab/qa
+##
# Install qa dependencies or fetch from cache if unchanged
+#
COPY ./qa/Gemfile* /home/gitlab/qa/
-RUN gem install bundler --no-document --conservative --version 2.3.6
-RUN bundle install --jobs=$(nproc) --retry=3 --without=development --quiet
+RUN bundle install --jobs=$(nproc) --retry=3
##
# Fetch chromedriver based on version of chrome
# Copy rakefile first so that webdriver is not reinstalled on every code change
# https://github.com/titusfortner/webdrivers
+#
COPY ./qa/tasks/webdrivers.rake /home/gitlab/qa/tasks/
RUN bundle exec rake -f tasks/webdrivers.rake webdrivers:chromedriver:update
diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb
index 356e509a81d..2244a78b9e5 100644
--- a/qa/qa/git/repository.rb
+++ b/qa/qa/git/repository.rb
@@ -21,7 +21,7 @@ module QA
# We set HOME to the current working directory (which is a
# temporary directory created in .perform()) so the temporarily dropped
# .netrc can be utilised
- self.env_vars = [%Q{HOME="#{tmp_home_dir}"}]
+ self.env_vars = [%(HOME="#{tmp_home_dir}")]
@use_lfs = false
@gpg_key_id = nil
@default_branch = Runtime::Env.default_branch
@@ -71,7 +71,7 @@ module QA
def checkout(branch_name, new_branch: false)
opts = new_branch ? '-b' : ''
- run_git(%Q{git checkout #{opts} "#{branch_name}"}).to_s
+ run_git(%(git checkout #{opts} "#{branch_name}")).to_s
end
def shallow_clone
@@ -79,8 +79,8 @@ module QA
end
def configure_identity(name, email)
- run_git(%Q{git config user.name "#{name}"})
- run_git(%Q{git config user.email #{email}})
+ run_git(%(git config user.name "#{name}"))
+ run_git(%(git config user.email #{email}))
end
def commit_file(name, contents, message)
@@ -94,11 +94,11 @@ module QA
::File.write(name, contents)
if use_lfs?
- git_lfs_track_result = run_git(%Q{git lfs track #{name} --lockable})
+ git_lfs_track_result = run_git(%(git lfs track #{name} --lockable))
return git_lfs_track_result.response unless git_lfs_track_result.success?
end
- git_add_result = run_git(%Q{git add #{name}})
+ git_add_result = run_git(%(git add #{name}))
git_lfs_track_result.to_s + git_add_result.to_s
end
@@ -108,15 +108,15 @@ module QA
end
def delete_tag(tag_name)
- run_git(%Q{git push origin --delete #{tag_name}}, max_attempts: 3).to_s
+ run_git(%(git push origin --delete #{tag_name}), max_attempts: 3).to_s
end
def commit(message)
- run_git(%Q{git commit -m "#{message}"}, max_attempts: 3).to_s
+ run_git(%(git commit -m "#{message}"), max_attempts: 3).to_s
end
def commit_with_gpg(message)
- run_git(%Q{git config user.signingkey #{@gpg_key_id} && git config gpg.program $(command -v gpg) && git commit -S -m "#{message}"}).to_s
+ run_git(%{git config user.signingkey #{@gpg_key_id} && git config gpg.program $(command -v gpg) && git commit -S -m "#{message}"}).to_s
end
def current_branch
@@ -159,11 +159,11 @@ module QA
@ssh = Support::SSH.perform do |ssh|
ssh.key = key
ssh.uri = uri
- ssh.setup(env: self.env_vars)
+ ssh.setup(env: env_vars)
ssh
end
- self.env_vars << %Q{GIT_SSH_COMMAND="ssh -i #{ssh.private_key_file.path} -o UserKnownHostsFile=#{ssh.known_hosts_file.path}"}
+ env_vars << %(GIT_SSH_COMMAND="ssh -i #{ssh.private_key_file.path} -o UserKnownHostsFile=#{ssh.known_hosts_file.path}")
end
def delete_ssh_key
@@ -182,7 +182,9 @@ module QA
end
def git_protocol=(value)
- raise ArgumentError, "Please specify the protocol you would like to use: 0, 1, or 2" unless %w[0 1 2].include?(value.to_s)
+ unless %w[0 1 2].include?(value.to_s)
+ raise ArgumentError, "Please specify the protocol you would like to use: 0, 1, or 2"
+ end
run_git("git config protocol.version #{value}")
end
@@ -190,8 +192,8 @@ module QA
def fetch_supported_git_protocol
# ls-remote is one command known to respond to Git protocol v2 so we use
# it to get output including the version reported via Git tracing
- result = run_git("git ls-remote #{uri}", max_attempts: 3, env: [*self.env_vars, "GIT_TRACE_PACKET=1"])
- result.response[/git< version (\d+)/, 1] || 'unknown'
+ result = run_git("git ls-remote #{uri}", max_attempts: 3, env: [*env_vars, "GIT_TRACE_PACKET=1"])
+ result.response[/ls-remote< version (\d+)/, 1] || 'unknown'
end
def try_add_credentials_to_netrc
@@ -266,7 +268,7 @@ module QA
#
FileUtils.mkdir_p(tmp_home_dir)
File.open(netrc_file_path, 'a') { |file| file.puts(netrc_content) }
- File.chmod(0600, netrc_file_path)
+ File.chmod(0o600, netrc_file_path)
end
def tmp_home_dir
@@ -302,7 +304,7 @@ module QA
read_netrc_content.grep(/^#{Regexp.escape(netrc_content)}$/).any?
end
- def run_git(command_str, env: self.env_vars, max_attempts: 1)
+ def run_git(command_str, env: env_vars, max_attempts: 1)
run(command_str, env: env, max_attempts: max_attempts, log_prefix: 'Git: ')
end
end
diff --git a/qa/spec/git/repository_spec.rb b/qa/spec/git/repository_spec.rb
index 6b100f9dc16..a6a49f5907a 100644
--- a/qa/spec/git/repository_spec.rb
+++ b/qa/spec/git/repository_spec.rb
@@ -206,19 +206,19 @@ RSpec.describe QA::Git::Repository do
it_behaves_like 'command with retries' do
let(:command) { "git ls-remote #{repo_uri_with_credentials}" }
- let(:result_output) { +'packet: git< version 2' }
+ let(:result_output) { +'packet: ls-remote< version 2' }
let(:command_return) { '2' }
let(:extra_env_vars) { ["GIT_TRACE_PACKET=1"] }
end
it "reports the detected version" do
- expect(repository).to receive(:run).and_return(described_class::Result.new(any_args, 0, "packet: git< version 2"))
+ expect(repository).to receive(:run).and_return(described_class::Result.new(any_args, 0, "packet: ls-remote< version 2"))
expect(call_method).to eq('2')
end
it 'reports unknown if version is unknown' do
- expect(repository).to receive(:run).and_return(described_class::Result.new(any_args, 0, "packet: git< version -1"))
+ expect(repository).to receive(:run).and_return(described_class::Result.new(any_args, 0, "packet: ls-remote< version -1"))
expect(call_method).to eq('unknown')
end
diff --git a/spec/frontend/__helpers__/init_vue_mr_page_helper.js b/spec/frontend/__helpers__/init_vue_mr_page_helper.js
index 6b719a32480..ee01e9e6268 100644
--- a/spec/frontend/__helpers__/init_vue_mr_page_helper.js
+++ b/spec/frontend/__helpers__/init_vue_mr_page_helper.js
@@ -13,16 +13,16 @@ export default function initVueMRPage() {
const diffsAppProjectPath = 'testproject';
const mrEl = document.createElement('div');
mrEl.className = 'merge-request fixture-mr';
- mrEl.dataset.mrAction = 'diffs';
+ mrEl.setAttribute('data-mr-action', 'diffs');
mrTestEl.appendChild(mrEl);
const mrDiscussionsEl = document.createElement('div');
mrDiscussionsEl.id = 'js-vue-mr-discussions';
- mrDiscussionsEl.dataset.currentUserData = JSON.stringify(userDataMock);
- mrDiscussionsEl.dataset.noteableData = JSON.stringify(noteableDataMock);
- mrDiscussionsEl.dataset.notesData = JSON.stringify(notesDataMock);
- mrDiscussionsEl.dataset.noteableType = 'merge-request';
- mrDiscussionsEl.dataset.isLocked = 'false';
+ mrDiscussionsEl.setAttribute('data-current-user-data', JSON.stringify(userDataMock));
+ mrDiscussionsEl.setAttribute('data-noteable-data', JSON.stringify(noteableDataMock));
+ mrDiscussionsEl.setAttribute('data-notes-data', JSON.stringify(notesDataMock));
+ mrDiscussionsEl.setAttribute('data-noteable-type', 'merge-request');
+ mrDiscussionsEl.setAttribute('data-is-locked', 'false');
mrTestEl.appendChild(mrDiscussionsEl);
const discussionCounterEl = document.createElement('div');
@@ -31,9 +31,9 @@ export default function initVueMRPage() {
const diffsAppEl = document.createElement('div');
diffsAppEl.id = 'js-diffs-app';
- diffsAppEl.dataset.endpoint = diffsAppEndpoint;
- diffsAppEl.dataset.projectPath = diffsAppProjectPath;
- diffsAppEl.dataset.currentUserData = JSON.stringify(userDataMock);
+ diffsAppEl.setAttribute('data-endpoint', diffsAppEndpoint);
+ diffsAppEl.setAttribute('data-project-path', diffsAppProjectPath);
+ diffsAppEl.setAttribute('data-current-user-data', JSON.stringify(userDataMock));
mrTestEl.appendChild(diffsAppEl);
const mock = new MockAdapter(axios);
diff --git a/spec/frontend/__helpers__/matchers/to_have_sprite_icon.js b/spec/frontend/__helpers__/matchers/to_have_sprite_icon.js
index 45b9c31c4db..bce9d93bea8 100644
--- a/spec/frontend/__helpers__/matchers/to_have_sprite_icon.js
+++ b/spec/frontend/__helpers__/matchers/to_have_sprite_icon.js
@@ -9,7 +9,7 @@ export const toHaveSpriteIcon = (element, iconName) => {
const iconReferences = [].slice.apply(element.querySelectorAll('svg use'));
const matchingIcon = iconReferences.find(
- (reference) => reference.parentNode.dataset.testid === `${iconName}-icon`,
+ (reference) => reference.parentNode.getAttribute('data-testid') === `${iconName}-icon`,
);
const pass = Boolean(matchingIcon);
diff --git a/spec/frontend/access_tokens/index_spec.js b/spec/frontend/access_tokens/index_spec.js
index 5f0a7dfc775..1d8ac7cec25 100644
--- a/spec/frontend/access_tokens/index_spec.js
+++ b/spec/frontend/access_tokens/index_spec.js
@@ -39,7 +39,7 @@ describe('access tokens', () => {
const input = document.createElement('input');
input.setAttribute('name', nameAttribute);
- input.dataset.jsName = fieldName;
+ input.setAttribute('data-js-name', fieldName);
input.setAttribute('id', idAttribute);
input.setAttribute('placeholder', 'Foo bar');
input.setAttribute('value', '1,2');
diff --git a/spec/frontend/admin/users/index_spec.js b/spec/frontend/admin/users/index_spec.js
index 961fa96acdd..06dbadd6d3d 100644
--- a/spec/frontend/admin/users/index_spec.js
+++ b/spec/frontend/admin/users/index_spec.js
@@ -12,8 +12,8 @@ describe('initAdminUsersApp', () => {
beforeEach(() => {
el = document.createElement('div');
- el.dataset.users = JSON.stringify(users);
- el.dataset.paths = JSON.stringify(paths);
+ el.setAttribute('data-users', JSON.stringify(users));
+ el.setAttribute('data-paths', JSON.stringify(paths));
wrapper = createWrapper(initAdminUsersApp(el));
});
@@ -40,8 +40,8 @@ describe('initAdminUserActions', () => {
beforeEach(() => {
el = document.createElement('div');
- el.dataset.user = JSON.stringify(user);
- el.dataset.paths = JSON.stringify(paths);
+ el.setAttribute('data-user', JSON.stringify(user));
+ el.setAttribute('data-paths', JSON.stringify(paths));
wrapper = createWrapper(initAdminUserActions(el));
});
diff --git a/spec/frontend/authentication/two_factor_auth/index_spec.js b/spec/frontend/authentication/two_factor_auth/index_spec.js
index f9a6b2df662..0ff9d60f409 100644
--- a/spec/frontend/authentication/two_factor_auth/index_spec.js
+++ b/spec/frontend/authentication/two_factor_auth/index_spec.js
@@ -15,8 +15,8 @@ describe('initRecoveryCodes', () => {
beforeEach(() => {
el = document.createElement('div');
el.setAttribute('class', 'js-2fa-recovery-codes');
- el.dataset.codes = codesJsonString;
- el.dataset.profileAccountPath = profileAccountPath;
+ el.setAttribute('data-codes', codesJsonString);
+ el.setAttribute('data-profile-account-path', profileAccountPath);
document.body.appendChild(el);
wrapper = createWrapper(initRecoveryCodes());
diff --git a/spec/frontend/blob/components/table_contents_spec.js b/spec/frontend/blob/components/table_contents_spec.js
index abb69a0be48..ade35d39b4f 100644
--- a/spec/frontend/blob/components/table_contents_spec.js
+++ b/spec/frontend/blob/components/table_contents_spec.js
@@ -10,7 +10,7 @@ function createComponent() {
}
async function setLoaded(loaded) {
- document.querySelector('.blob-viewer').dataset.loaded = loaded;
+ document.querySelector('.blob-viewer').setAttribute('data-loaded', loaded);
await nextTick();
}
@@ -51,7 +51,7 @@ describe('Markdown table of contents component', () => {
it('does not show dropdown when viewing non-rich content', async () => {
createComponent();
- document.querySelector('.blob-viewer').dataset.type = 'simple';
+ document.querySelector('.blob-viewer').setAttribute('data-type', 'simple');
await setLoaded(true);
diff --git a/spec/frontend/blob/viewer/index_spec.js b/spec/frontend/blob/viewer/index_spec.js
index 94422746fa7..fe55a537b89 100644
--- a/spec/frontend/blob/viewer/index_spec.js
+++ b/spec/frontend/blob/viewer/index_spec.js
@@ -77,9 +77,9 @@ describe('Blob viewer', () => {
return asyncClick()
.then(() => asyncClick())
.then(() => {
- expect(document.querySelector('.blob-viewer[data-type="simple"]').dataset.loaded).toBe(
- 'true',
- );
+ expect(
+ document.querySelector('.blob-viewer[data-type="simple"]').getAttribute('data-loaded'),
+ ).toBe('true');
});
});
diff --git a/spec/frontend/cascading_settings/components/lock_popovers_spec.js b/spec/frontend/cascading_settings/components/lock_popovers_spec.js
index 182e3c1c8ff..585e6ac505b 100644
--- a/spec/frontend/cascading_settings/components/lock_popovers_spec.js
+++ b/spec/frontend/cascading_settings/components/lock_popovers_spec.js
@@ -21,12 +21,12 @@ describe('LockPopovers', () => {
};
if (lockedByApplicationSetting) {
- popoverMountEl.dataset.popoverData = JSON.stringify(popoverData);
+ popoverMountEl.setAttribute('data-popover-data', JSON.stringify(popoverData));
} else if (lockedByAncestor) {
- popoverMountEl.dataset.popoverData = JSON.stringify({
- ...popoverData,
- ancestor_namespace: mockNamespace,
- });
+ popoverMountEl.setAttribute(
+ 'data-popover-data',
+ JSON.stringify({ ...popoverData, ancestor_namespace: mockNamespace }),
+ );
}
document.body.appendChild(popoverMountEl);
diff --git a/spec/frontend/code_navigation/store/actions_spec.js b/spec/frontend/code_navigation/store/actions_spec.js
index a734fd44403..c26416aca94 100644
--- a/spec/frontend/code_navigation/store/actions_spec.js
+++ b/spec/frontend/code_navigation/store/actions_spec.js
@@ -190,8 +190,8 @@ describe('Code navigation actions', () => {
it('commits SET_CURRENT_DEFINITION with LSIF data', () => {
target.classList.add('js-code-navigation');
- target.dataset.lineIndex = '0';
- target.dataset.charIndex = '0';
+ target.setAttribute('data-line-index', '0');
+ target.setAttribute('data-char-index', '0');
return testAction(
actions.showDefinition,
@@ -213,8 +213,8 @@ describe('Code navigation actions', () => {
it('adds hll class to target element', () => {
target.classList.add('js-code-navigation');
- target.dataset.lineIndex = '0';
- target.dataset.charIndex = '0';
+ target.setAttribute('data-line-index', '0');
+ target.setAttribute('data-char-index', '0');
return testAction(
actions.showDefinition,
@@ -238,8 +238,8 @@ describe('Code navigation actions', () => {
it('caches current target element', () => {
target.classList.add('js-code-navigation');
- target.dataset.lineIndex = '0';
- target.dataset.charIndex = '0';
+ target.setAttribute('data-line-index', '0');
+ target.setAttribute('data-char-index', '0');
return testAction(
actions.showDefinition,
diff --git a/spec/frontend/confirm_modal_spec.js b/spec/frontend/confirm_modal_spec.js
index 4224fb6be2a..53991349ee5 100644
--- a/spec/frontend/confirm_modal_spec.js
+++ b/spec/frontend/confirm_modal_spec.js
@@ -31,9 +31,9 @@ describe('ConfirmModal', () => {
buttons.forEach((x) => {
const button = document.createElement('button');
button.setAttribute('class', 'js-confirm-modal-button');
- button.dataset.path = x.path;
- button.dataset.method = x.method;
- button.dataset.modalAttributes = JSON.stringify(x.modalAttributes);
+ button.setAttribute('data-path', x.path);
+ button.setAttribute('data-method', x.method);
+ button.setAttribute('data-modal-attributes', JSON.stringify(x.modalAttributes));
button.innerHTML = 'Action';
buttonContainer.appendChild(button);
});
diff --git a/spec/frontend/helpers/startup_css_helper_spec.js b/spec/frontend/helpers/startup_css_helper_spec.js
index dca9faecea7..703bdbd342f 100644
--- a/spec/frontend/helpers/startup_css_helper_spec.js
+++ b/spec/frontend/helpers/startup_css_helper_spec.js
@@ -56,10 +56,9 @@ describe('waitForCSSLoaded', () => {
<link href="two.css" data-startupcss="loading">
`);
const events = waitForCSSLoaded(mockedCallback);
- document.querySelectorAll('[data-startupcss="loading"]').forEach((elem) => {
- // eslint-disable-next-line no-param-reassign
- elem.dataset.startupcss = 'loaded';
- });
+ document
+ .querySelectorAll('[data-startupcss="loading"]')
+ .forEach((elem) => elem.setAttribute('data-startupcss', 'loaded'));
document.dispatchEvent(new CustomEvent('CSSStartupLinkLoaded'));
await events;
diff --git a/spec/frontend/issues/create_merge_request_dropdown_spec.js b/spec/frontend/issues/create_merge_request_dropdown_spec.js
index cb7173c56a8..20b26f5abba 100644
--- a/spec/frontend/issues/create_merge_request_dropdown_spec.js
+++ b/spec/frontend/issues/create_merge_request_dropdown_spec.js
@@ -84,7 +84,7 @@ describe('CreateMergeRequestDropdown', () => {
});
it('enables when can create confidential issue', () => {
- document.querySelector('.js-create-mr').dataset.isConfidential = 'true';
+ document.querySelector('.js-create-mr').setAttribute('data-is-confidential', 'true');
confidentialState.selectedProject = { name: 'test' };
dropdown.enable();
@@ -93,7 +93,7 @@ describe('CreateMergeRequestDropdown', () => {
});
it('does not enable when can not create confidential issue', () => {
- document.querySelector('.js-create-mr').dataset.isConfidential = 'true';
+ document.querySelector('.js-create-mr').setAttribute('data-is-confidential', 'true');
dropdown.enable();
diff --git a/spec/frontend/labels/delete_label_modal_spec.js b/spec/frontend/labels/delete_label_modal_spec.js
index 67220821fe0..98049538948 100644
--- a/spec/frontend/labels/delete_label_modal_spec.js
+++ b/spec/frontend/labels/delete_label_modal_spec.js
@@ -25,11 +25,11 @@ describe('DeleteLabelModal', () => {
buttons.forEach((x) => {
const button = document.createElement('button');
button.setAttribute('class', 'js-delete-label-modal-button');
- button.dataset.labelName = x.labelName;
- button.dataset.destroyPath = x.destroyPath;
+ button.setAttribute('data-label-name', x.labelName);
+ button.setAttribute('data-destroy-path', x.destroyPath);
if (x.subjectName) {
- button.dataset.subjectName = x.subjectName;
+ button.setAttribute('data-subject-name', x.subjectName);
}
button.innerHTML = 'Action';
diff --git a/spec/frontend/lazy_loader_spec.js b/spec/frontend/lazy_loader_spec.js
index e0b6c7119f9..3d8b0d9c307 100644
--- a/spec/frontend/lazy_loader_spec.js
+++ b/spec/frontend/lazy_loader_spec.js
@@ -27,7 +27,7 @@ describe('LazyLoader', () => {
const createLazyLoadImage = () => {
const newImg = document.createElement('img');
newImg.className = 'lazy';
- newImg.dataset.src = TEST_PATH;
+ newImg.setAttribute('data-src', TEST_PATH);
document.body.appendChild(newImg);
triggerChildMutation();
@@ -108,7 +108,7 @@ describe('LazyLoader', () => {
expect(LazyLoader.loadImage).toHaveBeenCalledWith(img);
expect(img.getAttribute('src')).toBe(TEST_PATH);
- expect(img.dataset.src).toBeUndefined();
+ expect(img.getAttribute('data-src')).toBe(null);
expect(img).toHaveClass('js-lazy-loaded');
});
diff --git a/spec/frontend/members/index_spec.js b/spec/frontend/members/index_spec.js
index 251a8b0b774..efabe54f238 100644
--- a/spec/frontend/members/index_spec.js
+++ b/spec/frontend/members/index_spec.js
@@ -24,7 +24,7 @@ describe('initMembersApp', () => {
beforeEach(() => {
el = document.createElement('div');
- el.dataset.membersData = dataAttribute;
+ el.setAttribute('data-members-data', dataAttribute);
window.gon = { current_user_id: 123 };
});
diff --git a/spec/frontend/members/utils_spec.js b/spec/frontend/members/utils_spec.js
index b0c9459ff4f..a157cfa1c1d 100644
--- a/spec/frontend/members/utils_spec.js
+++ b/spec/frontend/members/utils_spec.js
@@ -256,7 +256,7 @@ describe('Members Utils', () => {
beforeEach(() => {
el = document.createElement('div');
- el.dataset.membersData = dataAttribute;
+ el.setAttribute('data-members-data', dataAttribute);
});
afterEach(() => {
diff --git a/spec/frontend/notebook/cells/markdown_spec.js b/spec/frontend/notebook/cells/markdown_spec.js
index de415b5bfe0..7dc6f90d202 100644
--- a/spec/frontend/notebook/cells/markdown_spec.js
+++ b/spec/frontend/notebook/cells/markdown_spec.js
@@ -78,8 +78,8 @@ describe('Markdown component', () => {
});
await nextTick();
- expect(findLink().dataset.remote).toBeUndefined();
- expect(findLink().dataset.type).toBeUndefined();
+ expect(findLink().getAttribute('data-remote')).toBe(null);
+ expect(findLink().getAttribute('data-type')).toBe(null);
});
describe('When parsing images', () => {
diff --git a/spec/frontend/notes/stores/actions_spec.js b/spec/frontend/notes/stores/actions_spec.js
index b18acd591ed..75e7756cd6b 100644
--- a/spec/frontend/notes/stores/actions_spec.js
+++ b/spec/frontend/notes/stores/actions_spec.js
@@ -400,13 +400,13 @@ describe('Actions Notes Store', () => {
beforeEach(() => {
axiosMock.onDelete(endpoint).replyOnce(200, {});
- document.body.dataset.page = '';
+ document.body.setAttribute('data-page', '');
});
afterEach(() => {
axiosMock.restore();
- document.body.dataset.page = '';
+ document.body.setAttribute('data-page', '');
});
it('commits DELETE_NOTE and dispatches updateMergeRequestWidget', () => {
@@ -436,7 +436,7 @@ describe('Actions Notes Store', () => {
it('dispatches removeDiscussionsFromDiff on merge request page', () => {
const note = { path: endpoint, id: 1 };
- document.body.dataset.page = 'projects:merge_requests:show';
+ document.body.setAttribute('data-page', 'projects:merge_requests:show');
return testAction(
actions.removeNote,
@@ -469,13 +469,13 @@ describe('Actions Notes Store', () => {
beforeEach(() => {
axiosMock.onDelete(endpoint).replyOnce(200, {});
- document.body.dataset.page = '';
+ document.body.setAttribute('data-page', '');
});
afterEach(() => {
axiosMock.restore();
- document.body.dataset.page = '';
+ document.body.setAttribute('data-page', '');
});
it('dispatches removeNote', () => {
diff --git a/spec/frontend/performance_bar/index_spec.js b/spec/frontend/performance_bar/index_spec.js
index af256b1d335..91cb46002be 100644
--- a/spec/frontend/performance_bar/index_spec.js
+++ b/spec/frontend/performance_bar/index_spec.js
@@ -16,11 +16,11 @@ describe('performance bar wrapper', () => {
performance.getEntriesByType = jest.fn().mockReturnValue([]);
peekWrapper.setAttribute('id', 'js-peek');
- peekWrapper.dataset.env = 'development';
- peekWrapper.dataset.requestId = '123';
- peekWrapper.dataset.peekUrl = '/-/peek/results';
- peekWrapper.dataset.statsUrl = 'https://log.gprd.gitlab.net/app/dashboards#/view/';
- peekWrapper.dataset.profileUrl = '?lineprofiler=true';
+ peekWrapper.setAttribute('data-env', 'development');
+ peekWrapper.setAttribute('data-request-id', '123');
+ peekWrapper.setAttribute('data-peek-url', '/-/peek/results');
+ peekWrapper.setAttribute('data-stats-url', 'https://log.gprd.gitlab.net/app/dashboards#/view/');
+ peekWrapper.setAttribute('data-profile-url', '?lineprofiler=true');
mock = new MockAdapter(axios);
diff --git a/spec/frontend/pipeline_editor/components/file-tree/container_spec.js b/spec/frontend/pipeline_editor/components/file-tree/container_spec.js
index 615a3eaac47..04a93e8db25 100644
--- a/spec/frontend/pipeline_editor/components/file-tree/container_spec.js
+++ b/spec/frontend/pipeline_editor/components/file-tree/container_spec.js
@@ -6,7 +6,7 @@ import { createMockDirective } from 'helpers/vue_mock_directive';
import PipelineEditorFileTreeContainer from '~/pipeline_editor/components/file_tree/container.vue';
import PipelineEditorFileTreeItem from '~/pipeline_editor/components/file_tree/file_item.vue';
import { FILE_TREE_TIP_DISMISSED_KEY } from '~/pipeline_editor/constants';
-import { mockCiConfigPath, mockIncludes } from '../../mock_data';
+import { mockCiConfigPath, mockIncludes, mockIncludesHelpPagePath } from '../../mock_data';
describe('Pipeline editor file nav', () => {
let wrapper;
@@ -16,6 +16,7 @@ describe('Pipeline editor file nav', () => {
shallowMount(PipelineEditorFileTreeContainer, {
provide: {
ciConfigPath: mockCiConfigPath,
+ includesHelpPagePath: mockIncludesHelpPagePath,
},
propsData: {
includes,
@@ -64,6 +65,10 @@ describe('Pipeline editor file nav', () => {
expect(findTip().exists()).toBe(true);
});
+ it('renders learn more link', async () => {
+ expect(findTip().props('secondaryButtonLink')).toBe(mockIncludesHelpPagePath);
+ });
+
it('can dismiss the tip', async () => {
expect(findTip().exists()).toBe(true);
diff --git a/spec/frontend/pipeline_editor/components/popovers/file_tree_popover_spec.js b/spec/frontend/pipeline_editor/components/popovers/file_tree_popover_spec.js
index 28f3b68b3f7..98ce3f6ea40 100644
--- a/spec/frontend/pipeline_editor/components/popovers/file_tree_popover_spec.js
+++ b/spec/frontend/pipeline_editor/components/popovers/file_tree_popover_spec.js
@@ -1,16 +1,23 @@
import { nextTick } from 'vue';
-import { GlPopover } from '@gitlab/ui';
+import { GlLink, GlPopover, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import FileTreePopover from '~/pipeline_editor/components/popovers/file_tree_popover.vue';
import { FILE_TREE_POPOVER_DISMISSED_KEY } from '~/pipeline_editor/constants';
+import { mockIncludesHelpPagePath } from '../../mock_data';
describe('FileTreePopover component', () => {
let wrapper;
const findPopover = () => wrapper.findComponent(GlPopover);
-
- const createComponent = (mountFn = shallowMount) => {
- wrapper = mountFn(FileTreePopover);
+ const findLink = () => findPopover().findComponent(GlLink);
+
+ const createComponent = ({ stubs } = {}) => {
+ wrapper = shallowMount(FileTreePopover, {
+ provide: {
+ includesHelpPagePath: mockIncludesHelpPagePath,
+ },
+ stubs,
+ });
};
afterEach(() => {
@@ -20,7 +27,7 @@ describe('FileTreePopover component', () => {
describe('default', () => {
beforeEach(async () => {
- createComponent();
+ createComponent({ stubs: { GlSprintf } });
});
it('renders dismissable popover', async () => {
@@ -31,6 +38,11 @@ describe('FileTreePopover component', () => {
expect(findPopover().exists()).toBe(false);
});
+
+ it('renders learn more link', () => {
+ expect(findLink().exists()).toBe(true);
+ expect(findLink().attributes('href')).toBe(mockIncludesHelpPagePath);
+ });
});
describe('when popover has already been dismissed before', () => {
diff --git a/spec/frontend/pipeline_editor/mock_data.js b/spec/frontend/pipeline_editor/mock_data.js
index 748519dfbae..560b2820fae 100644
--- a/spec/frontend/pipeline_editor/mock_data.js
+++ b/spec/frontend/pipeline_editor/mock_data.js
@@ -9,6 +9,7 @@ export const mockNewBranch = 'new-branch';
export const mockNewMergeRequestPath = '/-/merge_requests/new';
export const mockCommitSha = 'aabbccdd';
export const mockCommitNextSha = 'eeffgghh';
+export const mockIncludesHelpPagePath = '/-/includes/help';
export const mockLintHelpPagePath = '/-/lint-help';
export const mockLintUnavailableHelpPagePath = '/-/pipeline-editor/troubleshoot';
export const mockYmlHelpPagePath = '/-/yml-help';
diff --git a/spec/frontend/search_autocomplete_spec.js b/spec/frontend/search_autocomplete_spec.js
index 48e0aab6561..190f2803324 100644
--- a/spec/frontend/search_autocomplete_spec.js
+++ b/spec/frontend/search_autocomplete_spec.js
@@ -52,7 +52,7 @@ describe('Search autocomplete dropdown', () => {
};
const disableProjectIssues = () => {
- document.querySelector('.js-search-project-options').dataset.issuesDisabled = true;
+ document.querySelector('.js-search-project-options').setAttribute('data-issues-disabled', true);
};
// Mock `gl` object in window for dashboard specific page. App code will need it.
diff --git a/spec/frontend/user_popovers_spec.js b/spec/frontend/user_popovers_spec.js
index 3aa49eb8027..4edd526100a 100644
--- a/spec/frontend/user_popovers_spec.js
+++ b/spec/frontend/user_popovers_spec.js
@@ -22,7 +22,7 @@ describe('User Popovers', () => {
const link = document.createElement('a');
link.classList.add('js-user-link');
- link.dataset.user = '1';
+ link.setAttribute('data-user', '1');
return link;
};
diff --git a/spec/frontend/users_select/test_helper.js b/spec/frontend/users_select/test_helper.js
index 9231e38ea90..59edde48eab 100644
--- a/spec/frontend/users_select/test_helper.js
+++ b/spec/frontend/users_select/test_helper.js
@@ -95,10 +95,10 @@ export const setAssignees = (...users) => {
const input = document.createElement('input');
input.name = 'merge_request[assignee_ids][]';
input.value = user.id.toString();
- input.dataset.avatarUrl = user.avatar_url;
- input.dataset.name = user.name;
- input.dataset.username = user.username;
- input.dataset.canMerge = user.can_merge;
+ input.setAttribute('data-avatar-url', user.avatar_url);
+ input.setAttribute('data-name', user.name);
+ input.setAttribute('data-username', user.username);
+ input.setAttribute('data-can-merge', user.can_merge);
return input;
}),
);
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_merged_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_merged_spec.js
index 29ee7e0010f..8efc4d84624 100644
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_merged_spec.js
+++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_merged_spec.js
@@ -193,7 +193,9 @@ describe('MRWidgetMerged', () => {
it('shows button to copy commit SHA to clipboard', () => {
expect(selectors.copyMergeShaButton).not.toBe(null);
- expect(selectors.copyMergeShaButton.dataset.clipboardText).toBe(vm.mr.mergeCommitSha);
+ expect(selectors.copyMergeShaButton.getAttribute('data-clipboard-text')).toBe(
+ vm.mr.mergeCommitSha,
+ );
});
it('hides button to copy commit SHA if SHA does not exist', async () => {
diff --git a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js
index 1fe32605f09..9719e81fe12 100644
--- a/spec/frontend/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/frontend/vue_mr_widget/mr_widget_options_spec.js
@@ -423,7 +423,7 @@ describe('MrWidgetOptions', () => {
beforeEach(() => {
const favicon = document.createElement('link');
favicon.setAttribute('id', 'favicon');
- favicon.dataset.originalHref = faviconDataUrl;
+ favicon.setAttribute('data-original-href', faviconDataUrl);
document.body.appendChild(favicon);
faviconElement = document.getElementById('favicon');
diff --git a/spec/frontend_integration/ide/helpers/ide_helper.js b/spec/frontend_integration/ide/helpers/ide_helper.js
index 4245e1f04c8..00ce39a5598 100644
--- a/spec/frontend_integration/ide/helpers/ide_helper.js
+++ b/spec/frontend_integration/ide/helpers/ide_helper.js
@@ -40,14 +40,14 @@ export const findMonacoDiffEditor = () =>
export const findAndSetEditorValue = async (value) => {
const editor = await findMonacoEditor();
- const { uri } = editor.dataset;
+ const uri = editor.getAttribute('data-uri');
monacoEditor.getModel(uri).setValue(value);
};
export const getEditorValue = async () => {
const editor = await findMonacoEditor();
- const { uri } = editor.dataset;
+ const uri = editor.getAttribute('data-uri');
return monacoEditor.getModel(uri).getValue();
};
diff --git a/spec/helpers/ci/pipeline_editor_helper_spec.rb b/spec/helpers/ci/pipeline_editor_helper_spec.rb
index 12456deb538..429d4c7941a 100644
--- a/spec/helpers/ci/pipeline_editor_helper_spec.rb
+++ b/spec/helpers/ci/pipeline_editor_helper_spec.rb
@@ -45,6 +45,7 @@ RSpec.describe Ci::PipelineEditorHelper do
"default-branch" => project.default_branch_or_main,
"empty-state-illustration-path" => 'foo',
"initial-branch-name" => nil,
+ "includes-help-page-path" => help_page_path('ci/yaml/includes'),
"lint-help-page-path" => help_page_path('ci/lint', anchor: 'check-cicd-syntax'),
"lint-unavailable-help-page-path" => help_page_path('ci/pipeline_editor/index', anchor: 'configuration-validation-currently-not-available-message'),
"needs-help-page-path" => help_page_path('ci/yaml/index', anchor: 'needs'),
@@ -72,6 +73,7 @@ RSpec.describe Ci::PipelineEditorHelper do
"default-branch" => project.default_branch_or_main,
"empty-state-illustration-path" => 'foo',
"initial-branch-name" => nil,
+ "includes-help-page-path" => help_page_path('ci/yaml/includes'),
"lint-help-page-path" => help_page_path('ci/lint', anchor: 'check-cicd-syntax'),
"lint-unavailable-help-page-path" => help_page_path('ci/pipeline_editor/index', anchor: 'configuration-validation-currently-not-available-message'),
"needs-help-page-path" => help_page_path('ci/yaml/index', anchor: 'needs'),
diff --git a/spec/lib/bulk_imports/groups/pipelines/group_pipeline_spec.rb b/spec/lib/bulk_imports/groups/pipelines/group_pipeline_spec.rb
index 39e782dc093..441a34b0c74 100644
--- a/spec/lib/bulk_imports/groups/pipelines/group_pipeline_spec.rb
+++ b/spec/lib/bulk_imports/groups/pipelines/group_pipeline_spec.rb
@@ -23,7 +23,7 @@ RSpec.describe BulkImports::Groups::Pipelines::GroupPipeline do
let(:group_data) do
{
- 'name' => 'source_name',
+ 'name' => 'Source Group Name',
'full_path' => 'source/full/path',
'visibility' => 'private',
'project_creation_level' => 'developer',
diff --git a/spec/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline_spec.rb b/spec/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline_spec.rb
index e4a41428dd2..6949ac59948 100644
--- a/spec/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline_spec.rb
+++ b/spec/lib/bulk_imports/groups/pipelines/subgroup_entities_pipeline_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline do
let_it_be(:user) { create(:user) }
let_it_be(:group) { create(:group, path: 'group') }
- let_it_be(:parent) { create(:group, name: 'imported-group', path: 'imported-group') }
+ let_it_be(:parent) { create(:group, name: 'Imported Group', path: 'imported-group') }
let_it_be(:parent_entity) { create(:bulk_import_entity, destination_namespace: parent.full_path, group: parent) }
let_it_be(:tracker) { create(:bulk_import_tracker, entity: parent_entity) }
let_it_be(:context) { BulkImports::Pipeline::Context.new(tracker) }
@@ -14,8 +14,8 @@ RSpec.describe BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline do
let(:extracted_data) do
BulkImports::Pipeline::ExtractedData.new(data: {
- 'name' => 'subgroup',
- 'full_path' => 'parent/subgroup'
+ 'path' => 'sub-group',
+ 'full_path' => 'parent/sub-group'
})
end
@@ -33,9 +33,9 @@ RSpec.describe BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline do
subgroup_entity = BulkImports::Entity.last
- expect(subgroup_entity.source_full_path).to eq 'parent/subgroup'
+ expect(subgroup_entity.source_full_path).to eq 'parent/sub-group'
expect(subgroup_entity.destination_namespace).to eq 'imported-group'
- expect(subgroup_entity.destination_name).to eq 'subgroup'
+ expect(subgroup_entity.destination_name).to eq 'sub-group'
expect(subgroup_entity.parent_id).to eq parent_entity.id
end
end
@@ -51,9 +51,7 @@ RSpec.describe BulkImports::Groups::Pipelines::SubgroupEntitiesPipeline do
destination_namespace: parent_entity.group.full_path,
parent_id: parent_entity.id
}
-
expect { subject.load(context, data) }.to change(BulkImports::Entity, :count).by(1)
-
subgroup_entity = BulkImports::Entity.last
expect(subgroup_entity.source_full_path).to eq 'parent/subgroup'
diff --git a/spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb b/spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb
index 75d8c15088a..c42ca9bef3b 100644
--- a/spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb
+++ b/spec/lib/bulk_imports/groups/transformers/group_attributes_transformer_spec.rb
@@ -6,7 +6,6 @@ RSpec.describe BulkImports::Groups::Transformers::GroupAttributesTransformer do
describe '#transform' do
let_it_be(:user) { create(:user) }
let_it_be(:parent) { create(:group) }
- let_it_be(:group) { create(:group, name: 'My Source Group', parent: parent) }
let_it_be(:bulk_import) { create(:bulk_import, user: user) }
let_it_be(:entity) do
@@ -14,7 +13,7 @@ RSpec.describe BulkImports::Groups::Transformers::GroupAttributesTransformer do
:bulk_import_entity,
bulk_import: bulk_import,
source_full_path: 'source/full/path',
- destination_name: group.name,
+ destination_name: 'destination-name-path',
destination_namespace: parent.full_path
)
end
@@ -24,7 +23,8 @@ RSpec.describe BulkImports::Groups::Transformers::GroupAttributesTransformer do
let(:data) do
{
- 'name' => 'source_name',
+ 'name' => 'Source Group Name',
+ 'path' => 'source-group-path',
'full_path' => 'source/full/path',
'visibility' => 'private',
'project_creation_level' => 'developer',
@@ -34,23 +34,27 @@ RSpec.describe BulkImports::Groups::Transformers::GroupAttributesTransformer do
subject { described_class.new }
- it 'transforms name to destination name' do
- transformed_data = subject.transform(context, data)
+ it 'returns original data with some keys transformed' do
+ transformed_data = subject.transform(context, { 'name' => 'Name', 'description' => 'Description' })
- expect(transformed_data['name']).not_to eq('source_name')
- expect(transformed_data['name']).to eq(group.name)
+ expect(transformed_data).to eq({
+ 'name' => 'Name',
+ 'description' => 'Description',
+ 'parent_id' => parent.id,
+ 'path' => 'destination-name-path'
+ })
end
- it 'removes full path' do
+ it 'transforms path from destination_name' do
transformed_data = subject.transform(context, data)
- expect(transformed_data).not_to have_key('full_path')
+ expect(transformed_data['path']).to eq(entity.destination_name)
end
- it 'transforms path to parameterized name' do
+ it 'removes full path' do
transformed_data = subject.transform(context, data)
- expect(transformed_data['path']).to eq(group.name.parameterize)
+ expect(transformed_data).not_to have_key('full_path')
end
it 'transforms visibility level' do
diff --git a/spec/lib/bulk_imports/groups/transformers/subgroup_to_entity_transformer_spec.rb b/spec/lib/bulk_imports/groups/transformers/subgroup_to_entity_transformer_spec.rb
index 2f97a5721e7..6450d90ec0f 100644
--- a/spec/lib/bulk_imports/groups/transformers/subgroup_to_entity_transformer_spec.rb
+++ b/spec/lib/bulk_imports/groups/transformers/subgroup_to_entity_transformer_spec.rb
@@ -9,14 +9,14 @@ RSpec.describe BulkImports::Groups::Transformers::SubgroupToEntityTransformer do
parent_entity = instance_double(BulkImports::Entity, group: parent, id: 1)
context = instance_double(BulkImports::Pipeline::Context, entity: parent_entity)
subgroup_data = {
- "name" => "subgroup",
- "full_path" => "parent/subgroup"
+ "path" => "sub-group",
+ "full_path" => "parent/sub-group"
}
expect(subject.transform(context, subgroup_data)).to eq(
source_type: :group_entity,
- source_full_path: "parent/subgroup",
- destination_name: "subgroup",
+ source_full_path: "parent/sub-group",
+ destination_name: "sub-group",
destination_namespace: parent.full_path,
parent_id: 1
)
diff --git a/spec/lib/gitlab/auth/otp/strategies/forti_authenticator_spec.rb b/spec/lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp_spec.rb
index dc20df98185..f08c787382e 100644
--- a/spec/lib/gitlab/auth/otp/strategies/forti_authenticator_spec.rb
+++ b/spec/lib/gitlab/auth/otp/strategies/forti_authenticator/manual_otp_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Auth::Otp::Strategies::FortiAuthenticator do
+RSpec.describe Gitlab::Auth::Otp::Strategies::FortiAuthenticator::ManualOtp do
let_it_be(:user) { create(:user) }
let(:otp_code) { 42 }
diff --git a/spec/lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp_spec.rb b/spec/lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp_spec.rb
new file mode 100644
index 00000000000..231bd3f48f1
--- /dev/null
+++ b/spec/lib/gitlab/auth/otp/strategies/forti_authenticator/push_otp_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Auth::Otp::Strategies::FortiAuthenticator::PushOtp do
+ let_it_be(:user) { create(:user) }
+
+ let(:host) { 'forti_authenticator.example.com' }
+ let(:port) { '444' }
+ let(:api_username) { 'janedoe' }
+ let(:api_token) { 's3cr3t' }
+
+ let(:forti_authenticator_auth_url) { "https://#{host}:#{port}/api/v1/pushauth/" }
+ let(:response_status) { 200 }
+
+ subject(:validate) { described_class.new(user).validate }
+
+ before do
+ stub_feature_flags(forti_authenticator: user)
+
+ stub_forti_authenticator_config(
+ enabled: true,
+ host: host,
+ port: port,
+ username: api_username,
+ access_token: api_token
+ )
+
+ request_body = { username: user.username }
+
+ stub_request(:post, forti_authenticator_auth_url)
+ .with(body: JSON(request_body),
+ headers: { 'Content-Type': 'application/json' },
+ basic_auth: [api_username, api_token])
+ .to_return(status: response_status, body: '')
+ end
+
+ context 'successful validation' do
+ it 'returns success' do
+ expect(validate[:status]).to eq(:success)
+ end
+ end
+
+ context 'unsuccessful validation' do
+ let(:response_status) { 401 }
+
+ it 'returns error' do
+ expect(validate[:status]).to eq(:error)
+ end
+ end
+
+ context 'unexpected error' do
+ it 'returns error' do
+ error_message = 'boom!'
+ stub_request(:post, forti_authenticator_auth_url).to_raise(StandardError.new(error_message))
+
+ expect(validate[:status]).to eq(:error)
+ expect(validate[:message]).to eq(error_message)
+ end
+ end
+
+ def stub_forti_authenticator_config(forti_authenticator_settings)
+ allow(::Gitlab.config.forti_authenticator).to(receive_messages(forti_authenticator_settings))
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/expire_o_auth_tokens_spec.rb b/spec/lib/gitlab/background_migration/expire_o_auth_tokens_spec.rb
new file mode 100644
index 00000000000..3f250d13e84
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/expire_o_auth_tokens_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::ExpireOAuthTokens, :migration, schema: 20220428133724 do
+ let(:migration) { described_class.new }
+ let(:oauth_access_tokens_table) { table(:oauth_access_tokens) }
+
+ let(:table_name) { 'oauth_access_tokens' }
+
+ subject(:perform_migration) do
+ described_class.new(start_id: 1,
+ end_id: 30,
+ batch_table: :oauth_access_tokens,
+ batch_column: :id,
+ sub_batch_size: 2,
+ pause_ms: 0,
+ connection: ActiveRecord::Base.connection)
+ .perform(1000)
+ end
+
+ before do
+ oauth_access_tokens_table.create!(id: 1, token: 's3cr3t-1', expires_in: nil)
+ oauth_access_tokens_table.create!(id: 2, token: 's3cr3t-2', expires_in: 42)
+ oauth_access_tokens_table.create!(id: 3, token: 's3cr3t-3', expires_in: nil)
+ end
+
+ it 'adds expiry to oauth tokens', :aggregate_failures do
+ expect(ActiveRecord::QueryRecorder.new { perform_migration }.count).to eq(3)
+
+ expect(oauth_access_tokens_table.find(1).expires_in).to eq(7_200)
+ expect(oauth_access_tokens_table.find(2).expires_in).to eq(42)
+ expect(oauth_access_tokens_table.find(3).expires_in).to eq(7_200)
+ end
+end
diff --git a/spec/migrations/20220428133724_schedule_expire_o_auth_tokens_spec.rb b/spec/migrations/20220428133724_schedule_expire_o_auth_tokens_spec.rb
new file mode 100644
index 00000000000..05d1053a46c
--- /dev/null
+++ b/spec/migrations/20220428133724_schedule_expire_o_auth_tokens_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ScheduleExpireOAuthTokens do
+ let_it_be(:migration) { described_class::MIGRATION }
+
+ describe '#up' do
+ it 'schedules background jobs for each batch of oauth tokens' do
+ migrate!
+
+ expect(migration).to(
+ have_scheduled_batched_migration(
+ table_name: :oauth_access_tokens,
+ column_name: :id,
+ interval: described_class::INTERVAL
+ )
+ )
+ end
+ end
+
+ describe '#down' do
+ it 'deletes all batched migration records' do
+ migrate!
+ schema_migrate_down!
+
+ expect(migration).not_to have_scheduled_batched_migration
+ end
+ end
+end
diff --git a/spec/migrations/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints_spec.rb b/spec/migrations/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints_spec.rb
new file mode 100644
index 00000000000..0c4d0e86789
--- /dev/null
+++ b/spec/migrations/20220505174658_update_index_on_alerts_to_exclude_null_fingerprints_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe UpdateIndexOnAlertsToExcludeNullFingerprints do
+ let(:alerts) { 'alert_management_alerts'}
+ let(:old_index) { described_class::OLD_INDEX_NAME }
+ let(:new_index) { described_class::NEW_INDEX_NAME }
+
+ it 'correctly migrates up and down' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(subject.index_exists_by_name?(alerts, old_index)).to be_truthy
+ expect(subject.index_exists_by_name?(alerts, new_index)).to be_falsey
+ }
+
+ migration.after -> {
+ expect(subject.index_exists_by_name?(alerts, old_index)).to be_falsey
+ expect(subject.index_exists_by_name?(alerts, new_index)).to be_truthy
+ }
+ end
+ end
+end
diff --git a/spec/models/alert_management/alert_spec.rb b/spec/models/alert_management/alert_spec.rb
index e709bee695c..685ed81ec84 100644
--- a/spec/models/alert_management/alert_spec.rb
+++ b/spec/models/alert_management/alert_spec.rb
@@ -233,13 +233,13 @@ RSpec.describe AlertManagement::Alert do
end
end
- describe '.find_ongoing_alert' do
+ describe '.find_unresolved_alert' do
let_it_be(:fingerprint) { SecureRandom.hex }
let_it_be(:resolved_alert_with_fingerprint) { create(:alert_management_alert, :resolved, project: project, fingerprint: fingerprint) }
let_it_be(:alert_with_fingerprint_in_other_project) { create(:alert_management_alert, project: project2, fingerprint: fingerprint) }
let_it_be(:alert_with_fingerprint) { create(:alert_management_alert, project: project, fingerprint: fingerprint) }
- subject { described_class.find_ongoing_alert(project, fingerprint) }
+ subject { described_class.find_unresolved_alert(project, fingerprint) }
it { is_expected.to eq(alert_with_fingerprint) }
end
diff --git a/spec/requests/api/internal/base_spec.rb b/spec/requests/api/internal/base_spec.rb
index c33e2bce65e..acfe476a864 100644
--- a/spec/requests/api/internal/base_spec.rb
+++ b/spec/requests/api/internal/base_spec.rb
@@ -1466,6 +1466,89 @@ RSpec.describe API::Internal::Base do
subject
expect(json_response['success']).to be_falsey
+ expect(json_response['message']).to eq 'Feature is not available'
+ end
+ end
+
+ describe 'POST /internal/two_factor_manual_otp_check' do
+ let(:key_id) { key.id }
+ let(:otp) { '123456'}
+
+ subject do
+ post api('/internal/two_factor_manual_otp_check'),
+ params: {
+ secret_token: secret_token,
+ key_id: key_id,
+ otp_attempt: otp
+ }
+ end
+
+ it 'is not available' do
+ subject
+
+ expect(json_response['success']).to be_falsey
+ expect(json_response['message']).to eq 'Feature is not available'
+ end
+ end
+
+ describe 'POST /internal/two_factor_push_otp_check' do
+ let(:key_id) { key.id }
+ let(:otp) { '123456'}
+
+ subject do
+ post api('/internal/two_factor_push_otp_check'),
+ params: {
+ secret_token: secret_token,
+ key_id: key_id,
+ otp_attempt: otp
+ }
+ end
+
+ it 'is not available' do
+ subject
+
+ expect(json_response['success']).to be_falsey
+ expect(json_response['message']).to eq 'Feature is not available'
+ end
+ end
+
+ describe 'POST /internal/two_factor_manual_otp_check' do
+ let(:key_id) { key.id }
+ let(:otp) { '123456'}
+
+ subject do
+ post api('/internal/two_factor_manual_otp_check'),
+ params: {
+ secret_token: secret_token,
+ key_id: key_id,
+ otp_attempt: otp
+ }
+ end
+
+ it 'is not available' do
+ subject
+
+ expect(json_response['success']).to be_falsey
+ end
+ end
+
+ describe 'POST /internal/two_factor_push_otp_check' do
+ let(:key_id) { key.id }
+ let(:otp) { '123456'}
+
+ subject do
+ post api('/internal/two_factor_push_otp_check'),
+ params: {
+ secret_token: secret_token,
+ key_id: key_id,
+ otp_attempt: otp
+ }
+ end
+
+ it 'is not available' do
+ subject
+
+ expect(json_response['success']).to be_falsey
end
end
diff --git a/spec/services/alert_management/alerts/update_service_spec.rb b/spec/services/alert_management/alerts/update_service_spec.rb
index 882543fd701..f02607b8174 100644
--- a/spec/services/alert_management/alerts/update_service_spec.rb
+++ b/spec/services/alert_management/alerts/update_service_spec.rb
@@ -88,7 +88,7 @@ RSpec.describe AlertManagement::Alerts::UpdateService do
it_behaves_like 'title update'
end
- context 'when alert is resolved and another existing open alert' do
+ context 'when alert is resolved and another existing unresolved alert' do
let!(:alert) { create(:alert_management_alert, :resolved, project: project) }
let!(:existing_alert) { create(:alert_management_alert, :triggered, project: project) }
@@ -193,27 +193,38 @@ RSpec.describe AlertManagement::Alerts::UpdateService do
end
end
- context 'with an opening status and existing open alert' do
- let_it_be(:alert) { create(:alert_management_alert, :resolved, :with_fingerprint, project: project) }
- let_it_be(:existing_alert) { create(:alert_management_alert, :triggered, fingerprint: alert.fingerprint, project: project) }
- let_it_be(:url) { Gitlab::Routing.url_helpers.details_project_alert_management_path(project, existing_alert) }
- let_it_be(:link) { ActionController::Base.helpers.link_to(_('alert'), url) }
+ context 'with existing unresolved alert' do
+ context 'with fingerprints' do
+ let_it_be(:existing_alert) { create(:alert_management_alert, :triggered, fingerprint: alert.fingerprint, project: project) }
- let(:message) do
- "An #{link} with the same fingerprint is already open. " \
- 'To change the status of this alert, resolve the linked alert.'
- end
+ it 'does not query for existing alerts' do
+ expect(::AlertManagement::Alert).not_to receive(:find_unresolved_alert)
- it_behaves_like 'does not add a todo'
- it_behaves_like 'does not add a system note'
+ response
+ end
- it 'has an informative message' do
- expect(response).to be_error
- expect(response.message).to eq(message)
+ context 'when status was resolved' do
+ let_it_be(:alert) { create(:alert_management_alert, :resolved, :with_fingerprint, project: project) }
+ let_it_be(:existing_alert) { create(:alert_management_alert, :triggered, fingerprint: alert.fingerprint, project: project) }
+
+ let(:url) { Gitlab::Routing.url_helpers.details_project_alert_management_path(project, existing_alert) }
+ let(:link) { ActionController::Base.helpers.link_to(_('alert'), url) }
+ let(:message) do
+ "An #{link} with the same fingerprint is already open. " \
+ 'To change the status of this alert, resolve the linked alert.'
+ end
+
+ it_behaves_like 'does not add a todo'
+ it_behaves_like 'does not add a system note'
+
+ it 'has an informative message' do
+ expect(response).to be_error
+ expect(response.message).to eq(message)
+ end
+ end
end
- context 'fingerprints are blank' do
- let_it_be(:alert) { create(:alert_management_alert, :resolved, project: project, fingerprint: nil) }
+ context 'without fingerprints' do
let_it_be(:existing_alert) { create(:alert_management_alert, :triggered, fingerprint: alert.fingerprint, project: project) }
it 'successfully changes the status' do
diff --git a/spec/services/users/validate_otp_service_spec.rb b/spec/services/users/validate_manual_otp_service_spec.rb
index 46b80b2149f..d71735814f2 100644
--- a/spec/services/users/validate_otp_service_spec.rb
+++ b/spec/services/users/validate_manual_otp_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Users::ValidateOtpService do
+RSpec.describe Users::ValidateManualOtpService do
let_it_be(:user) { create(:user) }
let(:otp_code) { 42 }
@@ -25,8 +25,8 @@ RSpec.describe Users::ValidateOtpService do
allow(::Gitlab.config.forti_authenticator).to receive(:enabled).and_return(true)
end
- it 'calls FortiAuthenticator strategy' do
- expect_next_instance_of(::Gitlab::Auth::Otp::Strategies::FortiAuthenticator) do |strategy|
+ it 'calls ManualOtp strategy' do
+ expect_next_instance_of(::Gitlab::Auth::Otp::Strategies::FortiAuthenticator::ManualOtp) do |strategy|
expect(strategy).to receive(:validate).with(otp_code).once
end
@@ -48,4 +48,25 @@ RSpec.describe Users::ValidateOtpService do
validate
end
end
+
+ context 'unexpected error' do
+ before do
+ stub_feature_flags(forti_authenticator: user)
+ allow(::Gitlab.config.forti_authenticator).to receive(:enabled).and_return(true)
+ end
+
+ it 'returns error' do
+ error_message = "boom!"
+
+ expect_next_instance_of(::Gitlab::Auth::Otp::Strategies::FortiAuthenticator::ManualOtp) do |strategy|
+ expect(strategy).to receive(:validate).with(otp_code).once.and_raise(StandardError, error_message)
+ end
+ expect(Gitlab::ErrorTracking).to receive(:log_exception)
+
+ result = validate
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq(error_message)
+ end
+ end
end
diff --git a/spec/services/users/validate_push_otp_service_spec.rb b/spec/services/users/validate_push_otp_service_spec.rb
new file mode 100644
index 00000000000..960b6bcd3bb
--- /dev/null
+++ b/spec/services/users/validate_push_otp_service_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Users::ValidatePushOtpService do
+ let_it_be(:user) { create(:user) }
+
+ subject(:validate) { described_class.new(user).execute }
+
+ context 'FortiAuthenticator' do
+ before do
+ stub_feature_flags(forti_authenticator: user)
+ allow(::Gitlab.config.forti_authenticator).to receive(:enabled).and_return(true)
+ end
+
+ it 'calls PushOtp strategy' do
+ expect_next_instance_of(::Gitlab::Auth::Otp::Strategies::FortiAuthenticator::PushOtp) do |strategy|
+ expect(strategy).to receive(:validate).once
+ end
+
+ validate
+ end
+ end
+
+ context 'unexpected error' do
+ before do
+ stub_feature_flags(forti_authenticator: user)
+ allow(::Gitlab.config.forti_authenticator).to receive(:enabled).and_return(true)
+ end
+
+ it 'returns error' do
+ error_message = "boom!"
+
+ expect_next_instance_of(::Gitlab::Auth::Otp::Strategies::FortiAuthenticator::PushOtp) do |strategy|
+ expect(strategy).to receive(:validate).once.and_raise(StandardError, error_message)
+ end
+ expect(Gitlab::ErrorTracking).to receive(:log_exception)
+
+ result = validate
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq(error_message)
+ end
+ end
+end