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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-05-12 00:08:09 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-05-12 00:08:09 +0300
commitfdc26e021b1e3eea4161bf6891f3a151fb7414b0 (patch)
treef06ce58930f41f8d031e827df198fed5dfab09be
parent11df4bf91b8cf9ac7bb601241992e300eebf684c (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.eslintrc.yml2
-rw-r--r--.rubocop_todo/database/multiple_databases.yml10
-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/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/registrations/new/index.js5
-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/header/pipeline_editor_mini_graph.vue18
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue25
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue3
-rw-r--r--app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue8
-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/vue_merge_request_widget/components/mr_widget_pipeline.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_icon.vue33
-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/assets/stylesheets/bootstrap_migration_components.scss9
-rw-r--r--app/assets/stylesheets/framework/icons.scss72
-rw-r--r--app/assets/stylesheets/framework/wells.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/_pipeline_mixins.scss99
-rw-r--r--app/assets/stylesheets/page_bundles/merge_requests.scss4
-rw-r--r--app/assets/stylesheets/page_bundles/pipelines.scss65
-rw-r--r--app/assets/stylesheets/pages/commits.scss5
-rw-r--r--app/finders/error_tracking/errors_finder.rb46
-rw-r--r--app/graphql/types/ci/runner_status_enum.rb7
-rw-r--r--app/graphql/types/work_item_type.rb3
-rw-r--r--app/helpers/ci/runners_helper.rb2
-rw-r--r--app/models/ci/build.rb1
-rw-r--r--app/models/ci/namespace_settings.rb19
-rw-r--r--app/models/ci/runner.rb6
-rw-r--r--app/models/issue.rb14
-rw-r--r--app/models/namespace.rb1
-rw-r--r--app/models/namespace_ci_cd_setting.rb2
-rw-r--r--app/models/project.rb1
-rw-r--r--app/services/error_tracking/base_service.rb10
-rw-r--r--app/services/error_tracking/collect_error_service.rb26
-rw-r--r--app/services/error_tracking/issue_details_service.rb11
-rw-r--r--app/services/error_tracking/issue_latest_event_service.rb13
-rw-r--r--app/services/error_tracking/issue_update_service.rb6
-rw-r--r--app/services/error_tracking/list_issues_service.rb16
-rw-r--r--app/views/projects/_merge_request_merge_method_settings.html.haml2
-rw-r--r--config/application.rb2
-rw-r--r--config/feature_flags/development/enhanced_notify_css.yml4
-rw-r--r--config/feature_flags/ops/ci_runner_separation_by_plan.yml8
-rw-r--r--config/gitlab.yml.example4
-rw-r--r--config/initializers/1_settings.rb3
-rw-r--r--data/removals/15_0/15-0-runner-api-status-renames-not_connected.yml16
-rw-r--r--db/migrate/20220421141342_add_allowed_plans_to_ci_runners.rb9
-rw-r--r--db/migrate/20220505092254_add_allow_stale_runner_pruning_index_to_namespace_ci_cd_settings.rb18
-rw-r--r--db/post_migrate/20220511152439_remove_not_null_constraint_from_work_item_type.rb16
-rw-r--r--db/schema_migrations/202204211413421
-rw-r--r--db/schema_migrations/202205050922541
-rw-r--r--db/schema_migrations/202205111524391
-rw-r--r--db/structure.sql6
-rw-r--r--doc/.vale/gitlab/spelling-exceptions.txt23
-rw-r--r--doc/administration/configure.md2
-rw-r--r--doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md2
-rw-r--r--doc/administration/reference_architectures/10k_users.md4
-rw-r--r--doc/administration/reference_architectures/25k_users.md4
-rw-r--r--doc/administration/reference_architectures/2k_users.md4
-rw-r--r--doc/administration/reference_architectures/3k_users.md4
-rw-r--r--doc/administration/reference_architectures/50k_users.md4
-rw-r--r--doc/administration/reference_architectures/5k_users.md4
-rw-r--r--doc/administration/reference_architectures/index.md2
-rw-r--r--doc/administration/troubleshooting/diagnostics_tools.md2
-rw-r--r--doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md2
-rw-r--r--doc/administration/troubleshooting/log_parsing.md2
-rw-r--r--doc/api/graphql/reference/index.md3
-rw-r--r--doc/api/members.md29
-rw-r--r--doc/api/runners.md18
-rw-r--r--doc/architecture/blueprints/database_scaling/size-limits.md2
-rw-r--r--doc/ci/yaml/workflow.md10
-rw-r--r--doc/development/application_slis/index.md2
-rw-r--r--doc/development/audit_event_guide/index.md2
-rw-r--r--doc/development/database/batched_background_migrations.md2
-rw-r--r--doc/development/database/loose_foreign_keys.md6
-rw-r--r--doc/development/documentation/testing.md2
-rw-r--r--doc/development/fe_guide/registry_architecture.md2
-rw-r--r--doc/development/foreign_keys.md4
-rw-r--r--doc/development/go_guide/go_upgrade.md2
-rw-r--r--doc/development/performance.md2
-rw-r--r--doc/development/pipelines.md2
-rw-r--r--doc/development/redis/new_redis_instance.md4
-rw-r--r--doc/development/ruby_upgrade.md2
-rw-r--r--doc/development/snowplow/troubleshooting.md2
-rw-r--r--doc/install/aws/gitlab_hybrid_on_aws.md12
-rw-r--r--doc/install/aws/index.md2
-rw-r--r--doc/operations/feature_flags.md2
-rw-r--r--doc/update/index.md2
-rw-r--r--doc/update/removals.md14
-rw-r--r--doc/user/application_security/secret_detection/post_processing.md2
-rw-r--r--doc/user/clusters/agent/install/index.md10
-rw-r--r--doc/user/group/saml_sso/index.md2
-rw-r--r--doc/user/packages/container_registry/index.md2
-rw-r--r--doc/user/profile/notifications.md4
-rw-r--r--doc/user/project/merge_requests/approvals/settings.md2
-rw-r--r--doc/user/project/merge_requests/fast_forward_merge.md77
-rw-r--r--doc/user/project/merge_requests/img/merge_method_ff_v15_0.pngbin0 -> 4744 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_method_merge_commit_v15_0.pngbin0 -> 14531 bytes
-rw-r--r--doc/user/project/merge_requests/img/merge_method_merge_commit_with_semi_linear_history_v15_0.pngbin0 -> 14867 bytes
-rw-r--r--doc/user/project/merge_requests/methods/index.md116
-rw-r--r--doc/user/project/merge_requests/revert_changes.md2
-rw-r--r--doc/user/project/merge_requests/reviews/index.md17
-rw-r--r--doc/user/project/merge_requests/squash_and_merge.md6
-rw-r--r--doc/user/project/releases/index.md2
-rw-r--r--doc/user/project/settings/index.md2
-rw-r--r--lib/api/error_tracking/collector.rb2
-rw-r--r--lib/gitlab/ci/templates/Security/DAST-On-Demand-Scan.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml2
-rw-r--r--lib/gitlab/error_tracking/error_repository.rb113
-rw-r--r--lib/gitlab/error_tracking/error_repository/active_record_strategy.rb98
-rw-r--r--lib/gitlab/github_import/importer/issue_importer.rb3
-rw-r--r--locale/gitlab.pot14
-rw-r--r--rubocop/cop/database/multiple_databases.rb1
-rw-r--r--spec/factories/namespace_ci_cd_settings.rb7
-rw-r--r--spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb43
-rw-r--r--spec/features/projects/commit/mini_pipeline_graph_spec.rb2
-rw-r--r--spec/finders/error_tracking/errors_finder_spec.rb53
-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/lib/utils/datetime_utility_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/pipelines/components/pipelines_list/pipeline_stage_spec.js6
-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/lib/gitlab/database/migration_helpers_spec.rb28
-rw-r--r--spec/lib/gitlab/github_import/importer/issue_importer_spec.rb6
-rw-r--r--spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb2
-rw-r--r--spec/models/ci/build_spec.rb29
-rw-r--r--spec/models/ci/processable_spec.rb2
-rw-r--r--spec/models/ci/runner_spec.rb4
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb2
-rw-r--r--spec/models/concerns/pg_full_text_searchable_spec.rb4
-rw-r--r--spec/models/issue_spec.rb15
-rw-r--r--spec/models/project_spec.rb1
-rw-r--r--spec/requests/api/ci/runners_spec.rb2
-rw-r--r--spec/requests/api/error_tracking/collector_spec.rb12
-rw-r--r--spec/requests/api/graphql/ci/runner_spec.rb8
-rw-r--r--spec/requests/api/graphql/ci/runners_spec.rb4
-rw-r--r--spec/services/error_tracking/collect_error_service_spec.rb15
-rw-r--r--spec/services/error_tracking/issue_details_service_spec.rb12
-rw-r--r--spec/services/error_tracking/issue_latest_event_service_spec.rb12
185 files changed, 1035 insertions, 805 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
index d29af93319e..e02d2c02d79 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -108,6 +108,8 @@ 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/.rubocop_todo/database/multiple_databases.yml b/.rubocop_todo/database/multiple_databases.yml
index 1c63431c4d7..7cc54a5b373 100644
--- a/.rubocop_todo/database/multiple_databases.yml
+++ b/.rubocop_todo/database/multiple_databases.yml
@@ -1,25 +1,19 @@
---
Database/MultipleDatabases:
Exclude:
- - 'config/application.rb'
- 'config/initializers/active_record_data_types.rb'
- - 'config/initializers/active_record_force_reconnects.rb'
- 'config/initializers/active_record_lifecycle.rb'
- 'config/initializers/sidekiq.rb'
- - 'config/initializers/validate_database_config.rb'
- 'db/post_migrate/20210317104032_set_iteration_cadence_automatic_to_false.rb'
- 'db/post_migrate/20210811122206_update_external_project_bots.rb'
- 'db/post_migrate/20210812013042_remove_duplicate_project_authorizations.rb'
- - 'ee/lib/tasks/geo.rake'
- 'ee/spec/services/ee/merge_requests/update_service_spec.rb'
- - 'lib/backup/database.rb'
- 'lib/backup/manager.rb'
- - lib/gitlab/background_migration/backfill_projects_with_coverage.rb
- - lib/gitlab/background_migration/copy_ci_builds_columns_to_security_scans.rb
+ - 'lib/gitlab/background_migration/backfill_projects_with_coverage.rb'
+ - 'lib/gitlab/background_migration/copy_ci_builds_columns_to_security_scans.rb'
- 'lib/gitlab/database.rb'
- 'lib/gitlab/database/load_balancing/load_balancer.rb'
- 'lib/gitlab/database/migrations/observers/query_log.rb'
- - 'lib/tasks/dev.rake'
- 'lib/tasks/gitlab/db/validate_config.rake'
- 'spec/db/schema_spec.rb'
- 'spec/initializers/database_config_spec.rb'
diff --git a/app/assets/javascripts/behaviors/markdown/render_kroki.js b/app/assets/javascripts/behaviors/markdown/render_kroki.js
index abe71694d73..241585c30f1 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 (!parent.hasAttribute('data-kroki-processed')) {
- parent.setAttribute('data-kroki-processed', 'true');
+ if (!Object.hasOwn(parent.dataset, 'krokiProcessed')) {
+ parent.dataset.krokiProcessed = '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 12f47255bdf..7e1bf83f8a1 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.getAttribute('data-math-style') === 'display',
+ displayMode: el.dataset.mathStyle === '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.setAttribute('data-math-style', el.getAttribute('data-math-style'));
+ placeholder.dataset.mathStyle = el.dataset.mathStyle;
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 a3dd241604d..0a5bcf326a1 100644
--- a/app/assets/javascripts/blob/blob_line_permalink_updater.js
+++ b/app/assets/javascripts/blob/blob_line_permalink_updater.js
@@ -9,10 +9,11 @@ const updateLineNumbersOnBlobPermalinks = (linksToUpdate) => {
[].concat(Array.prototype.slice.call(linksToUpdate)).forEach((permalinkButton) => {
const baseHref =
- permalinkButton.getAttribute('data-original-href') ||
+ permalinkButton.dataset.originalHref ||
(() => {
const href = permalinkButton.getAttribute('href');
- permalinkButton.setAttribute('data-original-href', href);
+ // eslint-disable-next-line no-param-reassign
+ permalinkButton.dataset.originalHref = href;
return href;
})();
permalinkButton.setAttribute('href', `${baseHref}${hashUrlString}`);
diff --git a/app/assets/javascripts/blob/viewer/index.js b/app/assets/javascripts/blob/viewer/index.js
index a6eed4ecae3..a0d4f7ef4f2 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.getAttribute('data-url');
+ const { url } = viewer.dataset;
- if (!url || viewer.getAttribute('data-loaded') || viewer.getAttribute('data-loading')) {
+ if (!url || viewer.dataset.loaded || viewer.dataset.loading) {
return Promise.resolve(viewer);
}
- viewer.setAttribute('data-loading', 'true');
+ viewer.dataset.loading = 'true';
return axios.get(url).then(({ data }) => {
viewer.innerHTML = data.html;
window.requestIdleCallback(() => {
- viewer.removeAttribute('data-loading');
+ delete viewer.dataset.loading;
});
return viewer;
@@ -108,7 +108,7 @@ export class BlobViewer {
switchToInitialViewer() {
const initialViewer = this.$fileHolder[0].querySelector('.blob-viewer:not(.hidden)');
- let initialViewerName = initialViewer.getAttribute('data-type');
+ let initialViewerName = initialViewer.dataset.type;
if (this.switcher && window.location.hash.indexOf('#L') === 0) {
initialViewerName = 'simple';
@@ -138,12 +138,12 @@ export class BlobViewer {
e.preventDefault();
- this.switchToViewer(target.getAttribute('data-viewer'));
+ this.switchToViewer(target.dataset.viewer);
}
toggleCopyButtonState() {
if (!this.copySourceBtn) return;
- if (this.simpleViewer.getAttribute('data-loaded')) {
+ if (this.simpleViewer.dataset.loaded) {
this.copySourceBtnTooltip.setAttribute('title', __('Copy file contents'));
this.copySourceBtn.classList.remove('disabled');
} else if (this.activeViewer === this.simpleViewer) {
@@ -199,7 +199,8 @@ export class BlobViewer {
this.$fileHolder.trigger('highlight:line');
handleLocationHash();
- viewer.setAttribute('data-loaded', 'true');
+ // eslint-disable-next-line no-param-reassign
+ viewer.dataset.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 b9d3742974c..113840dbc52 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.setAttribute('data-container', 'body');
+ el.dataset.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 0d72153d8fe..46038df2f86 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.setAttribute('data-char-index', d.start_char);
- el.setAttribute('data-line-index', d.start_line);
+ el.dataset.charIndex = d.start_char;
+ el.dataset.lineIndex = 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 37287b9d981..f10c2d82b61 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.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.dataset.trackAction = 'click_text';
+ link.dataset.trackLabel = options.trackSuggestionClickedLabel;
+ link.dataset.trackValue = index;
+ link.dataset.trackProperty = 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 a12829f8420..47de7a76899 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 && firstFile.hasAttribute('data-can-create-note');
+ const canCreateNote = firstFile && Object.hasOwn(firstFile.dataset, 'canCreateNote');
$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 b57db73a86e..3913e4e8d81 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.getAttribute('data-group-id') || '';
+ return this.filteredSearchInput.dataset.groupId || '';
}
getProjectId() {
- return this.filteredSearchInput.getAttribute('data-project-id') || '';
+ return this.filteredSearchInput.dataset.projectId || '';
}
}
diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js
index 9d29782c9a7..93897b4ed6c 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 (selected.hasAttribute('data-value')) {
+ if (Object.hasOwn(selected.dataset, 'value')) {
this.dismissDropdown();
- } else if (selected.getAttribute('data-action') === 'submit') {
+ } else if (selected.dataset.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 fb9f25a8c45..cd0f541b4fb 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 (selected.hasAttribute('data-value')) {
+ if (Object.hasOwn(selected.dataset, '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 9a23ff25eac..26507a85fa8 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.getAttribute('data-group-id');
+ return this.input.dataset.groupId;
}
getProjectId() {
- return this.input.getAttribute('data-project-id');
+ return this.input.dataset.projectId;
}
projectOrGroupId() {
diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js
index c98d1f8e064..22e1604871a 100644
--- a/app/assets/javascripts/filtered_search/dropdown_utils.js
+++ b/app/assets/javascripts/filtered_search/dropdown_utils.js
@@ -87,6 +87,7 @@ 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) {
@@ -96,6 +97,7 @@ 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 05b741af191..398a7b26677 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.getAttribute('data-src');
- img.removeAttribute('data-src');
+ img.src = img.dataset.src;
+ delete img.dataset.src;
});
}
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index 07f2c75f00a..ac2cf27e873 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.getAttribute('data-username-params');
+ const attribute = this.filteredSearchInput.dataset.usernameParams;
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 3468a629f5a..180e927a3e7 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.setAttribute('data-position', JSON.stringify(positionObject));
+ el.dataset.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 3ea70c07058..7c611dbf39e 100644
--- a/app/assets/javascripts/issues/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/issues/create_merge_request_dropdown.js
@@ -81,10 +81,7 @@ export default class CreateMergeRequestDropdown {
this.init();
if (isConfidentialIssue()) {
- this.createMergeRequestButton.setAttribute(
- 'data-dropdown-trigger',
- '#create-merge-request-dropdown',
- );
+ this.createMergeRequestButton.dataset.dropdownTrigger = '#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 831cef66836..7594676489f 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.getAttribute('data-sourcepos').match(/\b\d+(?=:)/g);
+ const lineNumbers = parentElement.dataset.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 2b4dd205cf1..ba801082377 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.getAttribute('data-src')) {
+ if (selectedImage.dataset.src) {
const imgBoundRect = selectedImage.getBoundingClientRect();
const imgTop = scrollTop + imgBoundRect.top;
const imgBound = imgTop + imgBoundRect.height;
@@ -156,16 +156,17 @@ export default class LazyLoader {
}
static loadImage(img) {
- if (img.getAttribute('data-src')) {
+ if (img.dataset.src) {
img.setAttribute('loading', 'lazy');
- let imgUrl = img.getAttribute('data-src');
+ let imgUrl = img.dataset.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);
- img.removeAttribute('data-src');
+ // eslint-disable-next-line no-param-reassign
+ delete img.dataset.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 1adb6f9c26f..a0e730e9722 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.getAttribute('data-confirm-btn-variant');
+ const { confirmBtnVariant } = element.dataset;
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 fa895cf24c4..6cd8bf57313 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.setAttribute('data-qa-selector', 'access_level_dropdown');
+ dropdownToggle.dataset.qaSelector = 'access_level_dropdown';
}
},
methods: {
diff --git a/app/assets/javascripts/pages/registrations/new/index.js b/app/assets/javascripts/pages/registrations/new/index.js
index 8bbe81a9ed5..94a5c1cb29b 100644
--- a/app/assets/javascripts/pages/registrations/new/index.js
+++ b/app/assets/javascripts/pages/registrations/new/index.js
@@ -3,9 +3,14 @@ import { trackNewRegistrations } from '~/google_tag_manager';
import NoEmojiValidator from '~/emoji/no_emoji_validator';
import LengthValidator from '~/pages/sessions/new/length_validator';
import UsernameValidator from '~/pages/sessions/new/username_validator';
+import Tracking from '~/tracking';
new UsernameValidator(); // eslint-disable-line no-new
new LengthValidator(); // eslint-disable-line no-new
new NoEmojiValidator(); // eslint-disable-line no-new
trackNewRegistrations();
+
+Tracking.enableFormTracking({
+ forms: { allow: ['new_user'] },
+});
diff --git a/app/assets/javascripts/pages/shared/nav/sidebar_tracking.js b/app/assets/javascripts/pages/shared/nav/sidebar_tracking.js
index 79ce1a37d21..47aae36ecbb 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.setAttribute('data-track-action', action);
+ element.dataset.trackAction = action;
};
const setDataTrackExtra = (element, value) => {
@@ -12,10 +12,10 @@ function onSidebarLinkClick() {
? SIDEBAR_COLLAPSED
: SIDEBAR_EXPANDED;
- element.setAttribute(
- 'data-track-extra',
- JSON.stringify({ sidebar_display: sidebarCollapsed, menu_display: value }),
- );
+ element.dataset.trackExtra = 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 996e12bc105..94506d33b33 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.getAttribute('data-datetime')));
+ el.setAttribute('title', formatDate(el.dataset.datetime));
});
})
.catch(() =>
diff --git a/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue b/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue
index 1f2fdb7ce36..7beabcfe403 100644
--- a/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue
+++ b/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue
@@ -47,6 +47,12 @@ export default {
downstreamPipelines() {
return this.linkedPipelines?.downstream?.nodes || [];
},
+ hasDownstreamPipelines() {
+ return this.downstreamPipelines.length > 0;
+ },
+ hasPipelineStages() {
+ return this.pipelineStages.length > 0;
+ },
pipelinePath() {
return this.pipeline.detailedStatus?.detailsPath || '';
},
@@ -73,9 +79,6 @@ export default {
};
});
},
- showDownstreamPipelines() {
- return this.downstreamPipelines.length > 0;
- },
upstreamPipeline() {
return this.linkedPipelines?.upstream;
},
@@ -84,7 +87,10 @@ export default {
</script>
<template>
- <div v-if="pipelineStages.length > 0" class="stage-cell gl-mr-5">
+ <div
+ v-if="hasPipelineStages"
+ class="gl-align-items-center gl-display-inline-flex gl-flex-wrap stage-cell gl-mr-5"
+ >
<linked-pipelines-mini-list
v-if="upstreamPipeline"
:triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
@@ -92,9 +98,9 @@ export default {
] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
data-testid="pipeline-editor-mini-graph-upstream"
/>
- <pipeline-mini-graph class="gl-display-inline" :stages="pipelineStages" />
+ <pipeline-mini-graph :stages="pipelineStages" />
<linked-pipelines-mini-list
- v-if="showDownstreamPipelines"
+ v-if="hasDownstreamPipelines"
:triggered="downstreamPipelines"
:pipeline-path="pipelinePath"
data-testid="pipeline-editor-mini-graph-downstream"
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue
index 2b33467e948..6ed9d8d4dc9 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue
@@ -36,7 +36,7 @@ export default {
};
</script>
<template>
- <div data-testid="pipeline-mini-graph">
+ <div data-testid="pipeline-mini-graph" class="gl-display-inline-flex gl-flex-wrap gl-my-1">
<div
v-for="stage in stages"
:key="stage.name"
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue
index 23e2a36ddeb..53e21d4ce8b 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue
@@ -12,7 +12,8 @@
* 4. Commit widget
*/
-import { GlDropdown, GlLoadingIcon, GlTooltipDirective, GlIcon } from '@gitlab/ui';
+import { GlDropdown, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { __, sprintf } from '~/locale';
@@ -21,7 +22,7 @@ import JobItem from './job_item.vue';
export default {
components: {
- GlIcon,
+ CiIcon,
GlLoadingIcon,
GlDropdown,
JobItem,
@@ -51,14 +52,6 @@ export default {
dropdownContent: [],
};
},
- computed: {
- triggerButtonClass() {
- return `ci-status-icon-${this.stage.status.group}`;
- },
- borderlessIcon() {
- return `${this.stage.status.icon}_borderless`;
- },
- },
watch: {
updateDropdown() {
if (this.updateDropdown && this.isDropdownOpen() && !this.isLoading) {
@@ -117,14 +110,18 @@ export default {
:popper-opts="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
placement: 'bottom',
} /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
- :toggle-class="['mini-pipeline-graph-dropdown-toggle', triggerButtonClass]"
+ :toggle-class="['gl-rounded-full!']"
menu-class="mini-pipeline-graph-dropdown-menu"
@show="onShowDropdown"
>
<template #button-content>
- <span class="gl-pointer-events-none">
- <gl-icon :name="borderlessIcon" />
- </span>
+ <ci-icon
+ is-interactive
+ css-classes="gl-rounded-full"
+ :size="24"
+ :status="stage.status"
+ class="gl-align-items-center gl-display-inline-flex"
+ />
</template>
<gl-loading-icon v-if="isLoading" size="sm" />
<ul
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
index d77c0a39bea..5fe7c48b380 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
@@ -169,7 +169,7 @@ export default {
</template>
<template #cell(stages)="{ item }">
- <div class="stage-cell">
+ <div class="gl-align-items-center gl-display-inline-flex gl-flex-wrap stage-cell">
<!-- This empty div should be removed, see https://gitlab.com/gitlab-org/gitlab/-/issues/323488 -->
<div></div>
<linked-pipelines-mini-list
@@ -181,7 +181,6 @@ export default {
/>
<pipeline-mini-graph
v-if="item.details && item.details.stages && item.details.stages.length > 0"
- class="gl-display-inline"
:stages="item.details.stages"
:update-dropdown="updateGraphDropdown"
@pipelineActionRequestComplete="onPipelineActionRequestComplete"
diff --git a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
index efac9f3044a..6ed943d7df5 100644
--- a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
+++ b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
@@ -123,7 +123,7 @@ export default {
<template>
<div>
<gl-loading-icon v-if="$apollo.queries.pipeline.loading" />
- <div v-else>
+ <div v-else class="gl-align-items-center gl-display-flex">
<linked-pipelines-mini-list
v-if="upstreamPipeline"
:triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
@@ -132,11 +132,7 @@ export default {
data-testid="commit-box-mini-graph-upstream"
/>
- <pipeline-mini-graph
- :stages="formattedStages"
- class="gl-display-inline"
- data-testid="commit-box-mini-graph"
- />
+ <pipeline-mini-graph :stages="formattedStages" data-testid="commit-box-mini-graph" />
<linked-pipelines-mini-list
v-if="hasDownstream"
diff --git a/app/assets/javascripts/projects/commits/components/author_select.vue b/app/assets/javascripts/projects/commits/components/author_select.vue
index c8a0a3417f3..884ef732144 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.setAttribute('data-toggle', 'tooltip');
+ commitsSearchInput.dataset.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 2a7d967cb61..5e57d4c3d6e 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: el.hasAttribute('data-signed-in'),
+ signedIn: Object.hasOwn(el.dataset, 'signedIn'),
issuableType:
isInIssuePage() || isInIncidentPage() || isInDesignPage()
? IssuableType.Issue
@@ -149,7 +149,7 @@ function mountAssigneesComponent() {
},
provide: {
canUpdate: editable,
- directlyInviteMembers: el.hasAttribute('data-directly-invite-members'),
+ directlyInviteMembers: Object.hasOwn(el.dataset, 'directlyInviteMembers'),
},
render: (createElement) =>
createElement('sidebar-assignees-widget', {
diff --git a/app/assets/javascripts/terraform/index.js b/app/assets/javascripts/terraform/index.js
index 34261f3c4db..64e90f9227c 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: el.hasAttribute('data-terraform-admin'),
+ terraformAdmin: Object.hasOwn(el.dataset, 'terraformAdmin'),
},
});
},
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
index c0b80eef082..3b3b46e9772 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
@@ -276,12 +276,11 @@ export default {
</div>
</div>
<div>
- <span class="mr-widget-pipeline-graph">
- <span class="stage-cell">
+ <span class="gl-align-items-center gl-display-inline-flex mr-widget-pipeline-graph">
+ <span class="gl-align-items-center gl-display-inline-flex gl-flex-wrap stage-cell">
<linked-pipelines-mini-list v-if="triggeredBy.length" :triggered-by="triggeredBy" />
<pipeline-mini-graph
v-if="hasStages"
- class="gl-display-inline-block"
stages-class="mr-widget-pipeline-stages"
:stages="pipeline.details.stages"
:is-merge-train="isMergeTrain"
diff --git a/app/assets/javascripts/vue_shared/components/ci_icon.vue b/app/assets/javascripts/vue_shared/components/ci_icon.vue
index 07bd6019b80..9bccc49e894 100644
--- a/app/assets/javascripts/vue_shared/components/ci_icon.vue
+++ b/app/assets/javascripts/vue_shared/components/ci_icon.vue
@@ -11,17 +11,22 @@ import { GlIcon } from '@gitlab/ui';
* }
*
* Used in:
- * - Pipelines table Badge
- * - Pipelines table mini graph
- * - Pipeline graph
- * - Pipeline show view badge
- * - Jobs table
+ * - Extended MR Popover
* - Jobs show view header
* - Jobs show view sidebar
+ * - Jobs table
* - Linked pipelines
- * - Extended MR Popover
+ * - Pipeline graph
+ * - Pipeline mini graph
+ * - Pipeline show view badge
+ * - Pipelines table Badge
+ */
+
+/*
+ * These sizes are defined in gitlab-ui/src/scss/variables.scss
+ * under '$gl-icon-sizes'
*/
-const validSizes = [8, 12, 16, 18, 24, 32, 48, 72];
+const validSizes = [8, 12, 14, 16, 24, 32, 48, 72];
export default {
components: {
@@ -45,6 +50,11 @@ export default {
required: false,
default: false,
},
+ isInteractive: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
cssClasses: {
type: String,
required: false,
@@ -52,9 +62,9 @@ export default {
},
},
computed: {
- cssClass() {
+ wrapperStyleClasses() {
const status = this.status.group;
- return `ci-status-icon ci-status-icon-${status} js-ci-status-icon-${status}`;
+ return `ci-status-icon ci-status-icon-${status} js-ci-status-icon-${status} gl-rounded-full gl-justify-content-center`;
},
icon() {
return this.borderless ? `${this.status.icon}_borderless` : this.status.icon;
@@ -63,7 +73,10 @@ export default {
};
</script>
<template>
- <span :class="cssClass">
+ <span
+ :class="[wrapperStyleClasses, { interactive: isInteractive }]"
+ :style="{ height: `${size}px`, width: `${size}px` }"
+ >
<gl-icon :name="icon" :size="size" :class="cssClasses" :aria-label="status.icon" />
</span>
</template>
diff --git a/app/assets/javascripts/whats_new/components/app.vue b/app/assets/javascripts/whats_new/components/app.vue
index b74dba686ad..0c55cc2f8a6 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.getAttribute('data-namespace-id');
+ const { namespaceId } = body.dataset;
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 66ee3b1a971..41aff202f48 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.getAttribute('data-version-digest');
+export const getVersionDigest = (appEl) => appEl.dataset.versionDigest;
export const setNotification = (appEl) => {
const versionDigest = getVersionDigest(appEl);
diff --git a/app/assets/stylesheets/bootstrap_migration_components.scss b/app/assets/stylesheets/bootstrap_migration_components.scss
index d4f0c96c613..676e69707c7 100644
--- a/app/assets/stylesheets/bootstrap_migration_components.scss
+++ b/app/assets/stylesheets/bootstrap_migration_components.scss
@@ -118,7 +118,14 @@ input[type='file'] {
margin-bottom: 16px;
.well-segment {
- padding: 16px;
+ padding: 1rem;
+
+ &.pipeline-info {
+ align-items: center;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.25rem;
+ }
&:not(:last-of-type) {
border-bottom: 1px solid $well-inner-border;
diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss
index 0aeb7208c59..ca0240b6a65 100644
--- a/app/assets/stylesheets/framework/icons.scss
+++ b/app/assets/stylesheets/framework/icons.scss
@@ -3,6 +3,17 @@
svg {
fill: $green-500;
}
+
+ &.interactive {
+ &:hover {
+ background: $green-500;
+
+ svg {
+ --svg-status-bg: #{$green-100};
+ box-shadow: 0 0 0 1px $green-500;
+ }
+ }
+ }
}
.ci-status-icon-error,
@@ -10,6 +21,17 @@
svg {
fill: $red-500;
}
+
+ &.interactive {
+ &:hover {
+ background: $red-500;
+
+ svg {
+ --svg-status-bg: #{$red-100};
+ box-shadow: 0 0 0 1px $red-500;
+ }
+ }
+ }
}
.ci-status-icon-pending,
@@ -19,11 +41,33 @@
svg {
fill: $orange-500;
}
+
+ &.interactive {
+ &:hover {
+ background: $orange-500;
+
+ svg {
+ --svg-status-bg: #{$orange-100};
+ box-shadow: 0 0 0 1px $orange-500;
+ }
+ }
+ }
}
.ci-status-icon-running {
svg {
- fill: $blue-400;
+ fill: $blue-500;
+ }
+
+ &.interactive {
+ &:hover {
+ background: $blue-500;
+
+ svg {
+ --svg-status-bg: #{$blue-100};
+ box-shadow: 0 0 0 1px $blue-500;
+ }
+ }
}
}
@@ -32,7 +76,18 @@
.ci-status-icon-scheduled,
.ci-status-icon-manual {
svg {
- fill: $gl-text-color;
+ fill: $gray-900;
+ }
+
+ &.interactive {
+ &:hover {
+ background: $gray-900;
+
+ svg {
+ --svg-status-bg: #{$gray-100};
+ box-shadow: 0 0 0 1px $gray-900;
+ }
+ }
}
}
@@ -42,7 +97,18 @@
.ci-status-icon-skipped,
.ci-status-icon-notfound {
svg {
- fill: var(--gray-400, $gray-400);
+ fill: $gray-500;
+ }
+
+ &.interactive {
+ &:hover {
+ background: $gray-500;
+
+ svg {
+ --svg-status-bg: #{$gray-100};
+ box-shadow: 0 0 0 1px $gray-500;
+ }
+ }
}
}
diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss
index b796f04750b..cfd215b81b8 100644
--- a/app/assets/stylesheets/framework/wells.scss
+++ b/app/assets/stylesheets/framework/wells.scss
@@ -39,7 +39,7 @@
.icon-container {
display: inline-block;
- margin-right: 8px;
+ margin: 0 0.5rem 0 0.25rem;
svg {
position: relative;
diff --git a/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss b/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
index 594800c84fd..3327f8da632 100644
--- a/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
+++ b/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
@@ -13,105 +13,6 @@
}
}
-@mixin mini-pipeline-graph-color(
- $color-background-default,
- $color-background-hover-focus,
- $color-background-active,
- $color-foreground-default,
- $color-foreground-hover-focus,
- $color-foreground-active
-) {
- background-color: $color-background-default;
- border-color: $color-foreground-default;
-
- svg {
- fill: $color-foreground-default;
- }
-
- &:hover,
- &:focus {
- background-color: $color-background-hover-focus;
- border-color: $color-foreground-hover-focus;
-
- svg {
- fill: $color-foreground-hover-focus;
- }
- }
-
- &:active {
- background-color: $color-background-active;
- border-color: $color-foreground-active;
-
- svg {
- fill: $color-foreground-active;
- }
- }
-
- &:focus {
- box-shadow: 0 0 4px 1px $blue-300;
- }
-}
-
-@mixin mini-pipeline-item() {
- border-radius: 100px;
- background-color: var(--white, $white);
- border-width: 1px;
- border-style: solid;
- width: $ci-action-icon-size;
- height: $ci-action-icon-size;
- margin: 0;
- padding: 0;
- position: relative;
- vertical-align: middle;
-
- &:hover,
- &:active,
- &:focus {
- outline: none;
- border-width: 2px;
- }
-
- // Dropdown button animation in mini pipeline graph
- &.ci-status-icon-success {
- @include mini-pipeline-graph-color(var(--white, $white), $green-100, $green-200, $green-500, $green-600, $green-700);
- }
-
- &.ci-status-icon-failed {
- @include mini-pipeline-graph-color(var(--white, $white), $red-100, $red-200, $red-500, $red-600, $red-700);
- }
-
- &.ci-status-icon-pending,
- &.ci-status-icon-waiting-for-resource,
- &.ci-status-icon-success-with-warnings {
- @include mini-pipeline-graph-color(var(--white, $white), $orange-50, $orange-100, $orange-500, $orange-600, $orange-700);
- }
-
- &.ci-status-icon-running {
- @include mini-pipeline-graph-color(var(--white, $white), $blue-100, $blue-200, $blue-500, $blue-600, $blue-700);
- }
-
- &.ci-status-icon-canceled,
- &.ci-status-icon-scheduled,
- &.ci-status-icon-disabled,
- &.ci-status-icon-manual {
- @include mini-pipeline-graph-color(
- var(--white, $white),
- var(--gray-500, $gray-500),
- var(--gray-700, $gray-700),
- var(--gray-900, $gray-900),
- var(--gray-950, $gray-950),
- var(--black, $black)
- );
- }
-
- &.ci-status-icon-preparing,
- &.ci-status-icon-created,
- &.ci-status-icon-not-found,
- &.ci-status-icon-skipped {
- @include mini-pipeline-graph-color(var(--white, $white), var(--gray-100, $gray-100), var(--gray-200, $gray-200), var(--gray-400, $gray-400), var(--gray-500, $gray-500), var(--gray-600, $gray-600));
- }
-}
-
/**
Action icons inside dropdowns:
- mini graph in pipelines table
diff --git a/app/assets/stylesheets/page_bundles/merge_requests.scss b/app/assets/stylesheets/page_bundles/merge_requests.scss
index bd5888af3ad..bef7ecc8a7e 100644
--- a/app/assets/stylesheets/page_bundles/merge_requests.scss
+++ b/app/assets/stylesheets/page_bundles/merge_requests.scss
@@ -121,10 +121,6 @@ $tabs-holder-z-index: 250;
@include media-breakpoint-down(sm) {
flex-direction: column;
- .stage-cell .stage-container {
- margin-top: 16px;
- }
-
.dropdown .mini-pipeline-graph-dropdown-menu.dropdown-menu {
transform: initial;
}
diff --git a/app/assets/stylesheets/page_bundles/pipelines.scss b/app/assets/stylesheets/page_bundles/pipelines.scss
index 7b54be5c91f..a225a0f0061 100644
--- a/app/assets/stylesheets/page_bundles/pipelines.scss
+++ b/app/assets/stylesheets/page_bundles/pipelines.scss
@@ -73,36 +73,12 @@
// Mini Pipelines
.stage-cell {
- .mini-pipeline-graph-dropdown-toggle {
- svg {
- height: $ci-action-icon-size;
- width: $ci-action-icon-size;
- position: absolute;
- top: -1px;
- left: -1px;
- z-index: 2;
- overflow: visible;
- }
-
- &:hover,
- &:active,
- &:focus {
- svg {
- top: -2px;
- left: -2px;
- }
- }
- }
-
.stage-container {
- display: inline-block;
- position: relative;
- vertical-align: middle;
- height: $ci-action-icon-size;
- margin: 3px 0;
+ align-items: center;
+ display: inline-flex;
+ .stage-container {
- margin-left: 6px;
+ margin-left: 4px;
}
// Hack to show a button tooltip inline
@@ -118,44 +94,15 @@
&:not(:last-child) {
&::after {
content: '';
- width: 7px;
+ width: 4px;
position: absolute;
- right: -7px;
- top: 11px;
- border-bottom: 2px solid var(--border-color, $border-color);
- }
- }
-
- //delete when all pipelines are updated to new size
- &.mr-widget-pipeline-stages {
- + .stage-container {
- margin-left: 4px;
- }
-
- &:not(:last-child) {
- &::after {
- width: 4px;
- right: -4px;
- top: 11px;
- }
+ right: -4px;
+ border-bottom: 2px solid $gray-200;
}
}
}
}
-// Commit mini pipeline (HAML)
-button.mini-pipeline-graph-dropdown-toggle,
-// GlDropdown mini pipeline (Vue)
-// As the `mini-pipeline-item` mixin specificity is lower
-// than the toggle of dropdown with 'variant="link"' we add
-// classes ".gl-button.btn-link" to make it more specific
-// and avoid having the size overriden
-//
-// See https://gitlab.com/gitlab-org/gitlab/-/issues/320737
-button.gl-button.btn-link.mini-pipeline-graph-dropdown-toggle {
- @include mini-pipeline-item();
-}
-
// Action icons inside dropdowns:
// mini graph in pipelines table
// mini graph in MR widget pipeline
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index cc8ea1493fc..afe57bb26e6 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -35,9 +35,6 @@
}
.mr-widget-pipeline-graph {
- display: inline-block;
- vertical-align: middle;
-
.dropdown-menu {
margin-top: 11px;
}
@@ -45,8 +42,6 @@
}
.branch-info .commit-icon {
- margin-right: 8px;
-
svg {
top: 3px;
}
diff --git a/app/finders/error_tracking/errors_finder.rb b/app/finders/error_tracking/errors_finder.rb
deleted file mode 100644
index c361d6e2fc2..00000000000
--- a/app/finders/error_tracking/errors_finder.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-module ErrorTracking
- class ErrorsFinder
- def initialize(current_user, project, params)
- @current_user = current_user
- @project = project
- @params = params
- end
-
- def execute
- return ErrorTracking::Error.none unless authorized?
-
- collection = project.error_tracking_errors
- collection = by_status(collection)
- collection = sort(collection)
-
- collection.keyset_paginate(cursor: params[:cursor], per_page: limit)
- end
-
- private
-
- attr_reader :current_user, :project, :params
-
- def by_status(collection)
- if params[:status].present? && ErrorTracking::Error.statuses.key?(params[:status])
- collection.for_status(params[:status])
- else
- collection
- end
- end
-
- def authorized?
- Ability.allowed?(current_user, :read_sentry_issue, project)
- end
-
- def sort(collection)
- params[:sort] ? collection.sort_by_attribute(params[:sort]) : collection.order_id_desc
- end
-
- def limit
- # Restrict the maximum limit at 100 records.
- [(params[:limit] || 20).to_i, 100].min
- end
- end
-end
diff --git a/app/graphql/types/ci/runner_status_enum.rb b/app/graphql/types/ci/runner_status_enum.rb
index 2e65e2d4e1e..9ba680975f4 100644
--- a/app/graphql/types/ci/runner_status_enum.rb
+++ b/app/graphql/types/ci/runner_status_enum.rb
@@ -36,13 +36,8 @@ module Types
description: "Runner that has not contacted this instance within the last #{::Ci::Runner::STALE_TIMEOUT.inspect}. Only available if legacyMode is null. Will be a possible return value starting in 15.0.",
value: :stale
- value 'NOT_CONNECTED',
- description: 'Runner that has never contacted this instance.',
- deprecated: { reason: "Use NEVER_CONTACTED instead. NEVER_CONTACTED will have a slightly different scope starting in 15.0, with STALE being returned instead after #{::Ci::Runner::STALE_TIMEOUT.inspect} of no contact", milestone: '14.6' },
- value: :not_connected
-
value 'NEVER_CONTACTED',
- description: 'Runner that has never contacted this instance. Set legacyMode to null to utilize this value. Will replace NOT_CONNECTED starting in 15.0.',
+ description: 'Runner that has never contacted this instance.',
value: :never_contacted
end
end
diff --git a/app/graphql/types/work_item_type.rb b/app/graphql/types/work_item_type.rb
index da76c43a377..cd784d54959 100644
--- a/app/graphql/types/work_item_type.rb
+++ b/app/graphql/types/work_item_type.rb
@@ -19,8 +19,7 @@ module Types
field :title, GraphQL::Types::String, null: false,
description: 'Title of the work item.'
field :work_item_type, Types::WorkItems::TypeType, null: false,
- description: 'Type assigned to the work item.',
- method: :work_item_type_with_fallback # necessary until we validate the not null constraint
+ description: 'Type assigned to the work item.'
markdown_field :title_html, null: true
markdown_field :description_html, null: true
diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb
index b576b8802ae..6366ca0dfb1 100644
--- a/app/helpers/ci/runners_helper.rb
+++ b/app/helpers/ci/runners_helper.rb
@@ -17,7 +17,7 @@ module Ci
title = s_("Runners|Runner is online; last contact was %{runner_contact} ago") % { runner_contact: time_ago_in_words(contacted_at) }
icon = 'status-active'
span_class = 'gl-text-green-500'
- when :not_connected, :never_contacted
+ when :never_contacted
title = s_("Runners|Runner has never contacted this instance")
icon = 'warning-solid'
when :offline
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 5bb9f8fcfe1..11e2629e8dd 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -45,6 +45,7 @@ module Ci
has_one :runtime_metadata, class_name: 'Ci::RunningBuild', foreign_key: :build_id
has_many :trace_chunks, class_name: 'Ci::BuildTraceChunk', foreign_key: :build_id, inverse_of: :build
has_many :report_results, class_name: 'Ci::BuildReportResult', inverse_of: :build
+ has_one :namespace, through: :project
# Projects::DestroyService destroys Ci::Pipelines, which use_fast_destroy on :job_artifacts
# before we delete builds. By doing this, the relation should be empty and not fire any
diff --git a/app/models/ci/namespace_settings.rb b/app/models/ci/namespace_settings.rb
new file mode 100644
index 00000000000..d519a48311f
--- /dev/null
+++ b/app/models/ci/namespace_settings.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+# CI::NamespaceSettings mixin
+#
+# This module is intended to encapsulate CI/CD settings-specific logic
+# and be prepended in the `Namespace` model
+module Ci
+ module NamespaceSettings
+ # Overridden in EE::Namespace
+ def allow_stale_runner_pruning?
+ false
+ end
+
+ # Overridden in EE::Namespace
+ def allow_stale_runner_pruning=(_value)
+ raise NotImplementedError
+ end
+ end
+end
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 5f59dbba6b2..53cf7c080ad 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -12,6 +12,7 @@ module Ci
include Gitlab::Utils::StrongMemoize
include TaggableQueries
include Presentable
+ include EachBatch
add_authentication_token_field :token, encrypted: :optional, expires_at: :compute_token_expiration, expiration_enforced?: :token_expiration_enforced?
@@ -59,7 +60,7 @@ module Ci
AVAILABLE_TYPES_LEGACY = %w[specific shared].freeze
AVAILABLE_TYPES = runner_types.keys.freeze
- AVAILABLE_STATUSES = %w[active paused online offline not_connected never_contacted stale].freeze # TODO: Remove in %15.0: not_connected. In %16.0: active, paused. Relevant issues: https://gitlab.com/gitlab-org/gitlab/-/issues/347303, https://gitlab.com/gitlab-org/gitlab/-/issues/347305, https://gitlab.com/gitlab-org/gitlab/-/issues/344648
+ AVAILABLE_STATUSES = %w[active paused online offline never_contacted stale].freeze # TODO: Remove in %16.0: active, paused. Relevant issues: https://gitlab.com/gitlab-org/gitlab/-/issues/347303, https://gitlab.com/gitlab-org/gitlab/-/issues/344648
AVAILABLE_SCOPES = (AVAILABLE_TYPES_LEGACY + AVAILABLE_TYPES + AVAILABLE_STATUSES).freeze
FORM_EDITABLE = %i[description tag_list active run_untagged locked access_level maximum_timeout_human_readable].freeze
@@ -83,7 +84,6 @@ module Ci
scope :recent, -> { where('ci_runners.created_at >= :date OR ci_runners.contacted_at >= :date', date: stale_deadline) }
scope :stale, -> { where('ci_runners.created_at < :date AND (ci_runners.contacted_at IS NULL OR ci_runners.contacted_at < :date)', date: stale_deadline) }
scope :offline, -> { where(arel_table[:contacted_at].lteq(online_contact_time_deadline)) }
- scope :not_connected, -> { where(contacted_at: nil) } # TODO: Remove in 15.0
scope :never_contacted, -> { where(contacted_at: nil) }
scope :ordered, -> { order(id: :desc) }
@@ -337,7 +337,7 @@ module Ci
# TODO Remove in %16.0 in favor of `status` for REST calls, see https://gitlab.com/gitlab-org/gitlab/-/issues/344648
def deprecated_rest_status
if contacted_at.nil?
- :not_connected
+ :never_contacted
elsif active?
online? ? :online : :offline
else
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 5682ec08a76..d4eb77ef6de 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -186,8 +186,6 @@ class Issue < ApplicationRecord
after_save :ensure_metrics, unless: :importing?
after_create_commit :record_create_action, unless: :importing?
- before_validation :ensure_work_item_type
-
attr_spammable :title, spam_title: true
attr_spammable :description, spam_description: true
@@ -611,20 +609,12 @@ class Issue < ApplicationRecord
end
# Necessary until all issues are backfilled and we add a NOT NULL constraint on the DB
- def work_item_type_with_fallback
- work_item_type || WorkItems::Type.default_by_type(issue_type)
+ def work_item_type
+ super || WorkItems::Type.default_by_type(issue_type)
end
private
- # Issue create/update service provide a work item type
- # adding this callback as there might be other mechanisms to create/update issues we are not handling
- def ensure_work_item_type
- return if work_item_type
-
- self.work_item_type = WorkItems::Type.default_by_type(issue_type)
- end
-
override :persist_pg_full_text_search_vector
def persist_pg_full_text_search_vector(search_vector)
Issues::SearchData.upsert({ project_id: project_id, issue_id: id, search_vector: search_vector }, unique_by: %i(project_id issue_id))
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index f41bd6f5001..c9254aa432f 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -16,6 +16,7 @@ class Namespace < ApplicationRecord
include Namespaces::Traversal::Linear
include EachBatch
include BlocksUnsafeSerialization
+ include Ci::NamespaceSettings
# Temporary column used for back-filling project namespaces.
# Remove it once the back-filling of all project namespaces is done.
diff --git a/app/models/namespace_ci_cd_setting.rb b/app/models/namespace_ci_cd_setting.rb
index 0e044f4fb10..c9c3a3206b6 100644
--- a/app/models/namespace_ci_cd_setting.rb
+++ b/app/models/namespace_ci_cd_setting.rb
@@ -5,3 +5,5 @@ class NamespaceCiCdSetting < ApplicationRecord # rubocop:disable Gitlab/Namespac
self.primary_key = :namespace_id
end
+
+NamespaceCiCdSetting.prepend_mod
diff --git a/app/models/project.rb b/app/models/project.rb
index dd5bc9ded6a..027b2257f2e 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -412,7 +412,6 @@ class Project < ApplicationRecord
has_one :operations_feature_flags_client, class_name: 'Operations::FeatureFlagsClient'
has_many :operations_feature_flags_user_lists, class_name: 'Operations::FeatureFlags::UserList'
- has_many :error_tracking_errors, inverse_of: :project, class_name: 'ErrorTracking::Error'
has_many :error_tracking_client_keys, inverse_of: :project, class_name: 'ErrorTracking::ClientKey'
has_many :timelogs
diff --git a/app/services/error_tracking/base_service.rb b/app/services/error_tracking/base_service.rb
index 598621f70e1..d2ecd0a6d5a 100644
--- a/app/services/error_tracking/base_service.rb
+++ b/app/services/error_tracking/base_service.rb
@@ -71,5 +71,15 @@ module ErrorTracking
def can_update?
can?(current_user, :update_sentry_issue, project)
end
+
+ def error_repository
+ Gitlab::ErrorTracking::ErrorRepository.build(project)
+ end
+
+ def handle_error_repository_exceptions
+ yield
+ rescue Gitlab::ErrorTracking::ErrorRepository::DatabaseError => e
+ { error: e.message }
+ end
end
end
diff --git a/app/services/error_tracking/collect_error_service.rb b/app/services/error_tracking/collect_error_service.rb
index 6376b743255..8cb3793ba97 100644
--- a/app/services/error_tracking/collect_error_service.rb
+++ b/app/services/error_tracking/collect_error_service.rb
@@ -5,30 +5,24 @@ module ErrorTracking
include Gitlab::Utils::StrongMemoize
def execute
- # Error is a way to group events based on common data like name or cause
- # of exception. We need to keep a sane balance here between taking too little
- # and too much data into group logic.
- error = project.error_tracking_errors.report_error(
- name: exception['type'], # Example: ActionView::MissingTemplate
- description: exception['value'], # Example: Missing template posts/show in...
- actor: actor, # Example: PostsController#show
- platform: event['platform'], # Example: ruby
- timestamp: timestamp
- )
-
- # The payload field contains all the data on error including stacktrace in jsonb.
- # Together with occurred_at these are 2 main attributes that we need to save here.
- error.events.create!(
- environment: event['environment'],
+ error_repository.report_error(
+ name: exception['type'],
description: exception['value'],
- level: event['level'],
+ actor: actor,
+ platform: event['platform'],
occurred_at: timestamp,
+ environment: event['environment'],
+ level: event['level'],
payload: event
)
end
private
+ def error_repository
+ Gitlab::ErrorTracking::ErrorRepository.build(project)
+ end
+
def event
@event ||= format_event(params[:event])
end
diff --git a/app/services/error_tracking/issue_details_service.rb b/app/services/error_tracking/issue_details_service.rb
index 1614c597a8e..e82ad540e57 100644
--- a/app/services/error_tracking/issue_details_service.rb
+++ b/app/services/error_tracking/issue_details_service.rb
@@ -49,13 +49,10 @@ module ErrorTracking
# Issue https://gitlab.com/gitlab-org/gitlab/-/issues/329596
#
if project_error_tracking_setting.integrated_client?
- error = project.error_tracking_errors.find(issue_id)
-
- # We use the same response format as project_error_tracking_setting
- # method below for compatibility with existing code.
- {
- issue: error.to_sentry_detailed_error
- }
+ handle_error_repository_exceptions do
+ error = error_repository.find_error(issue_id)
+ { issue: error }
+ end
else
project_error_tracking_setting.issue_details(issue_id: issue_id)
end
diff --git a/app/services/error_tracking/issue_latest_event_service.rb b/app/services/error_tracking/issue_latest_event_service.rb
index 1bf86c658fc..0290c8eac86 100644
--- a/app/services/error_tracking/issue_latest_event_service.rb
+++ b/app/services/error_tracking/issue_latest_event_service.rb
@@ -26,14 +26,13 @@ module ErrorTracking
# Issue https://gitlab.com/gitlab-org/gitlab/-/issues/329596
#
if project_error_tracking_setting.integrated_client?
- error = project.error_tracking_errors.find(issue_id)
- event = error.events.last
+ handle_error_repository_exceptions do
+ event = error_repository.last_event_for(issue_id)
- # We use the same response format as project_error_tracking_setting
- # method below for compatibility with existing code.
- {
- latest_event: event.to_sentry_error_event
- }
+ # We use the same response format as project_error_tracking_setting
+ # method below for compatibility with existing code.
+ { latest_event: event }
+ end
else
project_error_tracking_setting.issue_latest_event(issue_id: issue_id)
end
diff --git a/app/services/error_tracking/issue_update_service.rb b/app/services/error_tracking/issue_update_service.rb
index 624e5f94dde..ca5e8d656a6 100644
--- a/app/services/error_tracking/issue_update_service.rb
+++ b/app/services/error_tracking/issue_update_service.rb
@@ -84,14 +84,12 @@ module ErrorTracking
# Issue https://gitlab.com/gitlab-org/gitlab/-/issues/329596
#
if project_error_tracking_setting.integrated_client?
- error = project.error_tracking_errors.find(opts[:issue_id])
- error.status = opts[:params][:status]
- error.save!
+ updated = error_repository.update_error(opts[:issue_id], status: opts[:params][:status])
# We use the same response format as project_error_tracking_setting
# method below for compatibility with existing code.
{
- updated: true
+ updated: updated
}
else
project_error_tracking_setting.update_issue(**opts)
diff --git a/app/services/error_tracking/list_issues_service.rb b/app/services/error_tracking/list_issues_service.rb
index 1979816b88d..ca7208dba96 100644
--- a/app/services/error_tracking/list_issues_service.rb
+++ b/app/services/error_tracking/list_issues_service.rb
@@ -73,24 +73,24 @@ module ErrorTracking
if project_error_tracking_setting.integrated_client?
# We are going to support more options in the future.
# For now we implement the bare minimum for rendering the list in UI.
- filter_opts = {
- status: opts[:issue_status],
+ list_opts = {
+ filters: { status: opts[:issue_status] },
sort: opts[:sort],
limit: opts[:limit],
cursor: opts[:cursor]
}
- errors = ErrorTracking::ErrorsFinder.new(current_user, project, filter_opts).execute
+ errors, pagination = error_repository.list_errors(**list_opts)
- pagination = {}
- pagination[:next] = { cursor: errors.cursor_for_next_page } if errors.has_next_page?
- pagination[:previous] = { cursor: errors.cursor_for_previous_page } if errors.has_previous_page?
+ pagination_hash = {}
+ pagination_hash[:next] = { cursor: pagination.next } if pagination.next
+ pagination_hash[:previous] = { cursor: pagination.prev } if pagination.prev
# We use the same response format as project_error_tracking_setting
# method below for compatibility with existing code.
{
- issues: errors.map(&:to_sentry_error),
- pagination: pagination
+ issues: errors,
+ pagination: pagination_hash
}
else
project_error_tracking_setting.list_sentry_issues(**opts)
diff --git a/app/views/projects/_merge_request_merge_method_settings.html.haml b/app/views/projects/_merge_request_merge_method_settings.html.haml
index 250f7e94e84..cb660750632 100644
--- a/app/views/projects/_merge_request_merge_method_settings.html.haml
+++ b/app/views/projects/_merge_request_merge_method_settings.html.haml
@@ -4,7 +4,7 @@
%b= s_('ProjectSettings|Merge method')
%p.text-secondary
= s_('ProjectSettings|Determine what happens to the commit history when you merge a merge request.')
- = link_to s_('ProjectSettings|Learn about commit history.'), help_page_path('user/project/merge_requests/commits.md'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to s_('ProjectSettings|How do they differ?'), help_page_path('user/project/merge_requests/methods/index.md'), target: '_blank', rel: 'noopener noreferrer'
.form-check.mb-2
= form.radio_button :merge_method, :merge, class: "js-merge-method-radio form-check-input"
= label_tag :project_merge_method_merge, class: 'form-check-label' do
diff --git a/config/application.rb b/config/application.rb
index 3670d787fbd..0f6178121ee 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -536,8 +536,10 @@ module Gitlab
# because we connect to database from routes
# https://github.com/rails/rails/blob/fdf840f69a2e33d78a9d40b91d9b7fddb76711e9/activerecord/lib/active_record/railtie.rb#L308
initializer :clear_active_connections_again, after: :set_routes_reloader_hook do
+ # rubocop:disable Database/MultipleDatabases
ActiveRecord::Base.clear_active_connections!
ActiveRecord::Base.flush_idle_connections!
+ # rubocop:enable Database/MultipleDatabases
end
# DO NOT PLACE ANY INITIALIZERS AFTER THIS.
diff --git a/config/feature_flags/development/enhanced_notify_css.yml b/config/feature_flags/development/enhanced_notify_css.yml
index 010b80caf5d..c17da36c7ed 100644
--- a/config/feature_flags/development/enhanced_notify_css.yml
+++ b/config/feature_flags/development/enhanced_notify_css.yml
@@ -2,7 +2,7 @@
name: enhanced_notify_css
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78604
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/355907
-milestone: '14.8'
+milestone: '14.9'
type: development
group: group::project management
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/ops/ci_runner_separation_by_plan.yml b/config/feature_flags/ops/ci_runner_separation_by_plan.yml
new file mode 100644
index 00000000000..37f4303abe3
--- /dev/null
+++ b/config/feature_flags/ops/ci_runner_separation_by_plan.yml
@@ -0,0 +1,8 @@
+---
+name: ci_runner_separation_by_plan
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/83780
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/361639
+milestone: '15.0'
+type: ops
+group: group::runner
+default_enabled: false
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index f231ecbea29..eba67ffa205 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -607,6 +607,10 @@ production: &base
elastic_index_initial_bulk_cron_worker:
cron: "*/10 * * * *"
+ # Periodically prune stale runners from namespaces having opted-in.
+ ci_runners_stale_group_runners_prune_worker_cron:
+ cron: "30 * * * *"
+
registry:
# enabled: true
# host: registry.example.com
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 454969a10cf..24547c78d9e 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -772,6 +772,9 @@ Gitlab.ee do
Settings.cron_jobs['arkose_blocked_users_report_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['arkose_blocked_users_report_worker']['cron'] ||= '0 6 * * *'
Settings.cron_jobs['arkose_blocked_users_report_worker']['job_class'] = 'Arkose::BlockedUsersReportWorker'
+ Settings.cron_jobs['ci_runners_stale_group_runners_prune_worker_cron'] ||= Settingslogic.new({})
+ Settings.cron_jobs['ci_runners_stale_group_runners_prune_worker_cron']['cron'] ||= '30 * * * *'
+ Settings.cron_jobs['ci_runners_stale_group_runners_prune_worker_cron']['job_class'] = 'Ci::Runners::StaleGroupRunnersPruneCronWorker'
end
#
diff --git a/data/removals/15_0/15-0-runner-api-status-renames-not_connected.yml b/data/removals/15_0/15-0-runner-api-status-renames-not_connected.yml
new file mode 100644
index 00000000000..9406936a2eb
--- /dev/null
+++ b/data/removals/15_0/15-0-runner-api-status-renames-not_connected.yml
@@ -0,0 +1,16 @@
+- name: "Runner status `not_connected` API value"
+ announcement_milestone: "14.6" # The milestone when this feature was first announced as deprecated.
+ removal_milestone: "15.0" # the milestone when this feature is planned to be removed
+ removal_date: "2022-05-22"
+ breaking_change: true
+ body: | # Do not modify this line, instead modify the lines below.
+ The GitLab Runner REST and GraphQL [API](https://docs.gitlab.com/ee/api/runners.html#runners-api) endpoints
+ deprecated the `not_connected` status value in GitLab 14.6 and will start returning `never_contacted` in its place
+ starting in GitLab 15.0.
+
+ Runners that have never contacted the GitLab instance will also return `stale` if created more than 3 months ago.
+ stage: Verify
+ tiers: [Core, Premium, Ultimate]
+ issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/347305
+ documentation_url: https://docs.gitlab.com/ee/api/runners.html
+ announcement_date: "2021-12-22"
diff --git a/db/migrate/20220421141342_add_allowed_plans_to_ci_runners.rb b/db/migrate/20220421141342_add_allowed_plans_to_ci_runners.rb
new file mode 100644
index 00000000000..46da684cfe2
--- /dev/null
+++ b/db/migrate/20220421141342_add_allowed_plans_to_ci_runners.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddAllowedPlansToCiRunners < Gitlab::Database::Migration[1.0]
+ def change
+ # rubocop:disable Migration/AddLimitToTextColumns
+ add_column :ci_runners, :allowed_plans, :text, array: true, null: false, default: []
+ # rubocop:enable Migration/AddLimitToTextColumns
+ end
+end
diff --git a/db/migrate/20220505092254_add_allow_stale_runner_pruning_index_to_namespace_ci_cd_settings.rb b/db/migrate/20220505092254_add_allow_stale_runner_pruning_index_to_namespace_ci_cd_settings.rb
new file mode 100644
index 00000000000..b7fd8fdee4f
--- /dev/null
+++ b/db/migrate/20220505092254_add_allow_stale_runner_pruning_index_to_namespace_ci_cd_settings.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddAllowStaleRunnerPruningIndexToNamespaceCiCdSettings < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = 'index_cicd_settings_on_namespace_id_where_stale_pruning_enabled'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :namespace_ci_cd_settings,
+ :namespace_id,
+ where: '(allow_stale_runner_pruning = true)',
+ name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :namespace_ci_cd_settings, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220511152439_remove_not_null_constraint_from_work_item_type.rb b/db/post_migrate/20220511152439_remove_not_null_constraint_from_work_item_type.rb
new file mode 100644
index 00000000000..f56763cdafb
--- /dev/null
+++ b/db/post_migrate/20220511152439_remove_not_null_constraint_from_work_item_type.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class RemoveNotNullConstraintFromWorkItemType < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85866 introduced a NOT NULL constraint on
+ # `issues` which caused QA failures (https://gitlab.com/gitlab-org/gitlab/-/issues/362023), and
+ # Helm database issues resulting in broken tests after restoring the database.
+ def up
+ remove_not_null_constraint :issues, :work_item_type_id, constraint_name: 'check_2addf801cd'
+ end
+
+ def down
+ add_not_null_constraint :issues, :work_item_type_id, validate: false
+ end
+end
diff --git a/db/schema_migrations/20220421141342 b/db/schema_migrations/20220421141342
new file mode 100644
index 00000000000..f28b4c06a1d
--- /dev/null
+++ b/db/schema_migrations/20220421141342
@@ -0,0 +1 @@
+cd332bdb33335750855cd0d6e49bed12a841defa24bc5ffb14ad49a39bd663aa \ No newline at end of file
diff --git a/db/schema_migrations/20220505092254 b/db/schema_migrations/20220505092254
new file mode 100644
index 00000000000..b720f68248e
--- /dev/null
+++ b/db/schema_migrations/20220505092254
@@ -0,0 +1 @@
+d37359ba5f697c4aaec738073c7705a64b54b97d2548f72571f3cb33848cfc3b \ No newline at end of file
diff --git a/db/schema_migrations/20220511152439 b/db/schema_migrations/20220511152439
new file mode 100644
index 00000000000..f58f97f4962
--- /dev/null
+++ b/db/schema_migrations/20220511152439
@@ -0,0 +1 @@
+ef899952453ddd870c45f2b4eded754152972944037309cf96701fe27a5db6cb \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 3a955ccac67..f3e33924169 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -13036,6 +13036,7 @@ CREATE TABLE ci_runners (
executor_type smallint,
maintainer_note text,
token_expires_at timestamp with time zone,
+ allowed_plans text[] DEFAULT '{}'::text[] NOT NULL,
CONSTRAINT check_ce275cee06 CHECK ((char_length(maintainer_note) <= 1024))
);
@@ -24112,9 +24113,6 @@ ALTER TABLE ONLY chat_names
ALTER TABLE ONLY chat_teams
ADD CONSTRAINT chat_teams_pkey PRIMARY KEY (id);
-ALTER TABLE issues
- ADD CONSTRAINT check_2addf801cd CHECK ((work_item_type_id IS NOT NULL)) NOT VALID;
-
ALTER TABLE vulnerability_scanners
ADD CONSTRAINT check_37608c9db5 CHECK ((char_length(vendor) <= 255)) NOT VALID;
@@ -27330,6 +27328,8 @@ CREATE INDEX index_ci_variables_on_key ON ci_variables USING btree (key);
CREATE UNIQUE INDEX index_ci_variables_on_project_id_and_key_and_environment_scope ON ci_variables USING btree (project_id, key, environment_scope);
+CREATE INDEX index_cicd_settings_on_namespace_id_where_stale_pruning_enabled ON namespace_ci_cd_settings USING btree (namespace_id) WHERE (allow_stale_runner_pruning = true);
+
CREATE INDEX index_cluster_agent_tokens_on_agent_id_status_last_used_at ON cluster_agent_tokens USING btree (agent_id, status, last_used_at DESC NULLS LAST);
CREATE INDEX index_cluster_agent_tokens_on_created_by_user_id ON cluster_agent_tokens USING btree (created_by_user_id);
diff --git a/doc/.vale/gitlab/spelling-exceptions.txt b/doc/.vale/gitlab/spelling-exceptions.txt
index 76e8f99b5db..662dc61af89 100644
--- a/doc/.vale/gitlab/spelling-exceptions.txt
+++ b/doc/.vale/gitlab/spelling-exceptions.txt
@@ -64,6 +64,7 @@ Bamboo
Bazel
Bhyve
Bitbucket
+Bitnami
blockquote
blockquoted
blockquotes
@@ -93,8 +94,12 @@ Camo
canonicalization
canonicalized
captcha
+Casdoor
CentOS
+Ceph
Certbot
+cgroup
+cgroups
chai
changeset
changesets
@@ -109,6 +114,7 @@ Citus
clonable
Cloudwatch
Cobertura
+Codeception
Codepen
Cognito
colocated
@@ -174,6 +180,7 @@ disambiguates
discoverability
dismissable
Disqus
+Distroless
Divio
Dockerfile
Dockerfiles
@@ -206,6 +213,7 @@ failovers
failsafe
Falco
falsy
+Fargate
fastlane
Fastzip
favicon
@@ -254,7 +262,9 @@ Gradle
Grafana
Grafonnet
gravatar
+Grype
Gzip
+Hackathon
Haml
hardcode
hardcoded
@@ -333,6 +343,7 @@ Leiningen
libFuzzer
Libravatar
liveness
+lockfiles
Lodash
Lograge
logrotate
@@ -352,6 +363,7 @@ Makefile
Makefiles
Markdown
markdownlint
+Marketo
matcher
matchers
Matomo
@@ -364,6 +376,7 @@ memoizing
Memorystore
mergeability
mergeable
+metaprogramming
Microsoft
middleware
middlewares
@@ -386,6 +399,7 @@ ModSecurity
Monokai
monorepo
monorepos
+monospace
multiline
mutex
nameserver
@@ -399,6 +413,7 @@ Nanoc
negatable
Netlify
Nokogiri
+nosniff
noteable
noteables
npm
@@ -472,6 +487,7 @@ proxies
proxyable
proxying
pseudocode
+pseudonymization
pseudonymized
pseudonymizer
Puma
@@ -484,6 +500,7 @@ Rackspace
Raspbian
rbenv
rbtrace
+Rclone
Rdoc
reachability
Realplayer
@@ -592,10 +609,12 @@ Slony
smartcard
smartcards
snapshotting
+Snyk
Sobelow
Solargraph
Solarized
Sourcegraph
+Spamcheck
spammable
sparkline
sparklines
@@ -660,6 +679,7 @@ Sysbench
syscall
syscalls
syslog
+systemd
tanuki
tcpdump
templated
@@ -700,6 +720,7 @@ Twilio
Twitter
TypeScript
Ubuntu
+Udemy
unapplied
unapprove
unapproved
@@ -828,6 +849,7 @@ wireframes
wireframing
Wireshark
Wordpress
+Workato
worktree
worktrees
Worldline
@@ -836,6 +858,7 @@ Xeon
YouTrack
ytt
Yubico
+Zabbix
Zeitwerk
Zendesk
ZenTao
diff --git a/doc/administration/configure.md b/doc/administration/configure.md
index e42eb632f14..e4d8aaf6b48 100644
--- a/doc/administration/configure.md
+++ b/doc/administration/configure.md
@@ -30,7 +30,7 @@ The tables lists features on the left and provides their capabilities to the rig
## Geo Capabilities
-If your availabity needs to span multiple zones or multiple locations, please read about [Geo](geo/index.md).
+If your availability needs to span multiple zones or multiple locations, please read about [Geo](geo/index.md).
| | Availability | Recoverability | Data Resiliency | Performance | Risks/Trade-offs|
|-|--------------|----------------|-----------------|-------------|-----------------|
diff --git a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md
index 55aa6435103..25f6999ce43 100644
--- a/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md
+++ b/doc/administration/geo/disaster_recovery/runbooks/planned_failover_single_node.md
@@ -120,7 +120,7 @@ follow these steps to avoid unnecessary data loss:
1. On the **primary** site:
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Monitoring > Background Jobs**.
- 1. On the Sidekiq dhasboard, select **Cron**.
+ 1. On the Sidekiq dashboard, select **Cron**.
1. Select `Disable All` to disable any non-Geo periodic background jobs.
1. Select `Enable` for the `geo_sidekiq_cron_config_worker` cron job.
This job re-enables several other cron jobs that are essential for planned
diff --git a/doc/administration/reference_architectures/10k_users.md b/doc/administration/reference_architectures/10k_users.md
index a5975caccf7..be9aef2fae1 100644
--- a/doc/administration/reference_architectures/10k_users.md
+++ b/doc/administration/reference_architectures/10k_users.md
@@ -39,7 +39,7 @@ full list of reference architectures, see
<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix -->
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work, however Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However it is also used optionally by Prometheus for Omnibus auto host discovery.
-2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
+2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
@@ -2333,7 +2333,7 @@ services where applicable):
<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix -->
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work, however Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However it is also used optionally by Prometheus for Omnibus auto host discovery.
-2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
+2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
diff --git a/doc/administration/reference_architectures/25k_users.md b/doc/administration/reference_architectures/25k_users.md
index 1af0d9ad81a..89f403c9bb1 100644
--- a/doc/administration/reference_architectures/25k_users.md
+++ b/doc/administration/reference_architectures/25k_users.md
@@ -39,7 +39,7 @@ full list of reference architectures, see
<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix -->
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work, however Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However it is also used optionally by Prometheus for Omnibus auto host discovery.
-2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
+2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
@@ -2331,7 +2331,7 @@ services where applicable):
<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix -->
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work, however Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However it is also used optionally by Prometheus for Omnibus auto host discovery.
-2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
+2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
diff --git a/doc/administration/reference_architectures/2k_users.md b/doc/administration/reference_architectures/2k_users.md
index a8e673d39d3..c1f53c5ebad 100644
--- a/doc/administration/reference_architectures/2k_users.md
+++ b/doc/administration/reference_architectures/2k_users.md
@@ -32,7 +32,7 @@ For a full list of reference architectures, see
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work, however Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However it is also used optionally by Prometheus for Omnibus auto host discovery.
-2. Can be optionally run as reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
+2. Can be optionally run as reputable third-party external PaaS Redis solutions. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run as reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
<!-- markdownlint-enable MD029 -->
@@ -1030,7 +1030,7 @@ services where applicable):
<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix -->
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work, however Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However it is also used optionally by Prometheus for Omnibus auto host discovery.
-2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
+2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS ElastiCache are known to work.
3. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
<!-- markdownlint-enable MD029 -->
diff --git a/doc/administration/reference_architectures/3k_users.md b/doc/administration/reference_architectures/3k_users.md
index c83387da7fc..a3a2bedc034 100644
--- a/doc/administration/reference_architectures/3k_users.md
+++ b/doc/administration/reference_architectures/3k_users.md
@@ -48,7 +48,7 @@ For a full list of reference architectures, see
<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix -->
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work, however Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However it is also used optionally by Prometheus for Omnibus auto host discovery.
-2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
+2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
@@ -2290,7 +2290,7 @@ services where applicable):
<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix -->
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work, however Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However it is also used optionally by Prometheus for Omnibus auto host discovery.
-2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
+2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
diff --git a/doc/administration/reference_architectures/50k_users.md b/doc/administration/reference_architectures/50k_users.md
index d8c32d1c3b7..27685925128 100644
--- a/doc/administration/reference_architectures/50k_users.md
+++ b/doc/administration/reference_architectures/50k_users.md
@@ -39,7 +39,7 @@ full list of reference architectures, see
<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix -->
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work, however Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However it is also used optionally by Prometheus for Omnibus auto host discovery.
-2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
+2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
@@ -2347,7 +2347,7 @@ services where applicable):
<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix -->
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work, however Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However it is also used optionally by Prometheus for Omnibus auto host discovery.
-2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
+2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
diff --git a/doc/administration/reference_architectures/5k_users.md b/doc/administration/reference_architectures/5k_users.md
index 47c3a19afda..6c0b66ab31e 100644
--- a/doc/administration/reference_architectures/5k_users.md
+++ b/doc/administration/reference_architectures/5k_users.md
@@ -45,7 +45,7 @@ costly-to-operate environment by using the
<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix -->
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work, however Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However it is also used optionally by Prometheus for Omnibus auto host discovery.
-2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
+2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
@@ -2265,7 +2265,7 @@ services where applicable):
<!-- Disable ordered list rule https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md#md029---ordered-list-item-prefix -->
<!-- markdownlint-disable MD029 -->
1. Can be optionally run on reputable third-party external PaaS PostgreSQL solutions. [Google Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) and [Amazon RDS](https://aws.amazon.com/rds/) are known to work, however Azure Database for PostgreSQL is **not recommended** due to [performance issues](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/61). Consul is primarily used for PostgreSQL high availability so can be ignored when using a PostgreSQL PaaS setup. However it is also used optionally by Prometheus for Omnibus auto host discovery.
-2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS Elasticache are known to work.
+2. Can be optionally run on reputable third-party external PaaS Redis solutions. Google Memorystore and AWS ElastiCache are known to work.
3. Can be optionally run on reputable third-party load balancing services (LB PaaS). AWS ELB is known to work.
4. Should be run on reputable third-party object storage (storage PaaS) for cloud implementations. Google Cloud Storage and AWS S3 are known to work.
5. Gitaly Cluster provides the benefits of fault tolerance, but comes with additional complexity of setup and management. Review the existing [technical limitations and considerations before deploying Gitaly Cluster](../gitaly/index.md#before-deploying-gitaly-cluster). If you want sharded Gitaly, use the same specs listed above for `Gitaly`.
diff --git a/doc/administration/reference_architectures/index.md b/doc/administration/reference_architectures/index.md
index be6a7674f43..089e8881d7d 100644
--- a/doc/administration/reference_architectures/index.md
+++ b/doc/administration/reference_architectures/index.md
@@ -358,7 +358,7 @@ Additionally, the following cloud provider services are validated and supported
<tr>
<td>Redis</td>
<td></td>
- <td>✅ &nbsp; <a href="https://aws.amazon.com/elasticache/" target="_blank" rel="noopener noreferrer">Elasticache</a></td>
+ <td>✅ &nbsp; <a href="https://aws.amazon.com/elasticache/" target="_blank" rel="noopener noreferrer">ElastiCache</a></td>
<td></td>
</tr>
</tbody>
diff --git a/doc/administration/troubleshooting/diagnostics_tools.md b/doc/administration/troubleshooting/diagnostics_tools.md
index d510df5976c..479fdb963cb 100644
--- a/doc/administration/troubleshooting/diagnostics_tools.md
+++ b/doc/administration/troubleshooting/diagnostics_tools.md
@@ -26,4 +26,4 @@ and summarize raw `strace` data.
## kubesos
-The [`kubesos`](https://gitlab.com/gitlab-com/support/toolbox/kubesos/) utiltity retrieves GitLab cluster configuration and logs from GitLab Cloud Native chart deployments.
+The [`kubesos`](https://gitlab.com/gitlab-com/support/toolbox/kubesos/) utility retrieves GitLab cluster configuration and logs from GitLab Cloud Native chart deployments.
diff --git a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
index 23edad398c1..1d6e917e147 100644
--- a/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
+++ b/doc/administration/troubleshooting/gitlab_rails_cheat_sheet.md
@@ -753,7 +753,7 @@ group.members_with_parents.count
parent.members_with_descendants.count
```
-### Find gropus that are pending deletion
+### Find groups that are pending deletion
```ruby
#
diff --git a/doc/administration/troubleshooting/log_parsing.md b/doc/administration/troubleshooting/log_parsing.md
index d8f21f1676c..9aa490f73ef 100644
--- a/doc/administration/troubleshooting/log_parsing.md
+++ b/doc/administration/troubleshooting/log_parsing.md
@@ -12,7 +12,7 @@ but if they are not available you can still quickly parse
(the default in GitLab 12.0 and later) using [`jq`](https://stedolan.github.io/jq/).
NOTE:
-Spefically for summarising error events and basic usage statistics,
+Specifically for summarizing error events and basic usage statistics,
the GitLab Support Team provides the specialised
[`fast-stats` tool](https://gitlab.com/gitlab-com/support/toolbox/fast-stats/#when-to-use-it).
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index df16eb54132..477936d8ed0 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -17993,8 +17993,7 @@ Values for sorting runners.
| Value | Description |
| ----- | ----------- |
| <a id="cirunnerstatusactive"></a>`ACTIVE` **{warning-solid}** | **Deprecated** in 14.6. This was renamed. Use: [`CiRunner.paused`](#cirunnerpaused). |
-| <a id="cirunnerstatusnever_contacted"></a>`NEVER_CONTACTED` | Runner that has never contacted this instance. Set legacyMode to null to utilize this value. Will replace NOT_CONNECTED starting in 15.0. |
-| <a id="cirunnerstatusnot_connected"></a>`NOT_CONNECTED` **{warning-solid}** | **Deprecated** in 14.6. Use NEVER_CONTACTED instead. NEVER_CONTACTED will have a slightly different scope starting in 15.0, with STALE being returned instead after 3 months of no contact. |
+| <a id="cirunnerstatusnever_contacted"></a>`NEVER_CONTACTED` | Runner that has never contacted this instance. |
| <a id="cirunnerstatusoffline"></a>`OFFLINE` **{warning-solid}** | **Deprecated** in 14.6. This field will have a slightly different scope starting in 15.0, with STALE being returned after a certain period offline. |
| <a id="cirunnerstatusonline"></a>`ONLINE` | Runner that contacted this instance within the last 2 hours. |
| <a id="cirunnerstatuspaused"></a>`PAUSED` **{warning-solid}** | **Deprecated** in 14.6. This was renamed. Use: [`CiRunner.paused`](#cirunnerpaused). |
diff --git a/doc/api/members.md b/doc/api/members.md
index 21ed6db7245..1db9714bfd1 100644
--- a/doc/api/members.md
+++ b/doc/api/members.md
@@ -483,6 +483,35 @@ DELETE /groups/:id/billable_members/:user_id
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/billable_members/:user_id"
```
+## Change membership state of a user in a group
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86705) in GitLab 15.0.
+
+Changes the membership state of a user in a group. The state is applied to
+all subgroups and projects.
+
+```plaintext
+PUT /groups/:id/members/:user_id/state
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) owned by the authenticated user. |
+| `user_id` | integer | yes | The user ID of the member. |
+| `state` | string | yes | The new state for the user. State is either `awaiting` or `active`. |
+
+```shell
+curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/groups/:id/members/:user_id/state?state=active"
+```
+
+Example response:
+
+```json
+{
+ "success":true
+}
+```
+
## Add a member to a group or project
Adds a member to a group or project.
diff --git a/doc/api/runners.md b/doc/api/runners.md
index 54f7df4be4c..7519c3595b6 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -44,13 +44,13 @@ GET /runners?paused=true
GET /runners?tag_list=tag1,tag2
```
-| Attribute | Type | Required | Description |
-|------------|--------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online` and `offline`; showing all runners if none provided |
-| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
-| `status` | string | no | The status of runners to show, one of: `online`, `offline` and `not_connected`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
-| `paused` | boolean | no | Whether to include only runners that are accepting or ignoring new jobs |
-| `tag_list` | string array | no | List of the runner's tags |
+| Attribute | Type | Required | Description |
+|------------|--------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online` and `offline`; showing all runners if none provided |
+| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
+| `status` | string | no | The status of runners to show, one of: `online`, `offline` and `never_contacted`. `active` and `paused` are also possible values which were deprecated in GitLab 14.8 and will be removed in GitLab 16.0 |
+| `paused` | boolean | no | Whether to include only runners that are accepting or ignoring new jobs |
+| `tag_list` | string array | no | List of the runner's tags |
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners"
@@ -610,7 +610,7 @@ Example response:
"runner_type": "instance_type",
"name": "gitlab-runner",
"online": null,
- "status": "not_connected"
+ "status": "never_contacted"
},
{
"id": 6,
@@ -634,7 +634,7 @@ Example response:
"runner_type": "group_type",
"name": "gitlab-runner",
"online": null,
- "status": "not_connected"
+ "status": "never_contacted"
}
]
```
diff --git a/doc/architecture/blueprints/database_scaling/size-limits.md b/doc/architecture/blueprints/database_scaling/size-limits.md
index d63aa3bd4e8..a0508488620 100644
--- a/doc/architecture/blueprints/database_scaling/size-limits.md
+++ b/doc/architecture/blueprints/database_scaling/size-limits.md
@@ -126,7 +126,7 @@ In order to maintain and improve operational stability and lessen development bu
This target is *pragmatic*: We understand table sizes depend on feature usage, code changes and other factors - which all change over time. We may not always find solutions where we can tightly limit the size of physical tables once and for all. That is acceptable though and we primarily aim to keep the situation on GitLab.com under control. We adapt our efforts to the situation present on GitLab.com and will re-evaluate frequently.
-While there are changes we can make that lead to a constant maximum physical table size over time, this doesn't need to be the case necessarily. Consider for example hash partitioniong, which breaks a table down into a static number of partitions. With data growth over time, individual partitions will also grow in size and may eventually reach the threshold size again. We strive to get constant table sizes, but it is acceptable to ship easier solutions that don't have this characteristic but improve the situation for a considerable amount of time.
+While there are changes we can make that lead to a constant maximum physical table size over time, this doesn't need to be the case necessarily. Consider for example hash partitioning, which breaks a table down into a static number of partitions. With data growth over time, individual partitions will also grow in size and may eventually reach the threshold size again. We strive to get constant table sizes, but it is acceptable to ship easier solutions that don't have this characteristic but improve the situation for a considerable amount of time.
As such, the target size of a physical table after refactoring depends on the situation and there is no hard rule for it. We suggest to consider historic data growth and forecast when physical tables will reach the threshold of 100 GB again. This allows us to understand how long a particular solution is expected to last until the model has to be revisited.
diff --git a/doc/ci/yaml/workflow.md b/doc/ci/yaml/workflow.md
index eac3f5a5606..a985db14d08 100644
--- a/doc/ci/yaml/workflow.md
+++ b/doc/ci/yaml/workflow.md
@@ -87,12 +87,14 @@ workflow:
- if: $CI_COMMIT_BRANCH
```
-If the pipeline is triggered by:
+If GitLab attempts to trigger:
-- A merge request, run a merge request pipeline. For example, a merge request pipeline
+- A merge request pipeline, start the pipeline. For example, a merge request pipeline
can be triggered by a push to a branch with an associated open merge request.
-- A change to a branch, but a merge request is open for that branch, do not run a branch pipeline.
-- A change to a branch, but without any open merge requests, run a branch pipeline.
+- A branch pipeline, but a merge request is open for that branch, do not run the branch pipeline.
+ For example, a branch pipeline can be triggered by a change to a branch, an API call,
+ a scheduled pipeline, and so on.
+- A branch pipeline, but there is no merge request open for the branch, run the branch pipeline.
You can also add a rule to an existing `workflow` section to switch from branch pipelines
to merge request pipelines when a merge request is created.
diff --git a/doc/development/application_slis/index.md b/doc/development/application_slis/index.md
index a202bc419e1..69cd30420b8 100644
--- a/doc/development/application_slis/index.md
+++ b/doc/development/application_slis/index.md
@@ -37,7 +37,7 @@ with all possible
label-combinations](https://prometheus.io/docs/practices/instrumentation/#avoid-missing-metrics). This
avoid confusing results when using these counters in calculations.
-To initialize an SLI, use the `.inilialize_sli` class method, for
+To initialize an SLI, use the `.initialize_sli` class method, for
example:
```ruby
diff --git a/doc/development/audit_event_guide/index.md b/doc/development/audit_event_guide/index.md
index 34f78174e5b..0d62bcdc3b2 100644
--- a/doc/development/audit_event_guide/index.md
+++ b/doc/development/audit_event_guide/index.md
@@ -25,7 +25,7 @@ To instrument an audit event, the following attributes should be provided:
| `scope` | User, Project, Group | true | Scope which the audit event belongs to |
| `target` | Object | true | Target object being audited |
| `message` | String | true | Message describing the action |
-| `created_at` | DateTime | false | The time when the action occured. Defaults to `DateTime.current` |
+| `created_at` | DateTime | false | The time when the action occurred. Defaults to `DateTime.current` |
## How to instrument new Audit Events
diff --git a/doc/development/database/batched_background_migrations.md b/doc/development/database/batched_background_migrations.md
index 3177044f3a6..3a0fa77eff9 100644
--- a/doc/development/database/batched_background_migrations.md
+++ b/doc/development/database/batched_background_migrations.md
@@ -80,7 +80,7 @@ end
```
Similarly the usage of `ActiveRecord::Base.connection` is disallowed and needs to be
-replaced preferrably with the usage of model connection.
+replaced preferably with the usage of model connection.
```ruby
# good
diff --git a/doc/development/database/loose_foreign_keys.md b/doc/development/database/loose_foreign_keys.md
index 2bcdc91202a..4123be890d6 100644
--- a/doc/development/database/loose_foreign_keys.md
+++ b/doc/development/database/loose_foreign_keys.md
@@ -593,7 +593,7 @@ Partitions: gitlab_partitions_dynamic.loose_foreign_keys_deleted_records_84 FOR
The `partition` column controls the insert direction, the `partition` value determines which
partition will get the deleted rows inserted via the trigger. Notice that the default value of
the `partition` table matches with the value of the list partition (84). In `INSERT` query
-within the trigger thevalue of the `partition` is omitted, the trigger always relies on the
+within the trigger the value of the `partition` is omitted, the trigger always relies on the
default value of the column.
Example `INSERT` query for the trigger:
@@ -605,7 +605,7 @@ SELECT TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME, old_table.id FROM old_table;
```
The partition "sliding" process is controlled by two, regularly executed callbacks. These
-callbackes are defined within the `LooseForeignKeys::DeletedRecord` model.
+callbacks are defined within the `LooseForeignKeys::DeletedRecord` model.
The `next_partition_if` callback controls when to create a new partition. A new partition will
be created when the current partition has at least one record older than 24 hours. A new partition
@@ -805,7 +805,7 @@ Possible solutions:
- Long-term: invoke the worker more frequently. Parallelize the worker
For a one-time fix, we can run the cleanup worker several times from the rails console. The worker
-can run parallelly however, this can introduce lock contention and it could increase the worker
+can run in parallel however, this can introduce lock contention and it could increase the worker
runtime.
```ruby
diff --git a/doc/development/documentation/testing.md b/doc/development/documentation/testing.md
index 485d2225e6d..81e1eca8724 100644
--- a/doc/development/documentation/testing.md
+++ b/doc/development/documentation/testing.md
@@ -346,7 +346,7 @@ To configure Vale in your editor, install one of the following as appropriate:
- Visual Studio Code [`errata-ai.vale-server` extension](https://marketplace.visualstudio.com/items?itemName=errata-ai.vale-server).
You can configure the plugin to [display only a subset of alerts](#show-subset-of-vale-alerts).
- Vim [ALE plugin](https://github.com/dense-analysis/ale).
-- Jetbrains IDEs - No plugin exists, but
+- JetBrains IDEs - No plugin exists, but
[this issue comment](https://github.com/errata-ai/vale-server/issues/39#issuecomment-751714451)
contains tips for configuring an external tool.
- Emacs [Flycheck extension](https://github.com/flycheck/flycheck).
diff --git a/doc/development/fe_guide/registry_architecture.md b/doc/development/fe_guide/registry_architecture.md
index 47a6dc40e19..56d67e094b7 100644
--- a/doc/development/fe_guide/registry_architecture.md
+++ b/doc/development/fe_guide/registry_architecture.md
@@ -56,7 +56,7 @@ in the container components when needed. This makes it easier to:
[`delete_package.vue`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/packages_and_registries/package_registry/components/functional/delete_package.vue)).
- Leverage [startup for GraphQL calls](graphql.md#making-initial-queries-early-with-graphql-startup-calls).
-## Shared compoenents library
+## Shared components library
Inside `vue_shared/components/registry` and `packages_and_registries/shared`, there's a set of
shared components that you can use to implement registry functionality. These components build the
diff --git a/doc/development/foreign_keys.md b/doc/development/foreign_keys.md
index db8367fe5f5..c20c70623ae 100644
--- a/doc/development/foreign_keys.md
+++ b/doc/development/foreign_keys.md
@@ -123,3 +123,7 @@ class UserConfig < ActiveRecord::Base
belongs_to :user
end
```
+
+Using a foreign key as primary key saves space but can make
+[batch counting](service_ping/implement.md#batch-counters) in [Service Ping](service_ping/index.md) less efficient.
+Consider using a regular `id` column if the table will be relevant for Service Ping.
diff --git a/doc/development/go_guide/go_upgrade.md b/doc/development/go_guide/go_upgrade.md
index 3267d1262f0..4e2a0d95910 100644
--- a/doc/development/go_guide/go_upgrade.md
+++ b/doc/development/go_guide/go_upgrade.md
@@ -158,7 +158,7 @@ if you need help finding the correct person or labels:
| GitLab Quality Images | [Issue Tracker](https://gitlab.com/gitlab-org/gitlab-build-images/-/issues) |
| GitLab Shell | [Issue Tracker](https://gitlab.com/gitlab-org/gitlab-shell/-/issues) |
| GitLab Workhorse | [Issue Tracker](https://gitlab.com/gitlab-org/gitlab/-/issues) |
-| Labkit | [Issue Tracker](https://gitlab.com/gitlab-org/labkit/-/issues) |
+| LabKit | [Issue Tracker](https://gitlab.com/gitlab-org/labkit/-/issues) |
| [Node Exporter](https://github.com/prometheus/node_exporter) | [Issue Tracker](https://gitlab.com/gitlab-org/gitlab/-/issues) |
| [PgBouncer Exporter](https://github.com/prometheus-community/pgbouncer_exporter) | [Issue Tracker](https://gitlab.com/gitlab-org/gitlab/-/issues) |
| [Postgres Exporter](https://github.com/prometheus-community/postgres_exporter) | [Issue Tracker](https://gitlab.com/gitlab-org/gitlab/-/issues) |
diff --git a/doc/development/performance.md b/doc/development/performance.md
index 716a0d5199a..6d0b833a2da 100644
--- a/doc/development/performance.md
+++ b/doc/development/performance.md
@@ -318,7 +318,7 @@ You can do this when using the [performance bar](profiling.md#speedscope-flamegr
and when [profiling code blocks](https://github.com/jlfwong/speedscope/wiki/Importing-from-stackprof-(ruby)).
This option isn't supported by `bin/rspec-stackprof`.
-You can profile speciific methods by using `--method method_name`:
+You can profile specific methods by using `--method method_name`:
```shell
$ stackprof tmp/project_policy_spec.rb.dump --method access_allowed_to
diff --git a/doc/development/pipelines.md b/doc/development/pipelines.md
index 8b5fd3952ba..fd5dff55642 100644
--- a/doc/development/pipelines.md
+++ b/doc/development/pipelines.md
@@ -345,7 +345,7 @@ We use the [`rules:`](../ci/yaml/index.md#rules) and [`needs:`](../ci/yaml/index
to determine the jobs that need to be run in a pipeline. Note that an MR that includes multiple types of changes would
have a pipelines that include jobs from multiple types (for example, a combination of docs-only and code-only pipelines).
-Following are graphs of the critical paths for each pipeline type. Jobs that aren't part of the critical path are ommitted.
+Following are graphs of the critical paths for each pipeline type. Jobs that aren't part of the critical path are omitted.
### Documentation pipeline
diff --git a/doc/development/redis/new_redis_instance.md b/doc/development/redis/new_redis_instance.md
index 96f860f3890..389cddbb4e5 100644
--- a/doc/development/redis/new_redis_instance.md
+++ b/doc/development/redis/new_redis_instance.md
@@ -232,8 +232,8 @@ a developer will need to add an implementation for missing Redis commands before
| metrics name | type | labels | description |
|-------------------------------------------------|--------------------|------------------------|----------------------------------------------------|
-| gitlab_redis_multi_store_read_fallback_total | Prometheus Counter | command, instance_name | Client side Redis MultiStore reading fallback total|
-| gitlab_redis_multi_store_method_missing_total | Prometheus Counter | command, instance_name | Client side Redis MultiStore method missing total |
+| `gitlab_redis_multi_store_read_fallback_total` | Prometheus Counter | command, instance_name | Client side Redis MultiStore reading fallback total|
+| `gitlab_redis_multi_store_method_missing_total` | Prometheus Counter | command, instance_name | Client side Redis MultiStore method missing total |
## Step 4: clean up after the migration
diff --git a/doc/development/ruby_upgrade.md b/doc/development/ruby_upgrade.md
index 551f8dd081e..3b89a6fd1ea 100644
--- a/doc/development/ruby_upgrade.md
+++ b/doc/development/ruby_upgrade.md
@@ -144,7 +144,7 @@ A [build matrix definition](../ci/yaml/index.md#parallelmatrix) can do this effi
When upgrading Ruby, consider updating the following repositories:
- [Gitaly](https://gitlab.com/gitlab-org/gitaly) ([example](https://gitlab.com/gitlab-org/gitaly/-/merge_requests/3771))
-- [GitLab Labkit](https://gitlab.com/gitlab-org/labkit-ruby) ([example](https://gitlab.com/gitlab-org/labkit-ruby/-/merge_requests/79))
+- [GitLab LabKit](https://gitlab.com/gitlab-org/labkit-ruby) ([example](https://gitlab.com/gitlab-org/labkit-ruby/-/merge_requests/79))
- [GitLab Exporter](https://gitlab.com/gitlab-org/gitlab-exporter) ([example](https://gitlab.com/gitlab-org/gitlab-exporter/-/merge_requests/150))
- [GitLab Experiment](https://gitlab.com/gitlab-org/ruby/gems/gitlab-experiment) ([example](https://gitlab.com/gitlab-org/ruby/gems/gitlab-experiment/-/merge_requests/128))
- [Gollum Lib](https://gitlab.com/gitlab-org/gollum-lib) ([example](https://gitlab.com/gitlab-org/gollum-lib/-/merge_requests/21))
diff --git a/doc/development/snowplow/troubleshooting.md b/doc/development/snowplow/troubleshooting.md
index 47d775d89aa..597e0436c67 100644
--- a/doc/development/snowplow/troubleshooting.md
+++ b/doc/development/snowplow/troubleshooting.md
@@ -21,7 +21,7 @@ While on CloudWatch dashboard set time range to last 4 weeks, to get better pict
1. `ELB New Flow Count` and `Collector Auto Scaling Group Network In/Out` - they show in order: number of connections to collectors via load balancers and data volume (in bytes) processed by collectors. If there is drop visible there, it means less events were fired from the GitLab application. Proceed to [application layer guide](#troubleshooting-gitlab-application-layer) for more details
1. `Firehose Records to S3` - it shows how many event records were saved to S3 bucket, if there was drop on this chart but not on the charts from 1. it means that problem is located at AWS infrastructure layer, please refer to [AWS layer guide](#troubleshooting-aws-layer)
-1. If drop wasn't visible on any of previous charts it means that probelm is at data warehouse layer, please refer to [data warehouse layer guide](#troubleshooting-data-warehouse-layer)
+1. If drop wasn't visible on any of previous charts it means that problem is at data warehouse layer, please refer to [data warehouse layer guide](#troubleshooting-data-warehouse-layer)
### Troubleshooting GitLab application layer
diff --git a/doc/install/aws/gitlab_hybrid_on_aws.md b/doc/install/aws/gitlab_hybrid_on_aws.md
index 5349be9fc92..e8fbd7c0f33 100644
--- a/doc/install/aws/gitlab_hybrid_on_aws.md
+++ b/doc/install/aws/gitlab_hybrid_on_aws.md
@@ -16,11 +16,11 @@ Amazon provides a managed Kubernetes service offering known as [Amazon Elastic K
| GitLab Cloud Native Hybrid Ref Arch | GitLab Baseline Perf Test Results Omnibus on Instances | AWS Bill of Materials (BOM) for CNH | AWS Build Performance Testing Results for [CNH](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) | CNH Cost Estimate 3 AZs* |
| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
-| [2K Omnibus](../../administration/reference_architectures/2k_users.md) | [2K Baseline](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/2k) | [2K Cloud Native Hybrid on EKS](#2k-cloud-native-hybrid-on-eks) | GPT Test Results | [1 YR Ec2 Compute Savings + 1 YR RDS & Elasticache RIs](https://calculator.aws/#/estimate?id=544bcf1162beae6b8130ad257d081cdf9d4504e3)<br />(2 AZ Cost Estimate is in BOM Below) |
-| [3K](../../administration/reference_architectures/3k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) | [3k Baseline](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/3k) | [3K Cloud Native Hybrid on EKS](#3k-cloud-native-hybrid-on-eks) | [3K 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)<br /><br />[3K 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/3K/3k-QuickStart-AutoScale-ARM-RDS-Cache_v13-12-3-ee_2021-07-23_194200/3k-QuickStart-AutoScale-ARM-RDS-Cache_v13-12-3-ee_2021-07-23_194200_results.txt) | [1 YR Ec2 Compute Savings + 1 YR RDS & Elasticache RIs](https://calculator.aws/#/estimate?id=f1294fec554e21be999711cddcdab9c5e7f83f14)<br />(2 AZ Cost Estimate is in BOM Below) |
-| [5K](../../administration/reference_architectures/5k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) | [5k Baseline](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/5k) | [5K Cloud Native Hybrid on EKS](#5k-cloud-native-hybrid-on-eks) | [5K 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)<br /><br />[5K AutoScale from 25% 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-AutoScale-From-25Percent-ARM-RDS-Redis_v13-12-3-ee_2021-07-24_102717/5k-QuickStart-AutoScale-From-25Percent-ARM-RDS-Redis_v13-12-3-ee_2021-07-24_102717_results.txt) | [1 YR Ec2 Compute Savings + 1 YR RDS & Elasticache RIs](https://calculator.aws/#/estimate?id=330ee43c5b14662db5df6e52b34898d181a09e16) |
-| [10K](../../administration/reference_architectures/10k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) | [10k Baseline](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/10k) | [10K Cloud Native Hybrid on EKS](#10k-cloud-native-hybrid-on-eks) | [10K 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)<br /><br />[10K Elastic Auto Scale GPT Test Results](hhttps://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) | [10K 1 YR Ec2 Compute Savings + 1 YR RDS & Elasticache RIs](https://calculator.aws/#/estimate?id=5ac2e07a22e01c36ee76b5477c5a046cd1bea792) |
-| [50K](../../administration/reference_architectures/50k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) | [50k Baseline](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/50k) | [50K Cloud Native Hybrid on EKS](#50k-cloud-native-hybrid-on-eks) | [50K 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)<br /><br />[10K 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) | [50K 1 YR Ec2 Compute Savings + 1 YR RDS & Elasticache RIs](https://calculator.aws/#/estimate?id=b9c9d6ac1d4a7848011d2050cef3120931fb7c22) |
+| [2K Omnibus](../../administration/reference_architectures/2k_users.md) | [2K Baseline](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/2k) | [2K Cloud Native Hybrid on EKS](#2k-cloud-native-hybrid-on-eks) | GPT Test Results | [1 YR Ec2 Compute Savings + 1 YR RDS & ElastiCache RIs](https://calculator.aws/#/estimate?id=544bcf1162beae6b8130ad257d081cdf9d4504e3)<br />(2 AZ Cost Estimate is in BOM Below) |
+| [3K](../../administration/reference_architectures/3k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) | [3k Baseline](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/3k) | [3K Cloud Native Hybrid on EKS](#3k-cloud-native-hybrid-on-eks) | [3K 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)<br /><br />[3K 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/3K/3k-QuickStart-AutoScale-ARM-RDS-Cache_v13-12-3-ee_2021-07-23_194200/3k-QuickStart-AutoScale-ARM-RDS-Cache_v13-12-3-ee_2021-07-23_194200_results.txt) | [1 YR Ec2 Compute Savings + 1 YR RDS & ElastiCache RIs](https://calculator.aws/#/estimate?id=f1294fec554e21be999711cddcdab9c5e7f83f14)<br />(2 AZ Cost Estimate is in BOM Below) |
+| [5K](../../administration/reference_architectures/5k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) | [5k Baseline](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/5k) | [5K Cloud Native Hybrid on EKS](#5k-cloud-native-hybrid-on-eks) | [5K 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)<br /><br />[5K AutoScale from 25% 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-AutoScale-From-25Percent-ARM-RDS-Redis_v13-12-3-ee_2021-07-24_102717/5k-QuickStart-AutoScale-From-25Percent-ARM-RDS-Redis_v13-12-3-ee_2021-07-24_102717_results.txt) | [1 YR Ec2 Compute Savings + 1 YR RDS & ElastiCache RIs](https://calculator.aws/#/estimate?id=330ee43c5b14662db5df6e52b34898d181a09e16) |
+| [10K](../../administration/reference_architectures/10k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) | [10k Baseline](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/10k) | [10K Cloud Native Hybrid on EKS](#10k-cloud-native-hybrid-on-eks) | [10K 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)<br /><br />[10K Elastic Auto Scale GPT Test Results](hhttps://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) | [10K 1 YR Ec2 Compute Savings + 1 YR RDS & ElastiCache RIs](https://calculator.aws/#/estimate?id=5ac2e07a22e01c36ee76b5477c5a046cd1bea792) |
+| [50K](../../administration/reference_architectures/50k_users.md#cloud-native-hybrid-reference-architecture-with-helm-charts-alternative) | [50k Baseline](https://gitlab.com/gitlab-org/quality/performance/-/wikis/Benchmarks/Latest/50k) | [50K Cloud Native Hybrid on EKS](#50k-cloud-native-hybrid-on-eks) | [50K 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)<br /><br />[10K 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) | [50K 1 YR Ec2 Compute Savings + 1 YR RDS & ElastiCache RIs](https://calculator.aws/#/estimate?id=b9c9d6ac1d4a7848011d2050cef3120931fb7c22) |
\*Cost calculations for actual implementations are a rough guideline with the following considerations:
@@ -99,7 +99,7 @@ Some services, such as log aggregation, outbound email are not specified by GitL
| ------------------------------------------------------------ | ------------------------------ | ------------------------------------------------------------ |
| <u>Tested PaaS Mentioned in Reference Architectures</u> | | |
| **PostgreSQL Database** | Amazon RDS PostgreSQL | Yes. |
-| **Redis Caching** | Redis Elasticache | Yes. |
+| **Redis Caching** | Redis ElastiCache | Yes. |
| **Gitaly Cluster (Git Repository Storage)**<br />(Including Praefect and PostgreSQL) | ASG and Instances | Yes - ASG and Instances<br />**Note: Gitaly cannot be put into a Kubernetes Cluster.** |
| **All GitLab storages besides Git Repository Storage**<br />(Includes Git-LFS which is S3 Compatible) | AWS S3 | Yes |
| | | |
diff --git a/doc/install/aws/index.md b/doc/install/aws/index.md
index 8dbda7420b0..ee7279d72cd 100644
--- a/doc/install/aws/index.md
+++ b/doc/install/aws/index.md
@@ -63,7 +63,7 @@ When deploying a GitLab instance using the official AMI, the root password to th
Instances running on Community Edition (CE) require a migration to Enterprise Edition (EE) in order to subscribe to the GitLab Premium or Ultimate plan. If you want to pursue a subscription, using the Free-forever plan of Enterprise Edition is the least disruptive method.
NOTE:
-Since any given GitLab upgrade might involve data disk updates or database schema upgrades, simply swapping out the AMI is not sufficent for taking upgrades.
+Since any given GitLab upgrade might involve data disk updates or database schema upgrades, simply swapping out the AMI is not sufficient for taking upgrades.
1. Log in to the AWS Web Console, so that clicking the links in the following step take you directly to the AMI list.
1. Pick the edition you want:
diff --git a/doc/operations/feature_flags.md b/doc/operations/feature_flags.md
index c96451ead4b..4b503af2cf7 100644
--- a/doc/operations/feature_flags.md
+++ b/doc/operations/feature_flags.md
@@ -430,7 +430,7 @@ Please note that the polling rate is configurable in SDKs. Provided that all cli
For applications looking for more scalable solution, we recommend to use [Unleash Proxy](#unleash-proxy-example).
This proxy server sits between the server and clients. It requests to the server as a behalf of the client groups,
-so the nubmer of outbound requests can be greatly reduced.
+so the number of outbound requests can be greatly reduced.
There is also an [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/295472) to give more
capacity to the current rate limit.
diff --git a/doc/update/index.md b/doc/update/index.md
index 7fe1d67168b..15a14d460bb 100644
--- a/doc/update/index.md
+++ b/doc/update/index.md
@@ -537,7 +537,7 @@ or [init scripts](upgrading_from_source.md#configure-sysv-init-script) by [follo
### 14.4.4
- For [zero-downtime upgrades](zero_downtime.md) on a GitLab cluster with separate Web and API nodes, you need to enable the `paginated_tree_graphql_query` [feature flag](../administration/feature_flags.md#enable-or-disable-the-feature) _before_ upgrading GitLab Web nodes to 14.4.
- This is because we [enabled `paginated_tree_graphql_query by default in 14.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70913/diffs), so if GitLab UI is on 14.4 and its API is on 14.3, the frontend will have this feature enabled but the backend will have it disabled. This will result in the following error:
+ This is because we [enabled `paginated_tree_graphql_query` by default in 14.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70913/diffs), so if GitLab UI is on 14.4 and its API is on 14.3, the frontend will have this feature enabled but the backend will have it disabled. This will result in the following error:
```shell
bundle.esm.js:63 Uncaught (in promise) Error: GraphQL error: Field 'paginatedTree' doesn't exist on type 'Repository'
diff --git a/doc/update/removals.md b/doc/update/removals.md
index 06e2be2ab44..2ae58a4ea6e 100644
--- a/doc/update/removals.md
+++ b/doc/update/removals.md
@@ -226,6 +226,20 @@ It also depends on a few third-party gems that are not actively maintained anymo
For more information, check the [summary section of the deprecation issue](https://gitlab.com/gitlab-org/gitlab/-/issues/352488#deprecation-summary).
+### Runner status `not_connected` API value
+
+WARNING:
+This feature was changed or removed in 15.0
+as a [breaking change](https://docs.gitlab.com/ee/development/contributing/#breaking-changes).
+Before updating GitLab, review the details carefully to determine if you need to make any
+changes to your code, settings, or workflow.
+
+The GitLab Runner REST and GraphQL [API](https://docs.gitlab.com/ee/api/runners.html#runners-api) endpoints
+deprecated the `not_connected` status value in GitLab 14.6 and will start returning `never_contacted` in its place
+starting in GitLab 15.0.
+
+Runners that have never contacted the GitLab instance will also return `stale` if created more than 3 months ago.
+
### Sidekiq configuration for metrics and health checks
WARNING:
diff --git a/doc/user/application_security/secret_detection/post_processing.md b/doc/user/application_security/secret_detection/post_processing.md
index 643da47d876..9771658da4e 100644
--- a/doc/user/application_security/secret_detection/post_processing.md
+++ b/doc/user/application_security/secret_detection/post_processing.md
@@ -46,7 +46,7 @@ sequenceDiagram
Cloud Vendor-->>+RevocationAPI: ACCEPTED
```
-## Integrate your cloud provider service with GitLab Saas
+## Integrate your cloud provider service with GitLab SaaS
Third party cloud and SaaS providers can [express integration interest by filling out this form](https://forms.gle/wWpvrtLRK21Q2WJL9).
diff --git a/doc/user/clusters/agent/install/index.md b/doc/user/clusters/agent/install/index.md
index 49d5afc56b1..f747c6c0e25 100644
--- a/doc/user/clusters/agent/install/index.md
+++ b/doc/user/clusters/agent/install/index.md
@@ -49,8 +49,6 @@ The name must:
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5786) in GitLab 14.1, you can create a new agent record directly from the GitLab UI.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/347240) in GitLab 14.9, the agent can be registered without creating an agent configuration file.
-You must register an agent with GitLab.
-
FLAG:
In GitLab 14.10, a [flag](../../../../administration/feature_flags.md) named `certificate_based_clusters` changed the **Actions** menu to focus on the agent rather than certificates. The flag is [enabled on GitLab.com and self-managed](https://gitlab.com/groups/gitlab-org/configure/-/epics/8).
@@ -59,9 +57,11 @@ Prerequisites:
- For a [GitLab CI/CD workflow](../ci_cd_workflow.md), ensure that
[GitLab CI/CD is enabled](../../../../ci/enable_or_disable_ci.md#enable-cicd-in-a-project).
-To register an agent with GitLab:
+You must register an agent before you can install the agent in your cluster. To register an agent:
1. On the top bar, select **Menu > Projects** and find your project.
+ If you have an [agent configuration file](#create-an-agent-configuration-file),
+ it must be in this project. Your cluster manifest files should also be in this project.
1. From the left sidebar, select **Infrastructure > Kubernetes clusters**.
1. Select **Connect a cluster (agent)**.
- If you want to create a configuration with CI/CD defaults, type a name that meets [the naming convention](#agent-naming-convention).
@@ -109,9 +109,9 @@ If you do not know which one to choose, we recommend starting with Helm.
To install the agent on your cluster using Helm:
-1. [Install Helm](https://helm.sh/docs/intro/install/)
+1. [Install Helm](https://helm.sh/docs/intro/install/).
1. In your computer, open a terminal and [connect to your cluster](https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/).
-1. Run the command you copied when registering your agent with GitLab.
+1. Run the command you copied when you [registered your agent with GitLab](#register-the-agent-with-gitlab).
Optionally, you can [customize the Helm installation](#customize-the-helm-installation).
diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md
index 4d122e337db..674b61f2a4b 100644
--- a/doc/user/group/saml_sso/index.md
+++ b/doc/user/group/saml_sso/index.md
@@ -589,7 +589,7 @@ Here are possible causes and solutions:
| Cause | Solution |
| ---------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| You've tried to link multiple SAML identities to the same user, for a given identity provider. | Change the identity that you sign in with. To do so, [unlink the previous SAML identity](#unlinking-accounts) from this GitLab account before attempting to sign in again. |
-| The NameID changes everytime the user requests SSO identification | Check the NameID is not set with `Transient` format, or the NameID is not changing on subsequent requests.|
+| The NameID changes every time the user requests SSO identification | Check the NameID is not set with `Transient` format, or the NameID is not changing on subsequent requests.|
### Message: "SAML authentication failed: Email has already been taken"
diff --git a/doc/user/packages/container_registry/index.md b/doc/user/packages/container_registry/index.md
index c90475f1bdd..fe7a41aa542 100644
--- a/doc/user/packages/container_registry/index.md
+++ b/doc/user/packages/container_registry/index.md
@@ -695,7 +695,7 @@ There may be some errors not properly cached. Follow these steps to investigate
`200 OK`, the body might have the error `AccessDenied`. This indicates a permission problem from
the S3 side.
-1. Ensure your S3 configuration has the `deleteObject` permisson scope. Here's an
+1. Ensure your S3 configuration has the `deleteObject` permission scope. Here's an
[example role for an S3 bucket](../../../administration/object_storage.md#iam-permissions).
Once adjusted, trigger another tag deletion. You should be able to successfully delete tags.
diff --git a/doc/user/profile/notifications.md b/doc/user/profile/notifications.md
index b363ee2f78d..3eda363d108 100644
--- a/doc/user/profile/notifications.md
+++ b/doc/user/profile/notifications.md
@@ -7,6 +7,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Notification emails **(FREE)**
+> Enhanced email styling [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/78604) in GitLab 14.9 [with a feature flag](../../administration/feature_flags.md) named `enhanced_notify_css`. Disabled by default.
+> Enhanced email styling [enabled on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/355907) in GitLab 14.9.
+> Enhanced email styling [enabled on self-managed](https://gitlab.com/gitlab-org/gitlab/-/issues/355907) in GitLab 15.0.
+
Stay informed about what's happening in GitLab with email notifications.
You can receive updates about activity in issues, merge requests, epics, and designs.
diff --git a/doc/user/project/merge_requests/approvals/settings.md b/doc/user/project/merge_requests/approvals/settings.md
index b2d3fb788cc..9c2b54888fb 100644
--- a/doc/user/project/merge_requests/approvals/settings.md
+++ b/doc/user/project/merge_requests/approvals/settings.md
@@ -119,7 +119,7 @@ when more changes are added to it:
1. Select the **Remove all approvals when commits are added to the source branch** checkbox.
1. Select **Save changes**.
-Approvals aren't reset when a merge request is [rebased from the UI](../fast_forward_merge.md)
+Approvals aren't reset when a merge request is [rebased from the UI](../methods/index.md#rebasing-in-semi-linear-merge-methods)
However, approvals are reset if the target branch is changed.
## Code coverage check approvals
diff --git a/doc/user/project/merge_requests/fast_forward_merge.md b/doc/user/project/merge_requests/fast_forward_merge.md
index 77162aa0b83..048421a3a5b 100644
--- a/doc/user/project/merge_requests/fast_forward_merge.md
+++ b/doc/user/project/merge_requests/fast_forward_merge.md
@@ -1,74 +1,11 @@
---
-stage: Create
-group: Source Code
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
-type: reference, concepts
+redirect_to: 'methods/index.md'
+remove_date: '2022-08-09'
---
-# Fast-forward merge requests **(FREE)**
+This document was moved to [another location](methods/index.md).
-Sometimes, a workflow policy might mandate a clean commit history without
-merge commits. In such cases, the fast-forward merge is the perfect candidate.
-
-With fast-forward merge requests, you can retain a linear Git history and a way
-to accept merge requests without creating merge commits.
-
-When the fast-forward merge
-([`--ff-only`](https://git-scm.com/docs/git-merge#git-merge---ff-only)) setting
-is enabled, no merge commits are created and all merges are fast-forwarded,
-which means that merging is only allowed if the branch can be fast-forwarded.
-
-When a fast-forward merge is not possible, the user is given the option to rebase.
-
-NOTE:
-Projects using the fast-forward merge strategy can't filter merge requests
-[by deployment date](../../search/index.md#filtering-merge-requests-by-environment-or-deployment-date),
-because no merge commit is created.
-
-## Enabling fast-forward merges
-
-1. On the top bar, select **Menu > Projects** and find your project.
-1. On the left sidebar, select **Settings > General**.
-1. Expand **Merge requests**.
-1. In the **Merge method** section, select **Fast-forward merge**.
-1. Select **Save changes**.
-
-Now, when you visit the merge request page, you can accept it
-**only if a fast-forward merge is possible**.
-
-![Fast forward merge request](img/ff_merge_mr.png)
-
-If a fast-forward merge is not possible but a conflict free rebase is possible,
-a rebase button is offered.
-
-You can also rebase without running a CI/CD pipeline.
-[Introduced in](https://gitlab.com/gitlab-org/gitlab/-/issues/118825) GitLab 14.7.
-
-The rebase action is also available as a [quick action command: `/rebase`](../../../topics/git/git_rebase.md#rebase-from-the-gitlab-ui).
-
-![Fast forward merge request](img/ff_merge_rebase_v14_9.png)
-
-If the target branch is ahead of the source branch and a conflict free rebase is
-not possible, you need to rebase the
-source branch locally before you can do a fast-forward merge.
-
-![Fast forward merge rebase locally](img/ff_merge_rebase_locally.png)
-
-## Fast-forward merges prevent squashing commits
-
-If your project has enabled fast-forward merges, to merge cleanly, the code in a
-merge request cannot use [squashing during merge](squash_and_merge.md). Squashing
-is available only when accepting a merge request. Rebasing may be required before
-squashing, even though squashing can itself be considered equivalent to rebasing.
-
-<!-- ## Troubleshooting
-
-Include any troubleshooting steps that you can foresee. If you know beforehand what issues
-one might have when setting this up, or when something is changed, or on upgrading, it's
-important to describe those, too. Think of things that may go wrong and include them here.
-This is important to minimize requests for support, and to avoid doc comments with
-questions that you know someone might ask.
-
-Each scenario can be a third-level heading, e.g. `### Getting error message X`.
-If you have none to add when creating a doc, leave this section in place
-but commented out to help encourage others to add to it in the future. -->
+<!-- This redirect file can be deleted after <2022-08-09>. -->
+<!-- Redirects that point to other docs in the same project expire in three months. -->
+<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
+<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->
diff --git a/doc/user/project/merge_requests/img/merge_method_ff_v15_0.png b/doc/user/project/merge_requests/img/merge_method_ff_v15_0.png
new file mode 100644
index 00000000000..323fd03ffa2
--- /dev/null
+++ b/doc/user/project/merge_requests/img/merge_method_ff_v15_0.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_method_merge_commit_v15_0.png b/doc/user/project/merge_requests/img/merge_method_merge_commit_v15_0.png
new file mode 100644
index 00000000000..b880c2c0e04
--- /dev/null
+++ b/doc/user/project/merge_requests/img/merge_method_merge_commit_v15_0.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/merge_method_merge_commit_with_semi_linear_history_v15_0.png b/doc/user/project/merge_requests/img/merge_method_merge_commit_with_semi_linear_history_v15_0.png
new file mode 100644
index 00000000000..9eab71e9d3c
--- /dev/null
+++ b/doc/user/project/merge_requests/img/merge_method_merge_commit_with_semi_linear_history_v15_0.png
Binary files differ
diff --git a/doc/user/project/merge_requests/methods/index.md b/doc/user/project/merge_requests/methods/index.md
new file mode 100644
index 00000000000..adfa5288f81
--- /dev/null
+++ b/doc/user/project/merge_requests/methods/index.md
@@ -0,0 +1,116 @@
+---
+stage: Create
+group: Source Code
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+type: reference, concepts
+---
+
+# Merge methods **(FREE)**
+
+The merge method you select for your project determines how the changes in your
+merge requests are merged into an existing branch.
+
+## Configure a project's merge method
+
+1. On the top bar, select **Menu > Projects** and find your project.
+1. On the left sidebar, select **Settings > General**.
+1. Expand **Merge requests**.
+1. In the **Merge method** section, select your desired merge method.
+1. Select **Save changes**.
+
+## Merge commit
+
+This setting is the default. It always creates a separate merge commit,
+even when using [squash](../squash_and_merge.md). An example commit graph generated using this merge method:
+
+![Commit graph for merge commits](../img/merge_method_merge_commit_v15_0.png)
+
+- For regular merges, it is equivalent to the command `git merge --no-ff <source-branch>`.
+- For squash merges, it squashes all commits in the source branch before merging it normally. It performs actions similar to:
+
+ ```shell
+ git checkout `git merge-base <source-branch> <target-branch>`
+ git merge --squash <source-branch>
+ SOURCE_SHA=`git rev-parse HEAD`
+ git checkout <target-branch>
+ git merge --no-ff $SOURCE_SHA
+ ```
+
+## Merge commit with semi-linear history
+
+A merge commit is created for every merge, but the branch is only merged if
+a fast-forward merge is possible. This ensures that if the merge request build
+succeeded, the target branch build also succeeds after the merge. An example commit graph generated using this merge method:
+
+![Commit graph for merge commit with semi-linear history](../img/merge_method_merge_commit_with_semi_linear_history_v15_0.png)
+
+When you visit the merge request page with `Merge commit with semi-linear history`
+method selected, you can accept it **only if a fast-forward merge is possible**.
+When a fast-forward merge is not possible, the user is given the option to rebase, see
+[Rebasing in (semi-)linear merge methods](#rebasing-in-semi-linear-merge-methods).
+
+This method is equivalent to the same Git commands as in the **Merge commit** method. However,
+if your source branch is based on an out-of-date version of the target branch (such as `main`),
+you must rebase your source branch.
+This merge method creates a cleaner-looking history, while still enabling you to
+see where every branch began and was merged.
+
+## Fast-forward merge
+
+Sometimes, a workflow policy might mandate a clean commit history without
+merge commits. In such cases, the fast-forward merge is appropriate. With
+fast-forward merge requests, you can retain a linear Git history and a way
+to accept merge requests without creating merge commits. An example commit graph
+generated using this merge method:
+
+![Commit graph for fast-forward merge](../img/merge_method_ff_v15_0.png)
+
+This method is equivalent to `git merge --ff <source-branch>` for regular merges, and to
+`git merge -squash <source-branch>` for squash merges.
+
+When the fast-forward merge
+([`--ff-only`](https://git-scm.com/docs/git-merge#git-merge---ff-only)) setting
+is enabled, no merge commits are created and all merges are fast-forwarded,
+which means that merging is only allowed if the branch can be fast-forwarded.
+When a fast-forward merge is not possible, the user is given the option to rebase, see
+[Rebasing in (semi-)linear merge methods](#rebasing-in-semi-linear-merge-methods).
+
+NOTE:
+Projects using the fast-forward merge strategy can't filter merge requests
+[by deployment date](../../../search/index.md#filtering-merge-requests-by-environment-or-deployment-date),
+because no merge commit is created.
+
+When you visit the merge request page with `Fast-forward merge`
+method selected, you can accept it **only if a fast-forward merge is possible**.
+
+![Fast-forward merge request](../img/ff_merge_mr.png)
+
+## Rebasing in (semi-)linear merge methods
+
+> Rebasing without running a CI/CD pipeline [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/118825) in GitLab 14.7.
+
+In these merge methods, you can merge only when your source branch is up-to-date with the target branch:
+
+- Merge commit with semi-linear history.
+- Fast-forward merge.
+
+If a fast-forward merge is not possible but a conflict-free rebase is possible,
+GitLab offers you the [`/rebase` quick action](../../../../topics/git/git_rebase.md#rebase-from-the-gitlab-ui),
+and the ability to **Rebase** from the user interface:
+
+![Fast forward merge request](../img/ff_merge_rebase_v14_9.png)
+
+In [GitLab 14.7](https://gitlab.com/gitlab-org/gitlab/-/issues/118825) and later, you can also rebase without running a CI/CD pipeline.
+
+If the target branch is ahead of the source branch and a conflict-free rebase is
+not possible, you must rebase the source branch locally before you can do a fast-forward merge.
+
+![Fast forward merge rebase locally](../img/ff_merge_rebase_locally.png)
+
+Rebasing may be required before squashing, even though squashing can itself be
+considered equivalent to rebasing.
+
+## Related topics
+
+- [Commits history](../commits.md)
+- [Squash and merge](../squash_and_merge.md)
diff --git a/doc/user/project/merge_requests/revert_changes.md b/doc/user/project/merge_requests/revert_changes.md
index 6441ccb73fe..7b4a41f9339 100644
--- a/doc/user/project/merge_requests/revert_changes.md
+++ b/doc/user/project/merge_requests/revert_changes.md
@@ -14,7 +14,7 @@ by clicking the **Revert** button in merge requests and commit details.
NOTE:
The **Revert** button is shown only for projects that use the
merge method "Merge Commit", which can be set under the project's
-**Settings > General > Merge request**. [Fast-forward commits](fast_forward_merge.md)
+**Settings > General > Merge request**. [Fast-forward commits](methods/index.md#fast-forward-merge)
can not be reverted by using the merge request view.
After the merge request has been merged, use the **Revert** button
diff --git a/doc/user/project/merge_requests/reviews/index.md b/doc/user/project/merge_requests/reviews/index.md
index 2c3376c2689..f6f648a8bea 100644
--- a/doc/user/project/merge_requests/reviews/index.md
+++ b/doc/user/project/merge_requests/reviews/index.md
@@ -131,17 +131,6 @@ the author of the merge request can request a new review from the reviewer:
GitLab creates a new [to-do item](../../../todos.md) for the reviewer, and sends
them a notification email.
-## Semi-linear history merge requests
-
-A merge commit is created for every merge, but the branch is only merged if
-a fast-forward merge is possible. This ensures that if the merge request build
-succeeded, the target branch build also succeeds after the merge.
-
-1. Go to your project and select **Settings > General**.
-1. Expand **Merge requests**.
-1. In the **Merge method** section, select **Merge commit with semi-linear history**.
-1. Select **Save changes**.
-
## Comment on multiple lines
> - [Introduced](https://gitlab.com/gitlab-org/ux-research/-/issues/870) in GitLab 13.2.
@@ -211,7 +200,7 @@ These features are associated with merge requests:
- [Cherry-pick changes](../cherry_pick_changes.md):
Cherry-pick any commit in the UI by selecting the **Cherry-pick** button in a merged merge requests or a commit.
-- [Fast-forward merge requests](../fast_forward_merge.md):
+- [Fast-forward merge requests](../methods/index.md#fast-forward-merge):
For a linear Git history and a way to accept merge requests without creating merge commits
- [Find the merge request that introduced a change](../versions.md):
When viewing the commit details page, GitLab links to the merge request(s) containing that commit.
@@ -365,3 +354,7 @@ All the above can be done with the [`git-mr`](https://gitlab.com/glensc/git-mr)
In a group, the sidebar displays the total count of open merge requests. This value is cached if it's greater than
than 1000. The cached value is rounded to thousands (or millions) and updated every 24 hours.
+
+## Related topics
+
+- [Merge methods](../methods/index.md)
diff --git a/doc/user/project/merge_requests/squash_and_merge.md b/doc/user/project/merge_requests/squash_and_merge.md
index a1d6959b75e..7e37990b9bf 100644
--- a/doc/user/project/merge_requests/squash_and_merge.md
+++ b/doc/user/project/merge_requests/squash_and_merge.md
@@ -18,8 +18,8 @@ in your Git repository by using the _squash and merge_ strategy.
Each time a branch merges into your base branch, up to two commits are added:
- The single commit created by squashing the commits from the branch.
-- A merge commit, unless you have [enabled fast-forward merges](fast_forward_merge.md#enabling-fast-forward-merges)
- in your project. Fast-forward merges disable both merge commits and squashing.
+- A merge commit, unless you have enabled [fast-forward merges](methods/index.md#fast-forward-merge)
+ in your project. Fast-forward merges disable merge commits.
By default, squashed commits contain the following metadata:
@@ -74,7 +74,7 @@ To configure the default squashing behavior for all merge requests in your proje
## Related topics
- [Commit message templates](commit_templates.md)
-- [Fast-forward merges](fast_forward_merge.md)
+- [Merge methods](methods/index.md)
<!-- ## Troubleshooting
diff --git a/doc/user/project/releases/index.md b/doc/user/project/releases/index.md
index 8abcd1d67d8..c0a6fa9c301 100644
--- a/doc/user/project/releases/index.md
+++ b/doc/user/project/releases/index.md
@@ -533,7 +533,7 @@ The physical location of the asset can change at any time and the direct link re
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16821) in GitLab 14.9.
-The `filepath` from [permanent links to release assets](#permanent-links-to-release-assets) can be used in combination with [permanent link to the latest release](#permanent-link-to-latest-release). It is useful when we want to link a permanant URL to download an asset from the *latest release*.
+The `filepath` from [permanent links to release assets](#permanent-links-to-release-assets) can be used in combination with [permanent link to the latest release](#permanent-link-to-latest-release). It is useful when we want to link a permanent URL to download an asset from the *latest release*.
The format of the URL is:
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index 0e6ba536abf..70f8d3a23ce 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -314,7 +314,7 @@ related to the project by selecting the **Disable email notifications** checkbox
Set up your project's merge request settings:
-- Set up the merge request method (merge commit, [fast-forward merge](../merge_requests/fast_forward_merge.md)).
+- Set up the [merge request method](../merge_requests/methods/index.md) (merge commit, fast-forward merge).
- Add merge request [description templates](../description_templates.md#description-templates).
- Enable [merge request approvals](../merge_requests/approvals/index.md).
- Enable [status checks](../merge_requests/status_checks.md).
diff --git a/lib/api/error_tracking/collector.rb b/lib/api/error_tracking/collector.rb
index 22a4e04a91c..29b213eaffb 100644
--- a/lib/api/error_tracking/collector.rb
+++ b/lib/api/error_tracking/collector.rb
@@ -12,7 +12,7 @@ module API
content_type :txt, 'text/plain'
default_format :envelope
- rescue_from ActiveRecord::RecordInvalid do |e|
+ rescue_from Gitlab::ErrorTracking::ErrorRepository::DatabaseError do |e|
render_api_error!(e.message, 400)
end
diff --git a/lib/gitlab/ci/templates/Security/DAST-On-Demand-Scan.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/DAST-On-Demand-Scan.gitlab-ci.yml
index 998425aa141..c71a1b1873a 100644
--- a/lib/gitlab/ci/templates/Security/DAST-On-Demand-Scan.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/DAST-On-Demand-Scan.gitlab-ci.yml
@@ -10,7 +10,7 @@ stages:
- dast
variables:
- DAST_VERSION: 2
+ DAST_VERSION: 3
# Setting this variable will affect all Security templates
# (SAST, Dependency Scanning, ...)
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/security-products"
diff --git a/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml
index e8e7fe62e70..3bc44fe5e1b 100644
--- a/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml
@@ -22,7 +22,7 @@
# List of available variables: https://docs.gitlab.com/ee/user/application_security/dast/#available-variables
variables:
- DAST_VERSION: 2
+ DAST_VERSION: 3
# Setting this variable will affect all Security templates
# (SAST, Dependency Scanning, ...)
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/security-products"
diff --git a/lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml
index c755211ec11..e5ac5099546 100644
--- a/lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Security/DAST.latest.gitlab-ci.yml
@@ -22,7 +22,7 @@
# List of available variables: https://docs.gitlab.com/ee/user/application_security/dast/#available-variables
variables:
- DAST_VERSION: 2
+ DAST_VERSION: 3
# Setting this variable will affect all Security templates
# (SAST, Dependency Scanning, ...)
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/security-products"
diff --git a/lib/gitlab/error_tracking/error_repository.rb b/lib/gitlab/error_tracking/error_repository.rb
new file mode 100644
index 00000000000..4ec636703d9
--- /dev/null
+++ b/lib/gitlab/error_tracking/error_repository.rb
@@ -0,0 +1,113 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module ErrorTracking
+ # Data access layer for errors and events related to Error Tracking feature.
+ class ErrorRepository
+ Pagination = Struct.new(:next, :prev)
+
+ # Generic database error
+ DatabaseError = Class.new(StandardError)
+ # Record was invalid
+ RecordInvalidError = Class.new(DatabaseError)
+
+ # Builds an instance of error repository backed by a strategy.
+ #
+ # @return [self]
+ def self.build(project)
+ strategy = ActiveRecordStrategy.new(project)
+
+ new(strategy)
+ end
+
+ # @private
+ def initialize(strategy)
+ @strategy = strategy
+ end
+
+ # Stores an error and the related error event.
+ #
+ # @param name [String] name of the error
+ # @param description [String] description of the error
+ # @param actor [String] culprit (class/method/function) which triggered this error
+ # @param platform [String] platform on which the error occurred
+ # @param environment [String] environment on which the error occurred
+ # @param level [String] severity of this error
+ # @param occurred_at [Time] timestamp when the error occurred
+ # @param payload [Hash] original error payload
+ #
+ # @return [void] nothing
+ #
+ # @raise [RecordInvalidError] if passed attributes were invalid to store an error or error event
+ # @raise [DatabaseError] if generic error occurred
+ def report_error(
+ name:, description:, actor:, platform:,
+ environment:, level:, occurred_at: Time.zone.now, payload: {}
+ )
+ strategy.report_error(
+ name: name,
+ description: description,
+ actor: actor,
+ platform: platform,
+ environment: environment,
+ level: level,
+ occurred_at: occurred_at,
+ payload: payload
+ )
+
+ nil
+ end
+
+ # Finds an error by +id+.
+ #
+ # @param id [Integer, String] unique error identifier
+ #
+ # @return [Gitlab::ErrorTracking::DetailedError] a detail error
+ def find_error(id)
+ strategy.find_error(id)
+ end
+
+ # Lists errors.
+ #
+ # @param sort [String] order list by 'first_seen', 'last_seen', or 'frequency'
+ # @param filters [Hash<Symbol, String>] filter list by
+ # @option filters [String] :status error status
+ # @param limit [Integer, String] limit result
+ # @param cursor [Hash] pagination information
+ #
+ # @return [Array<Array<Gitlab::ErrorTracking::Error>, Pagination>]
+ def list_errors(sort: 'last_seen', filters: {}, limit: 20, cursor: {})
+ limit = [limit.to_i, 100].min
+
+ strategy.list_errors(filters: filters, sort: sort, limit: limit, cursor: cursor)
+ end
+
+ # Fetches last event for error +id+.
+ #
+ # @param id [Integer, String] unique error identifier
+ #
+ # @return [Gitlab::ErrorTracking::ErrorEvent]
+ #
+ # @raise [DatabaseError] if generic error occurred
+ def last_event_for(id)
+ strategy.last_event_for(id)
+ end
+
+ # Updates attributes of an error.
+ #
+ # @param id [Integer, String] unique error identifier
+ # @param status [String] error status
+ #
+ # @return [true, false] if update was successful
+ #
+ # @raise [DatabaseError] if generic error occurred
+ def update_error(id, status:)
+ strategy.update_error(id, status: status)
+ end
+
+ private
+
+ attr_reader :strategy
+ end
+ end
+end
diff --git a/lib/gitlab/error_tracking/error_repository/active_record_strategy.rb b/lib/gitlab/error_tracking/error_repository/active_record_strategy.rb
new file mode 100644
index 00000000000..e5b532ee0f0
--- /dev/null
+++ b/lib/gitlab/error_tracking/error_repository/active_record_strategy.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module ErrorTracking
+ class ErrorRepository
+ class ActiveRecordStrategy
+ def initialize(project)
+ @project = project
+ end
+
+ def report_error(
+ name:, description:, actor:, platform:,
+ environment:, level:, occurred_at:, payload:
+ )
+ error = project_errors.report_error(
+ name: name, # Example: ActionView::MissingTemplate
+ description: description, # Example: Missing template posts/show in...
+ actor: actor, # Example: PostsController#show
+ platform: platform, # Example: ruby
+ timestamp: occurred_at
+ )
+
+ # The payload field contains all the data on error including stacktrace in jsonb.
+ # Together with occurred_at these are 2 main attributes that we need to save here.
+ error.events.create!(
+ environment: environment,
+ description: description,
+ level: level,
+ occurred_at: occurred_at,
+ payload: payload
+ )
+ rescue ActiveRecord::ActiveRecordError => e
+ handle_exceptions(e)
+ end
+
+ def find_error(id)
+ project_error(id).to_sentry_detailed_error
+ rescue ActiveRecord::ActiveRecordError => e
+ handle_exceptions(e)
+ end
+
+ def list_errors(filters:, sort:, limit:, cursor:)
+ errors = project_errors
+ errors = filter_by_status(errors, filters[:status])
+ errors = sort(errors, sort)
+ errors = errors.keyset_paginate(cursor: cursor, per_page: limit)
+
+ pagination = ErrorRepository::Pagination.new(errors.cursor_for_next_page, errors.cursor_for_previous_page)
+
+ [errors.map(&:to_sentry_error), pagination]
+ end
+
+ def last_event_for(id)
+ project_error(id).last_event&.to_sentry_error_event
+ rescue ActiveRecord::ActiveRecordError => e
+ handle_exceptions(e)
+ end
+
+ def update_error(id, **attributes)
+ project_error(id).update(attributes)
+ end
+
+ private
+
+ attr_reader :project
+
+ def project_errors
+ ::ErrorTracking::Error.where(project: project) # rubocop:disable CodeReuse/ActiveRecord
+ end
+
+ def project_error(id)
+ project_errors.find(id)
+ end
+
+ def filter_by_status(errors, status)
+ return errors unless ::ErrorTracking::Error.statuses.key?(status)
+
+ errors.for_status(status)
+ end
+
+ def sort(errors, sort)
+ return errors.order_id_desc unless sort
+
+ errors.sort_by_attribute(sort)
+ end
+
+ def handle_exceptions(exception)
+ case exception
+ when ActiveRecord::RecordInvalid
+ raise RecordInvalidError, exception.message
+ else
+ raise DatabaseError, exception.message
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/github_import/importer/issue_importer.rb b/lib/gitlab/github_import/importer/issue_importer.rb
index 17f2ea91cb9..7f46615f17e 100644
--- a/lib/gitlab/github_import/importer/issue_importer.rb
+++ b/lib/gitlab/github_import/importer/issue_importer.rb
@@ -54,8 +54,7 @@ module Gitlab
milestone_id: milestone_finder.id_for(issue),
state_id: ::Issue.available_states[issue.state],
created_at: issue.created_at,
- updated_at: issue.updated_at,
- work_item_type_id: ::WorkItems::Type.default_issue_type.id
+ updated_at: issue.updated_at
}
insert_and_return_id(attributes, project.issues)
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 0c54855ebf7..aba8e1415e3 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -17837,7 +17837,7 @@ msgstr ""
msgid "GroupSAML|Generate a SCIM token to set up your System for Cross-Domain Identity Management."
msgstr ""
-msgid "GroupSAML|Identity"
+msgid "GroupSAML|Identifier"
msgstr ""
msgid "GroupSAML|Identity provider single sign-on URL"
@@ -24939,12 +24939,6 @@ msgstr ""
msgid "NetworkPolicies|Kubernetes error: %{error}"
msgstr ""
-msgid "NetworkPolicies|Policy %{policyName} was successfully changed"
-msgstr ""
-
-msgid "NetworkPolicies|Something went wrong, failed to update policy"
-msgstr ""
-
msgid "NetworkPolicy|Policy"
msgstr ""
@@ -29590,6 +29584,9 @@ msgstr ""
msgid "ProjectSettings|Housekeeping, export, archive, change path, transfer, and delete."
msgstr ""
+msgid "ProjectSettings|How do they differ?"
+msgstr ""
+
msgid "ProjectSettings|If merge trains are enabled, merging is only possible if the branch can be rebased without conflicts."
msgstr ""
@@ -29605,9 +29602,6 @@ msgstr ""
msgid "ProjectSettings|LFS objects from this repository are available to forks. %{linkStart}How do I remove them?%{linkEnd}"
msgstr ""
-msgid "ProjectSettings|Learn about commit history."
-msgstr ""
-
msgid "ProjectSettings|Leave empty to use default template."
msgstr ""
diff --git a/rubocop/cop/database/multiple_databases.rb b/rubocop/cop/database/multiple_databases.rb
index 470c282f60f..1ac9bb4473b 100644
--- a/rubocop/cop/database/multiple_databases.rb
+++ b/rubocop/cop/database/multiple_databases.rb
@@ -19,6 +19,7 @@ module RuboCop
ALLOWED_METHODS = %i[
no_touching
+ configurations
].freeze
def_node_matcher :active_record_base_method_is_used?, <<~PATTERN
diff --git a/spec/factories/namespace_ci_cd_settings.rb b/spec/factories/namespace_ci_cd_settings.rb
new file mode 100644
index 00000000000..0e58a19ee8d
--- /dev/null
+++ b/spec/factories/namespace_ci_cd_settings.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :namespace_ci_cd_settings, class: 'NamespaceCiCdSetting' do
+ namespace
+ end
+end
diff --git a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
index 23b03e33f5d..03f9f6ef565 100644
--- a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
+++ b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
@@ -61,38 +61,6 @@ RSpec.describe 'Merge request < User sees mini pipeline graph', :js do
wait_for_requests
end
- # Status icon button styles should update as described in
- # https://gitlab.com/gitlab-org/gitlab-foss/issues/42769
- it 'has unique styles for default, :hover, :active, and :focus states' do
- default_background_color, default_foreground_color, default_box_shadow = get_toggle_colors(dropdown_selector)
-
- toggle.hover
- hover_background_color, hover_foreground_color, hover_box_shadow = get_toggle_colors(dropdown_selector)
-
- page.driver.browser.action.click_and_hold(toggle.native).perform
- active_background_color, active_foreground_color, active_box_shadow = get_toggle_colors(dropdown_selector)
- page.driver.browser.action.release(toggle.native).perform
-
- page.driver.browser.action.click(toggle.native).move_by(100, 100).perform
- focus_background_color, focus_foreground_color, focus_box_shadow = get_toggle_colors(dropdown_selector)
-
- expect(default_background_color).not_to eq(hover_background_color)
- expect(hover_background_color).not_to eq(active_background_color)
- expect(default_background_color).not_to eq(active_background_color)
-
- expect(default_foreground_color).not_to eq(hover_foreground_color)
- expect(hover_foreground_color).not_to eq(active_foreground_color)
- expect(default_foreground_color).not_to eq(active_foreground_color)
-
- expect(focus_background_color).to eq(hover_background_color)
- expect(focus_foreground_color).to eq(hover_foreground_color)
-
- expect(default_box_shadow).to eq('none')
- expect(hover_box_shadow).to eq('none')
- expect(active_box_shadow).not_to eq('none')
- expect(focus_box_shadow).not_to eq('none')
- end
-
it 'shows tooltip when hovered' do
toggle.hover
@@ -147,15 +115,4 @@ RSpec.describe 'Merge request < User sees mini pipeline graph', :js do
end
end
end
-
- private
-
- def get_toggle_colors(selector)
- find(selector)
- [
- evaluate_script("$('#{selector} button:visible').css('background-color');"),
- evaluate_script("$('#{selector} button:visible svg').css('fill');"),
- evaluate_script("$('#{selector} button:visible').css('box-shadow');")
- ]
- end
end
diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
index 57b35d81bb8..e472cff38ce 100644
--- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb
+++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
@@ -29,7 +29,7 @@ RSpec.describe 'Mini Pipeline Graph in Commit View', :js do
it 'displays a mini pipeline graph' do
expect(page).to have_selector('[data-testid="commit-box-mini-graph"]')
- first('.mini-pipeline-graph-dropdown-toggle').click
+ first('[data-testid="mini-pipeline-graph-dropdown"]').click
wait_for_requests
diff --git a/spec/finders/error_tracking/errors_finder_spec.rb b/spec/finders/error_tracking/errors_finder_spec.rb
deleted file mode 100644
index 66eb7769a4c..00000000000
--- a/spec/finders/error_tracking/errors_finder_spec.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-# frozen_string_literal: true
-
-require 'spec_helper'
-
-RSpec.describe ErrorTracking::ErrorsFinder do
- let_it_be(:project) { create(:project) }
- let_it_be(:user) { project.creator }
- let_it_be(:error) { create(:error_tracking_error, project: project) }
- let_it_be(:error_resolved) { create(:error_tracking_error, :resolved, project: project, first_seen_at: 2.hours.ago) }
- let_it_be(:error_yesterday) { create(:error_tracking_error, project: project, first_seen_at: Time.zone.now.yesterday) }
-
- before do
- project.add_maintainer(user)
- end
-
- describe '#execute' do
- let(:params) { {} }
-
- subject { described_class.new(user, project, params).execute }
-
- it { is_expected.to contain_exactly(error, error_resolved, error_yesterday) }
-
- context 'with status parameter' do
- let(:params) { { status: 'resolved' } }
-
- it { is_expected.to contain_exactly(error_resolved) }
- end
-
- context 'with sort parameter' do
- let(:params) { { status: 'unresolved', sort: 'first_seen' } }
-
- it { expect(subject.to_a).to eq([error, error_yesterday]) }
- end
-
- context 'pagination' do
- let(:params) { { limit: '1', sort: 'first_seen' } }
-
- # Sort by first_seen is DESC by default, so the most recent error is `error`
- it { is_expected.to contain_exactly(error) }
-
- it { expect(subject.has_next_page?).to be_truthy }
-
- it 'returns next page by cursor' do
- params_with_cursor = params.merge(cursor: subject.cursor_for_next_page)
- errors = described_class.new(user, project, params_with_cursor).execute
-
- expect(errors).to contain_exactly(error_resolved)
- expect(errors.has_next_page?).to be_truthy
- expect(errors.has_previous_page?).to be_truthy
- end
- end
- end
-end
diff --git a/spec/frontend/__helpers__/init_vue_mr_page_helper.js b/spec/frontend/__helpers__/init_vue_mr_page_helper.js
index ee01e9e6268..6b719a32480 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.setAttribute('data-mr-action', 'diffs');
+ mrEl.dataset.mrAction = 'diffs';
mrTestEl.appendChild(mrEl);
const mrDiscussionsEl = document.createElement('div');
mrDiscussionsEl.id = 'js-vue-mr-discussions';
- 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');
+ 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';
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.setAttribute('data-endpoint', diffsAppEndpoint);
- diffsAppEl.setAttribute('data-project-path', diffsAppProjectPath);
- diffsAppEl.setAttribute('data-current-user-data', JSON.stringify(userDataMock));
+ diffsAppEl.dataset.endpoint = diffsAppEndpoint;
+ diffsAppEl.dataset.projectPath = diffsAppProjectPath;
+ diffsAppEl.dataset.currentUserData = 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 bce9d93bea8..45b9c31c4db 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.getAttribute('data-testid') === `${iconName}-icon`,
+ (reference) => reference.parentNode.dataset.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 1d8ac7cec25..5f0a7dfc775 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.setAttribute('data-js-name', fieldName);
+ input.dataset.jsName = 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 06dbadd6d3d..961fa96acdd 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.setAttribute('data-users', JSON.stringify(users));
- el.setAttribute('data-paths', JSON.stringify(paths));
+ el.dataset.users = JSON.stringify(users);
+ el.dataset.paths = JSON.stringify(paths);
wrapper = createWrapper(initAdminUsersApp(el));
});
@@ -40,8 +40,8 @@ describe('initAdminUserActions', () => {
beforeEach(() => {
el = document.createElement('div');
- el.setAttribute('data-user', JSON.stringify(user));
- el.setAttribute('data-paths', JSON.stringify(paths));
+ el.dataset.user = JSON.stringify(user);
+ el.dataset.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 0ff9d60f409..f9a6b2df662 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.setAttribute('data-codes', codesJsonString);
- el.setAttribute('data-profile-account-path', profileAccountPath);
+ el.dataset.codes = codesJsonString;
+ el.dataset.profileAccountPath = 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 ade35d39b4f..abb69a0be48 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').setAttribute('data-loaded', loaded);
+ document.querySelector('.blob-viewer').dataset.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').setAttribute('data-type', 'simple');
+ document.querySelector('.blob-viewer').dataset.type = 'simple';
await setLoaded(true);
diff --git a/spec/frontend/blob/viewer/index_spec.js b/spec/frontend/blob/viewer/index_spec.js
index fe55a537b89..94422746fa7 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"]').getAttribute('data-loaded'),
- ).toBe('true');
+ expect(document.querySelector('.blob-viewer[data-type="simple"]').dataset.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 585e6ac505b..182e3c1c8ff 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.setAttribute('data-popover-data', JSON.stringify(popoverData));
+ popoverMountEl.dataset.popoverData = JSON.stringify(popoverData);
} else if (lockedByAncestor) {
- popoverMountEl.setAttribute(
- 'data-popover-data',
- JSON.stringify({ ...popoverData, ancestor_namespace: mockNamespace }),
- );
+ popoverMountEl.dataset.popoverData = 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 c26416aca94..a734fd44403 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.setAttribute('data-line-index', '0');
- target.setAttribute('data-char-index', '0');
+ target.dataset.lineIndex = '0';
+ target.dataset.charIndex = '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.setAttribute('data-line-index', '0');
- target.setAttribute('data-char-index', '0');
+ target.dataset.lineIndex = '0';
+ target.dataset.charIndex = '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.setAttribute('data-line-index', '0');
- target.setAttribute('data-char-index', '0');
+ target.dataset.lineIndex = '0';
+ target.dataset.charIndex = '0';
return testAction(
actions.showDefinition,
diff --git a/spec/frontend/confirm_modal_spec.js b/spec/frontend/confirm_modal_spec.js
index 53991349ee5..4224fb6be2a 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.setAttribute('data-path', x.path);
- button.setAttribute('data-method', x.method);
- button.setAttribute('data-modal-attributes', JSON.stringify(x.modalAttributes));
+ button.dataset.path = x.path;
+ button.dataset.method = x.method;
+ button.dataset.modalAttributes = 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 703bdbd342f..dca9faecea7 100644
--- a/spec/frontend/helpers/startup_css_helper_spec.js
+++ b/spec/frontend/helpers/startup_css_helper_spec.js
@@ -56,9 +56,10 @@ describe('waitForCSSLoaded', () => {
<link href="two.css" data-startupcss="loading">
`);
const events = waitForCSSLoaded(mockedCallback);
- document
- .querySelectorAll('[data-startupcss="loading"]')
- .forEach((elem) => elem.setAttribute('data-startupcss', 'loaded'));
+ document.querySelectorAll('[data-startupcss="loading"]').forEach((elem) => {
+ // eslint-disable-next-line no-param-reassign
+ elem.dataset.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 20b26f5abba..cb7173c56a8 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').setAttribute('data-is-confidential', 'true');
+ document.querySelector('.js-create-mr').dataset.isConfidential = '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').setAttribute('data-is-confidential', 'true');
+ document.querySelector('.js-create-mr').dataset.isConfidential = 'true';
dropdown.enable();
diff --git a/spec/frontend/labels/delete_label_modal_spec.js b/spec/frontend/labels/delete_label_modal_spec.js
index 98049538948..67220821fe0 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.setAttribute('data-label-name', x.labelName);
- button.setAttribute('data-destroy-path', x.destroyPath);
+ button.dataset.labelName = x.labelName;
+ button.dataset.destroyPath = x.destroyPath;
if (x.subjectName) {
- button.setAttribute('data-subject-name', x.subjectName);
+ button.dataset.subjectName = x.subjectName;
}
button.innerHTML = 'Action';
diff --git a/spec/frontend/lazy_loader_spec.js b/spec/frontend/lazy_loader_spec.js
index 3d8b0d9c307..e0b6c7119f9 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.setAttribute('data-src', TEST_PATH);
+ newImg.dataset.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.getAttribute('data-src')).toBe(null);
+ expect(img.dataset.src).toBeUndefined();
expect(img).toHaveClass('js-lazy-loaded');
});
diff --git a/spec/frontend/lib/utils/datetime_utility_spec.js b/spec/frontend/lib/utils/datetime_utility_spec.js
index 7a64b654baa..9459878ded0 100644
--- a/spec/frontend/lib/utils/datetime_utility_spec.js
+++ b/spec/frontend/lib/utils/datetime_utility_spec.js
@@ -308,7 +308,9 @@ describe('datefix', () => {
});
describe('parsePikadayDate', () => {
- // removed because of https://gitlab.com/gitlab-org/gitlab-foss/issues/39834
+ it('should return a UTC date', () => {
+ expect(datetimeUtility.parsePikadayDate('2020-01-29')).toEqual(new Date('2020-01-29'));
+ });
});
describe('pikadayToString', () => {
diff --git a/spec/frontend/members/index_spec.js b/spec/frontend/members/index_spec.js
index efabe54f238..251a8b0b774 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.setAttribute('data-members-data', dataAttribute);
+ el.dataset.membersData = dataAttribute;
window.gon = { current_user_id: 123 };
});
diff --git a/spec/frontend/members/utils_spec.js b/spec/frontend/members/utils_spec.js
index a157cfa1c1d..b0c9459ff4f 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.setAttribute('data-members-data', dataAttribute);
+ el.dataset.membersData = dataAttribute;
});
afterEach(() => {
diff --git a/spec/frontend/notebook/cells/markdown_spec.js b/spec/frontend/notebook/cells/markdown_spec.js
index 7dc6f90d202..de415b5bfe0 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().getAttribute('data-remote')).toBe(null);
- expect(findLink().getAttribute('data-type')).toBe(null);
+ expect(findLink().dataset.remote).toBeUndefined();
+ expect(findLink().dataset.type).toBeUndefined();
});
describe('When parsing images', () => {
diff --git a/spec/frontend/notes/stores/actions_spec.js b/spec/frontend/notes/stores/actions_spec.js
index 75e7756cd6b..b18acd591ed 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.setAttribute('data-page', '');
+ document.body.dataset.page = '';
});
afterEach(() => {
axiosMock.restore();
- document.body.setAttribute('data-page', '');
+ document.body.dataset.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.setAttribute('data-page', 'projects:merge_requests:show');
+ document.body.dataset.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.setAttribute('data-page', '');
+ document.body.dataset.page = '';
});
afterEach(() => {
axiosMock.restore();
- document.body.setAttribute('data-page', '');
+ document.body.dataset.page = '';
});
it('dispatches removeNote', () => {
diff --git a/spec/frontend/performance_bar/index_spec.js b/spec/frontend/performance_bar/index_spec.js
index 91cb46002be..af256b1d335 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.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');
+ 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';
mock = new MockAdapter(axios);
diff --git a/spec/frontend/pipelines/components/pipelines_list/pipeline_stage_spec.js b/spec/frontend/pipelines/components/pipelines_list/pipeline_stage_spec.js
index 93bc8faa51b..6d0e99ff63e 100644
--- a/spec/frontend/pipelines/components/pipelines_list/pipeline_stage_spec.js
+++ b/spec/frontend/pipelines/components/pipelines_list/pipeline_stage_spec.js
@@ -1,6 +1,7 @@
import { GlDropdown } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import axios from '~/lib/utils/axios_utils';
import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue';
import eventHub from '~/pipelines/event_hub';
@@ -48,11 +49,12 @@ describe('Pipelines stage component', () => {
mock.restore();
});
+ const findCiActionBtn = () => wrapper.find('.js-ci-action');
+ const findCiIcon = () => wrapper.findComponent(CiIcon);
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findDropdownToggle = () => wrapper.find('button.dropdown-toggle');
const findDropdownMenu = () =>
wrapper.find('[data-testid="mini-pipeline-graph-dropdown-menu-list"]');
- const findCiActionBtn = () => wrapper.find('.js-ci-action');
const findMergeTrainWarning = () => wrapper.find('[data-testid="warning-message-merge-trains"]');
const openStageDropdown = () => {
@@ -74,7 +76,7 @@ describe('Pipelines stage component', () => {
it('should render a dropdown with the status icon', () => {
expect(findDropdown().exists()).toBe(true);
expect(findDropdownToggle().exists()).toBe(true);
- expect(wrapper.find('[data-testid="status_success_borderless-icon"]').exists()).toBe(true);
+ expect(findCiIcon().exists()).toBe(true);
});
});
diff --git a/spec/frontend/search_autocomplete_spec.js b/spec/frontend/search_autocomplete_spec.js
index 190f2803324..48e0aab6561 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').setAttribute('data-issues-disabled', true);
+ document.querySelector('.js-search-project-options').dataset.issuesDisabled = 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 65137330ac3..e703d283540 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.setAttribute('data-user', '1');
+ link.dataset.user = '1';
return link;
};
diff --git a/spec/frontend/users_select/test_helper.js b/spec/frontend/users_select/test_helper.js
index 59edde48eab..9231e38ea90 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.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);
+ input.dataset.avatarUrl = user.avatar_url;
+ input.dataset.name = user.name;
+ input.dataset.username = user.username;
+ input.dataset.canMerge = 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 8efc4d84624..29ee7e0010f 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,9 +193,7 @@ describe('MRWidgetMerged', () => {
it('shows button to copy commit SHA to clipboard', () => {
expect(selectors.copyMergeShaButton).not.toBe(null);
- expect(selectors.copyMergeShaButton.getAttribute('data-clipboard-text')).toBe(
- vm.mr.mergeCommitSha,
- );
+ expect(selectors.copyMergeShaButton.dataset.clipboardText).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 9719e81fe12..1fe32605f09 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.setAttribute('data-original-href', faviconDataUrl);
+ favicon.dataset.originalHref = 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 00ce39a5598..4245e1f04c8 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.getAttribute('data-uri');
+ const { uri } = editor.dataset;
monacoEditor.getModel(uri).setValue(value);
};
export const getEditorValue = async () => {
const editor = await findMonacoEditor();
- const uri = editor.getAttribute('data-uri');
+ const { uri } = editor.dataset;
return monacoEditor.getModel(uri).getValue();
};
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index ebd8375e191..ffe2e71b50f 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -2422,8 +2422,6 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
describe '#backfill_iids' do
include MigrationsHelpers
- let_it_be(:issue_type) { table(:work_item_types).first }
-
let(:issue_class) do
Class.new(ActiveRecord::Base) do
include AtomicInternalId
@@ -2437,8 +2435,6 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
scope: :project,
init: ->(s, _scope) { s&.project&.issues&.maximum(:iid) },
presence: false
-
- before_validation -> { self.work_item_type_id = ::WorkItems::Type.default_issue_type.id }
end
end
@@ -2464,7 +2460,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
it 'generates iids properly for models created after the migration when iids are backfilled' do
project = setup
- issue_a = issues.create!(project_id: project.id, work_item_type_id: issue_type.id)
+ issue_a = issues.create!(project_id: project.id)
model.backfill_iids('issues')
@@ -2477,14 +2473,14 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
it 'generates iids properly for models created after the migration across multiple projects' do
project_a = setup
project_b = setup
- issues.create!(project_id: project_a.id, work_item_type_id: issue_type.id)
- issues.create!(project_id: project_b.id, work_item_type_id: issue_type.id)
- issues.create!(project_id: project_b.id, work_item_type_id: issue_type.id)
+ issues.create!(project_id: project_a.id)
+ issues.create!(project_id: project_b.id)
+ issues.create!(project_id: project_b.id)
model.backfill_iids('issues')
- issue_a = issue_class.create!(project_id: project_a.id, work_item_type_id: issue_type.id)
- issue_b = issue_class.create!(project_id: project_b.id, work_item_type_id: issue_type.id)
+ issue_a = issue_class.create!(project_id: project_a.id)
+ issue_b = issue_class.create!(project_id: project_b.id)
expect(issue_a.iid).to eq(2)
expect(issue_b.iid).to eq(3)
@@ -2494,7 +2490,7 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
it 'generates an iid' do
project_a = setup
project_b = setup
- issue_a = issues.create!(project_id: project_a.id, work_item_type_id: issue_type.id)
+ issue_a = issues.create!(project_id: project_a.id)
model.backfill_iids('issues')
@@ -2508,8 +2504,8 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
context 'when a row already has an iid set in the database' do
it 'backfills iids' do
project = setup
- issue_a = issues.create!(project_id: project.id, work_item_type_id: issue_type.id, iid: 1)
- issue_b = issues.create!(project_id: project.id, work_item_type_id: issue_type.id, iid: 2)
+ issue_a = issues.create!(project_id: project.id, iid: 1)
+ issue_b = issues.create!(project_id: project.id, iid: 2)
model.backfill_iids('issues')
@@ -2520,9 +2516,9 @@ RSpec.describe Gitlab::Database::MigrationHelpers do
it 'backfills for multiple projects' do
project_a = setup
project_b = setup
- issue_a = issues.create!(project_id: project_a.id, work_item_type_id: issue_type.id, iid: 1)
- issue_b = issues.create!(project_id: project_b.id, work_item_type_id: issue_type.id, iid: 1)
- issue_c = issues.create!(project_id: project_a.id, work_item_type_id: issue_type.id, iid: 2)
+ issue_a = issues.create!(project_id: project_a.id, iid: 1)
+ issue_b = issues.create!(project_id: project_b.id, iid: 1)
+ issue_c = issues.create!(project_id: project_a.id, iid: 2)
model.backfill_iids('issues')
diff --git a/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb b/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb
index 38ee87df4be..4287c32b947 100644
--- a/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/issue_importer_spec.rb
@@ -101,8 +101,7 @@ RSpec.describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redi
milestone_id: milestone.id,
state_id: 1,
created_at: created_at,
- updated_at: updated_at,
- work_item_type_id: ::WorkItems::Type.default_issue_type.id
+ updated_at: updated_at
},
project.issues
)
@@ -131,8 +130,7 @@ RSpec.describe Gitlab::GithubImport::Importer::IssueImporter, :clean_gitlab_redi
milestone_id: milestone.id,
state_id: 1,
created_at: created_at,
- updated_at: updated_at,
- work_item_type_id: ::WorkItems::Type.default_issue_type.id
+ updated_at: updated_at
},
project.issues
)
diff --git a/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb b/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
index 81910773dfa..d22bef5bda9 100644
--- a/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
+++ b/spec/lib/gitlab/markdown_cache/active_record/extension_spec.rb
@@ -11,8 +11,6 @@ RSpec.describe Gitlab::MarkdownCache::ActiveRecord::Extension do
attribute :author
attribute :project
-
- before_validation -> { self.work_item_type_id = ::WorkItems::Type.default_issue_type.id }
end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index f95feee47a9..3fe7171728e 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -743,7 +743,7 @@ RSpec.describe Ci::Build do
it { is_expected.to be_falsey }
end
- context 'when there are runners' do
+ context 'when there is a runner' do
let(:runner) { create(:ci_runner, :project, projects: [build.project]) }
before do
@@ -752,19 +752,28 @@ RSpec.describe Ci::Build do
it { is_expected.to be_truthy }
- it 'that is inactive' do
- runner.update!(active: false)
- is_expected.to be_falsey
+ context 'that is inactive' do
+ before do
+ runner.update!(active: false)
+ end
+
+ it { is_expected.to be_falsey }
end
- it 'that is not online' do
- runner.update!(contacted_at: nil)
- is_expected.to be_falsey
+ context 'that is not online' do
+ before do
+ runner.update!(contacted_at: nil)
+ end
+
+ it { is_expected.to be_falsey }
end
- it 'that cannot handle build' do
- expect_any_instance_of(Ci::Runner).to receive(:matches_build?).with(build).and_return(false)
- is_expected.to be_falsey
+ context 'that cannot handle build' do
+ before do
+ expect_any_instance_of(Gitlab::Ci::Matching::RunnerMatcher).to receive(:matches?).with(build.build_matcher).and_return(false)
+ end
+
+ it { is_expected.to be_falsey }
end
end
diff --git a/spec/models/ci/processable_spec.rb b/spec/models/ci/processable_spec.rb
index 49260afc232..cdd96d45561 100644
--- a/spec/models/ci/processable_spec.rb
+++ b/spec/models/ci/processable_spec.rb
@@ -76,7 +76,7 @@ RSpec.describe Ci::Processable do
end
let(:ignore_accessors) do
- %i[type lock_version target_url base_tags trace_sections
+ %i[type namespace lock_version target_url base_tags trace_sections
commit_id deployment erased_by_id project_id
runner_id tag_taggings taggings tags trigger_request_id
user_id auto_canceled_by_id retried failure_reason
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index bf88cc196d8..2983034868c 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -838,7 +838,7 @@ RSpec.describe Ci::Runner do
context 'with legacy_mode enabled' do
let(:legacy_mode) { '14.5' }
- it { is_expected.to eq(:not_connected) }
+ it { is_expected.to eq(:never_contacted) }
end
context 'with legacy_mode disabled' do
@@ -914,7 +914,7 @@ RSpec.describe Ci::Runner do
runner.contacted_at = nil
end
- it { is_expected.to eq(:not_connected) }
+ it { is_expected.to eq(:never_contacted) }
end
context 'contacted 1s ago' do
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index 16d88eeebf2..d46f22b2216 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -9,8 +9,6 @@ RSpec.describe CacheMarkdownField, :clean_gitlab_redis_cache do
include CacheMarkdownField
cache_markdown_field :title, pipeline: :single_line
cache_markdown_field :description
-
- before_validation -> { self.work_item_type_id = ::WorkItems::Type.default_issue_type.id }
end
end
diff --git a/spec/models/concerns/pg_full_text_searchable_spec.rb b/spec/models/concerns/pg_full_text_searchable_spec.rb
index d51de48da15..b6da481024a 100644
--- a/spec/models/concerns/pg_full_text_searchable_spec.rb
+++ b/spec/models/concerns/pg_full_text_searchable_spec.rb
@@ -14,8 +14,6 @@ RSpec.describe PgFullTextSearchable do
belongs_to :project
has_one :search_data, class_name: 'Issues::SearchData'
- before_validation -> { self.work_item_type_id = ::WorkItems::Type.default_issue_type.id }
-
def persist_pg_full_text_search_vector(search_vector)
Issues::SearchData.upsert({ project_id: project_id, issue_id: id, search_vector: search_vector }, unique_by: %i(project_id issue_id))
end
@@ -176,8 +174,6 @@ RSpec.describe PgFullTextSearchable do
belongs_to :project
has_one :search_data, class_name: 'Issues::SearchData'
- before_validation -> { self.work_item_type_id = ::WorkItems::Type.default_issue_type.id }
-
def self.name
'Issue'
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 035ae21e13b..c77c0a5504a 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -258,18 +258,13 @@ RSpec.describe Issue do
end
# TODO: Remove when NOT NULL constraint is added to the relationship
- describe '#work_item_type_with_fallback' do
- let(:issue) { create(:issue, project: reusable_project) }
+ describe '#work_item_type' do
+ let(:issue) { create(:issue, :incident, project: reusable_project, work_item_type: nil) }
- # Hard to test this scenario as no way to create a record in the DB with no work item type, so emulating in memory
it 'returns a default type if the legacy issue does not have a work item type associated yet' do
- expect(issue.work_item_type.base_type).to eq('issue')
- expect(issue.issue_type).to eq('issue')
-
- issue.issue_type = 'incident'
- issue.work_item_type = nil
-
- expect(issue.work_item_type_with_fallback).to eq(WorkItems::Type.default_by_type(:incident))
+ expect(issue.work_item_type_id).to be_nil
+ expect(issue.issue_type).to eq('incident')
+ expect(issue.work_item_type).to eq(WorkItems::Type.default_by_type(:incident))
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 1431a159f1d..7b01c653981 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -139,7 +139,6 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_many(:pipeline_artifacts).dependent(:restrict_with_error) }
it { is_expected.to have_many(:terraform_states).class_name('Terraform::State').inverse_of(:project) }
it { is_expected.to have_many(:timelogs) }
- it { is_expected.to have_many(:error_tracking_errors).class_name('ErrorTracking::Error') }
it { is_expected.to have_many(:error_tracking_client_keys).class_name('ErrorTracking::ClientKey') }
it { is_expected.to have_many(:pending_builds).class_name('Ci::PendingBuild') }
it { is_expected.to have_many(:ci_feature_usages).class_name('Projects::CiFeatureUsage') }
diff --git a/spec/requests/api/ci/runners_spec.rb b/spec/requests/api/ci/runners_spec.rb
index ef6be3d374b..3000bdc2ce7 100644
--- a/spec/requests/api/ci/runners_spec.rb
+++ b/spec/requests/api/ci/runners_spec.rb
@@ -274,7 +274,7 @@ RSpec.describe API::Ci::Runners do
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['description']).to eq(shared_runner.description)
expect(json_response['maximum_timeout']).to be_nil
- expect(json_response['status']).to eq("not_connected")
+ expect(json_response['status']).to eq('never_contacted')
expect(json_response['active']).to eq(true)
expect(json_response['paused']).to eq(false)
end
diff --git a/spec/requests/api/error_tracking/collector_spec.rb b/spec/requests/api/error_tracking/collector_spec.rb
index fa0b238dcad..c0d7eb5460f 100644
--- a/spec/requests/api/error_tracking/collector_spec.rb
+++ b/spec/requests/api/error_tracking/collector_spec.rb
@@ -152,7 +152,17 @@ RSpec.describe API::ErrorTracking::Collector do
context 'collector fails with validation error' do
before do
allow(::ErrorTracking::CollectErrorService)
- .to receive(:new).and_raise(ActiveRecord::RecordInvalid)
+ .to receive(:new).and_raise(Gitlab::ErrorTracking::ErrorRepository::DatabaseError)
+ end
+
+ it_behaves_like 'bad request'
+ end
+
+ context 'with platform field too long' do
+ let(:params) do
+ event = Gitlab::Json.parse(raw_event)
+ event['platform'] = 'a' * 256
+ Gitlab::Json.dump(event)
end
it_behaves_like 'bad request'
diff --git a/spec/requests/api/graphql/ci/runner_spec.rb b/spec/requests/api/graphql/ci/runner_spec.rb
index 542876438cc..16352c4dfec 100644
--- a/spec/requests/api/graphql/ci/runner_spec.rb
+++ b/spec/requests/api/graphql/ci/runner_spec.rb
@@ -237,8 +237,8 @@ RSpec.describe 'Query.runner(id)' do
stale_runner_data = graphql_data_at(:stale_runner)
expect(stale_runner_data).to match a_hash_including(
- 'status' => 'NOT_CONNECTED',
- 'legacyStatusWithExplicitVersion' => 'NOT_CONNECTED',
+ 'status' => 'NEVER_CONTACTED',
+ 'legacyStatusWithExplicitVersion' => 'NEVER_CONTACTED',
'newStatus' => 'STALE'
)
@@ -251,8 +251,8 @@ RSpec.describe 'Query.runner(id)' do
never_contacted_instance_runner_data = graphql_data_at(:never_contacted_instance_runner)
expect(never_contacted_instance_runner_data).to match a_hash_including(
- 'status' => 'NOT_CONNECTED',
- 'legacyStatusWithExplicitVersion' => 'NOT_CONNECTED',
+ 'status' => 'NEVER_CONTACTED',
+ 'legacyStatusWithExplicitVersion' => 'NEVER_CONTACTED',
'newStatus' => 'NEVER_CONTACTED'
)
end
diff --git a/spec/requests/api/graphql/ci/runners_spec.rb b/spec/requests/api/graphql/ci/runners_spec.rb
index 6b88c82b025..d3e94671724 100644
--- a/spec/requests/api/graphql/ci/runners_spec.rb
+++ b/spec/requests/api/graphql/ci/runners_spec.rb
@@ -56,9 +56,9 @@ RSpec.describe 'Query.runners' do
it_behaves_like 'a working graphql query returning expected runner'
end
- context 'runner_type is PROJECT_TYPE and status is NOT_CONNECTED' do
+ context 'runner_type is PROJECT_TYPE and status is NEVER_CONTACTED' do
let(:runner_type) { 'PROJECT_TYPE' }
- let(:status) { 'NOT_CONNECTED' }
+ let(:status) { 'NEVER_CONTACTED' }
let!(:expected_runner) { project_runner }
diff --git a/spec/services/error_tracking/collect_error_service_spec.rb b/spec/services/error_tracking/collect_error_service_spec.rb
index faca3c12a48..159c070c683 100644
--- a/spec/services/error_tracking/collect_error_service_spec.rb
+++ b/spec/services/error_tracking/collect_error_service_spec.rb
@@ -52,12 +52,13 @@ RSpec.describe ErrorTracking::CollectErrorService do
end
context 'with unusual payload' do
- let(:modified_event) { parsed_event }
- let(:event) { described_class.new(project, nil, event: modified_event).execute }
+ let(:event) { ErrorTracking::ErrorEvent.last! }
context 'when transaction is missing' do
it 'builds actor from stacktrace' do
- modified_event.delete('transaction')
+ parsed_event.delete('transaction')
+
+ subject.execute
expect(event.error.actor).to eq 'find()'
end
@@ -65,7 +66,9 @@ RSpec.describe ErrorTracking::CollectErrorService do
context 'when transaction is an empty string' do \
it 'builds actor from stacktrace' do
- modified_event['transaction'] = ''
+ parsed_event['transaction'] = ''
+
+ subject.execute
expect(event.error.actor).to eq 'find()'
end
@@ -73,7 +76,9 @@ RSpec.describe ErrorTracking::CollectErrorService do
context 'when timestamp is numeric' do
it 'parses timestamp' do
- modified_event['timestamp'] = '1631015580.50'
+ parsed_event['timestamp'] = '1631015580.50'
+
+ subject.execute
expect(event.occurred_at).to eq '2021-09-07T11:53:00.5'
end
diff --git a/spec/services/error_tracking/issue_details_service_spec.rb b/spec/services/error_tracking/issue_details_service_spec.rb
index 1eafba08c2f..29f8154a27c 100644
--- a/spec/services/error_tracking/issue_details_service_spec.rb
+++ b/spec/services/error_tracking/issue_details_service_spec.rb
@@ -53,6 +53,18 @@ RSpec.describe ErrorTracking::IssueDetailsService do
expect(result[:status]).to eq(:success)
expect(result[:issue].to_json).to eq(error.to_sentry_detailed_error.to_json)
end
+
+ context 'when error does not exist' do
+ let(:params) { { issue_id: non_existing_record_id } }
+
+ it 'returns the error in detailed format' do
+ expect(result).to match(
+ status: :error,
+ message: /Couldn't find ErrorTracking::Error/,
+ http_status: :bad_request
+ )
+ end
+ end
end
end
diff --git a/spec/services/error_tracking/issue_latest_event_service_spec.rb b/spec/services/error_tracking/issue_latest_event_service_spec.rb
index e17103083eb..aa2430ddffb 100644
--- a/spec/services/error_tracking/issue_latest_event_service_spec.rb
+++ b/spec/services/error_tracking/issue_latest_event_service_spec.rb
@@ -42,6 +42,18 @@ RSpec.describe ErrorTracking::IssueLatestEventService do
expect(result[:status]).to eq(:success)
expect(result[:latest_event].to_json).to eq(event.to_sentry_error_event.to_json)
end
+
+ context 'when error does not exist' do
+ let(:params) { { issue_id: non_existing_record_id } }
+
+ it 'returns the error in detailed format' do
+ expect(result).to match(
+ status: :error,
+ message: /Couldn't find ErrorTracking::Error/,
+ http_status: :bad_request
+ )
+ end
+ end
end
end