summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-02-28 15:11:48 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-02-28 15:11:48 +0000
commitbaed745d21710f1d78ece03558873acd6fd7d358 (patch)
tree628622c816195894985d03ab01f55abe7c6ac7ca
parent22ecb1e3fc02bb923c3e9941b1baa849348a036f (diff)
downloadgitlab-ce-baed745d21710f1d78ece03558873acd6fd7d358.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.checksum17
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/boards/components/board_content_sidebar.vue6
-rw-r--r--app/assets/javascripts/boards/constants.js12
-rw-r--r--app/assets/javascripts/boards/index.js7
-rw-r--r--app/assets/javascripts/boards/stores/actions.js23
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue16
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/router.js20
-rw-r--r--app/assets/javascripts/issues/show/index.js5
-rw-r--r--app/controllers/concerns/renders_notes.rb4
-rw-r--r--app/controllers/profiles/notifications_controller.rb5
-rw-r--r--app/controllers/projects/branches_controller.rb2
-rw-r--r--app/finders/autocomplete/users_finder.rb2
-rw-r--r--app/graphql/resolvers/ci/pipeline_job_artifacts_resolver.rb2
-rw-r--r--app/graphql/types/board_list_type.rb2
-rw-r--r--app/helpers/issuables_helper.rb4
-rw-r--r--app/models/ci/build.rb8
-rw-r--r--app/models/ci/build_metadata.rb4
-rw-r--r--app/models/ci/runner_machine.rb4
-rw-r--r--app/models/ci/runner_machine_build.rb23
-rw-r--r--app/models/concerns/ci/partitionable.rb1
-rw-r--r--app/models/concerns/partitioned_table.rb3
-rw-r--r--app/models/concerns/routable.rb57
-rw-r--r--app/models/draft_note.rb2
-rw-r--r--app/models/group.rb6
-rw-r--r--app/models/members_preloader.rb17
-rw-r--r--app/models/preloaders/commit_status_preloader.rb7
-rw-r--r--app/models/preloaders/labels_preloader.rb17
-rw-r--r--app/models/preloaders/project_policy_preloader.rb5
-rw-r--r--app/models/preloaders/project_root_ancestor_preloader.rb2
-rw-r--r--app/models/project.rb6
-rw-r--r--app/models/resource_label_event.rb5
-rw-r--r--app/presenters/README.md12
-rw-r--r--app/presenters/ci/build_runner_presenter.rb2
-rw-r--r--app/services/ci/register_job_service.rb2
-rw-r--r--app/services/issues/referenced_merge_requests_service.rb7
-rw-r--r--app/workers/all_queues.yml4
-rw-r--r--app/workers/group_destroy_worker.rb5
-rw-r--r--app/workers/project_destroy_worker.rb5
-rw-r--r--config/gitlab_loose_foreign_keys.yml4
-rw-r--r--config/initializers/active_record_preloader.rb11
-rw-r--r--config/initializers/postgres_partitioning.rb3
-rw-r--r--config/metrics/objects_schemas/index_inconsistencies_metric.json19
-rw-r--r--config/metrics/settings/20230203164341_index_inconsistencies_metric.yml25
-rw-r--r--config/routes/issues.rb9
-rw-r--r--db/docs/p_ci_runner_machine_builds.yml4
-rw-r--r--db/migrate/20230217232554_add_state_changed_in_to_vulnerability_state_transitions.rb11
-rw-r--r--db/migrate/20230228092612_add_index_next_over_limit_check_at_asc_order_synchronously.rb17
-rw-r--r--db/post_migrate/20230219191034_add_pipeline_fk_to_vulnerability_state_transitions.rb15
-rw-r--r--db/post_migrate/20230227151608_validate_fk_on_ci_build_trace_metadata_partition_id_and_build_id.rb15
-rw-r--r--db/post_migrate/20230227151609_remove_fk_to_ci_builds_ci_build_trace_metadata_on_build_id.rb35
-rw-r--r--db/schema_migrations/202302172325541
-rw-r--r--db/schema_migrations/202302191910341
-rw-r--r--db/schema_migrations/202302271516081
-rw-r--r--db/schema_migrations/202302271516091
-rw-r--r--db/schema_migrations/202302280926121
-rw-r--r--db/structure.sql10
-rw-r--r--doc/architecture/blueprints/runner_tokens/index.md14
-rw-r--r--doc/development/merge_request_concepts/performance.md2
-rw-r--r--doc/integration/partner_marketplace.md2
-rw-r--r--doc/operations/quickstart-guide.md229
-rw-r--r--doc/user/group/import/index.md20
-rw-r--r--doc/user/profile/personal_access_tokens.md2
-rw-r--r--lib/api/entities/project.rb2
-rw-r--r--lib/gitlab/analytics/cycle_analytics/aggregated/records_fetcher.rb8
-rw-r--r--lib/gitlab/analytics/cycle_analytics/records_fetcher.rb8
-rw-r--r--lib/gitlab/data_builder/pipeline.rb7
-rw-r--r--lib/gitlab/database/partitioning/ci_sliding_list_strategy.rb27
-rw-r--r--lib/gitlab/database/schema_validation/validators/base_validator.rb10
-rw-r--r--lib/gitlab/database/schema_validation/validators/different_definition_indexes.rb (renamed from lib/gitlab/database/schema_validation/validators/wrong_indexes.rb)4
-rw-r--r--lib/gitlab/database/schema_validation/validators/extra_indexes.rb2
-rw-r--r--lib/gitlab/database/schema_validation/validators/missing_indexes.rb2
-rw-r--r--lib/gitlab/usage/metrics/instrumentations/index_inconsistencies_metric.rb46
-rw-r--r--lib/tasks/gitlab/tw/codeowners.rake1
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/controllers/projects/branches_controller_spec.rb1
-rw-r--r--spec/db/schema_spec.rb2
-rw-r--r--spec/factories/ci/runner_machine_builds.rb8
-rw-r--r--spec/features/incidents/incident_timeline_events_spec.rb3
-rw-r--r--spec/features/incidents/user_views_alert_details_spec.rb34
-rw-r--r--spec/frontend/boards/components/board_top_bar_spec.js10
-rw-r--r--spec/frontend/boards/components/boards_selector_spec.js10
-rw-r--r--spec/frontend/boards/stores/actions_spec.js14
-rw-r--r--spec/frontend/issues/show/components/incidents/incident_tabs_spec.js42
-rw-r--r--spec/helpers/issuables_helper_spec.rb7
-rw-r--r--spec/lib/gitlab/database/schema_validation/runner_spec.rb4
-rw-r--r--spec/lib/gitlab/database/schema_validation/validators/base_validator_spec.rb2
-rw-r--r--spec/lib/gitlab/database/schema_validation/validators/different_definition_indexes_spec.rb (renamed from spec/lib/gitlab/database/schema_validation/validators/wrong_indexes_spec.rb)3
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml2
-rw-r--r--spec/lib/gitlab/usage/metrics/instrumentations/index_inconsistencies_metric_spec.rb30
-rw-r--r--spec/models/ci/build_metadata_spec.rb1
-rw-r--r--spec/models/ci/build_spec.rb2
-rw-r--r--spec/models/ci/processable_spec.rb2
-rw-r--r--spec/models/ci/runner_machine_build_spec.rb50
-rw-r--r--spec/models/ci/runner_machine_spec.rb4
-rw-r--r--spec/models/concerns/routable_spec.rb11
-rw-r--r--spec/requests/projects/issues_controller_spec.rb28
-rw-r--r--spec/support/shared_examples/features/incident_details_routing_shared_examples.rb14
-rw-r--r--spec/support/shared_examples/lib/gitlab/database/index_validators_shared_examples.rb7
-rw-r--r--spec/workers/group_destroy_worker_spec.rb23
-rw-r--r--spec/workers/project_destroy_worker_spec.rb25
-rw-r--r--workhorse/go.mod2
-rw-r--r--workhorse/go.sum4
104 files changed, 780 insertions, 474 deletions
diff --git a/Gemfile b/Gemfile
index 3bf40a6aba0..5f12365e395 100644
--- a/Gemfile
+++ b/Gemfile
@@ -518,7 +518,7 @@ gem 'kas-grpc', '~> 0.0.2'
gem 'grpc', '~> 1.42.0'
-gem 'google-protobuf', '~> 3.21', '>= 3.21.12'
+gem 'google-protobuf', '~> 3.22'
gem 'toml-rb', '~> 2.2.0'
diff --git a/Gemfile.checksum b/Gemfile.checksum
index c3dfd696b68..592d11baac2 100644
--- a/Gemfile.checksum
+++ b/Gemfile.checksum
@@ -232,14 +232,15 @@
{"name":"google-cloud-env","version":"1.6.0","platform":"ruby","checksum":"6179acb946975892c7908748df5722a4ebadfc8cf5bb7b0d8d933ca67183fa15"},
{"name":"google-cloud-errors","version":"1.3.0","platform":"ruby","checksum":"450b681e24c089a20721a01acc4408bb4a7b0df28c175aaab488da917480d64b"},
{"name":"google-cloud-storage","version":"1.44.0","platform":"ruby","checksum":"299a1e055c9277c8120f7c10d21d37e4d8c17c7b963350c0e0bff7e9d9a570ea"},
-{"name":"google-protobuf","version":"3.21.12","platform":"java","checksum":"35362ef8abf98ad597dffee588390b8b3b2f0f3d70261c3eed3f99e564f3289d"},
-{"name":"google-protobuf","version":"3.21.12","platform":"ruby","checksum":"4b09bb7e3168cda689efebcd3373304e124b14aabf776fbf1f0a7615259c8fb5"},
-{"name":"google-protobuf","version":"3.21.12","platform":"x64-mingw-ucrt","checksum":"e4444119acd56bf4661b3f38dc2795abae2cd5c2ade88154d5fc405008fbdcf7"},
-{"name":"google-protobuf","version":"3.21.12","platform":"x64-mingw32","checksum":"e6a879e1100f04506aea352d22f70a0ed77899fc64af3ff8c24a242331be923d"},
-{"name":"google-protobuf","version":"3.21.12","platform":"x86-linux","checksum":"54bbacbca58323fab222746df30e60a55df89f699e319ce0774d5bdd637b3a54"},
-{"name":"google-protobuf","version":"3.21.12","platform":"x86-mingw32","checksum":"979e6388dd5f3171043c5a00ac2f66b2789d7fc67b18207d1aabfa1dc27d9558"},
-{"name":"google-protobuf","version":"3.21.12","platform":"x86_64-darwin","checksum":"d7e59bd1040e510fd67fb96d08be84a4e362641f5229bf3fd870e383b2913574"},
-{"name":"google-protobuf","version":"3.21.12","platform":"x86_64-linux","checksum":"cb6820a68c7807e12ca1e6b69689b833d675ed81435a2179d502575ed5db3de0"},
+{"name":"google-protobuf","version":"3.22.0","platform":"arm64-darwin","checksum":"e47680f8cf46d5e0bf573052047276260d785caf7b586719af407767e96e535c"},
+{"name":"google-protobuf","version":"3.22.0","platform":"java","checksum":"24e55a0665113f60af35e27dd4c1fcc9b08d16f9da7605456634e505c95890b3"},
+{"name":"google-protobuf","version":"3.22.0","platform":"ruby","checksum":"58db86f65c686ef4b9389569faa176bada384850751675f0c3736ef76cdcae90"},
+{"name":"google-protobuf","version":"3.22.0","platform":"x64-mingw-ucrt","checksum":"43ce8f98fdfa06a81397577bb502230d545178afd592ef5e7d2daddacdda8eb6"},
+{"name":"google-protobuf","version":"3.22.0","platform":"x64-mingw32","checksum":"8255654f5a4a7fff0d430357fa9fe2f23db16eb17ed1067b46a1aab7fd9fcbab"},
+{"name":"google-protobuf","version":"3.22.0","platform":"x86-linux","checksum":"2794f32ecfbeec0fd7baa4a61292fa220f5aad07b656ba656ecf134b1f7e8425"},
+{"name":"google-protobuf","version":"3.22.0","platform":"x86-mingw32","checksum":"a037a5ed2d0a1faa556466c34f7177fee1f3aee8282c2195aed804fead65c65d"},
+{"name":"google-protobuf","version":"3.22.0","platform":"x86_64-darwin","checksum":"bff2987e4bf1a934a4555aea2020df18b557393b188c849fb9deacec9ed0f3d9"},
+{"name":"google-protobuf","version":"3.22.0","platform":"x86_64-linux","checksum":"a0ea6aa03602e9e4f11c1506a114777204aade2e04adc2051945536c35d88bfe"},
{"name":"googleapis-common-protos-types","version":"1.3.0","platform":"ruby","checksum":"c5411f3197cc3e02547ded1858303b1f830b4dc89c588c142ad6c8a231050671"},
{"name":"googleauth","version":"1.3.0","platform":"ruby","checksum":"51dd7362353cf1e90a2d01e1fb94321ae3926c776d4dc4a79db65230217ffcc2"},
{"name":"gpgme","version":"2.0.22","platform":"ruby","checksum":"7c6904952afdd0bf2c7c3ed6de98a5143f86c6b7390dbcd9d7012bddfa3ec862"},
diff --git a/Gemfile.lock b/Gemfile.lock
index e6e4c005945..c8c2c775198 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -668,7 +668,7 @@ GEM
google-cloud-core (~> 1.6)
googleauth (>= 0.16.2, < 2.a)
mini_mime (~> 1.0)
- google-protobuf (3.21.12)
+ google-protobuf (3.22.0)
googleapis-common-protos-types (1.3.0)
google-protobuf (~> 3.14)
googleauth (1.3.0)
@@ -1699,7 +1699,7 @@ DEPENDENCIES
google-apis-serviceusage_v1 (~> 0.28.0)
google-apis-sqladmin_v1beta4 (~> 0.41.0)
google-cloud-storage (~> 1.44.0)
- google-protobuf (~> 3.21, >= 3.21.12)
+ google-protobuf (~> 3.22)
gpgme (~> 2.0.22)
grape (~> 1.5.2)
grape-entity (~> 0.10.0)
diff --git a/app/assets/javascripts/boards/components/board_content_sidebar.vue b/app/assets/javascripts/boards/components/board_content_sidebar.vue
index 6227f185eda..e928ed6ae0e 100644
--- a/app/assets/javascripts/boards/components/board_content_sidebar.vue
+++ b/app/assets/javascripts/boards/components/board_content_sidebar.vue
@@ -6,9 +6,9 @@ import SidebarDropdownWidget from 'ee_else_ce/sidebar/components/sidebar_dropdow
import { __, sprintf } from '~/locale';
import BoardSidebarTimeTracker from '~/boards/components/sidebar/board_sidebar_time_tracker.vue';
import BoardSidebarTitle from '~/boards/components/sidebar/board_sidebar_title.vue';
-import { BoardType, ISSUABLE, INCIDENT } from '~/boards/constants';
+import { ISSUABLE, INCIDENT } from '~/boards/constants';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { TYPE_ISSUE } from '~/issues/constants';
+import { TYPE_ISSUE, WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants';
import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assignees_widget.vue';
import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue';
@@ -98,7 +98,7 @@ export default {
return this.activeBoardItem?.referencePath?.split('#')[0] || '';
},
parentType() {
- return this.isGroupBoard ? BoardType.group : BoardType.project;
+ return this.isGroupBoard ? WORKSPACE_GROUP : WORKSPACE_PROJECT;
},
createLabelTitle() {
return sprintf(__('Create %{workspace} label'), {
diff --git a/app/assets/javascripts/boards/constants.js b/app/assets/javascripts/boards/constants.js
index 7b7d2493ca5..948834d69e5 100644
--- a/app/assets/javascripts/boards/constants.js
+++ b/app/assets/javascripts/boards/constants.js
@@ -1,5 +1,5 @@
import boardListsQuery from 'ee_else_ce/boards/graphql/board_lists.query.graphql';
-import { TYPE_EPIC, TYPE_ISSUE } from '~/issues/constants';
+import { TYPE_EPIC, TYPE_ISSUE, WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants';
import { s__, __ } from '~/locale';
import updateEpicSubscriptionMutation from '~/sidebar/queries/update_epic_subscription.mutation.graphql';
import updateEpicTitleMutation from '~/sidebar/queries/update_epic_title.mutation.graphql';
@@ -12,11 +12,6 @@ import groupBoardQuery from './graphql/group_board.query.graphql';
import projectBoardQuery from './graphql/project_board.query.graphql';
import listIssuesQuery from './graphql/lists_issues.query.graphql';
-export const BoardType = {
- project: 'project',
- group: 'group',
-};
-
export const ListType = {
assignee: 'assignee',
milestone: 'milestone',
@@ -56,10 +51,10 @@ export const INCIDENT = 'INCIDENT';
export const flashAnimationDuration = 2000;
export const boardQuery = {
- [BoardType.group]: {
+ [WORKSPACE_GROUP]: {
query: groupBoardQuery,
},
- [BoardType.project]: {
+ [WORKSPACE_PROJECT]: {
query: projectBoardQuery,
},
};
@@ -147,7 +142,6 @@ export const MilestoneIDs = {
};
export default {
- BoardType,
ListType,
};
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 4c6f341828c..67388284d31 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -3,9 +3,8 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import BoardApp from '~/boards/components/board_app.vue';
import '~/boards/filters/due_date_filters';
-import { BoardType } from '~/boards/constants';
import store from '~/boards/stores';
-import { TYPE_ISSUE } from '~/issues/constants';
+import { TYPE_ISSUE, WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants';
import {
NavigationType,
isLoggedIn,
@@ -68,8 +67,8 @@ function mountBoardApp(el) {
initialFilterParams,
boardBaseUrl: el.dataset.boardBaseUrl,
boardType,
- isGroupBoard: boardType === BoardType.group,
- isProjectBoard: boardType === BoardType.project,
+ isGroupBoard: boardType === WORKSPACE_GROUP,
+ isProjectBoard: boardType === WORKSPACE_PROJECT,
currentUserId: gon.current_user_id || null,
boardWeight: el.dataset.boardWeight ? parseInt(el.dataset.boardWeight, 10) : null,
labelsManagePath: el.dataset.labelsManagePath,
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index c210a419771..a144054d680 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -1,7 +1,6 @@
import * as Sentry from '@sentry/browser';
import { sortBy } from 'lodash';
import {
- BoardType,
ListType,
inactiveId,
flashAnimationDuration,
@@ -34,7 +33,7 @@ import totalCountAndWeightQuery from 'ee_else_ce/boards/graphql/board_lists_defe
import { fetchPolicies } from '~/lib/graphql';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { defaultClient as gqlClient } from '~/graphql_shared/issuable_client';
-import { TYPE_ISSUE } from '~/issues/constants';
+import { TYPE_ISSUE, WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { queryToObject } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
@@ -61,7 +60,7 @@ export default {
return gqlClient
.query({
- query: boardType === BoardType.group ? groupBoardQuery : projectBoardQuery,
+ query: boardType === WORKSPACE_GROUP ? groupBoardQuery : projectBoardQuery,
variables,
})
.then(({ data }) => {
@@ -139,8 +138,8 @@ export default {
boardId: fullBoardId,
filters: filterParams,
...(issuableType === TYPE_ISSUE && {
- isGroup: boardType === BoardType.group,
- isProject: boardType === BoardType.project,
+ isGroup: boardType === WORKSPACE_GROUP,
+ isProject: boardType === WORKSPACE_PROJECT,
}),
};
@@ -234,8 +233,8 @@ export default {
const variables = {
fullPath,
searchTerm,
- isGroup: boardType === BoardType.group,
- isProject: boardType === BoardType.project,
+ isGroup: boardType === WORKSPACE_GROUP,
+ isProject: boardType === WORKSPACE_PROJECT,
};
commit(types.RECEIVE_LABELS_REQUEST);
@@ -268,10 +267,10 @@ export default {
};
let query;
- if (boardType === BoardType.project) {
+ if (boardType === WORKSPACE_PROJECT) {
query = projectBoardMilestonesQuery;
}
- if (boardType === BoardType.group) {
+ if (boardType === WORKSPACE_GROUP) {
query = groupBoardMilestonesQuery;
}
@@ -431,8 +430,8 @@ export default {
boardId: fullBoardId,
id: listId,
filters: filterParams,
- isGroup: boardType === BoardType.group,
- isProject: boardType === BoardType.project,
+ isGroup: boardType === WORKSPACE_GROUP,
+ isProject: boardType === WORKSPACE_PROJECT,
first: DEFAULT_BOARD_LIST_ITEMS_SIZE,
after: fetchNext ? state.pageInfoByListId[listId].endCursor : undefined,
};
@@ -710,7 +709,7 @@ export default {
) => {
const input = formatIssueInput(issueInput, boardConfig);
- if (boardType === BoardType.project) {
+ if (boardType === WORKSPACE_PROJECT) {
input.projectPath = fullPath;
}
diff --git a/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue b/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue
index 755287bf8a1..1c677c0d9e6 100644
--- a/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue
+++ b/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue
@@ -59,13 +59,16 @@ export default {
data() {
return {
alert: null,
- activeTabIndex: 0,
};
},
computed: {
loading() {
return this.$apollo.queries.alert.loading;
},
+ activeTabIndex() {
+ const { tabId } = this.$route.params;
+ return tabId ? this.tabMapping.tabNamesToIndex[tabId] : 0;
+ },
tabMapping() {
const availableTabs = [TAB_NAMES.SUMMARY];
@@ -93,20 +96,25 @@ export default {
return this.activeTabIndex;
},
set(index) {
- this.handleTabChange(index);
- this.activeTabIndex = index;
+ const newPath = `/${this.tabMapping.tabIndexToName[index]}`;
+ // Only push if the new path differs from the old path.
+ if (newPath !== this.$route.path) {
+ this.$router.push(newPath);
+ this.updateJsIssueWidgets(index);
+ }
},
},
},
mounted() {
this.trackPageViews();
+ this.updateJsIssueWidgets(this.activeTabIndex);
},
methods: {
trackPageViews() {
const { category, action } = trackIncidentDetailsViewsOptions;
Tracking.event(category, action);
},
- handleTabChange(tabIndex) {
+ updateJsIssueWidgets(tabIndex) {
/**
* TODO: Implement a solution that does not violate Vue principles in using
* DOM manipulation directly (#361618)
diff --git a/app/assets/javascripts/issues/show/components/incidents/router.js b/app/assets/javascripts/issues/show/components/incidents/router.js
new file mode 100644
index 00000000000..01326f3b5de
--- /dev/null
+++ b/app/assets/javascripts/issues/show/components/incidents/router.js
@@ -0,0 +1,20 @@
+import Vue from 'vue';
+import VueRouter from 'vue-router';
+
+Vue.use(VueRouter);
+
+export default (currentPath, currentTab = null) => {
+ // If navigating directly to a tab, determine the base
+ // path to initialize router, then set the current route.
+ const base = currentPath.replace(new RegExp(`/${currentTab}$`), '');
+
+ const router = new VueRouter({
+ mode: 'history',
+ base,
+ routes: [{ path: '/:tabId', name: 'tab' }],
+ });
+
+ if (currentTab) router.push(`/${currentTab}`);
+
+ return router;
+};
diff --git a/app/assets/javascripts/issues/show/index.js b/app/assets/javascripts/issues/show/index.js
index 52ed8bbc798..e677328cd2e 100644
--- a/app/assets/javascripts/issues/show/index.js
+++ b/app/assets/javascripts/issues/show/index.js
@@ -11,6 +11,7 @@ import IncidentTabs from './components/incidents/incident_tabs.vue';
import SentryErrorStackTrace from './components/sentry_error_stack_trace.vue';
import { issueState } from './constants';
import getIssueStateQuery from './queries/get_issue_state.query.graphql';
+import createRouter from './components/incidents/router';
const bootstrapApollo = (state = {}) => {
return apolloProvider.clients.defaultClient.cache.writeQuery({
@@ -36,6 +37,8 @@ export function initIncidentApp(issueData = {}, store) {
canUpdateTimelineEvent,
iid,
issuableId,
+ currentPath,
+ currentTab,
projectNamespace,
projectPath,
projectId,
@@ -46,12 +49,14 @@ export function initIncidentApp(issueData = {}, store) {
} = issueData;
const fullPath = `${projectNamespace}/${projectPath}`;
+ const router = createRouter(currentPath, currentTab);
return new Vue({
el,
name: 'DescriptionRoot',
apolloProvider,
store,
+ router,
provide: {
issueType: TYPE_INCIDENT,
canCreateIncident,
diff --git a/app/controllers/concerns/renders_notes.rb b/app/controllers/concerns/renders_notes.rb
index f8e3717acee..889d3f0a9d2 100644
--- a/app/controllers/concerns/renders_notes.rb
+++ b/app/controllers/concerns/renders_notes.rb
@@ -24,13 +24,13 @@ module RendersNotes
# rubocop: disable CodeReuse/ActiveRecord
def preload_noteable_for_regular_notes(notes)
- ActiveRecord::Associations::Preloader.new.preload(notes.reject(&:for_commit?), :noteable)
+ ActiveRecord::Associations::Preloader.new(records: notes.reject(&:for_commit?), associations: :noteable).call
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def preload_author_status(notes)
- ActiveRecord::Associations::Preloader.new.preload(notes, { author: :status })
+ ActiveRecord::Associations::Preloader.new(records: notes, associations: { author: :status }).call
end
# rubocop: enable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb
index 9323d266cd5..b663a75f04a 100644
--- a/app/controllers/profiles/notifications_controller.rb
+++ b/app/controllers/profiles/notifications_controller.rb
@@ -43,7 +43,10 @@ class Profiles::NotificationsController < Profiles::ApplicationController
.preload_source_route
projects = project_notifications.map(&:source)
- ActiveRecord::Associations::Preloader.new.preload(projects, { namespace: [:route, :owner], group: [] })
+ ActiveRecord::Associations::Preloader.new(
+ records: projects,
+ associations: { namespace: [:route, :owner], group: [] }
+ ).call
Preloaders::UserMaxAccessLevelInProjectsPreloader.new(projects, current_user).execute
project_notifications.select { |notification| current_user.can?(:read_project, notification.source) }
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index f19f143816f..1e17dd586c7 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -98,7 +98,7 @@ class Projects::BranchesController < Projects::ApplicationController
if success
render json: { name: branch_name, url: project_tree_url(@project, branch_name) }
else
- render json: result[:messsage], status: :unprocessable_entity
+ render json: result[:message], status: :unprocessable_entity
end
end
end
diff --git a/app/finders/autocomplete/users_finder.rb b/app/finders/autocomplete/users_finder.rb
index bb91f84de99..99e68991836 100644
--- a/app/finders/autocomplete/users_finder.rb
+++ b/app/finders/autocomplete/users_finder.rb
@@ -98,7 +98,7 @@ module Autocomplete
# rubocop: disable CodeReuse/ActiveRecord
def preload_associations(items)
- ActiveRecord::Associations::Preloader.new.preload(items, :status)
+ ActiveRecord::Associations::Preloader.new(records: items, associations: :status).call
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/graphql/resolvers/ci/pipeline_job_artifacts_resolver.rb b/app/graphql/resolvers/ci/pipeline_job_artifacts_resolver.rb
index 35d30827561..561c61e3b27 100644
--- a/app/graphql/resolvers/ci/pipeline_job_artifacts_resolver.rb
+++ b/app/graphql/resolvers/ci/pipeline_job_artifacts_resolver.rb
@@ -15,7 +15,7 @@ module Resolvers
def find_job_artifacts
BatchLoader::GraphQL.for(pipeline).batch do |pipelines, loader|
- ActiveRecord::Associations::Preloader.new.preload(pipelines, :job_artifacts) # rubocop: disable CodeReuse/ActiveRecord
+ ActiveRecord::Associations::Preloader.new(records: pipelines, associations: :job_artifacts).call # rubocop: disable CodeReuse/ActiveRecord
pipelines.each { |pl| loader.call(pl, pl.job_artifacts) }
end
diff --git a/app/graphql/types/board_list_type.rb b/app/graphql/types/board_list_type.rb
index 2352a21bd87..20661da8d94 100644
--- a/app/graphql/types/board_list_type.rb
+++ b/app/graphql/types/board_list_type.rb
@@ -55,7 +55,7 @@ module Types
# board lists have a data dependency on label - so we batch load them here
def title
BatchLoader::GraphQL.for(object).batch do |lists, callback|
- ActiveRecord::Associations::Preloader.new.preload(lists, :label) # rubocop: disable CodeReuse/ActiveRecord
+ ActiveRecord::Associations::Preloader.new(records: lists, associations: :label).call # rubocop: disable CodeReuse/ActiveRecord
# all list titles are preloaded at this point
lists.each { |list| callback.call(list, list.title) }
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 46d2d2c42d9..81d083bd082 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -281,7 +281,9 @@ module IssuablesHelper
{
hasLinkedAlerts: issue.alert_management_alerts.any?,
- canUpdateTimelineEvent: can?(current_user, :admin_incident_management_timeline_event, issue)
+ canUpdateTimelineEvent: can?(current_user, :admin_incident_management_timeline_event, issue),
+ currentPath: url_for(safe_params),
+ currentTab: safe_params[:incident_tab]
}
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 0c2332d8012..9749703bb48 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -55,7 +55,9 @@ module Ci
has_one :"job_artifacts_#{key}", -> { where(file_type: value) }, class_name: 'Ci::JobArtifact', foreign_key: :job_id, inverse_of: :job
end
- has_one :runner_machine, through: :metadata, class_name: 'Ci::RunnerMachine'
+ has_one :runner_machine_build, class_name: 'Ci::RunnerMachineBuild', foreign_key: :build_id, inverse_of: :build,
+ autosave: true
+ has_one :runner_machine, through: :runner_machine_build, class_name: 'Ci::RunnerMachine'
has_one :runner_session, class_name: 'Ci::BuildRunnerSession', validate: true, foreign_key: :build_id, inverse_of: :build
has_one :trace_metadata, class_name: 'Ci::BuildTraceMetadata', foreign_key: :build_id, inverse_of: :build
@@ -132,7 +134,7 @@ module Ci
scope :eager_load_job_artifacts, -> { includes(:job_artifacts) }
scope :eager_load_tags, -> { includes(:tags) }
- scope :eager_load_for_archiving_trace, -> { includes(:project, :pending_state) }
+ scope :eager_load_for_archiving_trace, -> { preload(:project, :pending_state) }
scope :eager_load_everything, -> do
includes(
@@ -808,7 +810,7 @@ module Ci
return unless project
return if user&.blocked?
- ActiveRecord::Associations::Preloader.new.preload([self], { runner: :tags })
+ ActiveRecord::Associations::Preloader.new(records: [self], associations: { runner: :tags }).call
project.execute_hooks(build_data.dup, :job_hooks) if project.has_active_hooks?(:job_hooks)
project.execute_integrations(build_data.dup, :job_hooks) if project.has_active_integrations?(:job_hooks)
diff --git a/app/models/ci/build_metadata.rb b/app/models/ci/build_metadata.rb
index b294afd405d..4b2be446fe3 100644
--- a/app/models/ci/build_metadata.rb
+++ b/app/models/ci/build_metadata.rb
@@ -10,15 +10,17 @@ module Ci
include Presentable
include ChronicDurationAttribute
include Gitlab::Utils::StrongMemoize
+ include IgnorableColumns
self.table_name = 'p_ci_builds_metadata'
self.primary_key = 'id'
partitionable scope: :build
+ ignore_column :runner_machine_id, remove_with: '16.0', remove_after: '2023-04-22'
+
belongs_to :build, class_name: 'CommitStatus'
belongs_to :project
- belongs_to :runner_machine, class_name: 'Ci::RunnerMachine'
before_create :set_build_project
diff --git a/app/models/ci/runner_machine.rb b/app/models/ci/runner_machine.rb
index 78f2ad48c82..ac892b3304f 100644
--- a/app/models/ci/runner_machine.rb
+++ b/app/models/ci/runner_machine.rb
@@ -14,8 +14,8 @@ module Ci
belongs_to :runner
- has_many :build_metadata, class_name: 'Ci::BuildMetadata'
- has_many :builds, through: :build_metadata, class_name: 'Ci::Build'
+ has_many :runner_machine_builds, inverse_of: :runner_machine, class_name: 'Ci::RunnerMachineBuild'
+ has_many :builds, through: :runner_machine_builds, class_name: 'Ci::Build'
belongs_to :runner_version, inverse_of: :runner_machines, primary_key: :version, foreign_key: :version,
class_name: 'Ci::RunnerVersion'
diff --git a/app/models/ci/runner_machine_build.rb b/app/models/ci/runner_machine_build.rb
new file mode 100644
index 00000000000..ac2b258557a
--- /dev/null
+++ b/app/models/ci/runner_machine_build.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Ci
+ class RunnerMachineBuild < Ci::ApplicationRecord
+ include Ci::Partitionable
+ include PartitionedTable
+
+ self.table_name = :p_ci_runner_machine_builds
+ self.primary_key = :build_id
+
+ partitionable scope: :build
+ partitioned_by :partition_id,
+ strategy: :ci_sliding_list,
+ next_partition_if: proc { false },
+ detach_partition_if: proc { false }
+
+ belongs_to :build, inverse_of: :runner_machine_build, class_name: 'Ci::Build'
+ belongs_to :runner_machine, inverse_of: :runner_machine_builds, class_name: 'Ci::RunnerMachine'
+
+ validates :build, presence: true
+ validates :runner_machine, presence: true
+ end
+end
diff --git a/app/models/concerns/ci/partitionable.rb b/app/models/concerns/ci/partitionable.rb
index 64f8a04c19f..fa0c2221968 100644
--- a/app/models/concerns/ci/partitionable.rb
+++ b/app/models/concerns/ci/partitionable.rb
@@ -36,6 +36,7 @@ module Ci
Ci::Pipeline
Ci::PendingBuild
Ci::RunningBuild
+ Ci::RunnerMachineBuild
Ci::PipelineVariable
Ci::Sources::Pipeline
Ci::Stage
diff --git a/app/models/concerns/partitioned_table.rb b/app/models/concerns/partitioned_table.rb
index f95f9dd8ad7..c322a736e79 100644
--- a/app/models/concerns/partitioned_table.rb
+++ b/app/models/concerns/partitioned_table.rb
@@ -8,7 +8,8 @@ module PartitionedTable
PARTITIONING_STRATEGIES = {
monthly: Gitlab::Database::Partitioning::MonthlyStrategy,
- sliding_list: Gitlab::Database::Partitioning::SlidingListStrategy
+ sliding_list: Gitlab::Database::Partitioning::SlidingListStrategy,
+ ci_sliding_list: Gitlab::Database::Partitioning::CiSlidingListStrategy
}.freeze
def partitioned_by(partitioning_key, strategy:, **kwargs)
diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb
index 262839a3fa6..d70aad4e9ae 100644
--- a/app/models/concerns/routable.rb
+++ b/app/models/concerns/routable.rb
@@ -99,39 +99,11 @@ module Routable
end
def full_name
- # We have to test for persistence as the cache key uses #updated_at
- return (route&.name || build_full_name) unless persisted? && Feature.enabled?(:cached_route_lookups, self, type: :ops)
-
- # Return the name as-is if the parent is missing
- return name if route.nil? && parent.nil? && name.present?
-
- # If the route is already preloaded, return directly, preventing an extra load
- return route.name if route_loaded? && route.present?
-
- # Similarly, we can allow the build if the parent is loaded
- return build_full_name if parent_loaded?
-
- Gitlab::Cache.fetch_once([cache_key, :full_name]) do
- route&.name || build_full_name
- end
+ full_attribute(:name)
end
def full_path
- # We have to test for persistence as the cache key uses #updated_at
- return (route&.path || build_full_path) unless persisted? && Feature.enabled?(:cached_route_lookups, self, type: :ops)
-
- # Return the path as-is if the parent is missing
- return path if route.nil? && parent.nil? && path.present?
-
- # If the route is already preloaded, return directly, preventing an extra load
- return route.path if route_loaded? && route.present?
-
- # Similarly, we can allow the build if the parent is loaded
- return build_full_path if parent_loaded?
-
- Gitlab::Cache.fetch_once([cache_key, :full_path]) do
- route&.path || build_full_path
- end
+ full_attribute(:path)
end
# Overriden in the Project model
@@ -163,6 +135,31 @@ module Routable
private
+ # rubocop: disable GitlabSecurity/PublicSend
+ def full_attribute(attribute)
+ attribute_from_route_or_self = ->(attribute) do
+ route&.public_send(attribute) || send("build_full_#{attribute}")
+ end
+
+ unless persisted? && Feature.enabled?(:cached_route_lookups, self, type: :ops)
+ return attribute_from_route_or_self.call(attribute)
+ end
+
+ # Return the attribute as-is if the parent is missing
+ return public_send(attribute) if route.nil? && parent.nil? && public_send(attribute).present?
+
+ # If the route is already preloaded, return directly, preventing an extra load
+ return route.public_send(attribute) if route_loaded? && route.present? && route.public_send(attribute)
+
+ # Similarly, we can allow the build if the parent is loaded
+ return send("build_full_#{attribute}") if parent_loaded?
+
+ Gitlab::Cache.fetch_once([cache_key, :"full_#{attribute}"]) do
+ attribute_from_route_or_self.call(attribute)
+ end
+ end
+ # rubocop: enable GitlabSecurity/PublicSend
+
def set_path_errors
route_path_errors = self.errors.delete(:"route.path")
route_path_errors&.each do |msg|
diff --git a/app/models/draft_note.rb b/app/models/draft_note.rb
index 9f7977fce68..ffc04f9bf90 100644
--- a/app/models/draft_note.rb
+++ b/app/models/draft_note.rb
@@ -108,7 +108,7 @@ class DraftNote < ApplicationRecord
end
def self.preload_author(draft_notes)
- ActiveRecord::Associations::Preloader.new.preload(draft_notes, { author: :status })
+ ActiveRecord::Associations::Preloader.new(records: draft_notes, associations: { author: :status }).call
end
def diff_file
diff --git a/app/models/group.rb b/app/models/group.rb
index 38eb11a0d74..d325d1d351a 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -817,8 +817,10 @@ class Group < Namespace
end
def preload_shared_group_links
- preloader = ActiveRecord::Associations::Preloader.new
- preloader.preload(self, shared_with_group_links: [shared_with_group: :route])
+ ActiveRecord::Associations::Preloader.new(
+ records: [self],
+ associations: { shared_with_group_links: [shared_with_group: :route] }
+ ).call
end
def update_shared_runners_setting!(state)
diff --git a/app/models/members_preloader.rb b/app/models/members_preloader.rb
index ba7e4b39989..169ce49b4d3 100644
--- a/app/models/members_preloader.rb
+++ b/app/models/members_preloader.rb
@@ -8,12 +8,17 @@ class MembersPreloader
end
def preload_all
- ActiveRecord::Associations::Preloader.new.preload(members, :user)
- ActiveRecord::Associations::Preloader.new.preload(members, :source)
- ActiveRecord::Associations::Preloader.new.preload(members, :created_by)
- ActiveRecord::Associations::Preloader.new.preload(members, user: :status)
- ActiveRecord::Associations::Preloader.new.preload(members, user: :u2f_registrations)
- ActiveRecord::Associations::Preloader.new.preload(members, user: :webauthn_registrations) if Feature.enabled?(:webauthn)
+ user_associations = [:status, :u2f_registrations]
+ user_associations << :webauthn_registrations if Feature.enabled?(:webauthn)
+
+ ActiveRecord::Associations::Preloader.new(
+ records: members,
+ associations: [
+ :source,
+ :created_by,
+ { user: user_associations }
+ ]
+ ).call
end
end
diff --git a/app/models/preloaders/commit_status_preloader.rb b/app/models/preloaders/commit_status_preloader.rb
index 535dd24ba6b..79c2549e371 100644
--- a/app/models/preloaders/commit_status_preloader.rb
+++ b/app/models/preloaders/commit_status_preloader.rb
@@ -9,10 +9,11 @@ module Preloaders
end
def execute(relations)
- preloader = ActiveRecord::Associations::Preloader.new
-
CLASSES.each do |klass|
- preloader.preload(objects(klass), associations(klass, relations))
+ ActiveRecord::Associations::Preloader.new(
+ records: objects(klass),
+ associations: associations(klass, relations)
+ ).call
end
end
diff --git a/app/models/preloaders/labels_preloader.rb b/app/models/preloaders/labels_preloader.rb
index b6e73c1cd02..2a3175be420 100644
--- a/app/models/preloaders/labels_preloader.rb
+++ b/app/models/preloaders/labels_preloader.rb
@@ -19,11 +19,20 @@ module Preloaders
end
def preload_all
- preloader = ActiveRecord::Associations::Preloader.new
+ ActiveRecord::Associations::Preloader.new(
+ records: labels,
+ associations: { parent_container: :route }
+ ).call
- preloader.preload(labels, parent_container: :route)
- preloader.preload(labels.select { |l| l.is_a? ProjectLabel }, { project: [:project_feature, namespace: :route] })
- preloader.preload(labels.select { |l| l.is_a? GroupLabel }, { group: :route })
+ ActiveRecord::Associations::Preloader.new(
+ records: labels.select { |l| l.is_a? ProjectLabel },
+ associations: { project: [:project_feature, namespace: :route] }
+ ).call
+
+ ActiveRecord::Associations::Preloader.new(
+ records: labels.select { |l| l.is_a? GroupLabel },
+ associations: { group: :route }
+ ).call
labels.each do |label|
label.lazy_subscription(user)
diff --git a/app/models/preloaders/project_policy_preloader.rb b/app/models/preloaders/project_policy_preloader.rb
index fe9db3464c7..e16eabf40a1 100644
--- a/app/models/preloaders/project_policy_preloader.rb
+++ b/app/models/preloaders/project_policy_preloader.rb
@@ -10,7 +10,10 @@ module Preloaders
def execute
return if projects.is_a?(ActiveRecord::NullRelation)
- ActiveRecord::Associations::Preloader.new.preload(projects, { group: :route, namespace: :owner })
+ ActiveRecord::Associations::Preloader.new(
+ records: projects,
+ associations: { group: :route, namespace: :owner }
+ ).call
::Preloaders::UserMaxAccessLevelInProjectsPreloader.new(projects, current_user).execute
end
diff --git a/app/models/preloaders/project_root_ancestor_preloader.rb b/app/models/preloaders/project_root_ancestor_preloader.rb
index 6192f79ce2c..ccb9d2eab98 100644
--- a/app/models/preloaders/project_root_ancestor_preloader.rb
+++ b/app/models/preloaders/project_root_ancestor_preloader.rb
@@ -19,7 +19,7 @@ module Preloaders
root_ancestors_by_id = root_query.group_by(&:source_id)
- ActiveRecord::Associations::Preloader.new.preload(@projects, :namespace)
+ ActiveRecord::Associations::Preloader.new(records: @projects, associations: :namespace).call
@projects.each do |project|
root_ancestor = root_ancestors_by_id[project.id]&.first
project.namespace.root_ancestor = root_ancestor if root_ancestor.present?
diff --git a/app/models/project.rb b/app/models/project.rb
index 20011b073ee..96e3a9e0f8f 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1079,8 +1079,10 @@ class Project < ApplicationRecord
end
def preload_protected_branches
- preloader = ActiveRecord::Associations::Preloader.new
- preloader.preload(self, protected_branches: [:push_access_levels, :merge_access_levels])
+ ActiveRecord::Associations::Preloader.new(
+ records: [self],
+ associations: { protected_branches: [:push_access_levels, :merge_access_levels] }
+ ).call
end
# returns all ancestor-groups upto but excluding the given namespace
diff --git a/app/models/resource_label_event.rb b/app/models/resource_label_event.rb
index efffc1bd6dc..13610d37a74 100644
--- a/app/models/resource_label_event.rb
+++ b/app/models/resource_label_event.rb
@@ -29,9 +29,8 @@ class ResourceLabelEvent < ResourceEvent
labels = events.map(&:label).compact
project_labels, group_labels = labels.partition { |label| label.is_a? ProjectLabel }
- preloader = ActiveRecord::Associations::Preloader.new
- preloader.preload(project_labels, { project: :project_feature })
- preloader.preload(group_labels, :group)
+ ActiveRecord::Associations::Preloader.new(records: project_labels, associations: { project: :project_feature }).call
+ ActiveRecord::Associations::Preloader.new(records: group_labels, associations: :group).call
end
def issuable
diff --git a/app/presenters/README.md b/app/presenters/README.md
index e2461580107..5b600e8f2b2 100644
--- a/app/presenters/README.md
+++ b/app/presenters/README.md
@@ -165,15 +165,15 @@ however, there is a risk that it accidentally overrides important logic.
For example, [this production incident](https://gitlab.com/gitlab-com/gl-infra/production/-/issues/5498)
was caused by [including `ActionView::Helpers::UrlHelper` in a presenter](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/69537/diffs#4b581cff00ef3cc9780efd23682af383de302e7d_3_3).
-The `tag` accesor in `Ci::Build` was accidentally overridden by `ActionView::Helpers::TagHelper#tag`,
-and as a conseuqence, a wrong `tag` value was persited into database.
+The `tag` accessor in `Ci::Build` was accidentally overridden by `ActionView::Helpers::TagHelper#tag`,
+and as a consequence, a wrong `tag` value was persisted into database.
-Starting from GitLab 14.4, we validate the presenters (specifically all of the subclasses of `Gitlab::View::Presenter::Delegated`)
+Starting from GitLab 14.4, we [validate](../../lib/gitlab/utils/delegator_override/validator.rb) the presenters (specifically all of the subclasses of `Gitlab::View::Presenter::Delegated`)
that they do not accidentally override core/backend logic. In such case, a pipeline in merge requests fails with an error message,
here is an example:
```plaintext
-We've detected that a presetner is overriding a specific method(s) on a subject model.
+We've detected that a presenter is overriding a specific method(s) on a subject model.
There is a risk that it accidentally modifies the backend/core logic that leads to production incident.
Please follow https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/presenters/README.md#validate-accidental-overrides
to resolve this error with caution.
@@ -193,7 +193,7 @@ Here are the potential solutions:
### How to use the `Gitlab::Utils::DelegatorOverride` validator
-If a presenter class inhertis from `Gitlab::View::Presenter::Delegated`,
+If a presenter class inherits from `Gitlab::View::Presenter::Delegated`,
you should define what object class is presented:
```ruby
@@ -201,7 +201,7 @@ class WebHookLogPresenter < Gitlab::View::Presenter::Delegated
presents ::WebHookLog, as: :web_hook_log # This defines that the presenter presents `WebHookLog` Active Record model.
```
-These presenters are validated not to accidentaly override the methods in the presented object.
+These presenters are validated not to accidentally override the methods in the presented object.
You can run the validation locally with:
```shell
diff --git a/app/presenters/ci/build_runner_presenter.rb b/app/presenters/ci/build_runner_presenter.rb
index 9a586a1733f..5da0655d21a 100644
--- a/app/presenters/ci/build_runner_presenter.rb
+++ b/app/presenters/ci/build_runner_presenter.rb
@@ -58,7 +58,7 @@ module Ci
# rubocop: disable CodeReuse/ActiveRecord
def all_dependencies
dependencies = super
- ActiveRecord::Associations::Preloader.new.preload(dependencies, :job_artifacts_archive)
+ ActiveRecord::Associations::Preloader.new(records: dependencies, associations: :job_artifacts_archive).call
dependencies
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index 205da2632c2..8db96ea47c2 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -244,7 +244,7 @@ module Ci
def assign_runner!(build, params)
build.runner_id = runner.id
build.runner_session_attributes = params[:session] if params[:session].present?
- build.ensure_metadata.runner_machine = runner_machine if runner_machine
+ build.runner_machine = runner_machine if runner_machine
failure_reason, _ = pre_assign_runner_checks.find { |_, check| check.call(build, params) }
diff --git a/app/services/issues/referenced_merge_requests_service.rb b/app/services/issues/referenced_merge_requests_service.rb
index a69cd324b1e..ff7cf65e757 100644
--- a/app/services/issues/referenced_merge_requests_service.rb
+++ b/app/services/issues/referenced_merge_requests_service.rb
@@ -6,10 +6,11 @@ module Issues
def execute(issue)
referenced = referenced_merge_requests(issue)
closed_by = closed_by_merge_requests(issue)
- preloader = ActiveRecord::Associations::Preloader.new
- preloader.preload(referenced + closed_by,
- head_pipeline: { project: [:route, { namespace: :route }] })
+ ActiveRecord::Associations::Preloader.new(
+ records: referenced + closed_by,
+ associations: { head_pipeline: { project: [:route, { namespace: :route }] } }
+ ).call
[sort_by_iid(referenced), sort_by_iid(closed_by)]
end
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index c660243d336..21946c0e52b 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -2611,7 +2611,7 @@
:urgency: :low
:resource_boundary: :unknown
:weight: 1
- :idempotent: false
+ :idempotent: true
:tags: []
- :name: group_export
:worker_name: GroupExportWorker
@@ -3070,7 +3070,7 @@
:urgency: :low
:resource_boundary: :unknown
:weight: 1
- :idempotent: false
+ :idempotent: true
:tags: []
- :name: project_export
:worker_name: ProjectExportWorker
diff --git a/app/workers/group_destroy_worker.rb b/app/workers/group_destroy_worker.rb
index 92195d3fe16..a116944feb9 100644
--- a/app/workers/group_destroy_worker.rb
+++ b/app/workers/group_destroy_worker.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class GroupDestroyWorker # rubocop:disable Scalability/IdempotentWorker
+class GroupDestroyWorker
include ApplicationWorker
data_consistency :always
@@ -10,6 +10,9 @@ class GroupDestroyWorker # rubocop:disable Scalability/IdempotentWorker
feature_category :subgroups
+ idempotent!
+ deduplicate :until_executed, ttl: 2.hours
+
def perform(group_id, user_id)
begin
group = Group.find(group_id)
diff --git a/app/workers/project_destroy_worker.rb b/app/workers/project_destroy_worker.rb
index 45d0ebd2b65..181eebe56e8 100644
--- a/app/workers/project_destroy_worker.rb
+++ b/app/workers/project_destroy_worker.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-class ProjectDestroyWorker # rubocop:disable Scalability/IdempotentWorker
+class ProjectDestroyWorker
include ApplicationWorker
data_consistency :always
@@ -10,6 +10,9 @@ class ProjectDestroyWorker # rubocop:disable Scalability/IdempotentWorker
feature_category :source_code_management
+ idempotent!
+ deduplicate :until_executed, ttl: 2.hours
+
def perform(project_id, user_id, params)
project = Project.find(project_id)
user = User.find(user_id)
diff --git a/config/gitlab_loose_foreign_keys.yml b/config/gitlab_loose_foreign_keys.yml
index c4be4cc1800..ca1129ef288 100644
--- a/config/gitlab_loose_foreign_keys.yml
+++ b/config/gitlab_loose_foreign_keys.yml
@@ -280,6 +280,10 @@ vulnerability_occurrence_pipelines:
- table: ci_pipelines
column: pipeline_id
on_delete: async_delete
+vulnerability_state_transitions:
+ - table: ci_pipelines
+ column: state_changed_at_pipeline_id
+ on_delete: async_nullify
vulnerability_statistics:
- table: ci_pipelines
column: latest_pipeline_id
diff --git a/config/initializers/active_record_preloader.rb b/config/initializers/active_record_preloader.rb
index 198c97cb849..19ca380a866 100644
--- a/config/initializers/active_record_preloader.rb
+++ b/config/initializers/active_record_preloader.rb
@@ -3,6 +3,17 @@
module ActiveRecord
module Associations
class Preloader
+ def initialize(records: nil, associations: nil)
+ super()
+
+ @records = records
+ @associations = associations
+ end
+
+ def call
+ preload(@records, @associations)
+ end
+
class NullPreloader
def self.new(*args, **kwargs)
self
diff --git a/config/initializers/postgres_partitioning.rb b/config/initializers/postgres_partitioning.rb
index e7f29ee1a84..572c8439ec1 100644
--- a/config/initializers/postgres_partitioning.rb
+++ b/config/initializers/postgres_partitioning.rb
@@ -5,7 +5,8 @@ Gitlab::Database::Partitioning.register_models(
AuditEvent,
WebHookLog,
LooseForeignKeys::DeletedRecord,
- Gitlab::Database::BackgroundMigration::BatchedJobTransitionLog
+ Gitlab::Database::BackgroundMigration::BatchedJobTransitionLog,
+ Ci::RunnerMachineBuild
])
if Gitlab.ee?
diff --git a/config/metrics/objects_schemas/index_inconsistencies_metric.json b/config/metrics/objects_schemas/index_inconsistencies_metric.json
new file mode 100644
index 00000000000..635cfa64d95
--- /dev/null
+++ b/config/metrics/objects_schemas/index_inconsistencies_metric.json
@@ -0,0 +1,19 @@
+{
+ "type": "array",
+ "items": {
+ "type": [
+ {
+ "type": "object",
+ "properties": {
+ "object_name": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string",
+ "description": "Type of index inconsistency"
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/config/metrics/settings/20230203164341_index_inconsistencies_metric.yml b/config/metrics/settings/20230203164341_index_inconsistencies_metric.yml
new file mode 100644
index 00000000000..5076eb9cdd8
--- /dev/null
+++ b/config/metrics/settings/20230203164341_index_inconsistencies_metric.yml
@@ -0,0 +1,25 @@
+---
+key_path: index_inconsistencies
+name: "index_inconsistencies"
+description: "List the index inconsistencies in the database"
+product_section: enablement
+product_stage: enablement
+product_group: database
+product_category: database
+value_type: object
+status: active
+milestone: "15.10"
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/111015
+time_frame: none
+data_source: system
+data_category: optional
+instrumentation_class: IndexInconsistenciesMetric
+performance_indicator_type: []
+distribution:
+- ce
+- ee
+tier:
+- free
+- premium
+- ultimate
+value_json_schema: "config/metrics/objects_schemas/index_inconsistencies_metric.json"
diff --git a/config/routes/issues.rb b/config/routes/issues.rb
index 25e59022272..13fdde5841b 100644
--- a/config/routes/issues.rb
+++ b/config/routes/issues.rb
@@ -14,6 +14,10 @@ resources :issues, concerns: :awardable, constraints: { id: /\d+/ } do
post :create_merge_request
get :discussions, format: :json
get '/designs(/*vueroute)', to: 'issues#designs', as: :designs, format: false
+ get '/:incident_tab',
+ action: :show,
+ as: :incident_issue,
+ constraints: { incident_tab: /timeline|metrics|alerts/ }
end
collection do
@@ -23,9 +27,10 @@ resources :issues, concerns: :awardable, constraints: { id: /\d+/ } do
post :export_csv
scope :incident do
- get '/:id',
+ get '/:id(/:incident_tab)',
to: 'incidents#show',
- as: :incident
+ as: :incident,
+ constraints: { incident_tab: /timeline|metrics|alerts/ }
end
end
diff --git a/db/docs/p_ci_runner_machine_builds.yml b/db/docs/p_ci_runner_machine_builds.yml
index f0e8ed26caf..8ffac67fb94 100644
--- a/db/docs/p_ci_runner_machine_builds.yml
+++ b/db/docs/p_ci_runner_machine_builds.yml
@@ -1,6 +1,6 @@
----
table_name: p_ci_runner_machine_builds
-classes: []
+classes:
+- Ci::RunnerMachineBuild
feature_categories:
- runner_fleet
description: Relationships between builds and runner machines
diff --git a/db/migrate/20230217232554_add_state_changed_in_to_vulnerability_state_transitions.rb b/db/migrate/20230217232554_add_state_changed_in_to_vulnerability_state_transitions.rb
new file mode 100644
index 00000000000..6a8b3b87e21
--- /dev/null
+++ b/db/migrate/20230217232554_add_state_changed_in_to_vulnerability_state_transitions.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddStateChangedInToVulnerabilityStateTransitions < Gitlab::Database::Migration[2.1]
+ def up
+ add_column :vulnerability_state_transitions, :state_changed_at_pipeline_id, :bigint
+ end
+
+ def down
+ remove_column :vulnerability_state_transitions, :state_changed_at_pipeline_id
+ end
+end
diff --git a/db/migrate/20230228092612_add_index_next_over_limit_check_at_asc_order_synchronously.rb b/db/migrate/20230228092612_add_index_next_over_limit_check_at_asc_order_synchronously.rb
new file mode 100644
index 00000000000..a811b67bb43
--- /dev/null
+++ b/db/migrate/20230228092612_add_index_next_over_limit_check_at_asc_order_synchronously.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexNextOverLimitCheckAtAscOrderSynchronously < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ TABLE_NAME = 'namespace_details'
+ INDEX_NAME = 'index_next_over_limit_check_at_asc_order'
+ COLUMN = 'next_over_limit_check_at'
+
+ def up
+ add_concurrent_index TABLE_NAME, COLUMN, name: INDEX_NAME, order: { next_over_limit_check_at: 'ASC NULLS FIRST' }
+ end
+
+ def down
+ remove_concurrent_index_by_name TABLE_NAME, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20230219191034_add_pipeline_fk_to_vulnerability_state_transitions.rb b/db/post_migrate/20230219191034_add_pipeline_fk_to_vulnerability_state_transitions.rb
new file mode 100644
index 00000000000..5c09c75861a
--- /dev/null
+++ b/db/post_migrate/20230219191034_add_pipeline_fk_to_vulnerability_state_transitions.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class AddPipelineFkToVulnerabilityStateTransitions < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_vulnerability_state_transitions_on_pipeline_id'
+
+ def up
+ add_concurrent_index :vulnerability_state_transitions, :state_changed_at_pipeline_id, name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :vulnerability_state_transitions, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20230227151608_validate_fk_on_ci_build_trace_metadata_partition_id_and_build_id.rb b/db/post_migrate/20230227151608_validate_fk_on_ci_build_trace_metadata_partition_id_and_build_id.rb
new file mode 100644
index 00000000000..b95c416c128
--- /dev/null
+++ b/db/post_migrate/20230227151608_validate_fk_on_ci_build_trace_metadata_partition_id_and_build_id.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class ValidateFkOnCiBuildTraceMetadataPartitionIdAndBuildId < Gitlab::Database::Migration[2.1]
+ TABLE_NAME = :ci_build_trace_metadata
+ FK_NAME = :fk_rails_aebc78111f_p
+ COLUMNS = [:partition_id, :build_id]
+
+ def up
+ validate_foreign_key(TABLE_NAME, COLUMNS, name: FK_NAME)
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20230227151609_remove_fk_to_ci_builds_ci_build_trace_metadata_on_build_id.rb b/db/post_migrate/20230227151609_remove_fk_to_ci_builds_ci_build_trace_metadata_on_build_id.rb
new file mode 100644
index 00000000000..16e24d7ed4c
--- /dev/null
+++ b/db/post_migrate/20230227151609_remove_fk_to_ci_builds_ci_build_trace_metadata_on_build_id.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+class RemoveFkToCiBuildsCiBuildTraceMetadataOnBuildId < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ SOURCE_TABLE_NAME = :ci_build_trace_metadata
+ TARGET_TABLE_NAME = :ci_builds
+ COLUMN = :build_id
+ TARGET_COLUMN = :id
+ FK_NAME = :fk_rails_aebc78111f
+
+ def up
+ with_lock_retries do
+ remove_foreign_key_if_exists(
+ SOURCE_TABLE_NAME,
+ TARGET_TABLE_NAME,
+ name: FK_NAME,
+ reverse_lock_order: true
+ )
+ end
+ end
+
+ def down
+ add_concurrent_foreign_key(
+ SOURCE_TABLE_NAME,
+ TARGET_TABLE_NAME,
+ column: COLUMN,
+ target_column: TARGET_COLUMN,
+ validate: true,
+ reverse_lock_order: true,
+ on_delete: :cascade,
+ name: FK_NAME
+ )
+ end
+end
diff --git a/db/schema_migrations/20230217232554 b/db/schema_migrations/20230217232554
new file mode 100644
index 00000000000..501e10db401
--- /dev/null
+++ b/db/schema_migrations/20230217232554
@@ -0,0 +1 @@
+56880a7008d06e9a30337cca7affbe4cdb796b8ef1ccc8b3fc8503af172281cb \ No newline at end of file
diff --git a/db/schema_migrations/20230219191034 b/db/schema_migrations/20230219191034
new file mode 100644
index 00000000000..911869229b3
--- /dev/null
+++ b/db/schema_migrations/20230219191034
@@ -0,0 +1 @@
+f3be6612c3669066d9a805bf56cae7b3f9a1b6bdaee1bdb3e3f9a596ed3cecef \ No newline at end of file
diff --git a/db/schema_migrations/20230227151608 b/db/schema_migrations/20230227151608
new file mode 100644
index 00000000000..333d71b1789
--- /dev/null
+++ b/db/schema_migrations/20230227151608
@@ -0,0 +1 @@
+b43ebf61392e2857bd38f783f1ae46c4adce76a94dd8e7aa64fc02f234991229 \ No newline at end of file
diff --git a/db/schema_migrations/20230227151609 b/db/schema_migrations/20230227151609
new file mode 100644
index 00000000000..ec320e3c256
--- /dev/null
+++ b/db/schema_migrations/20230227151609
@@ -0,0 +1 @@
+c2ac227a2e1a51423b043db9e992a519c096af8a309d3c1074fbd8bd744b4e3b \ No newline at end of file
diff --git a/db/schema_migrations/20230228092612 b/db/schema_migrations/20230228092612
new file mode 100644
index 00000000000..ad52eedb797
--- /dev/null
+++ b/db/schema_migrations/20230228092612
@@ -0,0 +1 @@
+79c17f4dfb9a208057562c8a9898fda4c1fa8819a3b7a1e594037fd8263aba6f \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 19171a90734..5e44f1f9d4e 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -23871,6 +23871,7 @@ CREATE TABLE vulnerability_state_transitions (
author_id bigint,
comment text,
dismissal_reason smallint,
+ state_changed_at_pipeline_id bigint,
CONSTRAINT check_d1ca8ec043 CHECK ((from_state <> to_state)),
CONSTRAINT check_fe2eb6a0f3 CHECK ((char_length(comment) <= 50000))
);
@@ -30973,6 +30974,8 @@ CREATE INDEX index_namespaces_storage_limit_exclusions_on_namespace_id ON namesp
CREATE INDEX index_namespaces_sync_events_on_namespace_id ON namespaces_sync_events USING btree (namespace_id);
+CREATE INDEX index_next_over_limit_check_at_asc_order ON namespace_details USING btree (next_over_limit_check_at NULLS FIRST);
+
CREATE INDEX index_non_requested_project_members_on_source_id_and_type ON members USING btree (source_id, source_type) WHERE ((requested_at IS NULL) AND ((type)::text = 'ProjectMember'::text));
CREATE UNIQUE INDEX index_note_diff_files_on_diff_note_id ON note_diff_files USING btree (diff_note_id);
@@ -32305,6 +32308,8 @@ CREATE INDEX index_vulnerability_state_transitions_id_and_vulnerability_id ON vu
CREATE INDEX index_vulnerability_state_transitions_on_author_id ON vulnerability_state_transitions USING btree (author_id);
+CREATE INDEX index_vulnerability_state_transitions_on_pipeline_id ON vulnerability_state_transitions USING btree (state_changed_at_pipeline_id);
+
CREATE INDEX index_vulnerability_statistics_on_latest_pipeline_id ON vulnerability_statistics USING btree (latest_pipeline_id);
CREATE INDEX index_vulnerability_statistics_on_letter_grade ON vulnerability_statistics USING btree (letter_grade);
@@ -36102,10 +36107,7 @@ ALTER TABLE ONLY metrics_dashboard_annotations
ADD CONSTRAINT fk_rails_aeb11a7643 FOREIGN KEY (environment_id) REFERENCES environments(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_build_trace_metadata
- ADD CONSTRAINT fk_rails_aebc78111f FOREIGN KEY (build_id) REFERENCES ci_builds(id) ON DELETE CASCADE;
-
-ALTER TABLE ONLY ci_build_trace_metadata
- ADD CONSTRAINT fk_rails_aebc78111f_p FOREIGN KEY (partition_id, build_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE NOT VALID;
+ ADD CONSTRAINT fk_rails_aebc78111f_p FOREIGN KEY (partition_id, build_id) REFERENCES ci_builds(partition_id, id) ON UPDATE CASCADE ON DELETE CASCADE;
ALTER TABLE ONLY bulk_import_trackers
ADD CONSTRAINT fk_rails_aed566d3f3 FOREIGN KEY (bulk_import_entity_id) REFERENCES bulk_import_entities(id) ON DELETE CASCADE;
diff --git a/doc/architecture/blueprints/runner_tokens/index.md b/doc/architecture/blueprints/runner_tokens/index.md
index 039c27b64c6..789869cdc16 100644
--- a/doc/architecture/blueprints/runner_tokens/index.md
+++ b/doc/architecture/blueprints/runner_tokens/index.md
@@ -183,14 +183,17 @@ CREATE TABLE ci_runners (
)
```
-The `ci_builds_metadata` table shall reference `ci_runner_machines`.
+A new `p_ci_runner_machine_builds` table joins the `ci_runner_machines` and `ci_builds` tables, to avoid
+adding more pressure to those tables.
We might consider a more efficient way to store `contacted_at` than updating the existing record.
```sql
-CREATE TABLE ci_builds_metadata (
- ...
+CREATE TABLE p_ci_runner_machine_builds (
+ partition_id bigint DEFAULT 100 NOT NULL,
+ build_id bigint NOT NULL,
runner_machine_id bigint NOT NULL
-);
+)
+PARTITION BY LIST (partition_id);
CREATE TABLE ci_runner_machines (
id bigint NOT NULL,
@@ -378,6 +381,9 @@ scope.
| GitLab Rails app | `%15.9` | Rename `ci_runner_machines.machine_xid` column to `system_xid` to be consistent with `system_id` passed in APIs. |
| GitLab Rails app | `%15.10` | Drop `ci_runner_machines.machine_xid` column. |
| GitLab Rails app | `%15.11` | Remove the ignore rule for `ci_runner_machines.machine_xid` column. |
+| GitLab Rails app | `%15.10` | Replace `ci_builds_metadata.runner_machine_id` with a new join table. |
+| GitLab Rails app | %15.11 | Drop `ci_builds_metadata.runner_machine_id` column. |
+| GitLab Rails app | %16.0 | Remove the ignore rule for `ci_builds_metadata.runner_machine_id` column. |
### Stage 4 - Create runners from the UI
diff --git a/doc/development/merge_request_concepts/performance.md b/doc/development/merge_request_concepts/performance.md
index 740b8f1607b..3b2a097ea2d 100644
--- a/doc/development/merge_request_concepts/performance.md
+++ b/doc/development/merge_request_concepts/performance.md
@@ -260,7 +260,7 @@ It re-instantiates project object for each build, instead of using the same in-m
In this particular case the workaround is fairly easy:
```ruby
-ActiveRecord::Associations::Preloader.new.preload(pipeline, [builds: :project])
+ActiveRecord::Associations::Preloader.new(records: pipeline, associations: [builds: :project]).call
pipeline.builds.each do |build|
build.to_json(only: [:name], include: [project: { only: [:name]}])
diff --git a/doc/integration/partner_marketplace.md b/doc/integration/partner_marketplace.md
index 010ce78c9fd..9266fa7ed48 100644
--- a/doc/integration/partner_marketplace.md
+++ b/doc/integration/partner_marketplace.md
@@ -1,6 +1,6 @@
---
stage: Fulfillment
-group: Commerce Integrations
+group: Provision
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
diff --git a/doc/operations/quickstart-guide.md b/doc/operations/quickstart-guide.md
deleted file mode 100644
index b661243b3e0..00000000000
--- a/doc/operations/quickstart-guide.md
+++ /dev/null
@@ -1,229 +0,0 @@
----
-stage: Monitor
-group: Observability
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
----
-
-# GitLab Observability Quickstart
-
-You can try GitLab Observability by [cloning or forking the project](https://gitlab.com/gitlab-org/opstrace/opstrace.git) and creating a local installation.
-
-## Prerequisites and dependencies
-
-To install GitLab Observability Platform (GOP), install and configure the following third-party dependencies. You can do this manually, or [automatically by using asdf](#install-dependencies-using-asdf):
-
-- [kind](https://kind.sigs.k8s.io/docs/user/quick-start/#installation) for creating a local Kubernetes cluster.
-- [Docker](https://docs.docker.com/install)
- - [Docker Compose](https://docs.docker.com/compose/compose-v2/) is now part of the `docker` distribution.
-- [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) for interacting with GitLab Observability.
-- [Telepresence](https://www.telepresence.io/) allows you to code and test microservices locally against a remote Kubernetes cluster.
-- [jq](https://stedolan.github.io/jq/download/) for some Makefile utilities.
-- [Go 1.19](https://go.dev/doc/install).
-
-The current versions of these dependencies are pinned in the `.tool-versions` file in the project.
-
-You can run the following commands to check the availability and versions of these dependencies on your machine:
-
-```shell
-kind --version
-docker --version
-kubectl version
-telepresence version
-jq --version
-go version
-```
-
-### Run GOP on macOS
-
-If you're running GOP on macOS, ensure you have enough resources dedicated to Docker Desktop. The recommended minimum is:
-
-- CPUs: 4+
-- Memory: 8 GB+
-- Swap: 1 GB+
-
-It's possible to run GOP with fewer resources, but this specification works.
-
-### Install dependencies using asdf
-
-If you install dependencies using [`asdf`](https://asdf-vm.com/#/core-manage-asdf), GOP manages them for you automatically.
-
-1. If you have not already done so, clone the `opstrace` repository into your preferred location:
-
- ```shell
- git clone https://gitlab.com/gitlab-org/opstrace/opstrace.git
- ```
-
-1. Change into the project directory:
-
- ```shell
- cd opstrace
- ```
-
-1. Optional. If you need to install `asdf`, run:
-
- ```shell
- make install-asdf
- ```
-
-1. Install dependencies using `asdf`:
-
- ```shell
- make bootstrap
- ```
-
-## Step 1: Create a local Kubernetes cluster with kind
-
-Make sure Docker Desktop is running. In the `opstrace` project you cloned, run the following command:
-
-```shell
-make kind
-```
-
-Wait a few minutes while kind creates your Kubernetes cluster. When it's finished, you should see the following message:
-
-```plaintext
-Traffic Manager installed successfully
-```
-
-Now deploy the scheduler by running the following command in the `opstrace` project:
-
-```shell
-make deploy
-```
-
-This takes around 1 minute.
-
-## Step 2: Create a GitLab application for authentication
-
-You must create a GitLab application to use for authentication.
-
-In the GitLab instance you'd like to connect with GOP, [create an OAuth application](../integration/oauth_provider.md).
-This application can be a user-owned, group-owned or instance-wide application.
-In production, you would create a trusted instance-wide application so that users are explicitly authorized without the consent screen.
-The following example shows how to configure the application.
-
-1. Select the API scope and enter `http://localhost/v1/auth/callback` as the redirect URI.
-
-1. Run the following command to create the secret that holds the authentication data:
-
- ```shell
- kubectl create secret generic \
- --from-literal=gitlab_oauth_client_id=<gitlab_application_client_id> \
- --from-literal=gitlab_oauth_client_secret=<gitlab_application_client_secret> \
- --from-literal=internal_endpoint_token=<error_tracking_internal_endpoint_token> \
- dev-secret
- ```
-
-1. Replace `<gitlab_application_client_id>` and `<gitlab_application_client_secret>` with the values from the GitLab application you just created.
-Replace `<error_tracking_internal_endpoint_token>` with any string if you do not plan to use error tracking.
-
-You can also view [this MR on how to get the token to test error tracking](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91928).
-You must specify all the parameters when creating the secret.
-
-## Step 3: Create the cluster definition
-
-1. In your `opstrace` project, run the following command to create a `Cluster.yaml` manifest file:
-
- ```shell
- cat <<EOF > Cluster.yaml
- apiVersion: opstrace.com/v1alpha1
- kind: Cluster
- metadata:
- name: dev-cluster
- spec:
- target: kind
- goui:
- image: "registry.gitlab.com/gitlab-org/opstrace/opstrace-ui/ gitlab-observability-ui:c9fb6e70"
- dns:
- acmeEmail: ""
- dns01Challenge: {}
- externalDNSProvider: {}
- gitlab:
- groupAllowedAccess: '*'
- groupAllowedSystemAccess: "6543"
- instanceUrl: https://gitlab.com
- authSecret:
- name: dev-secret
- EOF
- ```
-
-1. Apply the file you just created with the following command:
-
- ```shell
- kubectl apply -f Cluster.yaml
- ```
-
-1. Run the following command to wait for the cluster to be ready:
-
- ```shell
- kubectl wait --for=condition=ready cluster/dev-cluster --timeout=600s
- ```
-
-After the previous command exits, the cluster is ready.
-
-## Step 4: Enable Observability on a GitLab namespace you own
-
-Go to a namespace you own in the connected GitLab instance and copy the Group ID below the group name.
-
-GOP can only be enabled for groups you own.
-To list all the groups that your user owns, in the upper-left corner, select **Groups > View all Groups**. You then see the **Your groups** tab.
-
-In your browser, go to `http://localhost/-/{GroupID}`. For example, `http://localhost/-/14485840`.
-
-Follow the on-screen instructions to enable observability for the namespace.
-This can take a couple of minutes if it's the first time observability has been enabled for the root level namespace (GitLab.org in the previous example.)
-
-Once your namespace has been enabled and is ready, the page automatically redirects you to the GitLab Observability UI.
-
-## Step 5: Send traces to GOP
-
-[Follow this guide to send traces to your namespace and monitor them in the UI](https://gitlab.com/gitlab-org/opstrace/opstrace/-/blob/main/docs/guides/user/sending-traces-locally.md).
-
-## Step 6: Clean up your local GOP
-
-To tear down your locally running GOP instance, run the following command:
-
-```shell
-make destroy
-```
-
-## Known issues
-
-### Incorrect architecture for `kind/node` image
-
-If your machine has an Apple silicon (M1/M2) chip, you might encounter an architecture problem with the `kind/node` image when running the `make kind` command. For more details, see [issue 1802](https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/1802).
-
-To fix this problem, you first need to create a Dockerfile. Then build and deploy the image:
-
-1. Create a new Dockerfile (without a file extension) and paste the following commands:
-
- ```Dockerfile
- FROM --platform=arm64 kindest/node:v1.23.13
- RUN arch
- ```
-
-1. Save your Dockerfile, then build the image with the following command:
-
- ```shell
- docker build -t tempkind .
- ```
-
- Do not forget the period at the end.
-
-1. Create a cluster using your new image with the following command:
-
- ```shell
- kind create cluster --image tempkind
- ```
-
-### scheduler-controller-manager pod cannot start due to ImagePullBackOff
-
-If while executing `make deploy` in step 1, the `scheduler-controller-manager` pod cannot start due to `ImagePullBackOff`, you must set the `CI_COMMIT_TAG` to a non-dirty state. By setting the commit tag to the latest commit, you ensure the Docker image can be pulled from the container registry.
-
-Run the following command to set the commit tag:
-
-```shell
-make kind
-export CI_COMMIT_TAG=0.2.0-e1206acf
-make deploy
-```
diff --git a/doc/user/group/import/index.md b/doc/user/group/import/index.md
index 463330113c3..9190006fd67 100644
--- a/doc/user/group/import/index.md
+++ b/doc/user/group/import/index.md
@@ -65,16 +65,16 @@ transfer.
### Limits
-| Limit | Description |
-|:------------|:-------------------------------------------------------------------------------------------------------------------------------------|
-| 6 | Maximum number of migrations per minute per user. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/386452) in GitLab 15.9. |
-| 5 GB | Maximum relation size that can be downloaded from the source instance. |
-| 10 GB | Maximum size of a decompressed archive. |
-| 210 seconds | Maximum number of seconds to wait for decompressing an archive file. |
-| 50 MB | Maximum length an NDJSON row can have. |
-| 5 minutes | Maximum number of seconds until an empty export status on source instance is raised. |
-| 8 hours | Time until migration times out. |
-| 90 minutes | Time the destination is waiting for export to complete. |
+| Limit | Description |
+|:------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| 6 | Maximum number of migrations permitted by a destination GitLab instance per minute per user. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/386452) in GitLab 15.9. |
+| 5 GB | Maximum relation size that can be downloaded from the source instance. |
+| 10 GB | Maximum size of a decompressed archive. |
+| 210 seconds | Maximum number of seconds to wait for decompressing an archive file. |
+| 50 MB | Maximum length an NDJSON row can have. |
+| 5 minutes | Maximum number of seconds until an empty export status on source instance is raised. |
+| 8 hours | Time until migration times out. |
+| 90 minutes | Time the destination is waiting for export to complete. |
### Visibility rules
diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md
index a2aad8a3e27..0c733b8de30 100644
--- a/doc/user/profile/personal_access_tokens.md
+++ b/doc/user/profile/personal_access_tokens.md
@@ -222,7 +222,7 @@ Remember this if you set up an automation pipeline that depends on authenticatio
### Unrevoke a personal access token **(FREE SELF)**
-If a personal access token is revoked accidentally by any method, administrators can unrevoke that token.
+If a personal access token is revoked accidentally by any method, administrators can unrevoke that token. By default, a daily job deletes revoked tokens at 1:00 AM system time.
WARNING:
Running the following commands changes data directly. This could be damaging if not done correctly, or under the right conditions. You should first run these commands in a test environment with a backup of the instance ready to be restored, just in case.
diff --git a/lib/api/entities/project.rb b/lib/api/entities/project.rb
index fcb7ddb9567..7f4a0359e03 100644
--- a/lib/api/entities/project.rb
+++ b/lib/api/entities/project.rb
@@ -155,7 +155,7 @@ module API
# rubocop: disable CodeReuse/ActiveRecord
def self.preload_resource(project)
- ActiveRecord::Associations::Preloader.new.preload(project, project_group_links: { group: :route })
+ ActiveRecord::Associations::Preloader.new(records: [project], associations: { project_group_links: { group: :route } }).call
end
def self.preload_relation(projects_relation, options = {})
diff --git a/lib/gitlab/analytics/cycle_analytics/aggregated/records_fetcher.rb b/lib/gitlab/analytics/cycle_analytics/aggregated/records_fetcher.rb
index 07dc4c02ba8..2143497f084 100644
--- a/lib/gitlab/analytics/cycle_analytics/aggregated/records_fetcher.rb
+++ b/lib/gitlab/analytics/cycle_analytics/aggregated/records_fetcher.rb
@@ -94,10 +94,10 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord
def preload_associations(records)
- ActiveRecord::Associations::Preloader.new.preload(
- records,
- MAPPINGS.fetch(subject_class).fetch(:includes_for_query)
- )
+ ActiveRecord::Associations::Preloader.new(
+ records: records,
+ associations: MAPPINGS.fetch(subject_class).fetch(:includes_for_query)
+ ).call
records
end
diff --git a/lib/gitlab/analytics/cycle_analytics/records_fetcher.rb b/lib/gitlab/analytics/cycle_analytics/records_fetcher.rb
index 140c4a300ca..9deb5072112 100644
--- a/lib/gitlab/analytics/cycle_analytics/records_fetcher.rb
+++ b/lib/gitlab/analytics/cycle_analytics/records_fetcher.rb
@@ -67,10 +67,10 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord
def preload_associations(records)
# using preloader instead of includes to avoid AR generating a large column list
- ActiveRecord::Associations::Preloader.new.preload(
- records,
- MAPPINGS.fetch(subject_class).fetch(:includes_for_query)
- )
+ ActiveRecord::Associations::Preloader.new(
+ records: records,
+ associations: MAPPINGS.fetch(subject_class).fetch(:includes_for_query)
+ ).call
records
end
diff --git a/lib/gitlab/data_builder/pipeline.rb b/lib/gitlab/data_builder/pipeline.rb
index 939eaa377aa..ecb0cc20a64 100644
--- a/lib/gitlab/data_builder/pipeline.rb
+++ b/lib/gitlab/data_builder/pipeline.rb
@@ -45,8 +45,9 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord
def preload_builds(pipeline, association)
- ActiveRecord::Associations::Preloader.new.preload(pipeline,
- {
+ ActiveRecord::Associations::Preloader.new(
+ records: [pipeline],
+ associations: {
association => {
**::Ci::Pipeline::PROJECT_ROUTE_AND_NAMESPACE_ROUTE,
runner: :tags,
@@ -56,7 +57,7 @@ module Gitlab
ci_stage: []
}
}
- )
+ ).call
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/gitlab/database/partitioning/ci_sliding_list_strategy.rb b/lib/gitlab/database/partitioning/ci_sliding_list_strategy.rb
new file mode 100644
index 00000000000..67cf7018ded
--- /dev/null
+++ b/lib/gitlab/database/partitioning/ci_sliding_list_strategy.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Database
+ module Partitioning
+ class CiSlidingListStrategy < SlidingListStrategy
+ def initial_partition
+ partition_name = [table_name.to_s.delete_prefix('p_'), 100].join('_')
+
+ SingleNumericListPartition.new(table_name, 100, partition_name: partition_name)
+ end
+
+ def validate_and_fix; end
+
+ def after_adding_partitions; end
+
+ def extra_partitions
+ []
+ end
+
+ private
+
+ def ensure_partitioning_column_ignored_or_readonly!; end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/database/schema_validation/validators/base_validator.rb b/lib/gitlab/database/schema_validation/validators/base_validator.rb
index 2478d446e43..1fd3d7c85c4 100644
--- a/lib/gitlab/database/schema_validation/validators/base_validator.rb
+++ b/lib/gitlab/database/schema_validation/validators/base_validator.rb
@@ -5,7 +5,7 @@ module Gitlab
module SchemaValidation
module Validators
class BaseValidator
- Inconsistency = Struct.new(:name, :statement)
+ Inconsistency = Struct.new(:type, :object_name, :statement)
def initialize(structure_sql, database)
@structure_sql = structure_sql
@@ -16,7 +16,7 @@ module Gitlab
[
ExtraIndexes,
MissingIndexes,
- WrongIndexes
+ DifferentDefinitionIndexes
]
end
@@ -28,8 +28,10 @@ module Gitlab
attr_reader :structure_sql, :database
- def build_inconsistency(schema_object)
- Inconsistency.new(schema_object.name, schema_object.statement)
+ def build_inconsistency(validator_class, schema_object)
+ inconsistency_type = validator_class.name.demodulize.underscore
+
+ Inconsistency.new(inconsistency_type, schema_object.name, schema_object.statement)
end
end
end
diff --git a/lib/gitlab/database/schema_validation/validators/wrong_indexes.rb b/lib/gitlab/database/schema_validation/validators/different_definition_indexes.rb
index a92e7158be7..d54b62ac1e7 100644
--- a/lib/gitlab/database/schema_validation/validators/wrong_indexes.rb
+++ b/lib/gitlab/database/schema_validation/validators/different_definition_indexes.rb
@@ -4,7 +4,7 @@ module Gitlab
module Database
module SchemaValidation
module Validators
- class WrongIndexes < BaseValidator
+ class DifferentDefinitionIndexes < BaseValidator
def execute
structure_sql.indexes.filter_map do |structure_sql_index|
database_index = database.fetch_index_by_name(structure_sql_index.name)
@@ -12,7 +12,7 @@ module Gitlab
next if database_index.nil?
next if database_index.statement == structure_sql_index.statement
- build_inconsistency(structure_sql_index)
+ build_inconsistency(self.class, structure_sql_index)
end
end
end
diff --git a/lib/gitlab/database/schema_validation/validators/extra_indexes.rb b/lib/gitlab/database/schema_validation/validators/extra_indexes.rb
index 023052459ff..28384dd7cee 100644
--- a/lib/gitlab/database/schema_validation/validators/extra_indexes.rb
+++ b/lib/gitlab/database/schema_validation/validators/extra_indexes.rb
@@ -9,7 +9,7 @@ module Gitlab
database.indexes.filter_map do |index|
next if structure_sql.index_exists?(index.name)
- build_inconsistency(index)
+ build_inconsistency(self.class, index)
end
end
end
diff --git a/lib/gitlab/database/schema_validation/validators/missing_indexes.rb b/lib/gitlab/database/schema_validation/validators/missing_indexes.rb
index 890c00c58c9..ac0ea0152ba 100644
--- a/lib/gitlab/database/schema_validation/validators/missing_indexes.rb
+++ b/lib/gitlab/database/schema_validation/validators/missing_indexes.rb
@@ -9,7 +9,7 @@ module Gitlab
structure_sql.indexes.filter_map do |index|
next if database.index_exists?(index.name)
- build_inconsistency(index)
+ build_inconsistency(self.class, index)
end
end
end
diff --git a/lib/gitlab/usage/metrics/instrumentations/index_inconsistencies_metric.rb b/lib/gitlab/usage/metrics/instrumentations/index_inconsistencies_metric.rb
new file mode 100644
index 00000000000..409027925d1
--- /dev/null
+++ b/lib/gitlab/usage/metrics/instrumentations/index_inconsistencies_metric.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Usage
+ module Metrics
+ module Instrumentations
+ class IndexInconsistenciesMetric < GenericMetric
+ value do
+ runner = Gitlab::Database::SchemaValidation::Runner.new(structure_sql, database, validators: validators)
+
+ inconsistencies = runner.execute
+
+ inconsistencies.map do |inconsistency|
+ {
+ object_name: inconsistency.object_name,
+ inconsistency_type: inconsistency.type
+ }
+ end
+ end
+
+ class << self
+ private
+
+ def database
+ database_model = Gitlab::Database.database_base_models[Gitlab::Database::MAIN_DATABASE_NAME]
+ Gitlab::Database::SchemaValidation::Database.new(database_model.connection)
+ end
+
+ def structure_sql
+ stucture_sql_path = Rails.root.join('db/structure.sql')
+ Gitlab::Database::SchemaValidation::StructureSql.new(stucture_sql_path)
+ end
+
+ def validators
+ [
+ Gitlab::Database::SchemaValidation::Validators::MissingIndexes,
+ Gitlab::Database::SchemaValidation::Validators::DifferentDefinitionIndexes,
+ Gitlab::Database::SchemaValidation::Validators::ExtraIndexes
+ ]
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/tw/codeowners.rake b/lib/tasks/gitlab/tw/codeowners.rake
index 8861e8574b3..e6bbbc55cb1 100644
--- a/lib/tasks/gitlab/tw/codeowners.rake
+++ b/lib/tasks/gitlab/tw/codeowners.rake
@@ -29,7 +29,6 @@ namespace :tw do
CodeOwnerRule.new('Configure', '@phillipwells'),
CodeOwnerRule.new('Container Registry', '@marcel.amirault'),
CodeOwnerRule.new('Contributor Experience', '@eread'),
- CodeOwnerRule.new('Conversion', '@kpaizee'),
CodeOwnerRule.new('Database', '@aqualls'),
CodeOwnerRule.new('Development', '@sselhorn'),
CodeOwnerRule.new('Distribution', '@axil'),
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 67b374607f4..2a02f76fa64 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -15348,6 +15348,9 @@ msgstr ""
msgid "Edit image description"
msgstr ""
+msgid "Edit image text or link"
+msgstr ""
+
msgid "Edit in pipeline editor"
msgstr ""
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index 55c148bb66f..600f8047a1d 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -277,6 +277,7 @@ RSpec.describe Projects::BranchesController, feature_category: :source_code_mana
create_branch name: "<script>alert('merge');</script>", ref: "<script>alert('ref');</script>"
expect(response).to have_gitlab_http_status(:unprocessable_entity)
+ expect(response.body).to include 'Failed to create branch'
end
end
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index 756cb23b7a4..f10adfe5173 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -36,7 +36,7 @@ RSpec.describe 'Database schema', feature_category: :database do
ci_build_pending_states: %w[partition_id build_id],
ci_build_report_results: %w[partition_id build_id],
ci_build_trace_chunks: %w[partition_id build_id],
- ci_build_trace_metadata: %w[partition_id],
+ ci_build_trace_metadata: %w[partition_id build_id],
ci_builds: %w[erased_by_id trigger_request_id partition_id],
ci_builds_runner_session: %w[partition_id build_id],
p_ci_builds_metadata: %w[partition_id],
diff --git a/spec/factories/ci/runner_machine_builds.rb b/spec/factories/ci/runner_machine_builds.rb
new file mode 100644
index 00000000000..0181def26ba
--- /dev/null
+++ b/spec/factories/ci/runner_machine_builds.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :ci_runner_machine_build, class: 'Ci::RunnerMachineBuild' do
+ build factory: :ci_build, scheduling_type: :dag
+ runner_machine factory: :ci_runner_machine
+ end
+end
diff --git a/spec/features/incidents/incident_timeline_events_spec.rb b/spec/features/incidents/incident_timeline_events_spec.rb
index 7404ac64cc9..a4449ee2608 100644
--- a/spec/features/incidents/incident_timeline_events_spec.rb
+++ b/spec/features/incidents/incident_timeline_events_spec.rb
@@ -96,5 +96,6 @@ RSpec.describe 'Incident timeline events', :js, feature_category: :incident_mana
it_behaves_like 'for each incident details route',
'add, edit, and delete timeline events',
- tab_text: s_('Incident|Timeline')
+ tab_text: s_('Incident|Timeline'),
+ tab: 'timeline'
end
diff --git a/spec/features/incidents/user_views_alert_details_spec.rb b/spec/features/incidents/user_views_alert_details_spec.rb
new file mode 100644
index 00000000000..f3d0273071c
--- /dev/null
+++ b/spec/features/incidents/user_views_alert_details_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'User uploads alerts to incident', :js, feature_category: :incident_management do
+ let_it_be(:incident) { create(:incident) }
+ let_it_be(:project) { incident.project }
+ let_it_be(:user) { create(:user, developer_projects: [project]) }
+
+ context 'with alert' do
+ let_it_be(:alert) { create(:alert_management_alert, issue_id: incident.id, project: project) }
+
+ shared_examples 'shows alert tab with details' do
+ specify do
+ expect(page).to have_link(s_('Incident|Alert details'))
+ expect(page).to have_content(alert.title)
+ end
+ end
+
+ it_behaves_like 'for each incident details route',
+ 'shows alert tab with details',
+ tab_text: s_('Incident|Alert details'),
+ tab: 'alerts'
+ end
+
+ context 'with no alerts' do
+ it 'hides the Alert details tab' do
+ sign_in(user)
+ visit project_issue_path(project, incident)
+
+ expect(page).not_to have_link(s_('Incident|Alert details'))
+ end
+ end
+end
diff --git a/spec/frontend/boards/components/board_top_bar_spec.js b/spec/frontend/boards/components/board_top_bar_spec.js
index faf1547c47e..002e4034e70 100644
--- a/spec/frontend/boards/components/board_top_bar_spec.js
+++ b/spec/frontend/boards/components/board_top_bar_spec.js
@@ -11,7 +11,7 @@ import ConfigToggle from '~/boards/components/config_toggle.vue';
import IssueBoardFilteredSearch from '~/boards/components/issue_board_filtered_search.vue';
import NewBoardButton from '~/boards/components/new_board_button.vue';
import ToggleFocus from '~/boards/components/toggle_focus.vue';
-import { BoardType } from '~/boards/constants';
+import { WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants';
import groupBoardQuery from '~/boards/graphql/group_board.query.graphql';
import projectBoardQuery from '~/boards/graphql/project_board.query.graphql';
@@ -116,14 +116,14 @@ describe('BoardTopBar', () => {
describe('Apollo boards', () => {
it.each`
boardType | queryHandler | notCalledHandler
- ${BoardType.group} | ${groupBoardQueryHandlerSuccess} | ${projectBoardQueryHandlerSuccess}
- ${BoardType.project} | ${projectBoardQueryHandlerSuccess} | ${groupBoardQueryHandlerSuccess}
+ ${WORKSPACE_GROUP} | ${groupBoardQueryHandlerSuccess} | ${projectBoardQueryHandlerSuccess}
+ ${WORKSPACE_PROJECT} | ${projectBoardQueryHandlerSuccess} | ${groupBoardQueryHandlerSuccess}
`('fetches $boardType boards', async ({ boardType, queryHandler, notCalledHandler }) => {
createComponent({
provide: {
boardType,
- isProjectBoard: boardType === BoardType.project,
- isGroupBoard: boardType === BoardType.group,
+ isProjectBoard: boardType === WORKSPACE_PROJECT,
+ isGroupBoard: boardType === WORKSPACE_GROUP,
isApolloBoard: true,
},
});
diff --git a/spec/frontend/boards/components/boards_selector_spec.js b/spec/frontend/boards/components/boards_selector_spec.js
index 28f51e0ecbf..afa304a66ff 100644
--- a/spec/frontend/boards/components/boards_selector_spec.js
+++ b/spec/frontend/boards/components/boards_selector_spec.js
@@ -5,11 +5,11 @@ import Vuex from 'vuex';
import waitForPromises from 'helpers/wait_for_promises';
import { TEST_HOST } from 'spec/test_constants';
import BoardsSelector from '~/boards/components/boards_selector.vue';
-import { BoardType } from '~/boards/constants';
import groupBoardsQuery from '~/boards/graphql/group_boards.query.graphql';
import projectBoardsQuery from '~/boards/graphql/project_boards.query.graphql';
import groupRecentBoardsQuery from '~/boards/graphql/group_recent_boards.query.graphql';
import projectRecentBoardsQuery from '~/boards/graphql/project_recent_boards.query.graphql';
+import { WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants';
import createMockApollo from 'helpers/mock_apollo_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import {
@@ -228,13 +228,13 @@ describe('BoardsSelector', () => {
describe('fetching all boards', () => {
it.each`
boardType | queryHandler | notCalledHandler
- ${BoardType.group} | ${groupBoardsQueryHandlerSuccess} | ${projectBoardsQueryHandlerSuccess}
- ${BoardType.project} | ${projectBoardsQueryHandlerSuccess} | ${groupBoardsQueryHandlerSuccess}
+ ${WORKSPACE_GROUP} | ${groupBoardsQueryHandlerSuccess} | ${projectBoardsQueryHandlerSuccess}
+ ${WORKSPACE_PROJECT} | ${projectBoardsQueryHandlerSuccess} | ${groupBoardsQueryHandlerSuccess}
`('fetches $boardType boards', async ({ boardType, queryHandler, notCalledHandler }) => {
createStore();
createComponent({
- isGroupBoard: boardType === BoardType.group,
- isProjectBoard: boardType === BoardType.project,
+ isGroupBoard: boardType === WORKSPACE_GROUP,
+ isProjectBoard: boardType === WORKSPACE_PROJECT,
});
await nextTick();
diff --git a/spec/frontend/boards/stores/actions_spec.js b/spec/frontend/boards/stores/actions_spec.js
index b830ca1126d..13eb3865354 100644
--- a/spec/frontend/boards/stores/actions_spec.js
+++ b/spec/frontend/boards/stores/actions_spec.js
@@ -2,13 +2,7 @@ import * as Sentry from '@sentry/browser';
import { cloneDeep } from 'lodash';
import Vue from 'vue';
import Vuex from 'vuex';
-import {
- inactiveId,
- ISSUABLE,
- ListType,
- BoardType,
- DraggableItemTypes,
-} from 'ee_else_ce/boards/constants';
+import { inactiveId, ISSUABLE, ListType, DraggableItemTypes } from 'ee_else_ce/boards/constants';
import issueMoveListMutation from 'ee_else_ce/boards/graphql/issue_move_list.mutation.graphql';
import testAction from 'helpers/vuex_action_helper';
import {
@@ -26,7 +20,7 @@ import actions from '~/boards/stores/actions';
import * as types from '~/boards/stores/mutation_types';
import mutations from '~/boards/stores/mutations';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { TYPE_ISSUE } from '~/issues/constants';
+import { TYPE_ISSUE, WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/issues/constants';
import projectBoardMilestones from '~/boards/graphql/project_board_milestones.query.graphql';
import groupBoardMilestones from '~/boards/graphql/group_board_milestones.query.graphql';
@@ -300,8 +294,8 @@ describe('fetchLists', () => {
it.each`
issuableType | boardType | fullBoardId | isGroup | isProject
- ${TYPE_ISSUE} | ${BoardType.group} | ${'gid://gitlab/Board/1'} | ${true} | ${false}
- ${TYPE_ISSUE} | ${BoardType.project} | ${'gid://gitlab/Board/1'} | ${false} | ${true}
+ ${TYPE_ISSUE} | ${WORKSPACE_GROUP} | ${'gid://gitlab/Board/1'} | ${true} | ${false}
+ ${TYPE_ISSUE} | ${WORKSPACE_PROJECT} | ${'gid://gitlab/Board/1'} | ${false} | ${true}
`(
'calls $issuableType query with correct variables',
async ({ issuableType, boardType, fullBoardId, isGroup, isProject }) => {
diff --git a/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js b/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js
index 380b4b92f2e..0f4fb02a40b 100644
--- a/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js
+++ b/spec/frontend/issues/show/components/incidents/incident_tabs_spec.js
@@ -1,4 +1,5 @@
import merge from 'lodash/merge';
+import { nextTick } from 'vue';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { trackIncidentDetailsViewsOptions } from '~/incidents/constants';
import DescriptionComponent from '~/issues/show/components/description.vue';
@@ -11,6 +12,11 @@ import Tracking from '~/tracking';
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
import { descriptionProps } from '../../mock_data/mock_data';
+const push = jest.fn();
+const $router = {
+ push,
+};
+
const mockAlert = {
__typename: 'AlertManagementAlert',
detailsUrl: INVALID_URL,
@@ -28,6 +34,8 @@ const defaultMocks = {
},
},
},
+ $route: { params: {} },
+ $router,
};
describe('Incident Tabs component', () => {
@@ -165,6 +173,40 @@ describe('Incident Tabs component', () => {
expect(findActiveTabs()).toHaveLength(1);
expect(findActiveTabs().at(0).text()).toBe(incidentTabsI18n.timelineTitle);
+ expect(push).toHaveBeenCalledWith('/timeline');
+ });
+ });
+
+ describe('loading page with tab', () => {
+ it('shows the timeline tab when timeline path is passed', async () => {
+ mountComponent({
+ mount: mountExtended,
+ mocks: { $route: { params: { tabId: 'timeline' } } },
+ });
+ await nextTick();
+ expect(findActiveTabs()).toHaveLength(1);
+ expect(findActiveTabs().at(0).text()).toBe(incidentTabsI18n.timelineTitle);
+ });
+
+ it('shows the alerts tab when timeline path is passed', async () => {
+ mountComponent({
+ mount: mountExtended,
+ mocks: { $route: { params: { tabId: 'alerts' } } },
+ hasLinkedAlerts: true,
+ });
+ await nextTick();
+ expect(findActiveTabs()).toHaveLength(1);
+ expect(findActiveTabs().at(0).text()).toBe(incidentTabsI18n.alertsTitle);
+ });
+
+ it('shows the metrics tab when metrics path is passed', async () => {
+ mountComponent({
+ mount: mountExtended,
+ mocks: { $route: { params: { tabId: 'metrics' } } },
+ });
+ await nextTick();
+ expect(findActiveTabs()).toHaveLength(1);
+ expect(findActiveTabs().at(0).text()).toBe(incidentTabsI18n.metricsTitle);
});
});
});
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index 1ae834c0769..d5fff7f3a31 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -430,7 +430,8 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do
action: "show",
namespace_id: "foo",
project_id: "bar",
- id: incident.iid
+ id: incident.iid,
+ incident_tab: 'timeline'
}).permit!
end
@@ -441,7 +442,9 @@ RSpec.describe IssuablesHelper, feature_category: :team_planning do
expected_data = {
issueType: 'incident',
hasLinkedAlerts: false,
- canUpdateTimelineEvent: true
+ canUpdateTimelineEvent: true,
+ currentPath: "/foo/bar/-/issues/incident/#{incident.iid}/timeline",
+ currentTab: 'timeline'
}
expect(helper.issuable_initial_data(incident)).to match(hash_including(expected_data))
diff --git a/spec/lib/gitlab/database/schema_validation/runner_spec.rb b/spec/lib/gitlab/database/schema_validation/runner_spec.rb
index e07deec3062..13980cb148b 100644
--- a/spec/lib/gitlab/database/schema_validation/runner_spec.rb
+++ b/spec/lib/gitlab/database/schema_validation/runner_spec.rb
@@ -32,7 +32,7 @@ RSpec.describe Gitlab::Database::SchemaValidation::Runner, feature_category: :da
let(:extra_indexes) { class_double(class_name) }
let(:instace_extra_index) { instance_double(class_name, execute: [inconsistency]) }
- let(:inconsistency) { instance_double(inconsistency_class_name, name: 'test') }
+ let(:inconsistency) { instance_double(inconsistency_class_name, object_name: 'test') }
let(:validators) { [extra_indexes] }
@@ -43,7 +43,7 @@ RSpec.describe Gitlab::Database::SchemaValidation::Runner, feature_category: :da
expect(validator).not_to receive(:new).with(structure_sql, database)
end
- expect(inconsistencies.map(&:name)).to eql ['test']
+ expect(inconsistencies.map(&:object_name)).to eql ['test']
end
end
end
diff --git a/spec/lib/gitlab/database/schema_validation/validators/base_validator_spec.rb b/spec/lib/gitlab/database/schema_validation/validators/base_validator_spec.rb
index adac5b4e579..cf5207aee95 100644
--- a/spec/lib/gitlab/database/schema_validation/validators/base_validator_spec.rb
+++ b/spec/lib/gitlab/database/schema_validation/validators/base_validator_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Gitlab::Database::SchemaValidation::Validators::BaseValidator, fe
expect(all_validators).to eq([
Gitlab::Database::SchemaValidation::Validators::ExtraIndexes,
Gitlab::Database::SchemaValidation::Validators::MissingIndexes,
- Gitlab::Database::SchemaValidation::Validators::WrongIndexes
+ Gitlab::Database::SchemaValidation::Validators::DifferentDefinitionIndexes
])
end
end
diff --git a/spec/lib/gitlab/database/schema_validation/validators/wrong_indexes_spec.rb b/spec/lib/gitlab/database/schema_validation/validators/different_definition_indexes_spec.rb
index 026f3fb710c..b9744c86b80 100644
--- a/spec/lib/gitlab/database/schema_validation/validators/wrong_indexes_spec.rb
+++ b/spec/lib/gitlab/database/schema_validation/validators/different_definition_indexes_spec.rb
@@ -2,6 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::Database::SchemaValidation::Validators::WrongIndexes, feature_category: :database do
+RSpec.describe Gitlab::Database::SchemaValidation::Validators::DifferentDefinitionIndexes,
+ feature_category: :database do
include_examples 'index validators', described_class, ['wrong_index']
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index a22b9d11214..73f29f6debe 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -297,6 +297,7 @@ ci_pipelines:
- job_artifacts
- vulnerabilities_finding_pipelines
- vulnerability_findings
+- vulnerability_state_transitions
- pipeline_config
- security_scans
- security_findings
@@ -396,6 +397,7 @@ builds:
- job_artifacts_cyclonedx
- job_artifacts_requirements_v2
- runner_machine
+- runner_machine_build
- runner_session
- trace_metadata
- terraform_state_versions
diff --git a/spec/lib/gitlab/usage/metrics/instrumentations/index_inconsistencies_metric_spec.rb b/spec/lib/gitlab/usage/metrics/instrumentations/index_inconsistencies_metric_spec.rb
new file mode 100644
index 00000000000..afc9d610207
--- /dev/null
+++ b/spec/lib/gitlab/usage/metrics/instrumentations/index_inconsistencies_metric_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Usage::Metrics::Instrumentations::IndexInconsistenciesMetric, feature_category: :database do
+ it_behaves_like 'a correct instrumented metric value', { time_frame: 'all' } do
+ let(:expected_value) do
+ [
+ { inconsistency_type: 'wrong_indexes', object_name: 'index_name_1' },
+ { inconsistency_type: 'missing_indexes', object_name: 'index_name_2' },
+ { inconsistency_type: 'extra_indexes', object_name: 'index_name_3' }
+ ]
+ end
+
+ let(:runner) { instance_double(Gitlab::Database::SchemaValidation::Runner, execute: inconsistencies) }
+ let(:inconsistency_class) { Gitlab::Database::SchemaValidation::Validators::BaseValidator::Inconsistency }
+
+ let(:inconsistencies) do
+ [
+ instance_double(inconsistency_class, object_name: 'index_name_1', type: 'wrong_indexes'),
+ instance_double(inconsistency_class, object_name: 'index_name_2', type: 'missing_indexes'),
+ instance_double(inconsistency_class, object_name: 'index_name_3', type: 'extra_indexes')
+ ]
+ end
+
+ before do
+ allow(Gitlab::Database::SchemaValidation::Runner).to receive(:new).and_return(runner)
+ end
+ end
+end
diff --git a/spec/models/ci/build_metadata_spec.rb b/spec/models/ci/build_metadata_spec.rb
index fb50ba89cd3..c3b445cbbe5 100644
--- a/spec/models/ci/build_metadata_spec.rb
+++ b/spec/models/ci/build_metadata_spec.rb
@@ -22,7 +22,6 @@ RSpec.describe Ci::BuildMetadata do
it { is_expected.to belong_to(:build) }
it { is_expected.to belong_to(:project) }
- it { is_expected.to belong_to(:runner_machine) }
describe '#update_timeout_state' do
subject { metadata }
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 29cc94ff13f..6836d1b51ef 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -31,7 +31,7 @@ RSpec.describe Ci::Build, feature_category: :continuous_integration, factory_def
it { is_expected.to have_many(:pages_deployments).with_foreign_key(:ci_build_id) }
it { is_expected.to have_one(:deployment) }
- it { is_expected.to have_one(:runner_machine).through(:metadata) }
+ it { is_expected.to have_one(:runner_machine).through(:runner_machine_build) }
it { is_expected.to have_one(:runner_session).with_foreign_key(:build_id) }
it { is_expected.to have_one(:trace_metadata).with_foreign_key(:build_id) }
it { is_expected.to have_one(:runtime_metadata).with_foreign_key(:build_id) }
diff --git a/spec/models/ci/processable_spec.rb b/spec/models/ci/processable_spec.rb
index db22d8f3a6c..cf2c176816d 100644
--- a/spec/models/ci/processable_spec.rb
+++ b/spec/models/ci/processable_spec.rb
@@ -83,7 +83,7 @@ RSpec.describe Ci::Processable, feature_category: :continuous_integration do
runner_id tag_taggings taggings tags trigger_request_id
user_id auto_canceled_by_id retried failure_reason
sourced_pipelines sourced_pipeline artifacts_file_store artifacts_metadata_store
- metadata runner_machine_id runner_machine runner_session trace_chunks upstream_pipeline_id
+ metadata runner_machine_build runner_machine runner_session trace_chunks upstream_pipeline_id
artifacts_file artifacts_metadata artifacts_size commands
resource resource_group_id processed security_scans author
pipeline_id report_results pending_state pages_deployments
diff --git a/spec/models/ci/runner_machine_build_spec.rb b/spec/models/ci/runner_machine_build_spec.rb
new file mode 100644
index 00000000000..b15d232cbb5
--- /dev/null
+++ b/spec/models/ci/runner_machine_build_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::RunnerMachineBuild, model: true, feature_category: :runner_fleet do
+ it { is_expected.to belong_to(:build) }
+ it { is_expected.to belong_to(:runner_machine) }
+
+ describe 'partitioning' do
+ context 'with build' do
+ let(:build) { FactoryBot.build(:ci_build, partition_id: ci_testing_partition_id) }
+ let(:runner_machine_build) { FactoryBot.build(:ci_runner_machine_build, build: build) }
+
+ it 'sets partition_id to the current partition value' do
+ expect { runner_machine_build.valid? }.to change { runner_machine_build.partition_id }
+ .to(ci_testing_partition_id)
+ end
+
+ context 'when it is already set' do
+ let(:runner_machine_build) { FactoryBot.build(:ci_runner_machine_build, partition_id: 125) }
+
+ it 'does not change the partition_id value' do
+ expect { runner_machine_build.valid? }.not_to change { runner_machine_build.partition_id }
+ end
+ end
+ end
+
+ context 'without build' do
+ let(:runner_machine_build) { FactoryBot.build(:ci_runner_machine_build, build: nil) }
+
+ it { is_expected.to validate_presence_of(:partition_id) }
+
+ it 'does not change the partition_id value' do
+ expect { runner_machine_build.valid? }.not_to change { runner_machine_build.partition_id }
+ end
+ end
+ end
+
+ describe 'ci_sliding_list partitioning' do
+ let(:connection) { described_class.connection }
+ let(:partition_manager) { Gitlab::Database::Partitioning::PartitionManager.new(described_class) }
+
+ let(:partitioning_strategy) { described_class.partitioning_strategy }
+
+ it { expect(partitioning_strategy.missing_partitions).to be_empty }
+ it { expect(partitioning_strategy.extra_partitions).to be_empty }
+ it { expect(partitioning_strategy.current_partitions).to include partitioning_strategy.initial_partition }
+ it { expect(partitioning_strategy.active_partition).to be_present }
+ end
+end
diff --git a/spec/models/ci/runner_machine_spec.rb b/spec/models/ci/runner_machine_spec.rb
index 6162077a055..dbefd7aa449 100644
--- a/spec/models/ci/runner_machine_spec.rb
+++ b/spec/models/ci/runner_machine_spec.rb
@@ -7,8 +7,8 @@ RSpec.describe Ci::RunnerMachine, feature_category: :runner_fleet, type: :model
it { is_expected.to belong_to(:runner) }
it { is_expected.to belong_to(:runner_version).with_foreign_key(:version) }
- it { is_expected.to have_many(:build_metadata) }
- it { is_expected.to have_many(:builds).through(:build_metadata) }
+ it { is_expected.to have_many(:runner_machine_builds) }
+ it { is_expected.to have_many(:builds).through(:runner_machine_builds) }
describe 'validation' do
it { is_expected.to validate_presence_of(:runner) }
diff --git a/spec/models/concerns/routable_spec.rb b/spec/models/concerns/routable_spec.rb
index dc1002f3560..0bbe3dea812 100644
--- a/spec/models/concerns/routable_spec.rb
+++ b/spec/models/concerns/routable_spec.rb
@@ -74,6 +74,17 @@ RSpec.shared_examples 'routable resource with parent' do
describe '#full_name' do
it { expect(record.full_name).to eq "#{record.parent.human_name} / #{record.name}" }
+ context 'without route name' do
+ before do
+ stub_feature_flags(cached_route_lookups: true)
+ record.route.update_attribute(:name, nil)
+ end
+
+ it 'builds full name' do
+ expect(record.full_name).to eq("#{record.parent.human_name} / #{record.name}")
+ end
+ end
+
it 'hits the cache when not preloaded' do
forcibly_hit_cached_lookup(record, :full_name)
diff --git a/spec/requests/projects/issues_controller_spec.rb b/spec/requests/projects/issues_controller_spec.rb
index 67a73834f2d..2b9ff442f76 100644
--- a/spec/requests/projects/issues_controller_spec.rb
+++ b/spec/requests/projects/issues_controller_spec.rb
@@ -25,33 +25,23 @@ RSpec.describe Projects::IssuesController, feature_category: :team_planning do
end
describe 'GET #show' do
- include_context 'group project issue'
+ before do
+ login_as(user)
+ end
it_behaves_like "observability csp policy", described_class do
+ include_context 'group project issue'
let(:tested_path) do
project_issue_path(project, issue)
end
end
- end
-
- describe 'GET #index.json' do
- let_it_be(:public_project) { create(:project, :public) }
- it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit do
- let_it_be(:current_user) { create(:user) }
-
- before do
- sign_in current_user
- end
-
- def request
- get project_issues_path(public_project, format: :json), params: { scope: 'all', search: 'test' }
- end
- end
+ describe 'incident tabs' do
+ let_it_be(:incident) { create(:incident, project: project) }
- it_behaves_like 'rate limited endpoint', rate_limit_key: :search_rate_limit_unauthenticated do
- def request
- get project_issues_path(public_project, format: :json), params: { scope: 'all', search: 'test' }
+ it 'responds with selected tab for incidents' do
+ get incident_issue_project_issue_path(project, incident, 'timeline')
+ expect(response.body).to match(/&quot;currentTab&quot;:&quot;timeline&quot;/)
end
end
end
diff --git a/spec/support/shared_examples/features/incident_details_routing_shared_examples.rb b/spec/support/shared_examples/features/incident_details_routing_shared_examples.rb
index dab125caa60..b8e42843e6f 100644
--- a/spec/support/shared_examples/features/incident_details_routing_shared_examples.rb
+++ b/spec/support/shared_examples/features/incident_details_routing_shared_examples.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-RSpec.shared_examples 'for each incident details route' do |example, tab_text:|
+RSpec.shared_examples 'for each incident details route' do |example, tab_text:, tab:|
before do
sign_in(user)
visit incident_path
@@ -25,4 +25,16 @@ RSpec.shared_examples 'for each incident details route' do |example, tab_text:|
it_behaves_like example
end
+
+ context "for /-/issues/incident/:id/#{tab} route" do
+ let(:incident_path) { incident_project_issues_path(project, incident, tab) }
+
+ it_behaves_like example
+ end
+
+ context "for /-/issues/:id/#{tab} route" do
+ let(:incident_path) { incident_issue_project_issue_path(project, incident, tab) }
+
+ it_behaves_like example
+ end
end
diff --git a/spec/support/shared_examples/lib/gitlab/database/index_validators_shared_examples.rb b/spec/support/shared_examples/lib/gitlab/database/index_validators_shared_examples.rb
index 0a8fd39bc2c..6b0ea9421de 100644
--- a/spec/support/shared_examples/lib/gitlab/database/index_validators_shared_examples.rb
+++ b/spec/support/shared_examples/lib/gitlab/database/index_validators_shared_examples.rb
@@ -12,6 +12,8 @@ RSpec.shared_examples "index validators" do |validator, expected_result|
]
end
+ let(:inconsistency_type) { validator.name.demodulize.underscore }
+
let(:database_name) { 'main' }
let(:database_model) { Gitlab::Database.database_base_models[database_name] }
@@ -29,7 +31,8 @@ RSpec.shared_examples "index validators" do |validator, expected_result|
allow(connection).to receive(:exec_query).and_return(query_result)
end
- it 'returns extra indexes' do
- expect(result.map(&:name)).to match_array(expected_result)
+ it 'returns index inconsistencies' do
+ expect(result.map(&:object_name)).to match_array(expected_result)
+ expect(result.map(&:type)).to all(eql inconsistency_type)
end
end
diff --git a/spec/workers/group_destroy_worker_spec.rb b/spec/workers/group_destroy_worker_spec.rb
index 82ae9010a24..fba4573718a 100644
--- a/spec/workers/group_destroy_worker_spec.rb
+++ b/spec/workers/group_destroy_worker_spec.rb
@@ -2,20 +2,29 @@
require 'spec_helper'
-RSpec.describe GroupDestroyWorker do
- let(:group) { create(:group) }
- let!(:project) { create(:project, namespace: group) }
- let(:user) { create(:user) }
+RSpec.describe GroupDestroyWorker, feature_category: :subgroups do
+ let_it_be(:group) { create(:group) }
+ let_it_be(:project) { create(:project, namespace: group) }
+ let_it_be(:user) { create(:user) }
before do
group.add_owner(user)
end
- subject { described_class.new }
+ subject(:worker) { described_class.new }
+
+ include_examples 'an idempotent worker' do
+ let(:job_args) { [group.id, user.id] }
+
+ it 'does not change groups when run twice' do
+ expect { worker.perform(group.id, user.id) }.to change { Group.count }.by(-1)
+ expect { worker.perform(group.id, user.id) }.not_to change { Group.count }
+ end
+ end
describe "#perform" do
- it "deletes the project" do
- subject.perform(group.id, user.id)
+ it "deletes the group and associated projects" do
+ worker.perform(group.id, user.id)
expect(Group.all).not_to include(group)
expect(Project.all).not_to include(project)
diff --git a/spec/workers/project_destroy_worker_spec.rb b/spec/workers/project_destroy_worker_spec.rb
index 25508928bbf..d699393d7a0 100644
--- a/spec/workers/project_destroy_worker_spec.rb
+++ b/spec/workers/project_destroy_worker_spec.rb
@@ -2,15 +2,26 @@
require 'spec_helper'
-RSpec.describe ProjectDestroyWorker do
- let(:project) { create(:project, :repository, pending_delete: true) }
- let!(:repository) { project.repository.raw }
+RSpec.describe ProjectDestroyWorker, feature_category: :source_code_management do
+ let_it_be(:project) { create(:project, :repository, pending_delete: true) }
+ let_it_be(:repository) { project.repository.raw }
- subject { described_class.new }
+ let(:user) { project.first_owner }
+
+ subject(:worker) { described_class.new }
+
+ include_examples 'an idempotent worker' do
+ let(:job_args) { [project.id, user.id, {}] }
+
+ it 'does not change projects when run twice' do
+ expect { worker.perform(project.id, user.id, {}) }.to change { Project.count }.by(-1)
+ expect { worker.perform(project.id, user.id, {}) }.not_to change { Project.count }
+ end
+ end
describe '#perform' do
it 'deletes the project' do
- subject.perform(project.id, project.first_owner.id, {})
+ worker.perform(project.id, user.id, {})
expect(Project.all).not_to include(project)
expect(repository).not_to exist
@@ -18,13 +29,13 @@ RSpec.describe ProjectDestroyWorker do
it 'does not raise error when project could not be found' do
expect do
- subject.perform(-1, project.first_owner.id, {})
+ worker.perform(-1, user.id, {})
end.not_to raise_error
end
it 'does not raise error when user could not be found' do
expect do
- subject.perform(project.id, -1, {})
+ worker.perform(project.id, -1, {})
end.not_to raise_error
end
end
diff --git a/workhorse/go.mod b/workhorse/go.mod
index 02fa22a47d6..6e3f47b9afc 100644
--- a/workhorse/go.mod
+++ b/workhorse/go.mod
@@ -26,7 +26,7 @@ require (
github.com/sirupsen/logrus v1.9.0
github.com/smartystreets/goconvey v1.7.2
github.com/stretchr/testify v1.8.1
- gitlab.com/gitlab-org/gitaly/v15 v15.9.0-rc42
+ gitlab.com/gitlab-org/gitaly/v15 v15.9.0-rc43
gitlab.com/gitlab-org/golang-archive-zip v0.1.1
gitlab.com/gitlab-org/labkit v1.17.0
gocloud.dev v0.28.0
diff --git a/workhorse/go.sum b/workhorse/go.sum
index c266518925e..2240d85273e 100644
--- a/workhorse/go.sum
+++ b/workhorse/go.sum
@@ -1847,8 +1847,8 @@ github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
-gitlab.com/gitlab-org/gitaly/v15 v15.9.0-rc42 h1:mV3bwpQOlw+3+iAfqK6hmAZ2IL9snehPNls0iXa5up4=
-gitlab.com/gitlab-org/gitaly/v15 v15.9.0-rc42/go.mod h1:MLAmjPsXan0TixWBOnF2GUTjHcNLoAiYv1x1LRx7gHQ=
+gitlab.com/gitlab-org/gitaly/v15 v15.9.0-rc43 h1:yHDCgmgeCC+R1F40HleRpPSPe3MVCmS8okvolxZ1Ack=
+gitlab.com/gitlab-org/gitaly/v15 v15.9.0-rc43/go.mod h1:MLAmjPsXan0TixWBOnF2GUTjHcNLoAiYv1x1LRx7gHQ=
gitlab.com/gitlab-org/golang-archive-zip v0.1.1 h1:35k9giivbxwF03+8A05Cm8YoxoakU8FBCj5gysjCTCE=
gitlab.com/gitlab-org/golang-archive-zip v0.1.1/go.mod h1:ZDtqpWPGPB9qBuZnZDrKQjIdJtkN7ZAoVwhT6H2o2kE=
gitlab.com/gitlab-org/labkit v1.17.0 h1:mEkoLzXorLNdt8NkfgYS5xMDhdqCsIJaeEVtSf7d8cU=