summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-06-02 00:09:56 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-06-02 00:09:56 +0000
commit926711e4546e0cf845c6cbe5773076f2195357f0 (patch)
tree92edf881d2be503589848c218a85d2a584cf0d19
parentf7bc7dc5eafc4eef9043a3d1b2dcbc15ca76a571 (diff)
downloadgitlab-ce-926711e4546e0cf845c6cbe5773076f2195357f0.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock126
-rw-r--r--app/assets/javascripts/runner/components/runner_filtered_search_bar.vue2
-rw-r--r--app/assets/javascripts/runner/components/runner_list.vue2
-rw-r--r--app/assets/javascripts/runner/components/runner_pagination.vue57
-rw-r--r--app/assets/javascripts/runner/constants.js5
-rw-r--r--app/assets/javascripts/runner/graphql/get_runners.query.graphql25
-rw-r--r--app/assets/javascripts/runner/runner_list/filtered_search_utils.js48
-rw-r--r--app/assets/javascripts/runner/runner_list/runner_list_app.vue47
-rw-r--r--app/helpers/dropdowns_helper.rb2
-rw-r--r--app/helpers/tab_helper.rb16
-rw-r--r--app/models/ci/build_trace_chunk.rb6
-rw-r--r--app/models/clusters/clusters_hierarchy.rb9
-rw-r--r--app/models/concerns/bulk_insert_safe.rb6
-rw-r--r--app/models/concerns/deployment_platform.rb8
-rw-r--r--app/models/concerns/enum_with_nil.rb8
-rw-r--r--app/models/project_authorization.rb9
-rw-r--r--app/views/clusters/clusters/_advanced_settings.html.haml2
-rw-r--r--app/views/projects/merge_requests/tabs/_tab.html.haml4
-rw-r--r--cable/config.ru2
-rw-r--r--config/feature_flags/development/cluster_management_project.yml8
-rw-r--r--config/feature_flags/development/gitlab_ci_trace_read_consistency.yml8
-rw-r--r--config/initializers/labkit_middleware.rb16
-rw-r--r--doc/administration/audit_events.md11
-rw-r--r--doc/administration/repository_storage_paths.md4
-rw-r--r--doc/api/projects.md25
-rw-r--r--doc/topics/git/bisect.md6
-rw-r--r--doc/topics/git/cherry_picking.md2
-rw-r--r--doc/topics/git/feature_branching.md6
-rw-r--r--doc/topics/git/getting_started.md6
-rw-r--r--doc/topics/git/git_add.md6
-rw-r--r--doc/topics/git/git_log.md6
-rw-r--r--doc/topics/git/merge_conflicts.md6
-rw-r--r--doc/topics/git/rollback_commits.md6
-rw-r--r--doc/topics/git/stash.md6
-rw-r--r--doc/topics/git/subtree.md6
-rw-r--r--doc/topics/git/tags.md2
-rw-r--r--doc/topics/git/unstage.md6
-rw-r--r--doc/user/clusters/management_project.md4
-rw-r--r--lib/api/entities/project_repository_storage.rb16
-rw-r--r--lib/api/projects.rb12
-rw-r--r--lib/gitlab/ci/trace/chunked_io.rb9
-rw-r--r--lib/gitlab/database.rb2
-rw-r--r--lib/gitlab/metrics/samplers/database_sampler.rb4
-rw-r--r--lib/release_highlights/validator/entry.rb5
-rw-r--r--locale/gitlab.pot2
-rw-r--r--package.json4
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/container_registry_spec.rb8
-rw-r--r--spec/controllers/application_controller_spec.rb4
-rw-r--r--spec/controllers/search_controller_spec.rb2
-rw-r--r--spec/db/schema_spec.rb2
-rw-r--r--spec/features/projects/badges/pipeline_badge_spec.rb2
-rw-r--r--spec/frontend/runner/components/runner_filtered_search_bar_spec.js2
-rw-r--r--spec/frontend/runner/components/runner_pagination_spec.js160
-rw-r--r--spec/frontend/runner/mock_data.js7
-rw-r--r--spec/frontend/runner/runner_list/filtered_search_utils_spec.js77
-rw-r--r--spec/frontend/runner/runner_list/runner_list_app_spec.js44
-rw-r--r--spec/lib/gitlab/ci/trace/chunked_io_spec.rb2
-rw-r--r--spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb3
-rw-r--r--spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb2
-rw-r--r--spec/lib/gitlab/database/with_lock_retries_spec.rb6
-rw-r--r--spec/lib/gitlab/database_spec.rb8
-rw-r--r--spec/lib/gitlab/import_export/import_failure_service_spec.rb2
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb2
-rw-r--r--spec/models/clusters/clusters_hierarchy_spec.rb8
-rw-r--r--spec/models/concerns/bulk_insert_safe_spec.rb31
-rw-r--r--spec/models/concerns/deployment_platform_spec.rb32
-rw-r--r--spec/requests/api/files_spec.rb3
-rw-r--r--spec/requests/api/projects_spec.rb42
-rw-r--r--spec/requests/api/repositories_spec.rb10
-rw-r--r--spec/support/database_cleaner.rb2
-rw-r--r--spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/uncached_response_shared_examples.rb12
-rw-r--r--spec/views/shared/nav/_sidebar.html.haml_spec.rb2
-rw-r--r--yarn.lock18
75 files changed, 784 insertions, 291 deletions
diff --git a/Gemfile b/Gemfile
index 223ece0b787..67cb2aa6496 100644
--- a/Gemfile
+++ b/Gemfile
@@ -2,7 +2,7 @@
source 'https://rubygems.org'
-gem 'rails', '~> 6.0.3.7'
+gem 'rails', '~> 6.1.3.2'
gem 'bootsnap', '~> 1.4.6'
diff --git a/Gemfile.lock b/Gemfile.lock
index f30b1cde549..6e5f7edfd73 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -12,64 +12,68 @@ GEM
abstract_type (0.0.7)
acme-client (2.0.6)
faraday (>= 0.17, < 2.0.0)
- actioncable (6.0.3.7)
- actionpack (= 6.0.3.7)
+ actioncable (6.1.3.2)
+ actionpack (= 6.1.3.2)
+ activesupport (= 6.1.3.2)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
- actionmailbox (6.0.3.7)
- actionpack (= 6.0.3.7)
- activejob (= 6.0.3.7)
- activerecord (= 6.0.3.7)
- activestorage (= 6.0.3.7)
- activesupport (= 6.0.3.7)
+ actionmailbox (6.1.3.2)
+ actionpack (= 6.1.3.2)
+ activejob (= 6.1.3.2)
+ activerecord (= 6.1.3.2)
+ activestorage (= 6.1.3.2)
+ activesupport (= 6.1.3.2)
mail (>= 2.7.1)
- actionmailer (6.0.3.7)
- actionpack (= 6.0.3.7)
- actionview (= 6.0.3.7)
- activejob (= 6.0.3.7)
+ actionmailer (6.1.3.2)
+ actionpack (= 6.1.3.2)
+ actionview (= 6.1.3.2)
+ activejob (= 6.1.3.2)
+ activesupport (= 6.1.3.2)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
- actionpack (6.0.3.7)
- actionview (= 6.0.3.7)
- activesupport (= 6.0.3.7)
- rack (~> 2.0, >= 2.0.8)
+ actionpack (6.1.3.2)
+ actionview (= 6.1.3.2)
+ activesupport (= 6.1.3.2)
+ rack (~> 2.0, >= 2.0.9)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
- actiontext (6.0.3.7)
- actionpack (= 6.0.3.7)
- activerecord (= 6.0.3.7)
- activestorage (= 6.0.3.7)
- activesupport (= 6.0.3.7)
+ actiontext (6.1.3.2)
+ actionpack (= 6.1.3.2)
+ activerecord (= 6.1.3.2)
+ activestorage (= 6.1.3.2)
+ activesupport (= 6.1.3.2)
nokogiri (>= 1.8.5)
- actionview (6.0.3.7)
- activesupport (= 6.0.3.7)
+ actionview (6.1.3.2)
+ activesupport (= 6.1.3.2)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
- activejob (6.0.3.7)
- activesupport (= 6.0.3.7)
+ activejob (6.1.3.2)
+ activesupport (= 6.1.3.2)
globalid (>= 0.3.6)
- activemodel (6.0.3.7)
- activesupport (= 6.0.3.7)
- activerecord (6.0.3.7)
- activemodel (= 6.0.3.7)
- activesupport (= 6.0.3.7)
+ activemodel (6.1.3.2)
+ activesupport (= 6.1.3.2)
+ activerecord (6.1.3.2)
+ activemodel (= 6.1.3.2)
+ activesupport (= 6.1.3.2)
activerecord-explain-analyze (0.1.0)
activerecord (>= 4)
pg
- activestorage (6.0.3.7)
- actionpack (= 6.0.3.7)
- activejob (= 6.0.3.7)
- activerecord (= 6.0.3.7)
+ activestorage (6.1.3.2)
+ actionpack (= 6.1.3.2)
+ activejob (= 6.1.3.2)
+ activerecord (= 6.1.3.2)
+ activesupport (= 6.1.3.2)
marcel (~> 1.0.0)
- activesupport (6.0.3.7)
+ mini_mime (~> 1.0.2)
+ activesupport (6.1.3.2)
concurrent-ruby (~> 1.0, >= 1.0.2)
- i18n (>= 0.7, < 2)
- minitest (~> 5.1)
- tzinfo (~> 1.1)
- zeitwerk (~> 2.2, >= 2.2.2)
+ i18n (>= 1.6, < 2)
+ minitest (>= 5.1)
+ tzinfo (~> 2.0)
+ zeitwerk (~> 2.3)
acts-as-taggable-on (7.0.0)
activerecord (>= 5.0, < 6.2)
adamantium (0.2.0)
@@ -744,7 +748,7 @@ GEM
mime-types-data (3.2020.0512)
mini_histogram (0.3.1)
mini_magick (4.10.1)
- mini_mime (1.1.0)
+ mini_mime (1.0.2)
mini_portile2 (2.5.0)
minitest (5.11.3)
mixlib-cli (2.1.8)
@@ -963,20 +967,20 @@ GEM
rack-test (1.1.0)
rack (>= 1.0, < 3)
rack-timeout (0.5.2)
- rails (6.0.3.7)
- actioncable (= 6.0.3.7)
- actionmailbox (= 6.0.3.7)
- actionmailer (= 6.0.3.7)
- actionpack (= 6.0.3.7)
- actiontext (= 6.0.3.7)
- actionview (= 6.0.3.7)
- activejob (= 6.0.3.7)
- activemodel (= 6.0.3.7)
- activerecord (= 6.0.3.7)
- activestorage (= 6.0.3.7)
- activesupport (= 6.0.3.7)
- bundler (>= 1.3.0)
- railties (= 6.0.3.7)
+ rails (6.1.3.2)
+ actioncable (= 6.1.3.2)
+ actionmailbox (= 6.1.3.2)
+ actionmailer (= 6.1.3.2)
+ actionpack (= 6.1.3.2)
+ actiontext (= 6.1.3.2)
+ actionview (= 6.1.3.2)
+ activejob (= 6.1.3.2)
+ activemodel (= 6.1.3.2)
+ activerecord (= 6.1.3.2)
+ activestorage (= 6.1.3.2)
+ activesupport (= 6.1.3.2)
+ bundler (>= 1.15.0)
+ railties (= 6.1.3.2)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1)
@@ -990,12 +994,12 @@ GEM
rails-i18n (6.0.0)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 7)
- railties (6.0.3.7)
- actionpack (= 6.0.3.7)
- activesupport (= 6.0.3.7)
+ railties (6.1.3.2)
+ actionpack (= 6.1.3.2)
+ activesupport (= 6.1.3.2)
method_source
rake (>= 0.8.7)
- thor (>= 0.20.3, < 2.0)
+ thor (~> 1.0)
rainbow (3.0.0)
raindrops (0.19.1)
rake (13.0.3)
@@ -1306,8 +1310,8 @@ GEM
tty-screen (~> 0.8)
wisper (~> 2.0)
tty-screen (0.8.1)
- tzinfo (1.2.9)
- thread_safe (~> 0.1)
+ tzinfo (2.0.4)
+ concurrent-ruby (~> 1.0)
u2f (0.2.1)
uber (0.1.0)
unf (0.1.4)
@@ -1585,7 +1589,7 @@ DEPENDENCIES
rack-oauth2 (~> 1.16.0)
rack-proxy (~> 0.6.0)
rack-timeout (~> 0.5.1)
- rails (~> 6.0.3.7)
+ rails (~> 6.1.3.2)
rails-controller-testing
rails-i18n (~> 6.0)
rainbow (~> 3.0)
diff --git a/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue b/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue
index 8bcaa5df7b6..bec33ce2f44 100644
--- a/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue
+++ b/app/assets/javascripts/runner/components/runner_filtered_search_bar.vue
@@ -113,6 +113,7 @@ export default {
this.$emit('input', {
filters,
sort,
+ pagination: { page: 1 },
});
},
onSort(sort) {
@@ -121,6 +122,7 @@ export default {
this.$emit('input', {
filters,
sort,
+ pagination: { page: 1 },
});
},
},
diff --git a/app/assets/javascripts/runner/components/runner_list.vue b/app/assets/javascripts/runner/components/runner_list.vue
index f58f271c9ee..39d354db46d 100644
--- a/app/assets/javascripts/runner/components/runner_list.vue
+++ b/app/assets/javascripts/runner/components/runner_list.vue
@@ -136,7 +136,5 @@ export default {
<!-- TODO add actions to update runners -->
</template>
</gl-table>
-
- <!-- TODO implement pagination -->
</div>
</template>
diff --git a/app/assets/javascripts/runner/components/runner_pagination.vue b/app/assets/javascripts/runner/components/runner_pagination.vue
new file mode 100644
index 00000000000..8645b90f5cd
--- /dev/null
+++ b/app/assets/javascripts/runner/components/runner_pagination.vue
@@ -0,0 +1,57 @@
+<script>
+import { GlPagination } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlPagination,
+ },
+ props: {
+ value: {
+ required: false,
+ type: Object,
+ default: () => ({
+ page: 1,
+ }),
+ },
+ pageInfo: {
+ required: false,
+ type: Object,
+ default: () => ({}),
+ },
+ },
+ computed: {
+ prevPage() {
+ return this.pageInfo?.hasPreviousPage ? this.value?.page - 1 : null;
+ },
+ nextPage() {
+ return this.pageInfo?.hasNextPage ? this.value?.page + 1 : null;
+ },
+ },
+ methods: {
+ handlePageChange(page) {
+ if (page > this.value.page) {
+ this.$emit('input', {
+ page,
+ after: this.pageInfo.endCursor,
+ });
+ } else {
+ this.$emit('input', {
+ page,
+ before: this.pageInfo.startCursor,
+ });
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-pagination
+ :value="value.page"
+ :prev-page="prevPage"
+ :next-page="nextPage"
+ align="center"
+ class="gl-pagination gl-mt-3"
+ @input="handlePageChange"
+ />
+</template>
diff --git a/app/assets/javascripts/runner/constants.js b/app/assets/javascripts/runner/constants.js
index 51dc0afdd0b..c51825a0846 100644
--- a/app/assets/javascripts/runner/constants.js
+++ b/app/assets/javascripts/runner/constants.js
@@ -1,5 +1,7 @@
import { s__ } from '~/locale';
+export const RUNNER_PAGE_SIZE = 20;
+
export const I18N_DETAILS_TITLE = s__('Runners|Runner #%{runner_id}');
export const RUNNER_ENTITY_TYPE = 'Ci::Runner';
@@ -11,6 +13,9 @@ export const RUNNER_ENTITY_TYPE = 'Ci::Runner';
export const PARAM_KEY_STATUS = 'status';
export const PARAM_KEY_RUNNER_TYPE = 'runner_type';
export const PARAM_KEY_SORT = 'sort';
+export const PARAM_KEY_PAGE = 'page';
+export const PARAM_KEY_AFTER = 'after';
+export const PARAM_KEY_BEFORE = 'before';
// CiRunnerType
diff --git a/app/assets/javascripts/runner/graphql/get_runners.query.graphql b/app/assets/javascripts/runner/graphql/get_runners.query.graphql
index 1f094b72e79..3864dd5bf37 100644
--- a/app/assets/javascripts/runner/graphql/get_runners.query.graphql
+++ b/app/assets/javascripts/runner/graphql/get_runners.query.graphql
@@ -1,5 +1,23 @@
-query getRunners($status: CiRunnerStatus, $type: CiRunnerType, $sort: CiRunnerSort) {
- runners(status: $status, type: $type, sort: $sort) {
+#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
+
+query getRunners(
+ $before: String
+ $after: String
+ $first: Int
+ $last: Int
+ $status: CiRunnerStatus
+ $type: CiRunnerType
+ $sort: CiRunnerSort
+) {
+ runners(
+ before: $before
+ after: $after
+ first: $first
+ last: $last
+ status: $status
+ type: $type
+ sort: $sort
+ ) {
nodes {
id
description
@@ -13,5 +31,8 @@ query getRunners($status: CiRunnerStatus, $type: CiRunnerType, $sort: CiRunnerSo
tagList
contactedAt
}
+ pageInfo {
+ ...PageInfo
+ }
}
}
diff --git a/app/assets/javascripts/runner/runner_list/filtered_search_utils.js b/app/assets/javascripts/runner/runner_list/filtered_search_utils.js
index 4ae068c3eb6..2303dd587e1 100644
--- a/app/assets/javascripts/runner/runner_list/filtered_search_utils.js
+++ b/app/assets/javascripts/runner/runner_list/filtered_search_utils.js
@@ -3,7 +3,11 @@ import {
PARAM_KEY_STATUS,
PARAM_KEY_RUNNER_TYPE,
PARAM_KEY_SORT,
+ PARAM_KEY_PAGE,
+ PARAM_KEY_AFTER,
+ PARAM_KEY_BEFORE,
DEFAULT_SORT,
+ RUNNER_PAGE_SIZE,
} from '../constants';
const getValuesFromFilters = (paramKey, filters) => {
@@ -30,6 +34,23 @@ const getFilterFromParams = (paramKey, params) => {
});
};
+const getPaginationFromParams = (params) => {
+ const page = parseInt(params[PARAM_KEY_PAGE], 10);
+ const after = params[PARAM_KEY_AFTER];
+ const before = params[PARAM_KEY_BEFORE];
+
+ if (page && (before || after)) {
+ return {
+ page,
+ before,
+ after,
+ };
+ }
+ return {
+ page: 1,
+ };
+};
+
export const fromUrlQueryToSearch = (query = window.location.search) => {
const params = queryToObject(query, { gatherArrays: true });
@@ -39,10 +60,14 @@ export const fromUrlQueryToSearch = (query = window.location.search) => {
...getFilterFromParams(PARAM_KEY_RUNNER_TYPE, params),
],
sort: params[PARAM_KEY_SORT] || DEFAULT_SORT,
+ pagination: getPaginationFromParams(params),
};
};
-export const fromSearchToUrl = ({ filters = [], sort = null }, url = window.location.href) => {
+export const fromSearchToUrl = (
+ { filters = [], sort = null, pagination = {} },
+ url = window.location.href,
+) => {
const urlParams = {
[PARAM_KEY_STATUS]: getValuesFromFilters(PARAM_KEY_STATUS, filters),
[PARAM_KEY_RUNNER_TYPE]: getValuesFromFilters(PARAM_KEY_RUNNER_TYPE, filters),
@@ -52,10 +77,21 @@ export const fromSearchToUrl = ({ filters = [], sort = null }, url = window.loca
urlParams[PARAM_KEY_SORT] = sort;
}
+ // Remove pagination params for first page
+ if (pagination?.page === 1) {
+ urlParams[PARAM_KEY_PAGE] = null;
+ urlParams[PARAM_KEY_BEFORE] = null;
+ urlParams[PARAM_KEY_AFTER] = null;
+ } else {
+ urlParams[PARAM_KEY_PAGE] = pagination.page;
+ urlParams[PARAM_KEY_BEFORE] = pagination.before;
+ urlParams[PARAM_KEY_AFTER] = pagination.after;
+ }
+
return setUrlParams(urlParams, url, false, true, true);
};
-export const fromSearchToVariables = ({ filters = [], sort = null } = {}) => {
+export const fromSearchToVariables = ({ filters = [], sort = null, pagination = {} } = {}) => {
const variables = {};
// TODO Get more than one value when GraphQL API supports OR for "status"
@@ -68,5 +104,13 @@ export const fromSearchToVariables = ({ filters = [], sort = null } = {}) => {
variables.sort = sort;
}
+ if (pagination.before) {
+ variables.before = pagination.before;
+ variables.last = RUNNER_PAGE_SIZE;
+ } else {
+ variables.after = pagination.after;
+ variables.first = RUNNER_PAGE_SIZE;
+ }
+
return variables;
};
diff --git a/app/assets/javascripts/runner/runner_list/runner_list_app.vue b/app/assets/javascripts/runner/runner_list/runner_list_app.vue
index e0f3330fef5..93d1cf38b9b 100644
--- a/app/assets/javascripts/runner/runner_list/runner_list_app.vue
+++ b/app/assets/javascripts/runner/runner_list/runner_list_app.vue
@@ -4,6 +4,7 @@ import { updateHistory } from '~/lib/utils/url_utility';
import RunnerFilteredSearchBar from '../components/runner_filtered_search_bar.vue';
import RunnerList from '../components/runner_list.vue';
import RunnerManualSetupHelp from '../components/runner_manual_setup_help.vue';
+import RunnerPagination from '../components/runner_pagination.vue';
import RunnerTypeHelp from '../components/runner_type_help.vue';
import getRunnersQuery from '../graphql/get_runners.query.graphql';
import {
@@ -18,6 +19,7 @@ export default {
RunnerList,
RunnerManualSetupHelp,
RunnerTypeHelp,
+ RunnerPagination,
},
props: {
activeRunnersCount: {
@@ -32,7 +34,10 @@ export default {
data() {
return {
search: fromUrlQueryToSearch(),
- runners: [],
+ runners: {
+ items: [],
+ pageInfo: {},
+ },
};
},
apollo: {
@@ -41,8 +46,12 @@ export default {
variables() {
return this.variables;
},
- update({ runners }) {
- return runners?.nodes || [];
+ update(data) {
+ const { runners } = data;
+ return {
+ items: runners?.nodes || [],
+ pageInfo: runners?.pageInfo || {},
+ };
},
error(err) {
this.captureException(err);
@@ -57,17 +66,19 @@ export default {
return this.$apollo.queries.runners.loading;
},
noRunnersFound() {
- return !this.runnersLoading && !this.runners.length;
+ return !this.runnersLoading && !this.runners.items.length;
},
},
watch: {
- search() {
- // TODO Implement back button reponse using onpopstate
-
- updateHistory({
- url: fromSearchToUrl(this.search),
- title: document.title,
- });
+ search: {
+ deep: true,
+ handler() {
+ // TODO Implement back button reponse using onpopstate
+ updateHistory({
+ url: fromSearchToUrl(this.search),
+ title: document.title,
+ });
+ },
},
},
errorCaptured(err) {
@@ -99,11 +110,13 @@ export default {
<div v-if="noRunnersFound" class="gl-text-center gl-p-5">
{{ __('No runners found') }}
</div>
- <runner-list
- v-else
- :runners="runners"
- :loading="runnersLoading"
- :active-runners-count="activeRunnersCount"
- />
+ <template v-else>
+ <runner-list
+ :runners="runners.items"
+ :loading="runnersLoading"
+ :active-runners-count="activeRunnersCount"
+ />
+ <runner-pagination v-model="search.pagination" :page-info="runners.pageInfo" />
+ </template>
</div>
</template>
diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb
index c2f7fa2074c..0092743f96e 100644
--- a/app/helpers/dropdowns_helper.rb
+++ b/app/helpers/dropdowns_helper.rb
@@ -102,7 +102,7 @@ module DropdownsHelper
def dropdown_filter(placeholder, search_id: nil)
content_tag :div, class: "dropdown-input" do
- filter_output = search_field_tag search_id, nil, data: { qa_selector: "dropdown_input_field" }, class: "dropdown-input-field", placeholder: placeholder, autocomplete: 'off'
+ filter_output = search_field_tag search_id, nil, data: { qa_selector: "dropdown_input_field" }, id: nil, class: "dropdown-input-field", placeholder: placeholder, autocomplete: 'off'
filter_output << sprite_icon('search', css_class: 'dropdown-input-search')
filter_output << sprite_icon('close', size: 16, css_class: 'dropdown-input-clear js-dropdown-input-clear')
diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb
index 1d3242ca44a..e64e1c935dd 100644
--- a/app/helpers/tab_helper.rb
+++ b/app/helpers/tab_helper.rb
@@ -123,7 +123,21 @@ module TabHelper
def route_matches_pages?(pages)
Array(pages).compact.any? do |single_page|
- current_page?(single_page)
+ # We need to distinguish between Hash argument and other types of
+ # arguments (for example String) in order to fix deprecation kwargs
+ # warning.
+ #
+ # This can be refactored to
+ #
+ # current_page?(single_page)
+ #
+ # When we migrate to Ruby 3 or the Rails version contains the following:
+ # https://github.com/rails/rails/commit/81d90d81d0ee1fc1a649ab705119a71f2d04c8a2
+ if single_page.is_a?(Hash)
+ current_page?(**single_page)
+ else
+ current_page?(single_page)
+ end
end
end
diff --git a/app/models/ci/build_trace_chunk.rb b/app/models/ci/build_trace_chunk.rb
index 719511bbb8a..aaeff8bdd14 100644
--- a/app/models/ci/build_trace_chunk.rb
+++ b/app/models/ci/build_trace_chunk.rb
@@ -85,16 +85,10 @@ module Ci
# change the behavior in CE.
#
def with_read_consistency(build, &block)
- return yield unless consistent_reads_enabled?(build)
-
::Gitlab::Database::Consistency
.with_read_consistency(&block)
end
- def consistent_reads_enabled?(build)
- Feature.enabled?(:gitlab_ci_trace_read_consistency, build.project, type: :development, default_enabled: true)
- end
-
##
# Sometimes we do not want to read raw data. This method makes it easier
# to find attributes that are just metadata excluding raw data.
diff --git a/app/models/clusters/clusters_hierarchy.rb b/app/models/clusters/clusters_hierarchy.rb
index 125783e6ee1..162a1a3290d 100644
--- a/app/models/clusters/clusters_hierarchy.rb
+++ b/app/models/clusters/clusters_hierarchy.rb
@@ -4,9 +4,8 @@ module Clusters
class ClustersHierarchy
DEPTH_COLUMN = :depth
- def initialize(clusterable, include_management_project: true)
+ def initialize(clusterable)
@clusterable = clusterable
- @include_management_project = include_management_project
end
# Returns clusters in order from deepest to highest group
@@ -25,7 +24,7 @@ module Clusters
private
- attr_reader :clusterable, :include_management_project
+ attr_reader :clusterable
def recursive_cte
cte = Gitlab::SQL::RecursiveCTE.new(:clusters_cte)
@@ -39,7 +38,7 @@ module Clusters
raise ArgumentError, "unknown type for #{clusterable}"
end
- if clusterable.is_a?(::Project) && include_management_project
+ if clusterable.is_a?(::Project)
cte << same_namespace_management_clusters_query
end
@@ -71,7 +70,7 @@ module Clusters
# Only applicable if the clusterable is a project (most especially when
# requesting project.deployment_platform).
def depth_order_clause
- return { DEPTH_COLUMN => :asc } unless clusterable.is_a?(::Project) && include_management_project
+ return { DEPTH_COLUMN => :asc } unless clusterable.is_a?(::Project)
order = <<~SQL
(CASE clusters.management_project_id
diff --git a/app/models/concerns/bulk_insert_safe.rb b/app/models/concerns/bulk_insert_safe.rb
index 3748e77e933..908f0b6a7e2 100644
--- a/app/models/concerns/bulk_insert_safe.rb
+++ b/app/models/concerns/bulk_insert_safe.rb
@@ -141,6 +141,12 @@ module BulkInsertSafe
raise ArgumentError, "returns needs to be :ids or nil"
end
+ # Handle insertions for tables with a composite primary key
+ primary_keys = connection.schema_cache.primary_keys(table_name)
+ if unique_by.blank? && primary_key != primary_keys
+ unique_by = primary_keys
+ end
+
transaction do
items.each_slice(batch_size).flat_map do |item_batch|
attributes = _bulk_insert_item_attributes(
diff --git a/app/models/concerns/deployment_platform.rb b/app/models/concerns/deployment_platform.rb
index 02f7711e927..b6245e29746 100644
--- a/app/models/concerns/deployment_platform.rb
+++ b/app/models/concerns/deployment_platform.rb
@@ -10,10 +10,6 @@ module DeploymentPlatform
private
- def cluster_management_project_enabled?
- Feature.enabled?(:cluster_management_project, self, default_enabled: true)
- end
-
def find_deployment_platform(environment)
find_platform_kubernetes_with_cte(environment) ||
find_instance_cluster_platform_kubernetes(environment: environment)
@@ -21,13 +17,13 @@ module DeploymentPlatform
def find_platform_kubernetes_with_cte(environment)
if environment
- ::Clusters::ClustersHierarchy.new(self, include_management_project: cluster_management_project_enabled?)
+ ::Clusters::ClustersHierarchy.new(self)
.base_and_ancestors
.enabled
.on_environment(environment, relevant_only: true)
.first&.platform_kubernetes
else
- Clusters::ClustersHierarchy.new(self, include_management_project: cluster_management_project_enabled?).base_and_ancestors
+ Clusters::ClustersHierarchy.new(self).base_and_ancestors
.enabled.default_environment
.first&.platform_kubernetes
end
diff --git a/app/models/concerns/enum_with_nil.rb b/app/models/concerns/enum_with_nil.rb
index 6d0a21cf070..c66942025d7 100644
--- a/app/models/concerns/enum_with_nil.rb
+++ b/app/models/concerns/enum_with_nil.rb
@@ -11,14 +11,6 @@ module EnumWithNil
# override auto-defined methods only for the
# key which uses nil value
definitions.each do |name, values|
- next unless key_with_nil = values.key(nil)
-
- # E.g. for enum_with_nil failure_reason: { unknown_failure: nil }
- # this overrides auto-generated method `unknown_failure?`
- define_method("#{key_with_nil}?") do
- self[name].nil?
- end
-
# E.g. for enum_with_nil failure_reason: { unknown_failure: nil }
# this overrides auto-generated method `failure_reason`
define_method(name) do
diff --git a/app/models/project_authorization.rb b/app/models/project_authorization.rb
index 1fed166e4d0..64e768007ee 100644
--- a/app/models/project_authorization.rb
+++ b/app/models/project_authorization.rb
@@ -29,6 +29,15 @@ class ProjectAuthorization < ApplicationRecord
EOF
end
end
+
+ # This method overrides its ActiveRecord's version in order to work correctly
+ # with composite primary keys and fix the tests for Rails 6.1
+ #
+ # Consider using BulkInsertSafe module instead since we plan to refactor it in
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/331264
+ def self.insert_all(attributes)
+ super(attributes, unique_by: connection.schema_cache.primary_keys(table_name))
+ end
end
ProjectAuthorization.prepend_mod_with('ProjectAuthorization')
diff --git a/app/views/clusters/clusters/_advanced_settings.html.haml b/app/views/clusters/clusters/_advanced_settings.html.haml
index 7f508fd0a59..c84b3a923ca 100644
--- a/app/views/clusters/clusters/_advanced_settings.html.haml
+++ b/app/views/clusters/clusters/_advanced_settings.html.haml
@@ -16,7 +16,7 @@
.sub-section.form-group
= form_for @cluster, url: clusterable.cluster_path(@cluster), as: :cluster, html: { class: 'cluster_management_form' } do |field|
%h4
- = s_('ClusterIntegration|Cluster management project (alpha)')
+ = s_('ClusterIntegration|Cluster management project')
%p
= project_select_tag('cluster[management_project_id]', class: 'hidden-filter-value', toggle_class: 'js-project-search js-project-filter js-filter-submit', dropdown_class: 'dropdown-menu-selectable dropdown-menu-project js-filter-submit',
diff --git a/app/views/projects/merge_requests/tabs/_tab.html.haml b/app/views/projects/merge_requests/tabs/_tab.html.haml
index dcd8db90509..9d942da8098 100644
--- a/app/views/projects/merge_requests/tabs/_tab.html.haml
+++ b/app/views/projects/merge_requests/tabs/_tab.html.haml
@@ -2,6 +2,8 @@
- tab_class = local_assigns.fetch(:class, nil)
- qa_selector = local_assigns.fetch(:qa_selector, nil)
- id = local_assigns.fetch(:id, nil)
+- attrs = { class: [tab_class, ("active" if params[:tab] == tab_name)], data: { qa_selector: qa_selector } }
+- attrs[:id] = id if id.present?
-%li{ class: [tab_class, ("active" if params[:tab] == tab_name)], id: id, data: { qa_selector: qa_selector } }
+%li{ attrs }
= yield
diff --git a/cable/config.ru b/cable/config.ru
index c50bc41511d..421aee38346 100644
--- a/cable/config.ru
+++ b/cable/config.ru
@@ -5,6 +5,6 @@ Rails.application.eager_load!
ACTION_CABLE_SERVER = true
-use ActionDispatch::RequestId
+use ActionDispatch::RequestId, header: Rails.application.config.action_dispatch.request_id_header
run ActionCable.server
diff --git a/config/feature_flags/development/cluster_management_project.yml b/config/feature_flags/development/cluster_management_project.yml
deleted file mode 100644
index bcce2e027ff..00000000000
--- a/config/feature_flags/development/cluster_management_project.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: cluster_management_project
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/17866
-rollout_issue_url:
-milestone: '12.4'
-type: development
-group: group::configure
-default_enabled: true
diff --git a/config/feature_flags/development/gitlab_ci_trace_read_consistency.yml b/config/feature_flags/development/gitlab_ci_trace_read_consistency.yml
deleted file mode 100644
index ee0b4e46924..00000000000
--- a/config/feature_flags/development/gitlab_ci_trace_read_consistency.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: gitlab_ci_trace_read_consistency
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46976
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/320938
-milestone: '13.9'
-type: development
-group: group::continuous integration
-default_enabled: true
diff --git a/config/initializers/labkit_middleware.rb b/config/initializers/labkit_middleware.rb
index 748666b6cd7..771adabfeeb 100644
--- a/config/initializers/labkit_middleware.rb
+++ b/config/initializers/labkit_middleware.rb
@@ -3,22 +3,6 @@
# partial backport of https://github.com/rails/rails/pull/38169
# this is in order to be able to re-order rack middlewares.
-if ActionDispatch::MiddlewareStack.method_defined?(:move)
- warn "`move` is now defined in in ActionDispatch itself: https://github.com/rails/rails/pull/38169, please remove this patch from #{__FILE__}"
-else
- module ActionDispatch
- class MiddlewareStack
- def move(target, source)
- source_index = assert_index(source, :before)
- source_middleware = middlewares.delete_at(source_index)
-
- target_index = assert_index(target, :before)
- middlewares.insert(target_index, source_middleware)
- end
- end
- end
-end
-
unless Rails::Configuration::MiddlewareStackProxy.method_defined?(:move)
module Rails
module Configuration
diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md
index a7f4fc10655..52aa0f5ab4e 100644
--- a/doc/administration/audit_events.md
+++ b/doc/administration/audit_events.md
@@ -147,6 +147,17 @@ recorded:
Instance events can also be accessed via the [Instance Audit Events API](../api/audit_events.md#instance-audit-events).
+### Sign-in events **(FREE)**
+
+Successful sign-in events are the only Audit Events available at all tiers. To see
+successful sign-in events:
+
+1. Select your avatar.
+1. Select **Edit profile > Authentication log**.
+
+After upgrading from GitLab Free to a paid tier, successful sign-in events are the only Audit
+Events visible in Audit Events views until more events are logged.
+
### Missing events
Some events are not tracked in Audit Events. See the following
diff --git a/doc/administration/repository_storage_paths.md b/doc/administration/repository_storage_paths.md
index e46a010222f..7badfa12e3d 100644
--- a/doc/administration/repository_storage_paths.md
+++ b/doc/administration/repository_storage_paths.md
@@ -154,5 +154,5 @@ often it is chosen. That is, `(storage weight) / (sum of all weights) * 100 = ch
## Move repositories
-To move a repository to a different repository path, use the same process as
-[migrating to Gitaly Cluster](gitaly/praefect.md#migrate-to-gitaly-cluster).
+To move a repository to a different repository storage (for example, from `default` to `storage2`), use the
+same process as [migrating to Gitaly Cluster](gitaly/praefect.md#migrate-to-gitaly-cluster).
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 5b96bcb22e2..0f3d9cdd334 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -2597,3 +2597,28 @@ GET /projects/:id/snapshot
|-----------|----------------|------------------------|-------------|
| `id` | integer/string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). |
| `wiki` | boolean | **{dotted-circle}** No | Whether to download the wiki, rather than project, repository. |
+
+## Get the path to repository storage
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/29861) in GitLab 14.0.
+
+Get the path to repository storage for specified project. Available for administrators only.
+
+```plaintext
+GET /projects/:id/storage
+```
+
+| Attribute | Type | Required | Description |
+|--------------|----------------|------------------------|-------------|
+| `id` | integer/string | **{check-circle}** Yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). |
+
+```json
+[
+ {
+ "project_id": 1,
+ "disk_path": "@hashed/6b/86/6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b",
+ "created_at": "2012-10-12T17:04:47Z",
+ "repository_storage": "default"
+ }
+]
+```
diff --git a/doc/topics/git/bisect.md b/doc/topics/git/bisect.md
index 8af77031c93..e587a51ba17 100644
--- a/doc/topics/git/bisect.md
+++ b/doc/topics/git/bisect.md
@@ -1,11 +1,11 @@
---
-stage: none
-group: unassigned
+stage: Create
+group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
comments: false
---
-# Bisect
+# Bisect **(FREE)**
- Find a commit that introduced a bug
- Works through a process of elimination
diff --git a/doc/topics/git/cherry_picking.md b/doc/topics/git/cherry_picking.md
index 5a0867371bb..4a875e25e1b 100644
--- a/doc/topics/git/cherry_picking.md
+++ b/doc/topics/git/cherry_picking.md
@@ -5,7 +5,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
comments: false
---
-# Cherry Pick
+# Cherry pick **(FREE)**
Given an existing commit on one branch, apply the change to another branch.
diff --git a/doc/topics/git/feature_branching.md b/doc/topics/git/feature_branching.md
index f6233bddb18..f0ded5511ee 100644
--- a/doc/topics/git/feature_branching.md
+++ b/doc/topics/git/feature_branching.md
@@ -1,11 +1,11 @@
---
-stage: none
-group: unassigned
+stage: Create
+group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
comments: false
---
-# Feature branching
+# Feature branching **(FREE)**
- Efficient parallel workflow for teams
- Develop each feature in a branch
diff --git a/doc/topics/git/getting_started.md b/doc/topics/git/getting_started.md
index 2c3d5fe15de..7e04eae622f 100644
--- a/doc/topics/git/getting_started.md
+++ b/doc/topics/git/getting_started.md
@@ -1,11 +1,11 @@
---
-stage: none
-group: unassigned
+stage: Create
+group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
comments: false
---
-# Getting Started
+# Getting started **(FREE)**
## Instantiating Repositories
diff --git a/doc/topics/git/git_add.md b/doc/topics/git/git_add.md
index d136b9151bc..e15a1e9a60d 100644
--- a/doc/topics/git/git_add.md
+++ b/doc/topics/git/git_add.md
@@ -1,11 +1,11 @@
---
-stage: none
-group: unassigned
+stage: Create
+group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
comments: false
---
-# Git Add
+# Git Add **(FREE)**
Adds content to the index or staging area.
diff --git a/doc/topics/git/git_log.md b/doc/topics/git/git_log.md
index ae4ae69ce76..3988d7f7ac9 100644
--- a/doc/topics/git/git_log.md
+++ b/doc/topics/git/git_log.md
@@ -1,11 +1,11 @@
---
-stage: none
-group: unassigned
+stage: Create
+group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
comments: false
---
-# Git Log
+# Git Log **(FREE)**
Git log lists commit history. It allows searching and filtering.
diff --git a/doc/topics/git/merge_conflicts.md b/doc/topics/git/merge_conflicts.md
index 66771559298..bf69190030c 100644
--- a/doc/topics/git/merge_conflicts.md
+++ b/doc/topics/git/merge_conflicts.md
@@ -1,11 +1,11 @@
---
-stage: none
-group: unassigned
+stage: Create
+group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
comments: false
---
-# Merge conflicts
+# Merge conflicts **(FREE)**
- Happen often
- Learning to fix conflicts is hard
diff --git a/doc/topics/git/rollback_commits.md b/doc/topics/git/rollback_commits.md
index 34c2d9687bb..478dce179d2 100644
--- a/doc/topics/git/rollback_commits.md
+++ b/doc/topics/git/rollback_commits.md
@@ -1,11 +1,11 @@
---
-stage: none
-group: unassigned
+stage: Create
+group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
comments: false
---
-# Rollback Commits
+# Rollback commits **(FREE)**
## Undo Commits
diff --git a/doc/topics/git/stash.md b/doc/topics/git/stash.md
index 051103e5f4b..d321795e034 100644
--- a/doc/topics/git/stash.md
+++ b/doc/topics/git/stash.md
@@ -1,11 +1,11 @@
---
-stage: none
-group: unassigned
+stage: Create
+group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
comments: false
---
-# Git Stash
+# Git Stash **(FREE)**
We use `git stash` to store our changes when they are not ready to be committed
and we need to change to a different branch.
diff --git a/doc/topics/git/subtree.md b/doc/topics/git/subtree.md
index 54461915a05..0bf89668405 100644
--- a/doc/topics/git/subtree.md
+++ b/doc/topics/git/subtree.md
@@ -1,11 +1,11 @@
---
-stage: none
-group: unassigned
+stage: Create
+group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
comments: false
---
-# Subtree
+# Subtree **(FREE)**
- Used when there are nested repositories.
- Not recommended when the amount of dependencies is too large.
diff --git a/doc/topics/git/tags.md b/doc/topics/git/tags.md
index 70580ecf778..6e0622273bb 100644
--- a/doc/topics/git/tags.md
+++ b/doc/topics/git/tags.md
@@ -4,7 +4,7 @@ group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
-# Tags
+# Tags **(FREE)**
Tags are useful for marking certain deployments and releases for later
reference. Git supports two types of tags:
diff --git a/doc/topics/git/unstage.md b/doc/topics/git/unstage.md
index 30d26854135..b5f7c01de24 100644
--- a/doc/topics/git/unstage.md
+++ b/doc/topics/git/unstage.md
@@ -1,11 +1,11 @@
---
-stage: none
-group: unassigned
+stage: Create
+group: Source Code
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
comments: false
---
-# Unstage
+# Unstage **(FREE)**
- To remove files from stage use reset HEAD where HEAD is the last commit of the current branch. This unstages the file but maintain the modifications.
diff --git a/doc/user/clusters/management_project.md b/doc/user/clusters/management_project.md
index e728577e194..0a24bca67a5 100644
--- a/doc/user/clusters/management_project.md
+++ b/doc/user/clusters/management_project.md
@@ -6,10 +6,6 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Cluster management project **(FREE)**
-WARNING:
-This is an _alpha_ feature, and it is subject to change at any time without
-prior notice.
-
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/32810) in GitLab 12.5
A project can be designated as the management project for a cluster.
diff --git a/lib/api/entities/project_repository_storage.rb b/lib/api/entities/project_repository_storage.rb
new file mode 100644
index 00000000000..0816bebde2c
--- /dev/null
+++ b/lib/api/entities/project_repository_storage.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module API
+ module Entities
+ class ProjectRepositoryStorage < Grape::Entity
+ include Gitlab::Routing
+
+ expose :disk_path do |project|
+ project.repository.disk_path
+ end
+
+ expose :id, as: :project_id
+ expose :repository_storage, :created_at
+ end
+ end
+end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 4e8786fbe1f..bf55618768c 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -660,6 +660,18 @@ module API
render_api_error!("Failed to transfer project #{user_project.errors.messages}", 400)
end
end
+
+ desc 'Show the storage information' do
+ success Entities::ProjectRepositoryStorage
+ end
+ params do
+ requires :id, type: String, desc: 'ID of a project'
+ end
+ get ':id/storage', feature_category: :projects do
+ authenticated_as_admin!
+
+ present user_project, with: Entities::ProjectRepositoryStorage, current_user: current_user
+ end
end
end
end
diff --git a/lib/gitlab/ci/trace/chunked_io.rb b/lib/gitlab/ci/trace/chunked_io.rb
index 7c2e39b1e53..9f24ba99201 100644
--- a/lib/gitlab/ci/trace/chunked_io.rb
+++ b/lib/gitlab/ci/trace/chunked_io.rb
@@ -229,13 +229,8 @@ module Gitlab
def next_chunk
@chunks_cache[chunk_index] = begin
- if ::Ci::BuildTraceChunk.consistent_reads_enabled?(build)
- ::Ci::BuildTraceChunk
- .safe_find_or_create_by(build: build, chunk_index: chunk_index)
- else
- ::Ci::BuildTraceChunk
- .new(build: build, chunk_index: chunk_index)
- end
+ ::Ci::BuildTraceChunk
+ .safe_find_or_create_by(build: build, chunk_index: chunk_index)
end
end
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 3583338c58a..1dba3811519 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -61,7 +61,7 @@ module Gitlab
end
def self.config
- default_config_hash = ActiveRecord::Base.configurations.find_db_config(Rails.env)&.config || {}
+ default_config_hash = ActiveRecord::Base.configurations.find_db_config(Rails.env)&.configuration_hash || {}
default_config_hash.with_indifferent_access.tap do |hash|
# Match config/initializers/database_config.rb
diff --git a/lib/gitlab/metrics/samplers/database_sampler.rb b/lib/gitlab/metrics/samplers/database_sampler.rb
index 0a0ac6c5386..5d7f434b660 100644
--- a/lib/gitlab/metrics/samplers/database_sampler.rb
+++ b/lib/gitlab/metrics/samplers/database_sampler.rb
@@ -45,8 +45,8 @@ module Gitlab
def labels_for_class(klass)
{
- host: klass.connection_config[:host],
- port: klass.connection_config[:port],
+ host: klass.connection_db_config.host,
+ port: klass.connection_db_config.configuration_hash[:port],
class: klass.to_s
}
end
diff --git a/lib/release_highlights/validator/entry.rb b/lib/release_highlights/validator/entry.rb
index 133afcb52ae..dff55eead2f 100644
--- a/lib/release_highlights/validator/entry.rb
+++ b/lib/release_highlights/validator/entry.rb
@@ -46,7 +46,10 @@ module ReleaseHighlights
def add_line_numbers_to_errors!
errors.messages.each do |attribute, messages|
- messages.map! { |m| "#{m} (line #{line_number_for(attribute)})" }
+ extended_messages = messages.map { |m| "#{m} (line #{line_number_for(attribute)})" }
+
+ errors.delete(attribute)
+ extended_messages.each { |extended_message| errors.add(attribute, extended_message) }
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index c244c486c5d..1bd78281a6c 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -7075,7 +7075,7 @@ msgstr ""
msgid "ClusterIntegration|Cluster Region"
msgstr ""
-msgid "ClusterIntegration|Cluster management project (alpha)"
+msgid "ClusterIntegration|Cluster management project"
msgstr ""
msgid "ClusterIntegration|Cluster name is required."
diff --git a/package.json b/package.json
index 067a9745026..a0a136d75dc 100644
--- a/package.json
+++ b/package.json
@@ -57,8 +57,8 @@
"@gitlab/tributejs": "1.0.0",
"@gitlab/ui": "29.31.0",
"@gitlab/visual-review-tools": "1.6.1",
- "@rails/actioncable": "^6.0.3-4",
- "@rails/ujs": "^6.0.3-4",
+ "@rails/actioncable": "6.1.3-2",
+ "@rails/ujs": "6.1.3-2",
"@sentry/browser": "^5.22.3",
"@sourcegraph/code-host-integration": "0.0.57",
"@tiptap/core": "^2.0.0-beta.54",
diff --git a/qa/qa/specs/features/browser_ui/5_package/container_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/container_registry_spec.rb
index 635313b9fb0..9131cad7244 100644
--- a/qa/qa/specs/features/browser_ui/5_package/container_registry_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/container_registry_spec.rb
@@ -31,7 +31,13 @@ module QA
DOCKER_TLS_VERIFY: 1
DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
before_script:
- - until docker info; do sleep 1; done
+ - |
+ echo "Waiting for docker to start..."
+ for i in $(seq 1 30)
+ do
+ docker info && break
+ sleep 1s
+ done
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $IMAGE_TAG .
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 0235d7eb95a..218aa04dd3f 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -704,7 +704,7 @@ RSpec.describe ApplicationController do
get :index
- expect(response.headers['Cache-Control']).to eq 'max-age=0, private, must-revalidate, no-store'
+ expect(response.headers['Cache-Control']).to eq 'no-store'
expect(response.headers['Pragma']).to eq 'no-cache'
end
@@ -740,7 +740,7 @@ RSpec.describe ApplicationController do
it 'sets no-cache headers', :aggregate_failures do
subject
- expect(response.headers['Cache-Control']).to eq 'no-cache, no-store'
+ expect(response.headers['Cache-Control']).to eq 'no-store'
expect(response.headers['Pragma']).to eq 'no-cache'
expect(response.headers['Expires']).to eq 'Fri, 01 Jan 1990 00:00:00 GMT'
end
diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb
index 32ac83847aa..3a2986f6cbe 100644
--- a/spec/controllers/search_controller_spec.rb
+++ b/spec/controllers/search_controller_spec.rb
@@ -258,7 +258,7 @@ RSpec.describe SearchController do
expect(response).to have_gitlab_http_status(:ok)
- expect(response.headers['Cache-Control']).to include('max-age=60, private')
+ expect(response.headers['Cache-Control']).to eq('no-store')
end
end
diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb
index e0e2a012c81..325d675a68c 100644
--- a/spec/db/schema_spec.rb
+++ b/spec/db/schema_spec.rb
@@ -269,7 +269,7 @@ RSpec.describe 'Database schema' do
sql = <<~SQL
SELECT table_name, column_name, data_type
FROM information_schema.columns
- WHERE table_catalog = '#{ApplicationRecord.connection_config[:database]}'
+ WHERE table_catalog = '#{ApplicationRecord.connection_db_config.database}'
AND table_schema = 'public'
AND table_name NOT LIKE 'pg_%'
AND data_type = 'jsonb'
diff --git a/spec/features/projects/badges/pipeline_badge_spec.rb b/spec/features/projects/badges/pipeline_badge_spec.rb
index bfc924b5d9b..9d8f9872a1a 100644
--- a/spec/features/projects/badges/pipeline_badge_spec.rb
+++ b/spec/features/projects/badges/pipeline_badge_spec.rb
@@ -68,7 +68,7 @@ RSpec.describe 'Pipeline Badge' do
visit pipeline_project_badges_path(project, ref: ref, format: :svg)
expect(page.status_code).to eq(200)
- expect(page.response_headers['Cache-Control']).to include 'no-cache'
+ expect(page.response_headers['Cache-Control']).to eq('no-store')
end
end
diff --git a/spec/frontend/runner/components/runner_filtered_search_bar_spec.js b/spec/frontend/runner/components/runner_filtered_search_bar_spec.js
index aa1752d187f..61a8f821b30 100644
--- a/spec/frontend/runner/components/runner_filtered_search_bar_spec.js
+++ b/spec/frontend/runner/components/runner_filtered_search_bar_spec.js
@@ -118,6 +118,7 @@ describe('RunnerList', () => {
{
filters: mockFilters,
sort: mockDefaultSort,
+ pagination: { page: 1 },
},
]);
});
@@ -129,6 +130,7 @@ describe('RunnerList', () => {
{
filters: [],
sort: mockOtherSort,
+ pagination: { page: 1 },
},
]);
});
diff --git a/spec/frontend/runner/components/runner_pagination_spec.js b/spec/frontend/runner/components/runner_pagination_spec.js
new file mode 100644
index 00000000000..59feb32dd2a
--- /dev/null
+++ b/spec/frontend/runner/components/runner_pagination_spec.js
@@ -0,0 +1,160 @@
+import { GlPagination } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import RunnerPagination from '~/runner/components/runner_pagination.vue';
+
+const mockStartCursor = 'START_CURSOR';
+const mockEndCursor = 'END_CURSOR';
+
+describe('RunnerPagination', () => {
+ let wrapper;
+
+ const findPagination = () => wrapper.findComponent(GlPagination);
+
+ const createComponent = ({ page = 1, hasPreviousPage = false, hasNextPage = true } = {}) => {
+ wrapper = mount(RunnerPagination, {
+ propsData: {
+ value: {
+ page,
+ },
+ pageInfo: {
+ hasPreviousPage,
+ hasNextPage,
+ startCursor: mockStartCursor,
+ endCursor: mockEndCursor,
+ },
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('When on the first page', () => {
+ beforeEach(() => {
+ createComponent({
+ page: 1,
+ hasPreviousPage: false,
+ hasNextPage: true,
+ });
+ });
+
+ it('Contains the current page information', () => {
+ expect(findPagination().props('value')).toBe(1);
+ expect(findPagination().props('prevPage')).toBe(null);
+ expect(findPagination().props('nextPage')).toBe(2);
+ });
+
+ it('Shows prev page disabled', () => {
+ expect(findPagination().find('[aria-disabled]').text()).toBe('Prev');
+ });
+
+ it('Shows next page link', () => {
+ expect(findPagination().find('a').text()).toBe('Next');
+ });
+
+ it('Goes to the second page', () => {
+ findPagination().vm.$emit('input', 2);
+
+ expect(wrapper.emitted('input')[0]).toEqual([
+ {
+ after: mockEndCursor,
+ page: 2,
+ },
+ ]);
+ });
+ });
+
+ describe('When in between pages', () => {
+ beforeEach(() => {
+ createComponent({
+ page: 2,
+ hasPreviousPage: true,
+ hasNextPage: true,
+ });
+ });
+
+ it('Contains the current page information', () => {
+ expect(findPagination().props('value')).toBe(2);
+ expect(findPagination().props('prevPage')).toBe(1);
+ expect(findPagination().props('nextPage')).toBe(3);
+ });
+
+ it('Shows the next and previous pages', () => {
+ const links = findPagination().findAll('a');
+
+ expect(links).toHaveLength(2);
+ expect(links.at(0).text()).toBe('Prev');
+ expect(links.at(1).text()).toBe('Next');
+ });
+
+ it('Goes to the last page', () => {
+ findPagination().vm.$emit('input', 3);
+
+ expect(wrapper.emitted('input')[0]).toEqual([
+ {
+ after: mockEndCursor,
+ page: 3,
+ },
+ ]);
+ });
+
+ it('Goes to the first page', () => {
+ findPagination().vm.$emit('input', 1);
+
+ expect(wrapper.emitted('input')[0]).toEqual([
+ {
+ before: mockStartCursor,
+ page: 1,
+ },
+ ]);
+ });
+ });
+
+ describe('When in the last page', () => {
+ beforeEach(() => {
+ createComponent({
+ page: 3,
+ hasPreviousPage: true,
+ hasNextPage: false,
+ });
+ });
+
+ it('Contains the current page', () => {
+ expect(findPagination().props('value')).toBe(3);
+ expect(findPagination().props('prevPage')).toBe(2);
+ expect(findPagination().props('nextPage')).toBe(null);
+ });
+
+ it('Shows next page link', () => {
+ expect(findPagination().find('a').text()).toBe('Prev');
+ });
+
+ it('Shows next page disabled', () => {
+ expect(findPagination().find('[aria-disabled]').text()).toBe('Next');
+ });
+ });
+
+ describe('When only one page', () => {
+ beforeEach(() => {
+ createComponent({
+ page: 1,
+ hasPreviousPage: false,
+ hasNextPage: false,
+ });
+ });
+
+ it('does not display pagination', () => {
+ expect(wrapper.html()).toBe('');
+ });
+
+ it('Contains the current page', () => {
+ expect(findPagination().props('value')).toBe(1);
+ });
+
+ it('Shows no more page buttons', () => {
+ expect(findPagination().props('prevPage')).toBe(null);
+ expect(findPagination().props('nextPage')).toBe(null);
+ });
+ });
+});
diff --git a/spec/frontend/runner/mock_data.js b/spec/frontend/runner/mock_data.js
index 3c1b3a0b5e2..744942bfa73 100644
--- a/spec/frontend/runner/mock_data.js
+++ b/spec/frontend/runner/mock_data.js
@@ -31,6 +31,13 @@ export const runnersData = {
__typename: 'CiRunner',
},
],
+ pageInfo: {
+ endCursor: 'GRAPHQL_END_CURSOR',
+ startCursor: 'GRAPHQL_START_CURSOR',
+ hasNextPage: true,
+ hasPreviousPage: false,
+ __typename: 'PageInfo',
+ },
__typename: 'CiRunnerConnection',
},
},
diff --git a/spec/frontend/runner/runner_list/filtered_search_utils_spec.js b/spec/frontend/runner/runner_list/filtered_search_utils_spec.js
index e46821d6504..abbe05452d1 100644
--- a/spec/frontend/runner/runner_list/filtered_search_utils_spec.js
+++ b/spec/frontend/runner/runner_list/filtered_search_utils_spec.js
@@ -1,3 +1,4 @@
+import { RUNNER_PAGE_SIZE } from '~/runner/constants';
import {
fromUrlQueryToSearch,
fromSearchToUrl,
@@ -9,26 +10,28 @@ describe('search_params.js', () => {
{
name: 'a default query',
urlQuery: '',
- search: { filters: [], sort: 'CREATED_DESC' },
- graphqlVariables: { sort: 'CREATED_DESC' },
+ search: { filters: [], pagination: { page: 1 }, sort: 'CREATED_DESC' },
+ graphqlVariables: { sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE },
},
{
name: 'a single status',
urlQuery: '?status[]=ACTIVE',
search: {
filters: [{ type: 'status', value: { data: 'ACTIVE', operator: '=' } }],
+ pagination: { page: 1 },
sort: 'CREATED_DESC',
},
- graphqlVariables: { status: 'ACTIVE', sort: 'CREATED_DESC' },
+ graphqlVariables: { status: 'ACTIVE', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE },
},
{
name: 'single instance type',
urlQuery: '?runner_type[]=INSTANCE_TYPE',
search: {
filters: [{ type: 'runner_type', value: { data: 'INSTANCE_TYPE', operator: '=' } }],
+ pagination: { page: 1 },
sort: 'CREATED_DESC',
},
- graphqlVariables: { type: 'INSTANCE_TYPE', sort: 'CREATED_DESC' },
+ graphqlVariables: { type: 'INSTANCE_TYPE', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE },
},
{
name: 'multiple runner status',
@@ -38,9 +41,10 @@ describe('search_params.js', () => {
{ type: 'status', value: { data: 'ACTIVE', operator: '=' } },
{ type: 'status', value: { data: 'PAUSED', operator: '=' } },
],
+ pagination: { page: 1 },
sort: 'CREATED_DESC',
},
- graphqlVariables: { status: 'ACTIVE', sort: 'CREATED_DESC' },
+ graphqlVariables: { status: 'ACTIVE', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE },
},
{
name: 'multiple status, a single instance type and a non default sort',
@@ -50,9 +54,52 @@ describe('search_params.js', () => {
{ type: 'status', value: { data: 'ACTIVE', operator: '=' } },
{ type: 'runner_type', value: { data: 'INSTANCE_TYPE', operator: '=' } },
],
+ pagination: { page: 1 },
sort: 'CREATED_ASC',
},
- graphqlVariables: { status: 'ACTIVE', type: 'INSTANCE_TYPE', sort: 'CREATED_ASC' },
+ graphqlVariables: {
+ status: 'ACTIVE',
+ type: 'INSTANCE_TYPE',
+ sort: 'CREATED_ASC',
+ first: RUNNER_PAGE_SIZE,
+ },
+ },
+ {
+ name: 'the next page',
+ urlQuery: '?page=2&after=AFTER_CURSOR',
+ search: { filters: [], pagination: { page: 2, after: 'AFTER_CURSOR' }, sort: 'CREATED_DESC' },
+ graphqlVariables: { sort: 'CREATED_DESC', after: 'AFTER_CURSOR', first: RUNNER_PAGE_SIZE },
+ },
+ {
+ name: 'the previous page',
+ urlQuery: '?page=2&before=BEFORE_CURSOR',
+ search: {
+ filters: [],
+ pagination: { page: 2, before: 'BEFORE_CURSOR' },
+ sort: 'CREATED_DESC',
+ },
+ graphqlVariables: { sort: 'CREATED_DESC', before: 'BEFORE_CURSOR', last: RUNNER_PAGE_SIZE },
+ },
+ {
+ name:
+ 'the next page filtered by multiple status, a single instance type and a non default sort',
+ urlQuery:
+ '?status[]=ACTIVE&runner_type[]=INSTANCE_TYPE&sort=CREATED_ASC&page=2&after=AFTER_CURSOR',
+ search: {
+ filters: [
+ { type: 'status', value: { data: 'ACTIVE', operator: '=' } },
+ { type: 'runner_type', value: { data: 'INSTANCE_TYPE', operator: '=' } },
+ ],
+ pagination: { page: 2, after: 'AFTER_CURSOR' },
+ sort: 'CREATED_ASC',
+ },
+ graphqlVariables: {
+ status: 'ACTIVE',
+ type: 'INSTANCE_TYPE',
+ sort: 'CREATED_ASC',
+ after: 'AFTER_CURSOR',
+ first: RUNNER_PAGE_SIZE,
+ },
},
];
@@ -62,6 +109,24 @@ describe('search_params.js', () => {
expect(fromUrlQueryToSearch(urlQuery)).toEqual(search);
});
});
+
+ it('When a page cannot be parsed as a number, it defaults to `1`', () => {
+ expect(fromUrlQueryToSearch('?page=NONSENSE&after=AFTER_CURSOR').pagination).toEqual({
+ page: 1,
+ });
+ });
+
+ it('When a page is less than 1, it defaults to `1`', () => {
+ expect(fromUrlQueryToSearch('?page=0&after=AFTER_CURSOR').pagination).toEqual({
+ page: 1,
+ });
+ });
+
+ it('When a page with no cursor is given, it defaults to `1`', () => {
+ expect(fromUrlQueryToSearch('?page=2').pagination).toEqual({
+ page: 1,
+ });
+ });
});
describe('fromSearchToUrl', () => {
diff --git a/spec/frontend/runner/runner_list/runner_list_app_spec.js b/spec/frontend/runner/runner_list/runner_list_app_spec.js
index 19a5a60d2c1..e908e62db4f 100644
--- a/spec/frontend/runner/runner_list/runner_list_app_spec.js
+++ b/spec/frontend/runner/runner_list/runner_list_app_spec.js
@@ -1,5 +1,5 @@
import * as Sentry from '@sentry/browser';
-import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { createLocalVue, mount, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { TEST_HOST } from 'helpers/test_constants';
@@ -9,14 +9,17 @@ import { updateHistory } from '~/lib/utils/url_utility';
import RunnerFilteredSearchBar from '~/runner/components/runner_filtered_search_bar.vue';
import RunnerList from '~/runner/components/runner_list.vue';
import RunnerManualSetupHelp from '~/runner/components/runner_manual_setup_help.vue';
+import RunnerPagination from '~/runner/components/runner_pagination.vue';
import RunnerTypeHelp from '~/runner/components/runner_type_help.vue';
import {
CREATED_ASC,
+ CREATED_DESC,
DEFAULT_SORT,
INSTANCE_TYPE,
PARAM_KEY_STATUS,
STATUS_ACTIVE,
+ RUNNER_PAGE_SIZE,
} from '~/runner/constants';
import getRunnersQuery from '~/runner/graphql/get_runners.query.graphql';
import RunnerListApp from '~/runner/runner_list/runner_list_app.vue';
@@ -26,6 +29,7 @@ import { runnersData } from '../mock_data';
const mockRegistrationToken = 'MOCK_REGISTRATION_TOKEN';
const mockActiveRunnersCount = 2;
const mocKRunners = runnersData.data.runners.nodes;
+const mockPageInfo = runnersData.data.runners.pageInfo;
jest.mock('@sentry/browser');
jest.mock('~/lib/utils/url_utility', () => ({
@@ -44,6 +48,7 @@ describe('RunnerListApp', () => {
const findRunnerTypeHelp = () => wrapper.findComponent(RunnerTypeHelp);
const findRunnerManualSetupHelp = () => wrapper.findComponent(RunnerManualSetupHelp);
const findRunnerList = () => wrapper.findComponent(RunnerList);
+ const findRunnerPagination = () => wrapper.findComponent(RunnerPagination);
const findRunnerFilteredSearchBar = () => wrapper.findComponent(RunnerFilteredSearchBar);
const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => {
@@ -101,6 +106,7 @@ describe('RunnerListApp', () => {
status: undefined,
type: undefined,
sort: DEFAULT_SORT,
+ first: RUNNER_PAGE_SIZE,
});
});
@@ -128,6 +134,7 @@ describe('RunnerListApp', () => {
{ type: 'runner_type', value: { data: INSTANCE_TYPE, operator: '=' } },
],
sort: 'CREATED_DESC',
+ pagination: { page: 1 },
});
});
@@ -136,6 +143,7 @@ describe('RunnerListApp', () => {
status: STATUS_ACTIVE,
type: INSTANCE_TYPE,
sort: DEFAULT_SORT,
+ first: RUNNER_PAGE_SIZE,
});
});
});
@@ -159,6 +167,7 @@ describe('RunnerListApp', () => {
expect(mockRunnersQuery).toHaveBeenLastCalledWith({
status: STATUS_ACTIVE,
sort: CREATED_ASC,
+ first: RUNNER_PAGE_SIZE,
});
});
});
@@ -193,4 +202,37 @@ describe('RunnerListApp', () => {
expect(Sentry.captureException).toHaveBeenCalled();
});
});
+
+ describe('Pagination', () => {
+ beforeEach(() => {
+ createComponentWithApollo({ mountFn: mount });
+ });
+
+ it('more pages can be selected', () => {
+ expect(findRunnerPagination().text()).toMatchInterpolatedText('Prev Next');
+ });
+
+ it('cannot navigate to the previous page', () => {
+ expect(findRunnerPagination().find('[aria-disabled]').text()).toBe('Prev');
+ });
+
+ it('navigates to the next page', async () => {
+ const nextPageBtn = findRunnerPagination().find('a');
+ expect(nextPageBtn.text()).toBe('Next');
+
+ await nextPageBtn.trigger('click');
+
+ expect(mockRunnersQuery).toHaveBeenLastCalledWith({
+ sort: CREATED_DESC,
+ first: RUNNER_PAGE_SIZE,
+ after: expect.any(String),
+ });
+
+ expect(mockRunnersQuery).toHaveBeenLastCalledWith({
+ sort: CREATED_DESC,
+ first: RUNNER_PAGE_SIZE,
+ after: mockPageInfo.endCursor,
+ });
+ });
+ });
});
diff --git a/spec/lib/gitlab/ci/trace/chunked_io_spec.rb b/spec/lib/gitlab/ci/trace/chunked_io_spec.rb
index f878d24fe4b..63625244fe8 100644
--- a/spec/lib/gitlab/ci/trace/chunked_io_spec.rb
+++ b/spec/lib/gitlab/ci/trace/chunked_io_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe Gitlab::Ci::Trace::ChunkedIO, :clean_gitlab_redis_cache do
let(:chunked_io) { described_class.new(build) }
before do
- stub_feature_flags(ci_enable_live_trace: true, gitlab_ci_trace_read_consistency: true)
+ stub_feature_flags(ci_enable_live_trace: true)
end
describe "#initialize" do
diff --git a/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb b/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb
index 59f70165380..28d78c182ad 100644
--- a/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb
+++ b/spec/lib/gitlab/database/load_balancing/load_balancer_spec.rb
@@ -3,8 +3,7 @@
require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing::LoadBalancer, :request_store do
- let(:pool_spec) { ActiveRecord::Base.connection_pool.spec }
- let(:pool) { ActiveRecord::ConnectionAdapters::ConnectionPool.new(pool_spec) }
+ let(:pool) { Gitlab::Database.create_connection_pool(2) }
let(:conflict_error) { Class.new(RuntimeError) }
let(:lb) { described_class.new(%w(localhost localhost)) }
diff --git a/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb b/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb
index 79ddb450d7a..4f1d6302331 100644
--- a/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb
+++ b/spec/lib/gitlab/database/partitioning_migration_helpers/table_management_helpers_spec.rb
@@ -580,7 +580,7 @@ RSpec.describe Gitlab::Database::PartitioningMigrationHelpers::TableManagementHe
it 'idempotently cleans up after failed background migrations' do
expect(partitioned_model.count).to eq(0)
- partitioned_model.insert!(record2.attributes)
+ partitioned_model.insert(record2.attributes, unique_by: [:id, :created_at])
expect_next_instance_of(Gitlab::Database::PartitioningMigrationHelpers::BackfillPartitionedTable) do |backfill|
allow(backfill).to receive(:transaction_open?).and_return(false)
diff --git a/spec/lib/gitlab/database/with_lock_retries_spec.rb b/spec/lib/gitlab/database/with_lock_retries_spec.rb
index b08f39fc92a..df2c506e163 100644
--- a/spec/lib/gitlab/database/with_lock_retries_spec.rb
+++ b/spec/lib/gitlab/database/with_lock_retries_spec.rb
@@ -242,10 +242,10 @@ RSpec.describe Gitlab::Database::WithLockRetries do
let(:timing_configuration) { [[0.015.seconds, 0.025.seconds], [0.015.seconds, 0.025.seconds]] } # 15ms, 25ms
it 'executes `SET LOCAL lock_timeout` using the configured timeout value in milliseconds' do
- expect(ActiveRecord::Base.connection).to receive(:execute).with("SAVEPOINT active_record_1").and_call_original
- expect(ActiveRecord::Base.connection).to receive(:execute).with('RESET idle_in_transaction_session_timeout; RESET lock_timeout').and_call_original
+ expect(ActiveRecord::Base.connection).to receive(:execute).with("RESET idle_in_transaction_session_timeout; RESET lock_timeout").and_call_original
+ expect(ActiveRecord::Base.connection).to receive(:execute).with("SAVEPOINT active_record_1", "TRANSACTION").and_call_original
expect(ActiveRecord::Base.connection).to receive(:execute).with("SET LOCAL lock_timeout TO '15ms'").and_call_original
- expect(ActiveRecord::Base.connection).to receive(:execute).with("RELEASE SAVEPOINT active_record_1").and_call_original
+ expect(ActiveRecord::Base.connection).to receive(:execute).with("RELEASE SAVEPOINT active_record_1", "TRANSACTION").and_call_original
subject.run { }
end
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 2b31f3b4dee..5c9af1206c0 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -329,7 +329,7 @@ RSpec.describe Gitlab::Database do
expect(pool)
.to be_kind_of(ActiveRecord::ConnectionAdapters::ConnectionPool)
- expect(pool.spec.config[:pool]).to eq(5)
+ expect(pool.db_config.pool).to eq(5)
ensure
pool.disconnect!
end
@@ -339,7 +339,7 @@ RSpec.describe Gitlab::Database do
pool = described_class.create_connection_pool(5, '127.0.0.1')
begin
- expect(pool.spec.config[:host]).to eq('127.0.0.1')
+ expect(pool.db_config.host).to eq('127.0.0.1')
ensure
pool.disconnect!
end
@@ -349,8 +349,8 @@ RSpec.describe Gitlab::Database do
pool = described_class.create_connection_pool(5, '127.0.0.1', 5432)
begin
- expect(pool.spec.config[:host]).to eq('127.0.0.1')
- expect(pool.spec.config[:port]).to eq(5432)
+ expect(pool.db_config.host).to eq('127.0.0.1')
+ expect(pool.db_config.configuration_hash[:port]).to eq(5432)
ensure
pool.disconnect!
end
diff --git a/spec/lib/gitlab/import_export/import_failure_service_spec.rb b/spec/lib/gitlab/import_export/import_failure_service_spec.rb
index c8bb067d40c..51f1fc9c6a2 100644
--- a/spec/lib/gitlab/import_export/import_failure_service_spec.rb
+++ b/spec/lib/gitlab/import_export/import_failure_service_spec.rb
@@ -43,7 +43,7 @@ RSpec.describe Gitlab::ImportExport::ImportFailureService do
let(:importable) { create(:merge_request) }
it 'raise exception' do
- expect { subject }.to raise_exception(ActiveRecord::AssociationNotFoundError, "Association named 'import_failures' was not found on MergeRequest; perhaps you misspelled it?")
+ expect { subject }.to raise_exception(ActiveRecord::AssociationNotFoundError, /Association named 'import_failures' was not found on MergeRequest/)
end
end
end
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index 12bc5d9aa3c..c15910ef529 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -18,7 +18,7 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
it_behaves_like 'having unique enum values'
before do
- stub_feature_flags(ci_enable_live_trace: true, gitlab_ci_trace_read_consistency: true)
+ stub_feature_flags(ci_enable_live_trace: true)
stub_artifacts_object_storage
end
diff --git a/spec/models/clusters/clusters_hierarchy_spec.rb b/spec/models/clusters/clusters_hierarchy_spec.rb
index 5ac561eb2d0..5dd2fe98352 100644
--- a/spec/models/clusters/clusters_hierarchy_spec.rb
+++ b/spec/models/clusters/clusters_hierarchy_spec.rb
@@ -4,8 +4,8 @@ require 'spec_helper'
RSpec.describe Clusters::ClustersHierarchy do
describe '#base_and_ancestors' do
- def base_and_ancestors(clusterable, include_management_project: true)
- described_class.new(clusterable, include_management_project: include_management_project).base_and_ancestors
+ def base_and_ancestors(clusterable)
+ described_class.new(clusterable).base_and_ancestors
end
context 'project in nested group with clusters at every level' do
@@ -101,10 +101,6 @@ RSpec.describe Clusters::ClustersHierarchy do
expect(base_and_ancestors(management_project)).to eq([ancestor, child])
end
- it 'returns clusters for management_project' do
- expect(base_and_ancestors(management_project, include_management_project: false)).to eq([child, ancestor])
- end
-
it 'returns clusters for project' do
expect(base_and_ancestors(project)).to eq([child, ancestor])
end
diff --git a/spec/models/concerns/bulk_insert_safe_spec.rb b/spec/models/concerns/bulk_insert_safe_spec.rb
index ca6df506ee8..209ee1264d5 100644
--- a/spec/models/concerns/bulk_insert_safe_spec.rb
+++ b/spec/models/concerns/bulk_insert_safe_spec.rb
@@ -20,6 +20,13 @@ RSpec.describe BulkInsertSafe do
t.index :name, unique: true
end
+
+ create_table :bulk_insert_items_with_composite_pk, id: false, force: true do |t|
+ t.integer :id, null: true
+ t.string :name, null: true
+ end
+
+ execute("ALTER TABLE bulk_insert_items_with_composite_pk ADD PRIMARY KEY (id,name);")
end
end
@@ -27,6 +34,7 @@ RSpec.describe BulkInsertSafe do
ActiveRecord::Schema.define do
drop_table :bulk_insert_items, force: true
drop_table :bulk_insert_parent_items, force: true
+ drop_table :bulk_insert_items_with_composite_pk, force: true
end
end
@@ -227,5 +235,28 @@ RSpec.describe BulkInsertSafe do
end
end
end
+
+ context 'when a model with composite primary key is inserted' do
+ let_it_be(:bulk_insert_items_with_composite_pk_class) do
+ Class.new(ActiveRecord::Base) do
+ self.table_name = 'bulk_insert_items_with_composite_pk'
+
+ include BulkInsertSafe
+ end
+ end
+
+ let(:new_object) { bulk_insert_items_with_composite_pk_class.new(id: 1, name: 'composite') }
+
+ it 'successfully inserts an item' do
+ expect(ActiveRecord::InsertAll).to receive(:new)
+ .with(
+ bulk_insert_items_with_composite_pk_class, [new_object.as_json], on_duplicate: :raise, returning: false, unique_by: %w[id name]
+ ).and_call_original
+
+ expect { bulk_insert_items_with_composite_pk_class.bulk_insert!([new_object]) }.to(
+ change(bulk_insert_items_with_composite_pk_class, :count).from(0).to(1)
+ )
+ end
+ end
end
end
diff --git a/spec/models/concerns/deployment_platform_spec.rb b/spec/models/concerns/deployment_platform_spec.rb
index 2bb6aa27e21..7fa55184cf1 100644
--- a/spec/models/concerns/deployment_platform_spec.rb
+++ b/spec/models/concerns/deployment_platform_spec.rb
@@ -254,20 +254,8 @@ RSpec.describe DeploymentPlatform do
create(:cluster, :provided_by_user, projects: [another_project], management_project: project)
end
- context 'cluster_management_project feature is enabled' do
- it 'returns the cluster with management project' do
- is_expected.to eq(cluster_with_management_project.platform_kubernetes)
- end
- end
-
- context 'cluster_management_project feature is disabled' do
- before do
- stub_feature_flags(cluster_management_project: false)
- end
-
- it 'returns nothing' do
- is_expected.to be_nil
- end
+ it 'returns the cluster with management project' do
+ is_expected.to eq(cluster_with_management_project.platform_kubernetes)
end
end
@@ -311,20 +299,8 @@ RSpec.describe DeploymentPlatform do
create(:cluster, :provided_by_user, projects: [another_project], management_project: project)
end
- context 'cluster_management_project feature is enabled' do
- it 'returns the cluster with management project' do
- is_expected.to eq(cluster_with_management_project.platform_kubernetes)
- end
- end
-
- context 'cluster_management_project feature is disabled' do
- before do
- stub_feature_flags(cluster_management_project: false)
- end
-
- it 'returns the group cluster' do
- is_expected.to eq(group_cluster.platform_kubernetes)
- end
+ it 'returns the cluster with management project' do
+ is_expected.to eq(cluster_with_management_project.platform_kubernetes)
end
end
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index 71a4a1a2784..869df06b60c 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -558,8 +558,7 @@ RSpec.describe API::Files do
get api(url, current_user), params: params
- expect(response.headers["Cache-Control"]).to include("no-store")
- expect(response.headers["Cache-Control"]).to include("no-cache")
+ expect(response.headers["Cache-Control"]).to eq("max-age=0, private, must-revalidate, no-store, no-cache")
expect(response.headers["Pragma"]).to eq("no-cache")
expect(response.headers["Expires"]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 16619017dfe..e103aa3d6de 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -3864,6 +3864,48 @@ RSpec.describe API::Projects do
end
end
+ describe 'GET /projects/:id/storage' do
+ context 'when unauthenticated' do
+ it 'does not return project storage data' do
+ get api("/projects/#{project.id}/storage")
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+ end
+
+ it 'returns project storage data when user is admin' do
+ get api("/projects/#{project.id}/storage", create(:admin))
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response['project_id']).to eq(project.id)
+ expect(json_response['disk_path']).to eq(project.repository.disk_path)
+ expect(json_response['created_at']).to be_present
+ expect(json_response['repository_storage']).to eq(project.repository_storage)
+ end
+
+ it 'does not return project storage data when user is not admin' do
+ get api("/projects/#{project.id}/storage", user3)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+
+ it 'responds with a 401 for unauthenticated users trying to access a non-existent project id' do
+ expect(Project.find_by(id: non_existing_record_id)).to be_nil
+
+ get api("/projects/#{non_existing_record_id}/storage")
+
+ expect(response).to have_gitlab_http_status(:unauthorized)
+ end
+
+ it 'responds with a 403 for non-admin users trying to access a non-existent project id' do
+ expect(Project.find_by(id: non_existing_record_id)).to be_nil
+
+ get api("/projects/#{non_existing_record_id}/storage", user3)
+
+ expect(response).to have_gitlab_http_status(:forbidden)
+ end
+ end
+
it_behaves_like 'custom attributes endpoints', 'projects' do
let(:attributable) { project }
let(:other_attributable) { project2 }
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index a12b4dc9848..1b96efeca22 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -178,10 +178,12 @@ RSpec.describe API::Repositories do
expect(headers['Content-Disposition']).to eq 'inline'
end
- it_behaves_like 'uncached response' do
- before do
- get api(route, current_user)
- end
+ it 'defines an uncached header response' do
+ get api(route, current_user)
+
+ expect(response.headers["Cache-Control"]).to eq("max-age=0, private, must-revalidate, no-store, no-cache")
+ expect(response.headers["Pragma"]).to eq("no-cache")
+ expect(response.headers["Expires"]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
end
context 'when sha does not exist' do
diff --git a/spec/support/database_cleaner.rb b/spec/support/database_cleaner.rb
index 60d82f7e92a..f6339d7343c 100644
--- a/spec/support/database_cleaner.rb
+++ b/spec/support/database_cleaner.rb
@@ -35,8 +35,6 @@ RSpec.configure do |config|
puts "Recreating the database"
start = Gitlab::Metrics::System.monotonic_time
- ActiveRecord::AdvisoryLockBase.clear_all_connections!
-
ActiveRecord::Tasks::DatabaseTasks.drop_current
ActiveRecord::Tasks::DatabaseTasks.create_current
ActiveRecord::Tasks::DatabaseTasks.load_schema_current
diff --git a/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb b/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
index bdfeb7a97f0..9af35c189d0 100644
--- a/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/wiki_actions_shared_examples.rb
@@ -298,7 +298,7 @@ RSpec.shared_examples 'wiki controller actions' do
expect(response.headers['Content-Disposition']).to match(/^inline/)
expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq('true')
expect(response.cache_control[:public]).to be(false)
- expect(response.cache_control[:extras]).to include('no-store')
+ expect(response.headers['Cache-Control']).to eq('no-store')
end
end
end
diff --git a/spec/support/shared_examples/uncached_response_shared_examples.rb b/spec/support/shared_examples/uncached_response_shared_examples.rb
deleted file mode 100644
index 3997017ff35..00000000000
--- a/spec/support/shared_examples/uncached_response_shared_examples.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-# frozen_string_literal: true
-#
-# Pairs with lib/gitlab/no_cache_headers.rb
-#
-
-RSpec.shared_examples 'uncached response' do
- it 'defines an uncached header response' do
- expect(response.headers["Cache-Control"]).to include("no-store", "no-cache")
- expect(response.headers["Pragma"]).to eq("no-cache")
- expect(response.headers["Expires"]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
- end
-end
diff --git a/spec/views/shared/nav/_sidebar.html.haml_spec.rb b/spec/views/shared/nav/_sidebar.html.haml_spec.rb
index cf9452ba68c..2eeebdff7a8 100644
--- a/spec/views/shared/nav/_sidebar.html.haml_spec.rb
+++ b/spec/views/shared/nav/_sidebar.html.haml_spec.rb
@@ -25,13 +25,11 @@ RSpec.describe 'shared/nav/_sidebar.html.haml' do
context 'when sidebar does not have a scope menu' do
let(:scope_menu_view_path) { 'shared/nav/' }
let(:scope_menu_view_name) { 'scope_menu.html.haml' }
- let(:scope_menu_view) { "#{scope_menu_view_path}#{scope_menu_view_name}" }
let(:scope_menu_partial) { "#{scope_menu_view_path}_#{scope_menu_view_name}" }
let(:content) { 'Custom test content' }
context 'when sidebar has a custom scope menu partial defined' do
it 'renders the custom partial' do
- allow(sidebar).to receive(:render_raw_scope_menu_partial).and_return(scope_menu_view)
allow(view).to receive(:scope_menu).and_return(nil)
stub_template(scope_menu_partial => content)
diff --git a/yarn.lock b/yarn.lock
index 403ee6b3aac..fee3af0d725 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1176,15 +1176,15 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.2.tgz#adea7b6953cbb34651766b0548468e743c6a2353"
integrity sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==
-"@rails/actioncable@^6.0.3-4":
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.1.0.tgz#f336f25450b1bc43b99bc60557a70b6e6bb1d3d2"
- integrity sha512-eDgy+vcKN9RIzxmMBfSAe77rTj2cp6kJALiVQyKrW2O9EK2MdostOmP+99At/Dit3ur5+77NVnruxD7y14ZYFA==
-
-"@rails/ujs@^6.0.3-4":
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-6.1.0.tgz#9a48df6511cb2b472c9f596c1f37dc0af022e751"
- integrity sha512-kQNKyM4ePAc4u9eR1c4OqrbAHH+3SJXt++8izIjeaZeg+P7yBtgoF/dogMD/JPPowNC74ACFpM/4op0Ggp/fPw==
+"@rails/actioncable@6.1.3-2":
+ version "6.1.3-2"
+ resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-6.1.3-2.tgz#de22e2d7474dcca051f7060829450412a17ecc04"
+ integrity sha512-3mBLDwM85oj0Ot+wgC3c0wsfx5qvf8XJwSbkJk4ZqW4bA7ctn8BFW+cRQxrnQau+NDfmJvSECY8mmNIANcpULA==
+
+"@rails/ujs@6.1.3-2":
+ version "6.1.3-2"
+ resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-6.1.3-2.tgz#5d7e161e7061654e738a116a7ec8b58b51721a11"
+ integrity sha512-Nd0Im4cW8tIX8ZR3jE/dS3wnJrN46RJSdCfU59Cji2puctIWohq63LjKFMufUwm21bCasISNGoLdkr3S7nwONw==
"@sentry/browser@^5.22.3":
version "5.30.0"