Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-09-20 18:12:35 +00:00
parent df8a23a4f8
commit 813226e2ba
57 changed files with 1195 additions and 272 deletions

View File

@ -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'

View File

@ -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)

View File

@ -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>

View File

@ -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);
},
});
};

View File

@ -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>

View File

@ -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;

View File

@ -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,
},
});
};

View File

@ -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>

View File

@ -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);
},
},
});
},
});
};

View File

@ -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>

View File

@ -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

View File

@ -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',

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
ecb73d9a0be0cf04a8b405f1225d1a5de2fcdbb4c277fd5549f72a7a21596cdc

View File

@ -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();

View File

@ -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 |

View File

@ -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:

View File

@ -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
---

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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)
## 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).
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).
## Create a new cluster on GKE through cluster certificates (DEPRECATED)
> [Deprecated](https://gitlab.com/groups/gitlab-org/-/epics/6049) in GitLab 14.0.
Prerequisites:
- A [Google Cloud 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
- 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).
## Add an existing GKE cluster
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 new GKE cluster
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).
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

View File

@ -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
---

View File

@ -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
---

View File

@ -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"/ .
```

View File

@ -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
---

View File

@ -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"
---

View File

@ -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
---

View File

@ -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)

View File

@ -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

View File

@ -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 ""

View File

@ -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",

View File

@ -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))

View File

@ -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

View File

@ -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|

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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...

View File

@ -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)');
});
});
});

View File

@ -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();

View File

@ -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();
});
});
});

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"