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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-09-20 21:12:35 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-09-20 21:12:35 +0300
commit813226e2ba738fd7157e7014e1a115557a25b172 (patch)
treedaa4a09b816dfd758259d6828cb50a10df1e8185
parentdf8a23a4f81c205bad24a84525b56e69118fb3fb (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock11
-rw-r--r--app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue83
-rw-r--r--app/assets/javascripts/packages_and_registries/dependency_proxy/index.js26
-rw-r--r--app/assets/javascripts/pages/projects/new/components/new_project_url_select.vue38
-rw-r--r--app/assets/javascripts/projects/settings/access_dropdown.js53
-rw-r--r--app/assets/javascripts/projects/settings/api/access_dropdown_api.js45
-rw-r--r--app/assets/javascripts/projects/settings/components/access_dropdown.vue292
-rw-r--r--app/assets/javascripts/projects/settings/init_access_dropdown.js28
-rw-r--r--app/assets/javascripts/vue_shared/components/dismissible_feedback_alert.vue23
-rw-r--r--app/models/ci/build.rb4
-rw-r--r--app/workers/run_pipeline_schedule_worker.rb11
-rw-r--r--data/deprecations/14-0-nfs-fot-git-repository-storage.yml22
-rw-r--r--data/deprecations/14-3-deprecation-release-cli.yml13
-rw-r--r--db/post_migrate/20210915022415_cleanup_bigint_conversion_for_ci_builds.rb16
-rw-r--r--db/schema_migrations/202109150224151
-rw-r--r--db/structure.sql14
-rw-r--r--doc/install/requirements.md2
-rw-r--r--doc/integration/security_partners/index.md2
-rw-r--r--doc/public_access/public_access.md4
-rw-r--r--doc/update/package/index.md8
-rw-r--r--doc/user/application_security/coverage_fuzzing/index.md9
-rw-r--r--doc/user/group/import/index.md22
-rw-r--r--doc/user/packages/nuget_repository/index.md8
-rw-r--r--doc/user/project/clusters/add_gke_clusters.md58
-rw-r--r--doc/user/project/index.md4
-rw-r--r--doc/user/project/members/share_project_with_groups.md4
-rw-r--r--doc/user/project/settings/import_export.md9
-rw-r--r--doc/user/project/settings/index.md4
-rw-r--r--doc/user/project/working_with_projects.md4
-rw-r--r--doc/user/reserved_names.md4
-rw-r--r--lib/gitlab/git/keep_around.rb2
-rw-r--r--lib/gitlab/optimistic_locking.rb2
-rw-r--r--locale/gitlab.pot36
-rw-r--r--package.json8
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb6
-rw-r--r--qa/qa/specs/features/browser_ui/5_package/rubygems_registry_spec.rb6
-rw-r--r--spec/factories/ci/pipelines.rb1
-rw-r--r--spec/features/markdown/markdown_spec.rb8
-rw-r--r--spec/fixtures/markdown.md.erb9
-rw-r--r--spec/frontend/packages_and_registries/dependency_proxy/app_spec.js82
-rw-r--r--spec/frontend/pages/projects/new/components/new_project_url_select_spec.js63
-rw-r--r--spec/frontend/projects/settings/components/new_access_dropdown_spec.js204
-rw-r--r--spec/frontend/vue_shared/components/dismissible_feedback_alert_spec.js34
-rw-r--r--spec/lib/banzai/pipeline/full_pipeline_spec.rb8
-rw-r--r--spec/migrations/20210915022415_cleanup_bigint_conversion_for_ci_builds_spec.rb23
-rw-r--r--spec/models/ci/build_spec.rb6
-rw-r--r--spec/support/matchers/markdown_matchers.rb2
-rw-r--r--spec/workers/run_pipeline_schedule_worker_spec.rb15
-rw-r--r--yarn.lock82
57 files changed, 1194 insertions, 271 deletions
diff --git a/Gemfile b/Gemfile
index 3b637a02538..ef6061eb243 100644
--- a/Gemfile
+++ b/Gemfile
@@ -154,7 +154,7 @@ gem 'html-pipeline', '~> 2.13.2'
gem 'deckar01-task_list', '2.3.1'
gem 'gitlab-markup', '~> 1.7.1'
gem 'github-markup', '~> 1.7.0', require: 'github/markup'
-gem 'commonmarker', '~> 0.21'
+gem 'commonmarker', '~> 0.23.2'
gem 'kramdown', '~> 2.3.1'
gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~> 6.3.2'
@@ -474,7 +474,7 @@ end
gem 'spamcheck', '~> 0.1.0'
# Gitaly GRPC protocol definitions
-gem 'gitaly', '~> 14.3.0.pre.rc1'
+gem 'gitaly', '~> 14.3.0.pre.rc2'
# KAS GRPC protocol definitions
gem 'kas-grpc', '~> 0.0.2'
diff --git a/Gemfile.lock b/Gemfile.lock
index 99cd6b8bab6..f3d2860110e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -200,8 +200,7 @@ GEM
open4 (~> 1.3)
coderay (1.1.3)
colored2 (3.1.2)
- commonmarker (0.21.0)
- ruby-enum (~> 0.5)
+ commonmarker (0.23.2)
concurrent-ruby (1.1.9)
connection_pool (2.2.2)
contracts (0.11.0)
@@ -453,7 +452,7 @@ GEM
rails (>= 3.2.0)
git (1.7.0)
rchardet (~> 1.8)
- gitaly (14.3.0.pre.rc1)
+ gitaly (14.3.0.pre.rc2)
grpc (~> 1.0)
github-markup (1.7.0)
gitlab (4.16.1)
@@ -1116,8 +1115,6 @@ GEM
rubocop-rspec (1.44.1)
rubocop (~> 0.87)
rubocop-ast (>= 0.7.1)
- ruby-enum (0.8.0)
- i18n
ruby-fogbugz (0.2.1)
crack (~> 0.4)
ruby-magic (0.4.0)
@@ -1414,7 +1411,7 @@ DEPENDENCIES
capybara-screenshot (~> 1.0.22)
carrierwave (~> 1.3)
charlock_holmes (~> 0.7.7)
- commonmarker (~> 0.21)
+ commonmarker (~> 0.23.2)
concurrent-ruby (~> 1.1)
connection_pool (~> 2.0)
countries (~> 3.0)
@@ -1464,7 +1461,7 @@ DEPENDENCIES
gettext (~> 3.3)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly (~> 14.3.0.pre.rc1)
+ gitaly (~> 14.3.0.pre.rc2)
github-markup (~> 1.7.0)
gitlab-chronic (~> 0.10.5)
gitlab-dangerfiles (~> 2.3.0)
diff --git a/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue b/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue
new file mode 100644
index 00000000000..7c2df91150b
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/dependency_proxy/app.vue
@@ -0,0 +1,83 @@
+<script>
+import { GlAlert, GlFormGroup, GlFormInputGroup, GlSprintf } from '@gitlab/ui';
+import { helpPagePath } from '~/helpers/help_page_helper';
+import { numberToHumanSize } from '~/lib/utils/number_utils';
+import { __ } from '~/locale';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import TitleArea from '~/vue_shared/components/registry/title_area.vue';
+
+export default {
+ components: {
+ GlFormGroup,
+ GlAlert,
+ GlFormInputGroup,
+ GlSprintf,
+ ClipboardButton,
+ TitleArea,
+ },
+ inject: ['groupPath', 'dependencyProxyAvailable'],
+ i18n: {
+ subTitle: __(
+ 'Create a local proxy for storing frequently used upstream images. %{docLinkStart}Learn more%{docLinkEnd} about dependency proxies.',
+ ),
+ proxyNotAvailableText: __('Dependency proxy feature is limited to public groups for now.'),
+ proxyImagePrefix: __('Dependency proxy image prefix'),
+ copyImagePrefixText: __('Copy prefix'),
+ blobCountAndSize: __('Contains %{count} blobs of images (%{size})'),
+ },
+ data() {
+ return {
+ dependencyProxyTotalSize: 0,
+ dependencyProxyImagePrefix: '',
+ dependencyProxyBlobCount: 0,
+ };
+ },
+ computed: {
+ infoMessages() {
+ return [
+ {
+ text: this.$options.i18n.subTitle,
+ link: helpPagePath('user/packages/dependency_proxy/index'),
+ },
+ ];
+ },
+ humanizedTotalSize() {
+ return numberToHumanSize(this.dependencyProxyTotalSize);
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <title-area :title="__('Dependency Proxy')" :info-messages="infoMessages" />
+ <gl-alert v-if="!dependencyProxyAvailable" :dismissible="false">
+ {{ $options.i18n.proxyNotAvailableText }}
+ </gl-alert>
+
+ <div v-else data-testid="main-area">
+ <gl-form-group :label="$options.i18n.proxyImagePrefix">
+ <gl-form-input-group
+ readonly
+ :value="dependencyProxyImagePrefix"
+ class="gl-layout-w-limited"
+ >
+ <template #append>
+ <clipboard-button
+ :text="dependencyProxyImagePrefix"
+ :title="$options.i18n.copyImagePrefixText"
+ />
+ </template>
+ </gl-form-input-group>
+ <template #description>
+ <span data-qa-selector="dependency_proxy_count" data-testid="proxy-count">
+ <gl-sprintf :message="$options.i18n.blobCountAndSize">
+ <template #count>{{ dependencyProxyBlobCount }}</template>
+ <template #size>{{ humanizedTotalSize }}</template>
+ </gl-sprintf>
+ </span>
+ </template>
+ </gl-form-group>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/packages_and_registries/dependency_proxy/index.js b/app/assets/javascripts/packages_and_registries/dependency_proxy/index.js
new file mode 100644
index 00000000000..952953fb3b7
--- /dev/null
+++ b/app/assets/javascripts/packages_and_registries/dependency_proxy/index.js
@@ -0,0 +1,26 @@
+import Vue from 'vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import app from '~/packages_and_registries/dependency_proxy/app.vue';
+import { apolloProvider } from '~/packages_and_registries/package_registry/graphql';
+import Translate from '~/vue_shared/translate';
+
+Vue.use(Translate);
+
+export const initDependencyProxyApp = () => {
+ const el = document.getElementById('js-dependency-proxy');
+ if (!el) {
+ return null;
+ }
+ const { dependencyProxyAvailable, ...dataset } = el.dataset;
+ return new Vue({
+ el,
+ apolloProvider,
+ provide: {
+ dependencyProxyAvailable: parseBoolean(dependencyProxyAvailable),
+ ...dataset,
+ },
+ render(createElement) {
+ return createElement(app);
+ },
+ });
+};
diff --git a/app/assets/javascripts/pages/projects/new/components/new_project_url_select.vue b/app/assets/javascripts/pages/projects/new/components/new_project_url_select.vue
index ba8858c985a..c30d3ec5db4 100644
--- a/app/assets/javascripts/pages/projects/new/components/new_project_url_select.vue
+++ b/app/assets/javascripts/pages/projects/new/components/new_project_url_select.vue
@@ -4,6 +4,7 @@ import {
GlButtonGroup,
GlDropdown,
GlDropdownItem,
+ GlDropdownText,
GlDropdownSectionHeader,
GlLoadingIcon,
GlSearchBoxByType,
@@ -20,6 +21,7 @@ export default {
GlButtonGroup,
GlDropdown,
GlDropdownItem,
+ GlDropdownText,
GlDropdownSectionHeader,
GlLoadingIcon,
GlSearchBoxByType,
@@ -57,8 +59,20 @@ export default {
userNamespace() {
return this.currentUser.namespace || {};
},
+ hasGroupMatches() {
+ return this.userGroups.length;
+ },
+ hasNamespaceMatches() {
+ return this.userNamespace.fullPath?.toLowerCase().includes(this.search.toLowerCase());
+ },
+ hasNoMatches() {
+ return !this.hasGroupMatches && !this.hasNamespaceMatches;
+ },
},
methods: {
+ focusInput() {
+ this.$refs.search.focusInput();
+ },
handleClick({ id, fullPath }) {
this.selectedNamespace = {
id: getIdFromGraphQLId(id),
@@ -78,18 +92,24 @@ export default {
toggle-class="gl-rounded-top-right-base! gl-rounded-bottom-right-base!"
data-qa-selector="select_namespace_dropdown"
@show="track('activate_form_input', { label: trackLabel, property: 'project_path' })"
+ @shown="focusInput"
>
- <gl-search-box-by-type v-model.trim="search" />
+ <gl-search-box-by-type ref="search" v-model.trim="search" />
<gl-loading-icon v-if="$apollo.queries.currentUser.loading" />
<template v-else>
- <gl-dropdown-section-header>{{ __('Groups') }}</gl-dropdown-section-header>
- <gl-dropdown-item v-for="group of userGroups" :key="group.id" @click="handleClick(group)">
- {{ group.fullPath }}
- </gl-dropdown-item>
- <gl-dropdown-section-header>{{ __('Users') }}</gl-dropdown-section-header>
- <gl-dropdown-item @click="handleClick(userNamespace)">
- {{ userNamespace.fullPath }}
- </gl-dropdown-item>
+ <template v-if="hasGroupMatches">
+ <gl-dropdown-section-header>{{ __('Groups') }}</gl-dropdown-section-header>
+ <gl-dropdown-item v-for="group of userGroups" :key="group.id" @click="handleClick(group)">
+ {{ group.fullPath }}
+ </gl-dropdown-item>
+ </template>
+ <template v-if="hasNamespaceMatches">
+ <gl-dropdown-section-header>{{ __('Users') }}</gl-dropdown-section-header>
+ <gl-dropdown-item @click="handleClick(userNamespace)">
+ {{ userNamespace.fullPath }}
+ </gl-dropdown-item>
+ </template>
+ <gl-dropdown-text v-if="hasNoMatches">{{ __('No matches found') }}</gl-dropdown-text>
</template>
</gl-dropdown>
diff --git a/app/assets/javascripts/projects/settings/access_dropdown.js b/app/assets/javascripts/projects/settings/access_dropdown.js
index a5e53ee3927..7fb7a416dca 100644
--- a/app/assets/javascripts/projects/settings/access_dropdown.js
+++ b/app/assets/javascripts/projects/settings/access_dropdown.js
@@ -2,8 +2,8 @@
import { escape, find, countBy } from 'lodash';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
import createFlash from '~/flash';
-import axios from '~/lib/utils/axios_utils';
import { n__, s__, __, sprintf } from '~/locale';
+import { getUsers, getGroups, getDeployKeys } from './api/access_dropdown_api';
import { LEVEL_TYPES, LEVEL_ID_PROP, ACCESS_LEVELS, ACCESS_LEVEL_NONE } from './constants';
export default class AccessDropdown {
@@ -16,9 +16,6 @@ export default class AccessDropdown {
this.accessLevelsData = accessLevelsData.roles;
this.$dropdown = $dropdown;
this.$wrap = this.$dropdown.closest(`.${this.accessLevel}-container`);
- this.usersPath = '/-/autocomplete/users.json';
- this.groupsPath = '/-/autocomplete/project_groups.json';
- this.deployKeysPath = '/-/autocomplete/deploy_keys_with_owners.json';
this.defaultLabel = this.$dropdown.data('defaultLabel');
this.setSelectedItems([]);
@@ -318,9 +315,9 @@ export default class AccessDropdown {
getData(query, callback) {
if (this.hasLicense) {
Promise.all([
- this.getDeployKeys(query),
- this.getUsers(query),
- this.groupsData ? Promise.resolve(this.groupsData) : this.getGroups(),
+ getDeployKeys(query),
+ getUsers(query),
+ this.groupsData ? Promise.resolve(this.groupsData) : getGroups(),
])
.then(([deployKeysResponse, usersResponse, groupsResponse]) => {
this.groupsData = groupsResponse;
@@ -332,7 +329,7 @@ export default class AccessDropdown {
createFlash({ message: __('Failed to load groups, users and deploy keys.') });
});
} else {
- this.getDeployKeys(query)
+ getDeployKeys(query)
.then((deployKeysResponse) => callback(this.consolidateData(deployKeysResponse.data)))
.catch(() => createFlash({ message: __('Failed to load deploy keys.') }));
}
@@ -473,46 +470,6 @@ export default class AccessDropdown {
return consolidatedData;
}
- getUsers(query) {
- return axios.get(this.buildUrl(gon.relative_url_root, this.usersPath), {
- params: {
- search: query,
- per_page: 20,
- active: true,
- project_id: gon.current_project_id,
- push_code: true,
- },
- });
- }
-
- getGroups() {
- return axios.get(this.buildUrl(gon.relative_url_root, this.groupsPath), {
- params: {
- project_id: gon.current_project_id,
- },
- });
- }
-
- getDeployKeys(query) {
- return axios.get(this.buildUrl(gon.relative_url_root, this.deployKeysPath), {
- params: {
- search: query,
- per_page: 20,
- active: true,
- project_id: gon.current_project_id,
- push_code: true,
- },
- });
- }
-
- buildUrl(urlRoot, url) {
- let newUrl;
- if (urlRoot != null) {
- newUrl = urlRoot.replace(/\/$/, '') + url;
- }
- return newUrl;
- }
-
renderRow(item) {
let criteria = {};
let groupRowEl;
diff --git a/app/assets/javascripts/projects/settings/api/access_dropdown_api.js b/app/assets/javascripts/projects/settings/api/access_dropdown_api.js
new file mode 100644
index 00000000000..10f6c28a7bf
--- /dev/null
+++ b/app/assets/javascripts/projects/settings/api/access_dropdown_api.js
@@ -0,0 +1,45 @@
+import axios from '~/lib/utils/axios_utils';
+
+const USERS_PATH = '/-/autocomplete/users.json';
+const GROUPS_PATH = '/-/autocomplete/project_groups.json';
+const DEPLOY_KEYS_PATH = '/-/autocomplete/deploy_keys_with_owners.json';
+
+const buildUrl = (urlRoot, url) => {
+ let newUrl;
+ if (urlRoot != null) {
+ newUrl = urlRoot.replace(/\/$/, '') + url;
+ }
+ return newUrl;
+};
+
+export const getUsers = (query) => {
+ return axios.get(buildUrl(gon.relative_url_root || '', USERS_PATH), {
+ params: {
+ search: query,
+ per_page: 20,
+ active: true,
+ project_id: gon.current_project_id,
+ push_code: true,
+ },
+ });
+};
+
+export const getGroups = () => {
+ return axios.get(buildUrl(gon.relative_url_root || '', GROUPS_PATH), {
+ params: {
+ project_id: gon.current_project_id,
+ },
+ });
+};
+
+export const getDeployKeys = (query) => {
+ return axios.get(buildUrl(gon.relative_url_root || '', DEPLOY_KEYS_PATH), {
+ params: {
+ search: query,
+ per_page: 20,
+ active: true,
+ project_id: gon.current_project_id,
+ push_code: true,
+ },
+ });
+};
diff --git a/app/assets/javascripts/projects/settings/components/access_dropdown.vue b/app/assets/javascripts/projects/settings/components/access_dropdown.vue
new file mode 100644
index 00000000000..35b001f8ef9
--- /dev/null
+++ b/app/assets/javascripts/projects/settings/components/access_dropdown.vue
@@ -0,0 +1,292 @@
+<script>
+import {
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownSectionHeader,
+ GlDropdownDivider,
+ GlSearchBoxByType,
+ GlLoadingIcon,
+ GlAvatar,
+ GlSprintf,
+} from '@gitlab/ui';
+import { debounce } from 'lodash';
+import createFlash from '~/flash';
+import { __, s__, n__ } from '~/locale';
+import { getUsers, getGroups, getDeployKeys } from '../api/access_dropdown_api';
+import { LEVEL_TYPES, ACCESS_LEVELS } from '../constants';
+
+export const i18n = {
+ selectUsers: s__('ProtectedEnvironment|Select users'),
+ rolesSectionHeader: s__('AccessDropdown|Roles'),
+ groupsSectionHeader: s__('AccessDropdown|Groups'),
+ usersSectionHeader: s__('AccessDropdown|Users'),
+ deployKeysSectionHeader: s__('AccessDropdown|Deploy Keys'),
+ ownedBy: __('Owned by %{image_tag}'),
+};
+
+export default {
+ i18n,
+ components: {
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownSectionHeader,
+ GlDropdownDivider,
+ GlSearchBoxByType,
+ GlLoadingIcon,
+ GlAvatar,
+ GlSprintf,
+ },
+ props: {
+ accessLevelsData: {
+ type: Array,
+ required: true,
+ },
+ accessLevel: {
+ required: true,
+ type: String,
+ },
+ hasLicense: {
+ required: false,
+ type: Boolean,
+ default: true,
+ },
+ },
+ data() {
+ return {
+ loading: false,
+ query: '',
+ users: [],
+ groups: [],
+ roles: [],
+ deployKeys: [],
+ selected: {
+ [LEVEL_TYPES.GROUP]: [],
+ [LEVEL_TYPES.USER]: [],
+ [LEVEL_TYPES.ROLE]: [],
+ [LEVEL_TYPES.DEPLOY_KEY]: [],
+ },
+ };
+ },
+ computed: {
+ showDeployKeys() {
+ return this.accessLevel === ACCESS_LEVELS.PUSH && this.deployKeys.length;
+ },
+ toggleLabel() {
+ const counts = Object.entries(this.selected).reduce((acc, [key, value]) => {
+ acc[key] = value.length;
+ return acc;
+ }, {});
+
+ const isOnlyRoleSelected =
+ counts[LEVEL_TYPES.ROLE] === 1 &&
+ [counts[LEVEL_TYPES.USER], counts[LEVEL_TYPES.GROUP], counts[LEVEL_TYPES.DEPLOY_KEY]].every(
+ (count) => count === 0,
+ );
+
+ if (isOnlyRoleSelected) {
+ return this.selected[LEVEL_TYPES.ROLE][0].text;
+ }
+
+ const labelPieces = [];
+
+ if (counts[LEVEL_TYPES.ROLE] > 0) {
+ labelPieces.push(n__('1 role', '%d roles', counts[LEVEL_TYPES.ROLE]));
+ }
+
+ if (counts[LEVEL_TYPES.USER] > 0) {
+ labelPieces.push(n__('1 user', '%d users', counts[LEVEL_TYPES.USER]));
+ }
+
+ if (counts[LEVEL_TYPES.DEPLOY_KEY] > 0) {
+ labelPieces.push(n__('1 deploy key', '%d deploy keys', counts[LEVEL_TYPES.DEPLOY_KEY]));
+ }
+
+ if (counts[LEVEL_TYPES.GROUP] > 0) {
+ labelPieces.push(n__('1 group', '%d groups', counts[LEVEL_TYPES.GROUP]));
+ }
+
+ return labelPieces.join(', ') || i18n.selectUsers;
+ },
+ toggleClass() {
+ return this.toggleLabel === i18n.selectUsers ? 'gl-text-gray-500!' : '';
+ },
+ },
+ watch: {
+ query: debounce(function debouncedSearch() {
+ return this.getData();
+ }, 500),
+ },
+ created() {
+ this.getData();
+ },
+
+ methods: {
+ focusInput() {
+ this.$refs.search.focusInput();
+ },
+ getData() {
+ this.loading = true;
+
+ if (this.hasLicense) {
+ Promise.all([
+ getDeployKeys(this.query),
+ getUsers(this.query),
+ this.groups.length ? Promise.resolve({ data: this.groups }) : getGroups(),
+ ])
+ .then(([deployKeysResponse, usersResponse, groupsResponse]) =>
+ this.consolidateData(deployKeysResponse.data, usersResponse.data, groupsResponse.data),
+ )
+ .catch(() =>
+ createFlash({ message: __('Failed to load groups, users and deploy keys.') }),
+ )
+ .finally(() => {
+ this.loading = false;
+ });
+ } else {
+ getDeployKeys(this.query)
+ .then((deployKeysResponse) => this.consolidateData(deployKeysResponse.data))
+ .catch(() => createFlash({ message: __('Failed to load deploy keys.') }))
+ .finally(() => {
+ this.loading = false;
+ });
+ }
+ },
+ consolidateData(deployKeysResponse, usersResponse = [], groupsResponse = []) {
+ // This re-assignment is intentional as level.type property is being used for comparision,
+ // and accessLevelsData is provided by gon.create_access_levels which doesn't have `type` included.
+ // See this discussion https://gitlab.com/gitlab-org/gitlab/merge_requests/1629#note_31285823
+ this.roles = this.accessLevelsData.map((role) => ({ ...role, type: LEVEL_TYPES.ROLE }));
+
+ if (this.hasLicense) {
+ this.groups = groupsResponse.map((group) => ({ ...group, type: LEVEL_TYPES.GROUP }));
+ this.users = usersResponse.map((user) => ({ ...user, type: LEVEL_TYPES.USER }));
+ }
+
+ this.deployKeys = deployKeysResponse.map((response) => {
+ const {
+ id,
+ fingerprint,
+ title,
+ owner: { avatar_url, name, username },
+ } = response;
+
+ const shortFingerprint = `(${fingerprint.substring(0, 14)}...)`;
+
+ return {
+ id,
+ title: title.concat(' ', shortFingerprint),
+ avatar_url,
+ fullname: name,
+ username,
+ type: LEVEL_TYPES.DEPLOY_KEY,
+ };
+ });
+ },
+ onItemClick(item) {
+ this.toggleSelection(this.selected[item.type], item);
+ this.emitUpdate();
+ },
+ toggleSelection(arr, item) {
+ const itemIndex = arr.indexOf(item);
+ if (itemIndex > -1) {
+ arr.splice(itemIndex, 1);
+ } else arr.push(item);
+ },
+ isSelected(item) {
+ return this.selected[item.type].some((selected) => selected.id === item.id);
+ },
+ emitUpdate() {
+ const selected = Object.values(this.selected).flat();
+ this.$emit('select', selected);
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-dropdown
+ :text="toggleLabel"
+ class="gl-display-block"
+ :toggle-class="toggleClass"
+ aria-labelledby="allowed-users-label"
+ @shown="focusInput"
+ >
+ <template #header>
+ <gl-search-box-by-type ref="search" v-model.trim="query" />
+ <gl-loading-icon v-if="loading" size="sm" />
+ </template>
+ <template v-if="roles.length">
+ <gl-dropdown-section-header>{{
+ $options.i18n.rolesSectionHeader
+ }}</gl-dropdown-section-header>
+ <gl-dropdown-item
+ v-for="role in roles"
+ :key="role.id"
+ is-check-item
+ :is-checked="isSelected(role)"
+ @click.native.capture.stop="onItemClick(role)"
+ >
+ {{ role.text }}
+ </gl-dropdown-item>
+ <gl-dropdown-divider v-if="groups.length || users.length || showDeployKeys" />
+ </template>
+
+ <template v-if="groups.length">
+ <gl-dropdown-section-header>{{
+ $options.i18n.groupsSectionHeader
+ }}</gl-dropdown-section-header>
+ <gl-dropdown-item
+ v-for="group in groups"
+ :key="group.id"
+ :avatar-url="group.avatar_url"
+ is-check-item
+ :is-checked="isSelected(group)"
+ @click.native.capture.stop="onItemClick(group)"
+ >
+ {{ group.name }}
+ </gl-dropdown-item>
+ <gl-dropdown-divider v-if="users.length || showDeployKeys" />
+ </template>
+
+ <template v-if="users.length">
+ <gl-dropdown-section-header>{{
+ $options.i18n.usersSectionHeader
+ }}</gl-dropdown-section-header>
+ <gl-dropdown-item
+ v-for="user in users"
+ :key="user.id"
+ :avatar-url="user.avatar_url"
+ :secondary-text="user.username"
+ is-check-item
+ :is-checked="isSelected(user)"
+ @click.native.capture.stop="onItemClick(user)"
+ >
+ {{ user.name }}
+ </gl-dropdown-item>
+ <gl-dropdown-divider v-if="showDeployKeys" />
+ </template>
+
+ <template v-if="showDeployKeys">
+ <gl-dropdown-section-header>{{
+ $options.i18n.deployKeysSectionHeader
+ }}</gl-dropdown-section-header>
+ <gl-dropdown-item
+ v-for="key in deployKeys"
+ :key="key.id"
+ is-check-item
+ :is-checked="isSelected(key)"
+ class="gl-text-truncate"
+ @click.native.capture.stop="onItemClick(key)"
+ >
+ <div class="gl-text-truncate gl-font-weight-bold">{{ key.title }}</div>
+ <div class="gl-text-gray-700 gl-text-truncate">
+ <gl-sprintf :message="$options.i18n.ownedBy">
+ <template #image_tag>
+ <gl-avatar :src="key.avatar_url" :size="24" />
+ </template> </gl-sprintf
+ >{{ key.fullname }} ({{ key.username }})
+ </div>
+ </gl-dropdown-item>
+ </template>
+ </gl-dropdown>
+</template>
diff --git a/app/assets/javascripts/projects/settings/init_access_dropdown.js b/app/assets/javascripts/projects/settings/init_access_dropdown.js
new file mode 100644
index 00000000000..9944b05d206
--- /dev/null
+++ b/app/assets/javascripts/projects/settings/init_access_dropdown.js
@@ -0,0 +1,28 @@
+import Vue from 'vue';
+import AccessDropdown from './components/access_dropdown.vue';
+
+export const initAccessDropdown = (el, options) => {
+ if (!el) {
+ return false;
+ }
+
+ const { accessLevelsData, accessLevel } = options;
+
+ return new Vue({
+ el,
+ render(createElement) {
+ const vm = this;
+ return createElement(AccessDropdown, {
+ props: {
+ accessLevel,
+ accessLevelsData: accessLevelsData.roles,
+ },
+ on: {
+ select(selected) {
+ vm.$emit('select', selected);
+ },
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/vue_shared/components/dismissible_feedback_alert.vue b/app/assets/javascripts/vue_shared/components/dismissible_feedback_alert.vue
index c4dfcf93a18..014276c7e36 100644
--- a/app/assets/javascripts/vue_shared/components/dismissible_feedback_alert.vue
+++ b/app/assets/javascripts/vue_shared/components/dismissible_feedback_alert.vue
@@ -1,13 +1,11 @@
<script>
-import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
+import { GlAlert } from '@gitlab/ui';
import { slugifyWithUnderscore } from '~/lib/utils/text_utility';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
export default {
components: {
GlAlert,
- GlSprintf,
- GlLink,
LocalStorageSync,
},
props: {
@@ -15,10 +13,6 @@ export default {
type: String,
required: true,
},
- feedbackLink: {
- type: String,
- required: true,
- },
},
data() {
return {
@@ -44,19 +38,8 @@ export default {
<template>
<div v-show="showAlert">
<local-storage-sync v-model="isDismissed" :storage-key="storageKey" as-json />
- <gl-alert v-if="showAlert" class="gl-mt-5" @dismiss="dismissFeedbackAlert">
- <gl-sprintf
- :message="
- __(
- 'Please share your feedback about %{featureName} %{linkStart}in this issue%{linkEnd} to help us improve the experience.',
- )
- "
- >
- <template #featureName>{{ featureName }}</template>
- <template #link="{ content }">
- <gl-link :href="feedbackLink" target="_blank">{{ content }}</gl-link>
- </template>
- </gl-sprintf>
+ <gl-alert v-if="showAlert" @dismiss="dismissFeedbackAlert">
+ <slot></slot>
</gl-alert>
</div>
</template>
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index e2e24247679..ddecdb7a397 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -64,8 +64,8 @@ module Ci
delegate :gitlab_deploy_token, to: :project
delegate :trigger_short_token, to: :trigger_request, allow_nil: true
- ignore_columns :id_convert_to_bigint, remove_with: '14.1', remove_after: '2021-07-22'
- ignore_columns :stage_id_convert_to_bigint, remove_with: '14.1', remove_after: '2021-07-22'
+ ignore_columns :id_convert_to_bigint, remove_with: '14.5', remove_after: '2021-10-22'
+ ignore_columns :stage_id_convert_to_bigint, remove_with: '14.5', remove_after: '2021-10-22'
##
# Since Gitlab 11.5, deployments records started being created right after
diff --git a/app/workers/run_pipeline_schedule_worker.rb b/app/workers/run_pipeline_schedule_worker.rb
index dd0f14a5cab..12042ebc4f0 100644
--- a/app/workers/run_pipeline_schedule_worker.rb
+++ b/app/workers/run_pipeline_schedule_worker.rb
@@ -27,8 +27,9 @@ class RunPipelineScheduleWorker # rubocop:disable Scalability/IdempotentWorker
user,
ref: schedule.ref)
.execute!(:schedule, ignore_skip_ci: true, save_on_errors: false, schedule: schedule)
- rescue Ci::CreatePipelineService::CreateError
- # no-op. This is a user operation error such as corrupted .gitlab-ci.yml.
+ rescue Ci::CreatePipelineService::CreateError => e
+ # This is a user operation error such as corrupted .gitlab-ci.yml. Log the error for debugging purpose.
+ log_extra_metadata_on_done(:pipeline_creation_error, e)
rescue StandardError => e
error(schedule, e)
end
@@ -37,10 +38,16 @@ class RunPipelineScheduleWorker # rubocop:disable Scalability/IdempotentWorker
def error(schedule, error)
failed_creation_counter.increment
+ log_error(schedule, error)
+ track_error(schedule, error)
+ end
+ def log_error(schedule, error)
Gitlab::AppLogger.error "Failed to create a scheduled pipeline. " \
"schedule_id: #{schedule.id} message: #{error.message}"
+ end
+ def track_error(schedule, error)
Gitlab::ErrorTracking
.track_and_raise_for_dev_exception(error,
issue_url: 'https://gitlab.com/gitlab-org/gitlab-foss/issues/41231',
diff --git a/data/deprecations/14-0-nfs-fot-git-repository-storage.yml b/data/deprecations/14-0-nfs-fot-git-repository-storage.yml
new file mode 100644
index 00000000000..416e4e3f447
--- /dev/null
+++ b/data/deprecations/14-0-nfs-fot-git-repository-storage.yml
@@ -0,0 +1,22 @@
+- name: "NFS for Git repository storage deprecated" # The name of the feature to be deprecated
+ announcement_milestone: "14.0" # The milestone when this feature was first announced as deprecated.
+ announcement_date: "2021-06-22" # The date of the milestone release when this feature was first announced as deprecated
+ removal_milestone: "15.2" # The milestone when this feature is planned to be removed
+ body: | # Do not modify this line, instead modify the lines below.
+ With the general availability of Gitaly Cluster ([introduced in GitLab 13.0](https://about.gitlab.com/releases/2020/05/22/gitlab-13-0-released/)), we have deprecated development (bugfixes, performance improvements, etc) for NFS for Git repository storage in GitLab 14.0. We will continue to provide technical support for NFS for Git repositories throughout 14.x, but we will remove all support for NFS in GitLab 15.0. Please see our official [Statement of Support](https://about.gitlab.com/support/statement-of-support.html#gitaly-and-nfs) for further information.
+
+ Gitaly Cluster offers tremendous benefits for our customers such as:
+
+ - [Variable replication factors](https://docs.gitlab.com/ee/administration/gitaly/praefect.html#replication-factor).
+ - [Strong consistency](https://docs.gitlab.com/ee/administration/gitaly/praefect.html#strong-consistency).
+ - [Distributed read capabilities](https://docs.gitlab.com/ee/administration/gitaly/praefect.html#distributed-reads).
+
+ We encourage customers currently using NFS for Git repositories to plan their migration by reviewing our documentation on [migrating to Gitaly Cluster](https://docs.gitlab.com/ee/administration/gitaly/praefect.html#migrate-to-gitaly-cluster).
+
+ stage: # (optional - may be required in the future) String value of the stage that the feature was created in. e.g., Growth
+ tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ issue_url: # (optional) This is a link to the deprecation issue in GitLab
+ documentation_url: # (optional) This is a link to the current documentation page
+ image_url: # (optional) This is a link to a thumbnail image depicting the feature
+ video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
+ removal_date: "2022-06-22" # (optional - may be required in the future) YYYY-MM-DD format - the date of the milestone release when this feature is planned to be removed
diff --git a/data/deprecations/14-3-deprecation-release-cli.yml b/data/deprecations/14-3-deprecation-release-cli.yml
new file mode 100644
index 00000000000..4273d00fc73
--- /dev/null
+++ b/data/deprecations/14-3-deprecation-release-cli.yml
@@ -0,0 +1,13 @@
+- name: "Release CLI be distributed as a generic package" # The name of the feature to be deprecated
+ announcement_milestone: "14.2" # The milestone when this feature was first announced as deprecated.
+ announcement_date: "2021-08-22" # The date of the milestone release when this feature was first announced as deprecated
+ removal_milestone: "14.6" # The milestone when this feature is planned to be removed
+ body: | # Do not modify this line, instead modify the lines below.
+ The [release-cli](https://gitlab.com/gitlab-org/release-cli) will be released as a [generic package](https://gitlab.com/gitlab-org/release-cli/-/packages) starting in GitLab 14.2. We will continue to deploy it as a binary to S3 until GitLab 14.5 and stop distributing it in S3 in GitLab 14.6.
+ stage: # (optional - may be required in the future) String value of the stage that the feature was created in. e.g., Growth
+ tiers: # (optional - may be required in the future) An array of tiers that the feature is available in currently. e.g., [Free, Silver, Gold, Core, Premium, Ultimate]
+ issue_url: # (optional) This is a link to the deprecation issue in GitLab
+ documentation_url: # (optional) This is a link to the current documentation page
+ image_url: # (optional) This is a link to a thumbnail image depicting the feature
+ video_url: # (optional) Use the youtube thumbnail URL with the structure of https://img.youtube.com/vi/UNIQUEID/hqdefault.jpg
+ removal_date: "2021-12-22" # (optional - may be required in the future) YYYY-MM-DD format - the date of the milestone release when this feature is planned to be removed
diff --git a/db/post_migrate/20210915022415_cleanup_bigint_conversion_for_ci_builds.rb b/db/post_migrate/20210915022415_cleanup_bigint_conversion_for_ci_builds.rb
new file mode 100644
index 00000000000..d72866691ff
--- /dev/null
+++ b/db/post_migrate/20210915022415_cleanup_bigint_conversion_for_ci_builds.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class CleanupBigintConversionForCiBuilds < Gitlab::Database::Migration[1.0]
+ enable_lock_retries!
+
+ TABLE = :ci_builds
+ COLUMNS = [:id, :stage_id]
+
+ def up
+ cleanup_conversion_of_integer_to_bigint(TABLE, COLUMNS)
+ end
+
+ def down
+ restore_conversion_of_integer_to_bigint(TABLE, COLUMNS)
+ end
+end
diff --git a/db/schema_migrations/20210915022415 b/db/schema_migrations/20210915022415
new file mode 100644
index 00000000000..ab3ca284269
--- /dev/null
+++ b/db/schema_migrations/20210915022415
@@ -0,0 +1 @@
+ecb73d9a0be0cf04a8b405f1225d1a5de2fcdbb4c277fd5549f72a7a21596cdc \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index cb79fe4610c..80e4fe99c38 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -77,16 +77,6 @@ RETURN NULL;
END
$$;
-CREATE FUNCTION trigger_3f6129be01d2() RETURNS trigger
- LANGUAGE plpgsql
- AS $$
-BEGIN
- NEW."id_convert_to_bigint" := NEW."id";
- NEW."stage_id_convert_to_bigint" := NEW."stage_id";
- RETURN NEW;
-END;
-$$;
-
CREATE FUNCTION trigger_542d6c2ad72e() RETURNS trigger
LANGUAGE plpgsql
AS $$
@@ -11337,7 +11327,6 @@ CREATE TABLE ci_build_trace_metadata (
);
CREATE TABLE ci_builds (
- id_convert_to_bigint integer DEFAULT 0 NOT NULL,
status character varying,
finished_at timestamp without time zone,
trace text,
@@ -11372,7 +11361,6 @@ CREATE TABLE ci_builds (
coverage_regex character varying,
auto_canceled_by_id integer,
retried boolean,
- stage_id_convert_to_bigint integer,
protected boolean,
failure_reason integer,
scheduled_at timestamp with time zone,
@@ -27366,8 +27354,6 @@ ALTER INDEX product_analytics_events_experimental_pkey ATTACH PARTITION gitlab_p
ALTER INDEX product_analytics_events_experimental_pkey ATTACH PARTITION gitlab_partitions_static.product_analytics_events_experimental_63_pkey;
-CREATE TRIGGER trigger_3f6129be01d2 BEFORE INSERT OR UPDATE ON ci_builds FOR EACH ROW EXECUTE FUNCTION trigger_3f6129be01d2();
-
CREATE TRIGGER trigger_542d6c2ad72e BEFORE INSERT OR UPDATE ON ci_builds_metadata FOR EACH ROW EXECUTE FUNCTION trigger_542d6c2ad72e();
CREATE TRIGGER trigger_8487d4de3e7b BEFORE INSERT OR UPDATE ON ci_builds_metadata FOR EACH ROW EXECUTE FUNCTION trigger_8487d4de3e7b();
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 641b092e1e3..4ced5d01447 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -114,7 +114,7 @@ MySQL/MariaDB are advised to [migrate to PostgreSQL](../update/mysql_to_postgres
The server running PostgreSQL should have _at least_ 5-10 GB of storage
available, though the exact requirements [depend on the number of users](../administration/reference_architectures/index.md).
-We highly recommend using the minimum PostgreSQL versions (as specified in
+We highly recommend using at least the minimum PostgreSQL versions (as specified in
the following table) as these were used for development and testing:
| GitLab version | Minimum PostgreSQL version |
diff --git a/doc/integration/security_partners/index.md b/doc/integration/security_partners/index.md
index 2b851b5f614..36099fdb2a6 100644
--- a/doc/integration/security_partners/index.md
+++ b/doc/integration/security_partners/index.md
@@ -5,7 +5,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
type: index
---
-# Security partner integrations
+# Security partner integrations **(FREE)**
You can integrate GitLab with its security partners. This page has information on how do this with
each security partner:
diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md
index 1b4e1e64562..0d7885bf875 100644
--- a/doc/public_access/public_access.md
+++ b/doc/public_access/public_access.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Workspace
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
type: reference
---
diff --git a/doc/update/package/index.md b/doc/update/package/index.md
index 44be79f22fb..f6b97a6325c 100644
--- a/doc/update/package/index.md
+++ b/doc/update/package/index.md
@@ -57,10 +57,10 @@ For safety reasons, you should maintain an up-to-date backup on your own if you
Updating to major versions might need some manual intervention. For more information,
check the version your are upgrading to:
-- [GitLab 14](https://docs.gitlab.com/omnibus/gitlab_14_changes.html)
-- [GitLab 13](https://docs.gitlab.com/omnibus/gitlab_13_changes.html)
-- [GitLab 12](https://docs.gitlab.com/omnibus/gitlab_12_changes.html)
-- [GitLab 11](https://docs.gitlab.com/omnibus/gitlab_11_changes.html)
+- [GitLab 14](https://docs.gitlab.com/omnibus/update/gitlab_14_changes.html)
+- [GitLab 13](https://docs.gitlab.com/omnibus/update/gitlab_13_changes.html)
+- [GitLab 12](https://docs.gitlab.com/omnibus/update/gitlab_12_changes.html)
+- [GitLab 11](https://docs.gitlab.com/omnibus/update/gitlab_11_changes.html)
## Upgrade using the official repositories
diff --git a/doc/user/application_security/coverage_fuzzing/index.md b/doc/user/application_security/coverage_fuzzing/index.md
index 0d5eb2b6d50..8e03369a702 100644
--- a/doc/user/application_security/coverage_fuzzing/index.md
+++ b/doc/user/application_security/coverage_fuzzing/index.md
@@ -7,15 +7,14 @@ type: reference, howto
# Coverage-guided fuzz testing **(ULTIMATE)**
+Coverage-guided fuzzing sends random inputs to an instrumented version of your application in an
+effort to cause unexpected behavior. Such behavior indicates a bug that you should address.
GitLab allows you to add coverage-guided fuzz testing to your pipelines. This helps you discover
-bugs and potential security issues that other QA processes may miss. Coverage-guided fuzzing sends
-random inputs to an instrumented version of your application in an effort to cause unexpected
-behavior, such as a crash. Such behavior indicates a bug that you should address.
+bugs and potential security issues that other QA processes may miss.
We recommend that you use fuzz testing in addition to the other security scanners in [GitLab Secure](../index.md)
and your own test processes. If you're using [GitLab CI/CD](../../../ci/index.md),
-you can run your coverage-guided fuzz tests as part your CI/CD workflow. You can take advantage of
-coverage-guided fuzzing by including the CI job in your existing `.gitlab-ci.yml` file.
+you can run your coverage-guided fuzz tests as part your CI/CD workflow.
## Supported fuzzing engines and languages
diff --git a/doc/user/group/import/index.md b/doc/user/group/import/index.md
index 31c3a8e774e..5718d57319e 100644
--- a/doc/user/group/import/index.md
+++ b/doc/user/group/import/index.md
@@ -102,8 +102,9 @@ This might involve reconfiguring your firewall to prevent blocking connection on
### Connect to the remote GitLab instance
-1. Navigate to the New Group page, either via the `+` button in the top navigation bar, or the **New subgroup** button
-on an existing group's page.
+1. Go to the New Group page:
+ - On the top bar, select `+` and then **New group**.
+ - Or, on an existing group's page, in the top right, select **New subgroup**.
![Navigation paths to create a new group](img/new_group_navigation_v13_8.png)
@@ -111,21 +112,18 @@ on an existing group's page.
![Fill in import details](img/import_panel_v14_1.png)
-1. Fill in source URL of your GitLab.
-1. Fill in [personal access token](../../../user/profile/personal_access_tokens.md) for remote GitLab instance.
-1. Click "Connect instance".
+1. Enter the source URL of your GitLab instance.
+1. Enter the [personal access token](../../../user/profile/personal_access_tokens.md) for your remote GitLab instance.
+1. Select **Connect instance**.
### Selecting which groups to import
After you have authorized access to the GitLab instance, you are redirected to the GitLab Group
-Migration importer page. Listed are the remote GitLab groups to which you have the Owner role.
+Migration importer page. The remote groups you have the Owner role for are listed.
1. By default, the proposed group namespaces match the names as they exist in remote instance, but based on your permissions, you can choose to edit these names before you proceed to import any of them.
-
-1. Select the **Import** button next to any number of groups.
-
-1. The **Status** column shows the import status of each group. You can choose to leave the page open and it updates in real-time.
-
-1. Once a group has been imported, click its GitLab path to open its GitLab URL.
+1. Next to the groups you want to import, select **Import**.
+1. The **Status** column shows the import status of each group. If you leave the page open, it updates in real-time.
+1. After a group has been imported, select its GitLab path to open its GitLab URL.
![Group Importer page](img/bulk_imports_v14_1.png)
diff --git a/doc/user/packages/nuget_repository/index.md b/doc/user/packages/nuget_repository/index.md
index 40e8b74c2b6..2af6dc60078 100644
--- a/doc/user/packages/nuget_repository/index.md
+++ b/doc/user/packages/nuget_repository/index.md
@@ -77,6 +77,8 @@ To use the GitLab endpoint for NuGet Packages, choose an option:
Some features such as [publishing](#publish-a-nuget-package) a package are only available on the project-level endpoint.
+When asking for versions of a given NuGet package name, the GitLab Package Registry returns a maximum of 300 most recent versions.
+
WARNING:
Because of how NuGet handles credentials, the Package Registry rejects anonymous requests on the group-level endpoint.
To work around this limitation, set up [authentication](#add-the-package-registry-as-a-source-for-nuget-packages).
@@ -352,12 +354,12 @@ the existing package is overwritten.
## Install packages
-To install a NuGet package from the Package Registry, you must first
-[add a project-level or group-level endpoint](#add-the-package-registry-as-a-source-for-nuget-packages).
-
If multiple packages have the same name and version, when you install
a package, the most recently-published package is retrieved.
+To install a NuGet package from the Package Registry, you must first
+[add a project-level or group-level endpoint](#add-the-package-registry-as-a-source-for-nuget-packages).
+
### Install a package with the NuGet CLI
WARNING:
diff --git a/doc/user/project/clusters/add_gke_clusters.md b/doc/user/project/clusters/add_gke_clusters.md
index 78d4bce737d..0d35e89a560 100644
--- a/doc/user/project/clusters/add_gke_clusters.md
+++ b/doc/user/project/clusters/add_gke_clusters.md
@@ -4,48 +4,55 @@ group: Configure
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
---
-# GKE clusters (DEPRECATED) **(FREE)**
-
-> - [Deprecated](https://gitlab.com/groups/gitlab-org/-/epics/6049) in GitLab 14.0.
+# Connect GKE clusters through cluster certificates **(FREE)**
WARNING:
-Use [Infrastructure as Code](../../infrastructure/index.md) to create new clusters. The method described in this document is deprecated as of GitLab 14.0.
+Use [Infrastrucure as Code](../../infrastructure/clusters/connect/new_gke_cluster.md)
+to create a cluster hosted on Google Kubernetes Engine (GKE).
-Through GitLab, you can create new clusters and add existing clusters hosted on Amazon Elastic
-Kubernetes Service (EKS).
+Through GitLab, you can create new and connect existing clusters
+hosted on Google Kubernetes Engine (GKE).
-GitLab supports adding new and existing GKE clusters.
+## Connect an existing GKE cluster
-## GKE requirements
+If you already have a GKE cluster and want to connect it to GitLab,
+use the [GitLab Kubernetes Agent](../../clusters/agent/index.md).
-Before creating your first cluster on Google GKE with GitLab integration, make sure the following
-requirements are met:
+Alternatively, you can [connect them with cluster certificates](add_existing_cluster.md),
+altough this method is not recommended for [security implications](../../infrastructure/clusters/connect/index.md#security-implications-for-clusters-connected-with-certificates).
-- A [billing account](https://cloud.google.com/billing/docs/how-to/manage-billing-account)
- set up with access.
-- The Kubernetes Engine API and related service are enabled. It should work immediately but may
- take up to 10 minutes after you create a project. For more information see the
- ["Before you begin" section of the Kubernetes Engine docs](https://cloud.google.com/kubernetes-engine/docs/quickstart#before-you-begin).
+## Create a new GKE cluster from GitLab
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/25925) in GitLab 12.4, all the GKE clusters provisioned by GitLab are [VPC-native](https://cloud.google.com/kubernetes-engine/docs/how-to/alias-ips).
+
+To create a new GKE cluster from GitLab, use [Infrastructure as Code](../../infrastructure/clusters/connect/new_gke_cluster.md).
-## Add an existing GKE cluster
+Alternatively, you can [create new GKE clusters using cluster certificates](#create-a-new-cluster-on-gke-through-cluster-certificates-deprecated).
+Although still available in the GitLab UI, this method was deprecated
+in GitLab 14.0 and is scheduled for removal in GitLab 15.0.
+It also has [security implications](../../infrastructure/clusters/connect/index.md#security-implications-for-clusters-connected-with-certificates).
-If you already have a GKE cluster and want to integrate it with GitLab,
-see how to [add an existing cluster](add_existing_cluster.md).
+## Create a new cluster on GKE through cluster certificates (DEPRECATED)
-## Create new GKE cluster
+> [Deprecated](https://gitlab.com/groups/gitlab-org/-/epics/6049) in GitLab 14.0.
-Starting from [GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/issues/25925), all the GKE clusters
-provisioned by GitLab are [VPC-native](https://cloud.google.com/kubernetes-engine/docs/how-to/alias-ips).
+Prerequisites:
+
+- A [Google Cloud billing account](https://cloud.google.com/billing/docs/how-to/manage-billing-account)
+ set up with access.
+- Kubernetes Engine API and related services enabled. It should work immediately but may
+ take up to 10 minutes after you create a project. For more information see the
+ ["Before you begin" section of the Kubernetes Engine docs](https://cloud.google.com/kubernetes-engine/docs/quickstart#before-you-begin).
Note the following:
- The [Google authentication integration](../../../integration/google.md) must be enabled in GitLab
at the instance level. If that's not the case, ask your GitLab administrator to enable it. On
GitLab.com, this is enabled.
-- Starting from [GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/55902), all GKE clusters
+- In [GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/55902) and later, all GKE clusters
created by GitLab are RBAC-enabled. Take a look at the [RBAC section](cluster_access.md#rbac-cluster-resources) for
more information.
-- Starting from [GitLab 12.5](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18341), the
+- In [GitLab 12.5](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/18341) and later, the
cluster's pod address IP range is set to `/16` instead of the regular `/14`. `/16` is a CIDR
notation.
- GitLab requires basic authentication enabled and a client certificate issued for the cluster to
@@ -54,9 +61,8 @@ Note the following:
explicitly requests GKE to create clusters with basic authentication enabled and a client
certificate.
-### Creating the cluster on GKE
-
-To create and add a new Kubernetes cluster to your project, group, or instance:
+To create new Kubernetes clusters to your project, group, or instance, through
+cluster certificates:
1. Navigate to your:
- Project's **{cloud-gear}** **Infrastructure > Kubernetes clusters** page, for a project-level
diff --git a/doc/user/project/index.md b/doc/user/project/index.md
index 8c81af418a0..05006b8ac38 100644
--- a/doc/user/project/index.md
+++ b/doc/user/project/index.md
@@ -1,6 +1,6 @@
---
-stage: Create
-group: Source Code
+stage: Manage
+group: Workspace
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
type: reference
---
diff --git a/doc/user/project/members/share_project_with_groups.md b/doc/user/project/members/share_project_with_groups.md
index e1149b85cd5..abca140411a 100644
--- a/doc/user/project/members/share_project_with_groups.md
+++ b/doc/user/project/members/share_project_with_groups.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Workspace
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
---
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index 662d7e70910..d983bcc9cf2 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -139,6 +139,11 @@ The following items are **not** exported:
- Any encrypted tokens
- Merge Request Approvers
+These content rules also apply to creating projects from templates on the
+[group](../../group/custom_project_templates.md)
+or [instance](../../admin_area/custom_project_templates.md)
+levels, because the same export and import mechanisms are used.
+
NOTE:
For more details on the specific data persisted in a project export, see the
[`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/project/import_export.yml) file.
@@ -255,13 +260,13 @@ reduce the repository size for another import attempt.
git reflog expire --expire=now --all
git gc --prune=now --aggressive
- # Prepare recreating an importable file
+ # Prepare recreating an importable file
git bundle create ../project.bundle smaller-tmp-main
cd ..
mv project/ ../"$EXPORT"-project
cd ..
- # Recreate an importable file
+ # Recreate an importable file
tar -czf "$EXPORT"-smaller.tar.gz --directory="$EXPORT"/ .
```
diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md
index 8b159a75451..e473e160f4e 100644
--- a/doc/user/project/settings/index.md
+++ b/doc/user/project/settings/index.md
@@ -1,6 +1,6 @@
---
-stage: Create
-group: Source Code
+stage: Manage
+group: Workspace
info: "To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments"
type: reference, index, howto
---
diff --git a/doc/user/project/working_with_projects.md b/doc/user/project/working_with_projects.md
index 32bb202767a..f7a89c89779 100644
--- a/doc/user/project/working_with_projects.md
+++ b/doc/user/project/working_with_projects.md
@@ -1,6 +1,6 @@
---
-stage: Create
-group: Source Code
+stage: Manage
+group: Workspace
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"
---
diff --git a/doc/user/reserved_names.md b/doc/user/reserved_names.md
index bf9abcca640..203b1c9e93a 100644
--- a/doc/user/reserved_names.md
+++ b/doc/user/reserved_names.md
@@ -1,6 +1,6 @@
---
-stage: none
-group: unassigned
+stage: Manage
+group: Workspace
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
---
diff --git a/lib/gitlab/git/keep_around.rb b/lib/gitlab/git/keep_around.rb
index b6fc335c979..38f0e47c4c7 100644
--- a/lib/gitlab/git/keep_around.rb
+++ b/lib/gitlab/git/keep_around.rb
@@ -19,7 +19,7 @@ module Gitlab
end
def execute(shas)
- shas.each do |sha|
+ shas.uniq.each do |sha|
next unless sha.present? && commit_by(oid: sha)
next if kept_around?(sha)
diff --git a/lib/gitlab/optimistic_locking.rb b/lib/gitlab/optimistic_locking.rb
index b5e304599ab..9f39b5f122f 100644
--- a/lib/gitlab/optimistic_locking.rb
+++ b/lib/gitlab/optimistic_locking.rb
@@ -11,7 +11,7 @@ module Gitlab
retry_attempts = 0
begin
- ActiveRecord::Base.transaction do # rubocop: disable Database/MultipleDatabases
+ subject.transaction do
yield(subject)
end
rescue ActiveRecord::StaleObjectError
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 029198a83e5..73fd4f3aece 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -6569,17 +6569,20 @@ msgstr ""
msgid "Checkout|%{name}'s GitLab subscription"
msgstr ""
+msgid "Checkout|%{quantity} GB of storage"
+msgstr ""
+
+msgid "Checkout|%{quantity} storage pack"
+msgid_plural "Checkout|%{quantity} storage packs"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Checkout|%{selectedPlanText} plan"
msgstr ""
msgid "Checkout|%{startDate} - %{endDate}"
msgstr ""
-msgid "Checkout|%{totalCiMinutes} CI minute"
-msgid_plural "Checkout|%{totalCiMinutes} CI minutes"
-msgstr[0] ""
-msgstr[1] ""
-
msgid "Checkout|%{totalCiMinutes} CI minutes"
msgstr ""
@@ -6655,6 +6658,9 @@ msgstr ""
msgid "Checkout|Failed to register credit card. Please try again."
msgstr ""
+msgid "Checkout|GB"
+msgstr ""
+
msgid "Checkout|GitLab group"
msgstr ""
@@ -6691,6 +6697,9 @@ msgstr ""
msgid "Checkout|State"
msgstr ""
+msgid "Checkout|Storage packs"
+msgstr ""
+
msgid "Checkout|Street address"
msgstr ""
@@ -6712,6 +6721,9 @@ msgstr ""
msgid "Checkout|Total minutes: %{quantity}"
msgstr ""
+msgid "Checkout|Total storage: %{quantity} GB"
+msgstr ""
+
msgid "Checkout|Users"
msgstr ""
@@ -6730,7 +6742,10 @@ msgstr ""
msgid "Checkout|company or team"
msgstr ""
-msgid "Checkout|x 1,000 minutes per pack = %{strong}"
+msgid "Checkout|minutes"
+msgstr ""
+
+msgid "Checkout|x %{quantity} %{units} per pack ="
msgstr ""
msgid "Cherry-pick this commit"
@@ -9234,6 +9249,9 @@ msgstr ""
msgid "Copy link to chart"
msgstr ""
+msgid "Copy prefix"
+msgstr ""
+
msgid "Copy reference"
msgstr ""
@@ -9456,6 +9474,9 @@ msgstr ""
msgid "Create a Mattermost team for this group"
msgstr ""
+msgid "Create a local proxy for storing frequently used upstream images. %{docLinkStart}Learn more%{docLinkEnd} about dependency proxies."
+msgstr ""
+
msgid "Create a local proxy for storing frequently used upstream images. %{link_start}Learn more%{link_end} about dependency proxies."
msgstr ""
@@ -25351,9 +25372,6 @@ msgstr ""
msgid "Please set a new password before proceeding."
msgstr ""
-msgid "Please share your feedback about %{featureName} %{linkStart}in this issue%{linkEnd} to help us improve the experience."
-msgstr ""
-
msgid "Please solve the captcha"
msgstr ""
diff --git a/package.json b/package.json
index fc1630acf09..da980bb3b77 100644
--- a/package.json
+++ b/package.json
@@ -63,12 +63,12 @@
"@rails/ujs": "6.1.3-2",
"@sentry/browser": "5.30.0",
"@sourcegraph/code-host-integration": "0.0.60",
- "@tiptap/core": "^2.0.0-beta.105",
+ "@tiptap/core": "^2.0.0-beta.108",
"@tiptap/extension-blockquote": "^2.0.0-beta.15",
"@tiptap/extension-bold": "^2.0.0-beta.15",
"@tiptap/extension-bullet-list": "^2.0.0-beta.15",
"@tiptap/extension-code": "^2.0.0-beta.16",
- "@tiptap/extension-code-block-lowlight": "2.0.0-beta.37",
+ "@tiptap/extension-code-block-lowlight": "2.0.0-beta.38",
"@tiptap/extension-document": "^2.0.0-beta.13",
"@tiptap/extension-dropcursor": "^2.0.0-beta.19",
"@tiptap/extension-gapcursor": "^2.0.0-beta.19",
@@ -85,14 +85,14 @@
"@tiptap/extension-strike": "^2.0.0-beta.17",
"@tiptap/extension-subscript": "^2.0.0-beta.4",
"@tiptap/extension-superscript": "^2.0.0-beta.4",
- "@tiptap/extension-table": "^2.0.0-beta.30",
+ "@tiptap/extension-table": "^2.0.0-beta.31",
"@tiptap/extension-table-cell": "^2.0.0-beta.15",
"@tiptap/extension-table-header": "^2.0.0-beta.17",
"@tiptap/extension-table-row": "^2.0.0-beta.14",
"@tiptap/extension-task-item": "^2.0.0-beta.18",
"@tiptap/extension-task-list": "^2.0.0-beta.17",
"@tiptap/extension-text": "^2.0.0-beta.13",
- "@tiptap/vue-2": "^2.0.0-beta.50",
+ "@tiptap/vue-2": "^2.0.0-beta.52",
"@toast-ui/editor": "^2.5.2",
"@toast-ui/vue-editor": "^2.5.2",
"apollo-cache-inmemory": "^1.6.6",
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
index 7519f4daae2..cfa9b900448 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
@@ -38,7 +38,11 @@ module QA
end
end
- context 'when using attachments in comments', :object_storage do
+ context 'when using attachments in comments', :object_storage, quarantine: {
+ only: { job: 'object_storage' },
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341209#note_681513082',
+ type: :investigating
+ } do
let(:png_file_name) { 'testfile.png' }
let(:file_to_attach) do
File.absolute_path(File.join('qa', 'fixtures', 'designs', png_file_name))
diff --git a/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb
index 9ddf485870d..bb9563acc90 100644
--- a/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/composer_registry_spec.rb
@@ -1,7 +1,11 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage, quarantine: {
+ only: { job: 'object_storage' },
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341209#note_681513082',
+ type: :investigating
+ } do
describe 'Composer Repository' do
include Runtime::Fixtures
diff --git a/qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb
index 2e5fa2c2904..f8ca3f5af3e 100644
--- a/qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/generic_repository_spec.rb
@@ -1,7 +1,11 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage, quarantine: {
+ only: { job: 'object_storage' },
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341209#note_681513082',
+ type: :investigating
+ } do
describe 'Generic Repository' do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
diff --git a/qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb
index fb3f2abd87a..7ad46ef480b 100644
--- a/qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/maven_gradle_repository_spec.rb
@@ -1,7 +1,11 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage, quarantine: {
+ only: { job: 'object_storage' },
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341209#note_681513082',
+ type: :investigating
+ } do
describe 'Maven Repository with Gradle' do
using RSpec::Parameterized::TableSyntax
include Runtime::Fixtures
diff --git a/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
index bf1d2a04dba..ea55a07db15 100644
--- a/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/maven_repository_spec.rb
@@ -1,7 +1,11 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :packages, :reliable, :object_storage do
+ RSpec.describe 'Package', :orchestrated, :packages, :reliable, :object_storage, quarantine: {
+ only: { job: 'object_storage' },
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341209#note_681513082',
+ type: :investigating
+ } do
describe 'Maven Repository' do
include Runtime::Fixtures
diff --git a/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
index 5a3b4388f0c..19c194cf5ac 100644
--- a/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/npm_registry_spec.rb
@@ -1,7 +1,11 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :packages, :reliable, :object_storage do
+ RSpec.describe 'Package', :orchestrated, :packages, :reliable, :object_storage, quarantine: {
+ only: { job: 'object_storage' },
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341209#note_681513082',
+ type: :investigating
+ } do
describe 'npm registry' do
using RSpec::Parameterized::TableSyntax
include Runtime::Fixtures
diff --git a/qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb
index 8a6752ed817..5813e7358d4 100644
--- a/qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/nuget_repository_spec.rb
@@ -1,7 +1,11 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage, quarantine: {
+ only: { job: 'object_storage' },
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341209#note_681513082',
+ type: :investigating
+ } do
describe 'NuGet Repository' do
using RSpec::Parameterized::TableSyntax
include Runtime::Fixtures
diff --git a/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb b/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb
index dfc9202ebed..74407e112a1 100644
--- a/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/pypi_repository_spec.rb
@@ -1,7 +1,11 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :packages, :object_storage do
+ RSpec.describe 'Package', :orchestrated, :packages, :object_storage, quarantine: {
+ only: { job: 'object_storage' },
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341209#note_681513082',
+ type: :investigating
+ } do
describe 'PyPI Repository' do
include Runtime::Fixtures
let(:project) do
diff --git a/qa/qa/specs/features/browser_ui/5_package/rubygems_registry_spec.rb b/qa/qa/specs/features/browser_ui/5_package/rubygems_registry_spec.rb
index 9a45b072eed..0d22b3bbbff 100644
--- a/qa/qa/specs/features/browser_ui/5_package/rubygems_registry_spec.rb
+++ b/qa/qa/specs/features/browser_ui/5_package/rubygems_registry_spec.rb
@@ -1,7 +1,11 @@
# frozen_string_literal: true
module QA
- RSpec.describe 'Package', :orchestrated, :requires_admin, :packages, :object_storage do
+ RSpec.describe 'Package', :orchestrated, :requires_admin, :packages, :object_storage, quarantine: {
+ only: { job: 'object_storage' },
+ issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/341209#note_681513082',
+ type: :investigating
+ } do
describe 'RubyGems Repository' do
include Runtime::Fixtures
diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb
index 4fc7d945881..7548167efae 100644
--- a/spec/factories/ci/pipelines.rb
+++ b/spec/factories/ci/pipelines.rb
@@ -48,6 +48,7 @@ FactoryBot.define do
transient { ci_ref_presence { true } }
before(:create) do |pipeline, evaluator|
+ pipeline.ensure_project_iid!
pipeline.ensure_ci_ref! if evaluator.ci_ref_presence && pipeline.ci_ref_id.nil?
end
diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb
index 3208ad82c03..9eff02a8c1b 100644
--- a/spec/features/markdown/markdown_spec.rb
+++ b/spec/features/markdown/markdown_spec.rb
@@ -133,8 +133,9 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do
expect(doc.at_css('td:contains("Baz")')['align']).to eq 'left'
end
+ # note that 2 are from the hardcoded <sup>, and 2 from footnotes
aggregate_failures 'permits superscript elements' do
- expect(doc).to have_selector('sup', count: 2)
+ expect(doc).to have_selector('sup', count: 4)
end
aggregate_failures 'permits subscript elements' do
@@ -148,6 +149,11 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do
aggregate_failures "removes `href` from `a` elements if it's fishy" do
expect(doc).not_to have_selector('a[href*="javascript"]')
end
+
+ aggregate_failures 'permits footnotes' do
+ expect(doc).to have_selector('section.footnotes ol li p:contains("Footnote 1")')
+ expect(doc).to have_selector('section.footnotes ol li p:contains("Footnote with w")')
+ end
end
describe 'Escaping' do
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
index 100d17cc16e..2da16408fbc 100644
--- a/spec/fixtures/markdown.md.erb
+++ b/spec/fixtures/markdown.md.erb
@@ -52,6 +52,15 @@ Redcarpet supports this superscript syntax ( x^2 ).
This (C<sub>6</sub>H<sub>12</sub>O<sub>6</sub>) is an example of subscripts in Markdown.
+### Footnotes
+
+This is footnote 1.[^f1]
+
+A footnote with a `w` was failing.[^f2-w]
+
+[^f1]: Footnote 1
+[^f2-w]: Footnote with w
+
### Next step
After the Markdown has been turned into HTML, it gets passed through...
diff --git a/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js b/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js
new file mode 100644
index 00000000000..54bb91ef492
--- /dev/null
+++ b/spec/frontend/packages_and_registries/dependency_proxy/app_spec.js
@@ -0,0 +1,82 @@
+import { GlAlert, GlFormInputGroup, GlFormGroup, GlSprintf } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+
+import DependencyProxyApp from '~/packages_and_registries/dependency_proxy/app.vue';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+
+describe('DependencyProxyApp', () => {
+ let wrapper;
+
+ const provideDefaults = {
+ groupPath: 'gitlab-org',
+ dependencyProxyAvailable: true,
+ };
+
+ function createComponent({ provide = provideDefaults } = {}) {
+ wrapper = shallowMountExtended(DependencyProxyApp, {
+ provide,
+ stubs: {
+ GlFormInputGroup,
+ GlFormGroup,
+ GlSprintf,
+ },
+ });
+ }
+
+ const findProxyNotAvailableAlert = () => wrapper.findComponent(GlAlert);
+ const findClipBoardButton = () => wrapper.findComponent(ClipboardButton);
+ const findFormGroup = () => wrapper.findComponent(GlFormGroup);
+ const findFormInputGroup = () => wrapper.findComponent(GlFormInputGroup);
+ const findMainArea = () => wrapper.findByTestId('main-area');
+ const findProxyCountText = () => wrapper.findByTestId('proxy-count');
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('when the dependency proxy is not available', () => {
+ beforeEach(() => {
+ createComponent({ provide: { ...provideDefaults, dependencyProxyAvailable: false } });
+ });
+
+ it('renders an info alert', () => {
+ expect(findProxyNotAvailableAlert().text()).toBe(
+ DependencyProxyApp.i18n.proxyNotAvailableText,
+ );
+ });
+
+ it('does not render the main area', () => {
+ expect(findMainArea().exists()).toBe(false);
+ });
+ });
+
+ describe('when the dependency proxy is available', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('does not render the info alert', () => {
+ expect(findProxyNotAvailableAlert().exists()).toBe(false);
+ });
+
+ it('renders the main area', () => {
+ expect(findMainArea().exists()).toBe(true);
+ });
+
+ it('renders a form group with a label', () => {
+ expect(findFormGroup().attributes('label')).toBe(DependencyProxyApp.i18n.proxyImagePrefix);
+ });
+
+ it('renders a form input group', () => {
+ expect(findFormInputGroup().exists()).toBe(true);
+ });
+
+ it('form input group has a clipboard button', () => {
+ expect(findClipBoardButton().exists()).toBe(true);
+ });
+
+ it('from group has a description with proxy count', () => {
+ expect(findProxyCountText().text()).toBe('Contains 0 blobs of images (0 bytes)');
+ });
+ });
+});
diff --git a/spec/frontend/pages/projects/new/components/new_project_url_select_spec.js b/spec/frontend/pages/projects/new/components/new_project_url_select_spec.js
index 8a7f9229503..4902b830d0e 100644
--- a/spec/frontend/pages/projects/new/components/new_project_url_select_spec.js
+++ b/spec/frontend/pages/projects/new/components/new_project_url_select_spec.js
@@ -1,4 +1,10 @@
-import { GlButton, GlDropdown, GlDropdownItem, GlDropdownSectionHeader } from '@gitlab/ui';
+import {
+ GlButton,
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownSectionHeader,
+ GlSearchBoxByType,
+} from '@gitlab/ui';
import { createLocalVue, mount, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
@@ -34,9 +40,6 @@ describe('NewProjectUrlSelect component', () => {
const localVue = createLocalVue();
localVue.use(VueApollo);
- const requestHandlers = [[searchQuery, jest.fn().mockResolvedValue({ data })]];
- const apolloProvider = createMockApollo(requestHandlers);
-
const provide = {
namespaceFullPath: 'h5bp',
namespaceId: '28',
@@ -44,11 +47,25 @@ describe('NewProjectUrlSelect component', () => {
trackLabel: 'blank_project',
};
- const mountComponent = ({ mountFn = shallowMount } = {}) =>
- mountFn(NewProjectUrlSelect, { localVue, apolloProvider, provide });
+ const mountComponent = ({ search = '', queryResponse = data, mountFn = shallowMount } = {}) => {
+ const requestHandlers = [[searchQuery, jest.fn().mockResolvedValue({ data: queryResponse })]];
+ const apolloProvider = createMockApollo(requestHandlers);
+
+ return mountFn(NewProjectUrlSelect, {
+ localVue,
+ apolloProvider,
+ provide,
+ data() {
+ return {
+ search,
+ };
+ },
+ });
+ };
const findButtonLabel = () => wrapper.findComponent(GlButton);
const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findInput = () => wrapper.findComponent(GlSearchBoxByType);
const findHiddenInput = () => wrapper.find('input');
afterEach(() => {
@@ -74,6 +91,19 @@ describe('NewProjectUrlSelect component', () => {
expect(findHiddenInput().attributes('value')).toBe(provide.namespaceId);
});
+ it('focuses on the input when the dropdown is opened', async () => {
+ wrapper = mountComponent({ mountFn: mount });
+
+ jest.runOnlyPendingTimers();
+ await wrapper.vm.$nextTick();
+
+ const spy = jest.spyOn(findInput().vm, 'focusInput');
+
+ findDropdown().vm.$emit('shown');
+
+ expect(spy).toHaveBeenCalledTimes(1);
+ });
+
it('renders expected dropdown items', async () => {
wrapper = mountComponent({ mountFn: mount });
@@ -89,6 +119,27 @@ describe('NewProjectUrlSelect component', () => {
expect(listItems.at(4).text()).toBe(data.currentUser.namespace.fullPath);
});
+ it('renders `No matches found` when there are no matching dropdown items', async () => {
+ const queryResponse = {
+ currentUser: {
+ groups: {
+ nodes: [],
+ },
+ namespace: {
+ id: 'gid://gitlab/Namespace/1',
+ fullPath: 'root',
+ },
+ },
+ };
+
+ wrapper = mountComponent({ search: 'no matches', queryResponse, mountFn: mount });
+
+ jest.runOnlyPendingTimers();
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.find('li').text()).toBe('No matches found');
+ });
+
it('updates hidden input with selected namespace', async () => {
wrapper = mountComponent();
diff --git a/spec/frontend/projects/settings/components/new_access_dropdown_spec.js b/spec/frontend/projects/settings/components/new_access_dropdown_spec.js
new file mode 100644
index 00000000000..d8e8af9651a
--- /dev/null
+++ b/spec/frontend/projects/settings/components/new_access_dropdown_spec.js
@@ -0,0 +1,204 @@
+import {
+ GlSprintf,
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownSectionHeader,
+ GlSearchBoxByType,
+} from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import { nextTick } from 'vue';
+import waitForPromises from 'helpers/wait_for_promises';
+import { getUsers, getGroups, getDeployKeys } from '~/projects/settings/api/access_dropdown_api';
+import AccessDropdown, { i18n } from '~/projects/settings/components/access_dropdown.vue';
+import { ACCESS_LEVELS } from '~/projects/settings/constants';
+
+jest.mock('~/projects/settings/api/access_dropdown_api', () => ({
+ getUsers: jest.fn().mockResolvedValue({ data: [{ id: 1 }, { id: 2 }] }),
+ getGroups: jest.fn().mockResolvedValue({ data: [{ id: 3 }, { id: 4 }, { id: 5 }] }),
+ getDeployKeys: jest.fn().mockResolvedValue({
+ data: [
+ { id: 6, title: 'key1', fingerprint: 'abcdefghijklmnop', owner: { name: 'user1' } },
+ { id: 7, title: 'key1', fingerprint: 'abcdefghijklmnop', owner: { name: 'user2' } },
+ { id: 8, title: 'key1', fingerprint: 'abcdefghijklmnop', owner: { name: 'user3' } },
+ { id: 9, title: 'key1', fingerprint: 'abcdefghijklmnop', owner: { name: 'user4' } },
+ ],
+ }),
+}));
+
+describe('Access Level Dropdown', () => {
+ let wrapper;
+ const mockAccessLevelsData = [
+ {
+ id: 42,
+ text: 'Dummy Role',
+ },
+ ];
+
+ const createComponent = ({
+ accessLevelsData = mockAccessLevelsData,
+ accessLevel = ACCESS_LEVELS.PUSH,
+ hasLicense = true,
+ } = {}) => {
+ wrapper = shallowMount(AccessDropdown, {
+ propsData: {
+ accessLevelsData,
+ accessLevel,
+ hasLicense,
+ },
+ stubs: {
+ GlSprintf,
+ GlDropdown,
+ },
+ });
+ };
+
+ const findDropdown = () => wrapper.findComponent(GlDropdown);
+ const findDropdownToggleLabel = () => findDropdown().props('text');
+ const findAllDropdownItems = () => findDropdown().findAllComponents(GlDropdownItem);
+ const findAllDropdownHeaders = () => findDropdown().findAllComponents(GlDropdownSectionHeader);
+ const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
+
+ describe('data request', () => {
+ it('should make an api call for users, groups && deployKeys when user has a license', () => {
+ createComponent();
+ expect(getUsers).toHaveBeenCalled();
+ expect(getGroups).toHaveBeenCalled();
+ expect(getDeployKeys).toHaveBeenCalled();
+ });
+
+ it('should make an api call for deployKeys but not for users or groups when user does not have a license', () => {
+ createComponent({ hasLicense: false });
+ expect(getUsers).not.toHaveBeenCalled();
+ expect(getGroups).not.toHaveBeenCalled();
+ expect(getDeployKeys).toHaveBeenCalled();
+ });
+
+ it('should make api calls when search query is updated', async () => {
+ createComponent();
+ const query = 'root';
+
+ findSearchBox().vm.$emit('input', query);
+ await nextTick();
+ expect(getUsers).toHaveBeenCalledWith(query);
+ expect(getGroups).toHaveBeenCalled();
+ expect(getDeployKeys).toHaveBeenCalledWith(query);
+ });
+ });
+ describe('layout', () => {
+ beforeEach(async () => {
+ createComponent();
+ await waitForPromises();
+ });
+
+ it('renders headers for each section ', () => {
+ expect(findAllDropdownHeaders()).toHaveLength(4);
+ });
+
+ it('renders dropdown item for each access level type', () => {
+ expect(findAllDropdownItems()).toHaveLength(10);
+ });
+ });
+ describe('toggleLabel', () => {
+ beforeEach(async () => {
+ createComponent();
+ await waitForPromises();
+ });
+
+ const triggerNthItemClick = async (n) => {
+ findAllDropdownItems().at(n).trigger('click');
+ await nextTick();
+ };
+
+ it('when no items selected displays a default label and has default CSS class ', () => {
+ expect(findDropdownToggleLabel()).toBe(i18n.selectUsers);
+ expect(findDropdown().props('toggleClass')).toBe('gl-text-gray-500!');
+ });
+
+ it('displays a number of selected items for each group level', async () => {
+ findAllDropdownItems().wrappers.forEach((item) => {
+ item.trigger('click');
+ });
+ await nextTick();
+ expect(findDropdownToggleLabel()).toBe('1 role, 2 users, 4 deploy keys, 3 groups');
+ });
+
+ it('with only role selected displays the role name and has no class applied', async () => {
+ await triggerNthItemClick(0);
+ expect(findDropdownToggleLabel()).toBe('Dummy Role');
+ expect(findDropdown().props('toggleClass')).toBe('');
+ });
+
+ it('with only groups selected displays the number of selected groups', async () => {
+ await triggerNthItemClick(1);
+ await triggerNthItemClick(2);
+ await triggerNthItemClick(3);
+ expect(findDropdownToggleLabel()).toBe('3 groups');
+ expect(findDropdown().props('toggleClass')).toBe('');
+ });
+
+ it('with only users selected displays the number of selected users', async () => {
+ await triggerNthItemClick(4);
+ await triggerNthItemClick(5);
+ expect(findDropdownToggleLabel()).toBe('2 users');
+ expect(findDropdown().props('toggleClass')).toBe('');
+ });
+
+ it('with users and groups selected displays the number of selected users & groups', async () => {
+ await triggerNthItemClick(1);
+ await triggerNthItemClick(2);
+ await triggerNthItemClick(4);
+ await triggerNthItemClick(5);
+ expect(findDropdownToggleLabel()).toBe('2 users, 2 groups');
+ expect(findDropdown().props('toggleClass')).toBe('');
+ });
+
+ it('with users and deploy keys selected displays the number of selected users & keys', async () => {
+ await triggerNthItemClick(1);
+ await triggerNthItemClick(2);
+ await triggerNthItemClick(6);
+ expect(findDropdownToggleLabel()).toBe('1 deploy key, 2 groups');
+ expect(findDropdown().props('toggleClass')).toBe('');
+ });
+ });
+
+ describe('selecting an item', () => {
+ beforeEach(async () => {
+ createComponent();
+ await waitForPromises();
+ });
+
+ it('selects the item on click and deselects on the next click ', async () => {
+ const item = findAllDropdownItems().at(1);
+ item.trigger('click');
+ await nextTick();
+ expect(item.props('isChecked')).toBe(true);
+ item.trigger('click');
+ await nextTick();
+ expect(item.props('isChecked')).toBe(false);
+ });
+
+ it('emits an update on selection ', async () => {
+ const spy = jest.spyOn(wrapper.vm, '$emit');
+ findAllDropdownItems().at(4).trigger('click');
+ findAllDropdownItems().at(3).trigger('click');
+ await nextTick();
+ expect(spy).toHaveBeenLastCalledWith('select', [
+ { id: 5, type: 'group' },
+ { id: 1, type: 'user' },
+ ]);
+ });
+ });
+
+ describe('on dropdown open', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('should set the search input focus', () => {
+ wrapper.vm.$refs.search.focusInput = jest.fn();
+ findDropdown().vm.$emit('shown');
+
+ expect(wrapper.vm.$refs.search.focusInput).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/frontend/vue_shared/components/dismissible_feedback_alert_spec.js b/spec/frontend/vue_shared/components/dismissible_feedback_alert_spec.js
index 175d79dd1c2..194681a6138 100644
--- a/spec/frontend/vue_shared/components/dismissible_feedback_alert_spec.js
+++ b/spec/frontend/vue_shared/components/dismissible_feedback_alert_spec.js
@@ -1,4 +1,4 @@
-import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
+import { GlAlert, GlSprintf } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import Component from '~/vue_shared/components/dismissible_feedback_alert.vue';
@@ -8,20 +8,13 @@ describe('Dismissible Feedback Alert', () => {
let wrapper;
- const defaultProps = {
- featureName: 'Dependency List',
- feedbackLink: 'https://gitlab.link',
- };
-
+ const featureName = 'Dependency List';
const STORAGE_DISMISSAL_KEY = 'dependency_list_feedback_dismissed';
- const createComponent = ({ props, shallow } = {}) => {
- const mountFn = shallow ? shallowMount : mount;
-
+ const createComponent = ({ mountFn = shallowMount } = {}) => {
wrapper = mountFn(Component, {
propsData: {
- ...defaultProps,
- ...props,
+ featureName,
},
stubs: {
GlSprintf,
@@ -34,8 +27,8 @@ describe('Dismissible Feedback Alert', () => {
wrapper = null;
});
- const findAlert = () => wrapper.find(GlAlert);
- const findLink = () => wrapper.find(GlLink);
+ const createFullComponent = () => createComponent({ mountFn: mount });
+ const findAlert = () => wrapper.findComponent(GlAlert);
describe('with default', () => {
beforeEach(() => {
@@ -46,17 +39,6 @@ describe('Dismissible Feedback Alert', () => {
expect(findAlert().exists()).toBe(true);
});
- it('contains feature name', () => {
- expect(findAlert().text()).toContain(defaultProps.featureName);
- });
-
- it('contains provided link', () => {
- const link = findLink();
-
- expect(link.attributes('href')).toBe(defaultProps.feedbackLink);
- expect(link.attributes('target')).toBe('_blank');
- });
-
it('should have the storage key set', () => {
expect(wrapper.vm.storageKey).toBe(STORAGE_DISMISSAL_KEY);
});
@@ -65,7 +47,7 @@ describe('Dismissible Feedback Alert', () => {
describe('dismissible', () => {
describe('after dismissal', () => {
beforeEach(() => {
- createComponent({ shallow: false });
+ createFullComponent();
findAlert().vm.$emit('dismiss');
});
@@ -81,7 +63,7 @@ describe('Dismissible Feedback Alert', () => {
describe('already dismissed', () => {
it('should not show the alert once dismissed', async () => {
localStorage.setItem(STORAGE_DISMISSAL_KEY, 'true');
- createComponent({ shallow: false });
+ createFullComponent();
await wrapper.vm.$nextTick();
expect(findAlert().exists()).toBe(false);
diff --git a/spec/lib/banzai/pipeline/full_pipeline_spec.rb b/spec/lib/banzai/pipeline/full_pipeline_spec.rb
index 72661003361..7a335fad3f8 100644
--- a/spec/lib/banzai/pipeline/full_pipeline_spec.rb
+++ b/spec/lib/banzai/pipeline/full_pipeline_spec.rb
@@ -34,15 +34,16 @@ RSpec.describe Banzai::Pipeline::FullPipeline do
let(:identifier) { html[/.*fnref1-(\d+).*/, 1] }
let(:footnote_markdown) do
<<~EOF
- first[^1] and second[^second]
+ first[^1] and second[^second] and twenty[^twenty]
[^1]: one
[^second]: two
+ [^twenty]: twenty
EOF
end
let(:filtered_footnote) do
<<~EOF
- <p dir="auto">first<sup class="footnote-ref"><a href="#fn1-#{identifier}" id="fnref1-#{identifier}">1</a></sup> and second<sup class="footnote-ref"><a href="#fn2-#{identifier}" id="fnref2-#{identifier}">2</a></sup></p>
+ <p dir="auto">first<sup class="footnote-ref"><a href="#fn1-#{identifier}" id="fnref1-#{identifier}">1</a></sup> and second<sup class="footnote-ref"><a href="#fn2-#{identifier}" id="fnref2-#{identifier}">2</a></sup> and twenty<sup class="footnote-ref"><a href="#fn3-#{identifier}" id="fnref3-#{identifier}">3</a></sup></p>
<section class="footnotes"><ol>
<li id="fn1-#{identifier}">
@@ -51,6 +52,9 @@ RSpec.describe Banzai::Pipeline::FullPipeline do
<li id="fn2-#{identifier}">
<p>two <a href="#fnref2-#{identifier}" class="footnote-backref"><gl-emoji title="leftwards arrow with hook" data-name="leftwards_arrow_with_hook" data-unicode-version="1.1">↩</gl-emoji></a></p>
</li>
+ <li id="fn3-#{identifier}">
+ <p>twenty <a href="#fnref3-#{identifier}" class="footnote-backref"><gl-emoji title="leftwards arrow with hook" data-name="leftwards_arrow_with_hook" data-unicode-version="1.1">↩</gl-emoji></a></p>
+ </li>
</ol></section>
EOF
end
diff --git a/spec/migrations/20210915022415_cleanup_bigint_conversion_for_ci_builds_spec.rb b/spec/migrations/20210915022415_cleanup_bigint_conversion_for_ci_builds_spec.rb
new file mode 100644
index 00000000000..ee71322433d
--- /dev/null
+++ b/spec/migrations/20210915022415_cleanup_bigint_conversion_for_ci_builds_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!('cleanup_bigint_conversion_for_ci_builds')
+
+RSpec.describe CleanupBigintConversionForCiBuilds do
+ let(:ci_builds) { table(:ci_builds) }
+
+ it 'correctly migrates up and down' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(ci_builds.column_names).to include('id_convert_to_bigint')
+ expect(ci_builds.column_names).to include('stage_id_convert_to_bigint')
+ }
+
+ migration.after -> {
+ ci_builds.reset_column_information
+ expect(ci_builds.column_names).not_to include('id_convert_to_bigint')
+ expect(ci_builds.column_names).not_to include('stage_id_convert_to_bigint')
+ }
+ end
+ end
+end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 436af1d59e9..474e14bebca 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -5289,4 +5289,10 @@ RSpec.describe Ci::Build do
expect(build.reload.queuing_entry).not_to be_present
end
end
+
+ it 'does not generate cross DB queries when a record is created via FactoryBot' do
+ with_cross_database_modification_prevented do
+ create(:ci_build)
+ end
+ end
end
diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb
index dfdb5bc01ae..f01c4075eeb 100644
--- a/spec/support/matchers/markdown_matchers.rb
+++ b/spec/support/matchers/markdown_matchers.rb
@@ -41,7 +41,7 @@ module MarkdownMatchers
set_default_markdown_messages
match do |actual|
- expect(actual).to have_selector('gl-emoji', count: 10)
+ expect(actual).to have_selector('gl-emoji', count: 12)
emoji_element = actual.at_css('gl-emoji')
expect(emoji_element['data-name'].to_s).not_to be_empty
diff --git a/spec/workers/run_pipeline_schedule_worker_spec.rb b/spec/workers/run_pipeline_schedule_worker_spec.rb
index fc572c0d9c3..bb11d1dbb58 100644
--- a/spec/workers/run_pipeline_schedule_worker_spec.rb
+++ b/spec/workers/run_pipeline_schedule_worker_spec.rb
@@ -68,5 +68,20 @@ RSpec.describe RunPipelineScheduleWorker do
worker.perform(pipeline_schedule.id, user.id)
end
end
+
+ context 'when pipeline cannot be created' do
+ before do
+ allow(Ci::CreatePipelineService).to receive(:new) { raise Ci::CreatePipelineService::CreateError }
+ end
+
+ it 'logging a pipeline error' do
+ expect(worker)
+ .to receive(:log_extra_metadata_on_done)
+ .with(:pipeline_creation_error, an_instance_of(Ci::CreatePipelineService::CreateError))
+ .and_call_original
+
+ worker.perform(pipeline_schedule.id, user.id)
+ end
+ end
end
end
diff --git a/yarn.lock b/yarn.lock
index f5af5247db4..e603b675ba0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1467,10 +1467,10 @@
dom-accessibility-api "^0.5.1"
pretty-format "^26.4.2"
-"@tiptap/core@^2.0.0-beta.105":
- version "2.0.0-beta.105"
- resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.0.0-beta.105.tgz#3d758cbbf3e8c9b806d1017cd2e7444f192d8109"
- integrity sha512-ut0ts9hrJXUJlSXZIN8iEt2TPbqYLFBanucUAr0ENLjIlpyyNVXz9IhS3bBEmo7vWExirZmY8O9oidQztesf2A==
+"@tiptap/core@^2.0.0-beta.108":
+ version "2.0.0-beta.108"
+ resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.0.0-beta.108.tgz#fdab0b549c6915d2e1710ecc915d219857c21eef"
+ integrity sha512-cUMAkiCHVQk7EYyge+ChFDLBl9SktVOrFogHnjUJxQw+r1iesXf8A6u8bqi/TCMoWQetyP7ELpscMpxNaIE/rg==
dependencies:
"@types/prosemirror-commands" "^1.0.4"
"@types/prosemirror-inputrules" "^1.0.4"
@@ -1479,7 +1479,7 @@
"@types/prosemirror-schema-list" "^1.0.3"
"@types/prosemirror-state" "^1.2.7"
"@types/prosemirror-transform" "^1.1.4"
- "@types/prosemirror-view" "^1.19.0"
+ "@types/prosemirror-view" "^1.19.1"
prosemirror-commands "^1.1.10"
prosemirror-inputrules "^1.1.3"
prosemirror-keymap "^1.1.3"
@@ -1487,7 +1487,7 @@
prosemirror-schema-list "^1.1.5"
prosemirror-state "^1.3.4"
prosemirror-transform "^1.3.2"
- prosemirror-view "^1.20.0"
+ prosemirror-view "^1.20.1"
"@tiptap/extension-blockquote@^2.0.0-beta.15":
version "2.0.0-beta.15"
@@ -1501,13 +1501,13 @@
resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.0.0-beta.15.tgz#cf9ddb3fc316be9707753ad4e497bfb8a3ebb0c2"
integrity sha512-jKyV6iiwhxwa0+7uuKD74jNDVNLNOS1GmU14MgaA95pY5e1fyaRBPPX8Gtt89niz2CLOY711AV17RPZTe/e60w==
-"@tiptap/extension-bubble-menu@^2.0.0-beta.33":
- version "2.0.0-beta.33"
- resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.33.tgz#50ee84c25118f7ee932385961211b64496355c74"
- integrity sha512-EvXSXyeLFnOAEPvbVz9B4ppP+paDGyK/Fy9b36fkH0hacFLS/lKb/8HrfTXItc0uZIe6xY6RhXTdkx1eAPTdTA==
+"@tiptap/extension-bubble-menu@^2.0.0-beta.34":
+ version "2.0.0-beta.34"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.34.tgz#07598730c3ac755c86b2ac1e283b5be3591fbb0a"
+ integrity sha512-vw2RpwgqJUE7tpDayOey1AYs2qp1J0TVdlo4HYzlOWV1kJ+2Zevf0oA83j+IX6d0XmYqNTp+r+VgLZEd0K1Neg==
dependencies:
prosemirror-state "^1.3.4"
- prosemirror-view "^1.20.0"
+ prosemirror-view "^1.20.1"
tippy.js "^6.3.1"
"@tiptap/extension-bullet-list@^2.0.0-beta.15":
@@ -1517,17 +1517,17 @@
dependencies:
prosemirror-inputrules "^1.1.3"
-"@tiptap/extension-code-block-lowlight@2.0.0-beta.37":
- version "2.0.0-beta.37"
- resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.0.0-beta.37.tgz#672b2f21d49077285a507c2e204b07085df93906"
- integrity sha512-EN8MoCZKB23nHNqgvB/wOS84ySUY9ahB6oao7wDpKAYqBkAF/hEXmDsylDySvyCKXf824lS/vztfaF0hHQbQ/g==
+"@tiptap/extension-code-block-lowlight@2.0.0-beta.38":
+ version "2.0.0-beta.38"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.0.0-beta.38.tgz#2d0dc97ce2a25f4a8bd57fa179dbe591f3d66440"
+ integrity sha512-1JVmesa0RuKMYUKLbW34hCqHjPm5gRKv9NOU6jhI90xJHWG0pe+Am6MEfWosilB66DtcE43zP08aiKWgWBvI2A==
dependencies:
"@tiptap/extension-code-block" "^2.0.0-beta.18"
"@types/lowlight" "^0.0.3"
lowlight "^1.20.0"
prosemirror-model "^1.14.3"
prosemirror-state "^1.3.4"
- prosemirror-view "^1.20.0"
+ prosemirror-view "^1.20.1"
"@tiptap/extension-code-block@^2.0.0-beta.18":
version "2.0.0-beta.18"
@@ -1554,13 +1554,13 @@
"@types/prosemirror-dropcursor" "^1.0.3"
prosemirror-dropcursor "^1.3.5"
-"@tiptap/extension-floating-menu@^2.0.0-beta.27":
- version "2.0.0-beta.27"
- resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.27.tgz#692686854116823be624028fd8d73aa900cf71a9"
- integrity sha512-PgoqO5OxBDMFZroXFD7mpwfDLxeG44xFHu6WK4Gf3O8jqGAQpEr4rMKyTnoNL9PYFhEwtNBzxUopumOT0Po/zQ==
+"@tiptap/extension-floating-menu@^2.0.0-beta.28":
+ version "2.0.0-beta.28"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.28.tgz#be45bd0a558498e0fdfc593832726618e69ee8fc"
+ integrity sha512-GxrD4i75Px6RLlY6ZZRU5C5aCjnEiKODA07TpyITlrB6cwmdzZG4L7n+x4Z/VGiUu6+fkiLpnjLbX/Fbk9Yn2w==
dependencies:
prosemirror-state "^1.3.4"
- prosemirror-view "^1.20.0"
+ prosemirror-view "^1.20.1"
tippy.js "^6.3.1"
"@tiptap/extension-gapcursor@^2.0.0-beta.19":
@@ -1662,13 +1662,13 @@
resolved "https://registry.yarnpkg.com/@tiptap/extension-table-row/-/extension-table-row-2.0.0-beta.14.tgz#9ec98c73e309ee966b71ccd140019874d179e0c8"
integrity sha512-mewdlTqgBCyzeZIZ6F08gfuzwsiYjQ7BvABo2UhDfr0+EN2UvfJj0bT3tGgeZhMxT5Js2DXL+c+ZOVJxWJ9faQ==
-"@tiptap/extension-table@^2.0.0-beta.30":
- version "2.0.0-beta.30"
- resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-2.0.0-beta.30.tgz#f6ff97ea71b17ecf3371ddf80374df9f49042334"
- integrity sha512-s6+HRo3sFv7SUprsUAAF27hg7inITpzl78If3XdrpscuzTVuRmd7GsFnY+aZGPVikekwCMjp/0klE92P4A7w0w==
+"@tiptap/extension-table@^2.0.0-beta.31":
+ version "2.0.0-beta.31"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-2.0.0-beta.31.tgz#96987fe14017be2fd3e4dbd2ce349eec641724de"
+ integrity sha512-yMqnbxaq2DjaZ6EOE9FLSQSO+qHH7oE0rA+ahQkJdy9KycSboKthXBY7P9JeXxariTyD2B/My9x41cuDLWes9w==
dependencies:
prosemirror-tables "^1.1.1"
- prosemirror-view "^1.20.0"
+ prosemirror-view "^1.20.1"
"@tiptap/extension-task-item@^2.0.0-beta.18":
version "2.0.0-beta.18"
@@ -1687,14 +1687,14 @@
resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.0.0-beta.13.tgz#da0af8d9a3f149d20076e15d88c6af21fb6d940f"
integrity sha512-0EtAwuRldCAoFaL/iXgkRepEeOd55rPg5N4FQUN1xTwZT7PDofukP0DG/2jff/Uj17x4uTaJAa9qlFWuNnDvjw==
-"@tiptap/vue-2@^2.0.0-beta.50":
- version "2.0.0-beta.50"
- resolved "https://registry.yarnpkg.com/@tiptap/vue-2/-/vue-2-2.0.0-beta.50.tgz#1c1c3e7d696aaa167fc776e11b393b182f1bfbb4"
- integrity sha512-o325eQbSoxxd6QXc27Mjd+T+yj1CnSTo0IQkbjbzYure8E0ReYXzW2wx5oE6DZA43Ejss2KPXZDEgiUNYRyX1Q==
+"@tiptap/vue-2@^2.0.0-beta.52":
+ version "2.0.0-beta.52"
+ resolved "https://registry.yarnpkg.com/@tiptap/vue-2/-/vue-2-2.0.0-beta.52.tgz#b61fd95f2368a86e64bee20b35a85a69a48fc930"
+ integrity sha512-81UfvJTK68hy+KZDdHSbtMVXbUhkZ8zW9u+dotga34YHgfTeZjO3UrF5eJ34dwzsPaaSU+buFu/TMH5vP3KcWQ==
dependencies:
- "@tiptap/extension-bubble-menu" "^2.0.0-beta.33"
- "@tiptap/extension-floating-menu" "^2.0.0-beta.27"
- prosemirror-view "^1.20.0"
+ "@tiptap/extension-bubble-menu" "^2.0.0-beta.34"
+ "@tiptap/extension-floating-menu" "^2.0.0-beta.28"
+ prosemirror-view "^1.20.1"
"@toast-ui/editor@^2.5.2":
version "2.5.2"
@@ -1950,10 +1950,10 @@
dependencies:
"@types/prosemirror-model" "*"
-"@types/prosemirror-view@*", "@types/prosemirror-view@^1.19.0":
- version "1.19.0"
- resolved "https://registry.yarnpkg.com/@types/prosemirror-view/-/prosemirror-view-1.19.0.tgz#35320b6875ae7c750bce799cccf735e5da91af7a"
- integrity sha512-Y8OX9L+Yni0HgXAN9wcNSf61IId13uqpURnRC5WkmCOlVDsr35vfGjj+tcaQL4dZzblsu3bRkXI/c0oGXp+xgw==
+"@types/prosemirror-view@*", "@types/prosemirror-view@^1.19.1":
+ version "1.19.1"
+ resolved "https://registry.yarnpkg.com/@types/prosemirror-view/-/prosemirror-view-1.19.1.tgz#f12309ef07dfb701d20c2e4d0292d42ba34a081b"
+ integrity sha512-fyQ4NVxAdfISWrE2qT8cpZdosXoH/1JuVYMBs9CdaXPbvi/8R2L2tkkcMRM314piKrO8nfYH5OBZKzP2Ax3jtA==
dependencies:
"@types/prosemirror-model" "*"
"@types/prosemirror-state" "*"
@@ -9670,10 +9670,10 @@ prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transfor
dependencies:
prosemirror-model "^1.0.0"
-prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.16.5, prosemirror-view@^1.20.0:
- version "1.20.0"
- resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.20.0.tgz#64198845f0d112c14a5594732c46a96ac3d9d828"
- integrity sha512-OqU/bHUIiJhpyb2ytX4fLalYAJJOyZ0k5H0AibP/WPsdHq9CqmJFU676gO+N8WWhR5tVz1NxsqMZgEBy5Lc6GQ==
+prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.16.5, prosemirror-view@^1.20.1:
+ version "1.20.1"
+ resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.20.1.tgz#174ba8ca358c73cc05e9a92a3d252bcf181ea337"
+ integrity sha512-djWORhy3a706mUH4A2dgEEV0IPZqQd1tFyz/ZVHJNoqhSgq82FwG6dq7uqHeUB2KdVSNfI2yc3rwfqlC/ll2pA==
dependencies:
prosemirror-model "^1.14.3"
prosemirror-state "^1.0.0"