Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
678db4e394
commit
71701f2da5
|
@ -22,6 +22,7 @@
|
|||
variables:
|
||||
DECOMPOSED_DB: "true"
|
||||
GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: "main"
|
||||
GITLAB_USE_MODEL_LOAD_BALANCING: "true"
|
||||
|
||||
.rspec-base:
|
||||
extends: .rails-job-base
|
||||
|
|
|
@ -138,7 +138,7 @@ export default {
|
|||
/>
|
||||
</form>
|
||||
<template #modal-footer>
|
||||
<gl-button @click="onCancel">{{ s__('Cancel') }}</gl-button>
|
||||
<gl-button @click="onCancel">{{ __('Cancel') }}</gl-button>
|
||||
<gl-button
|
||||
:disabled="!canSubmit"
|
||||
category="secondary"
|
||||
|
|
|
@ -3,7 +3,7 @@ import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui';
|
|||
import { GlSingleStat } from '@gitlab/ui/dist/charts';
|
||||
import createFlash from '~/flash';
|
||||
import { number } from '~/lib/utils/unit_format';
|
||||
import { s__ } from '~/locale';
|
||||
import { __, s__ } from '~/locale';
|
||||
import usageTrendsCountQuery from '../graphql/queries/usage_trends_count.query.graphql';
|
||||
|
||||
const defaultPrecision = 0;
|
||||
|
@ -52,7 +52,7 @@ export default {
|
|||
mergeRequests: s__('UsageTrends|Merge requests'),
|
||||
pipelines: s__('UsageTrends|Pipelines'),
|
||||
},
|
||||
loadCountsError: s__('Could not load usage counts. Please refresh the page to try again.'),
|
||||
loadCountsError: __('Could not load usage counts. Please refresh the page to try again.'),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { GlSprintf, GlModal } from '@gitlab/ui';
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
import createFlash from '~/flash';
|
||||
import { s__ } from '~/locale';
|
||||
import { __, s__ } from '~/locale';
|
||||
import Badge from './badge.vue';
|
||||
import BadgeForm from './badge_form.vue';
|
||||
import BadgeList from './badge_list.vue';
|
||||
|
@ -25,13 +25,13 @@ export default {
|
|||
...mapState(['badgeInModal', 'isEditing']),
|
||||
primaryProps() {
|
||||
return {
|
||||
text: s__('Delete badge'),
|
||||
text: __('Delete badge'),
|
||||
attributes: [{ category: 'primary' }, { variant: 'danger' }],
|
||||
};
|
||||
},
|
||||
cancelProps() {
|
||||
return {
|
||||
text: s__('Cancel'),
|
||||
text: __('Cancel'),
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { GlModal, GlSprintf, GlLink, GlButton } from '@gitlab/ui';
|
||||
import Cookies from 'js-cookie';
|
||||
import { s__ } from '~/locale';
|
||||
import { __, s__ } from '~/locale';
|
||||
import Tracking from '~/tracking';
|
||||
|
||||
const trackingMixin = Tracking.mixin();
|
||||
|
@ -69,7 +69,7 @@ export default {
|
|||
},
|
||||
},
|
||||
i18n: {
|
||||
modalTitle: s__("That's it, well done!"),
|
||||
modalTitle: __("That's it, well done!"),
|
||||
pipelinesButton: s__('MR widget|See your pipeline in action'),
|
||||
mergeRequestButton: s__('MR widget|Back to the Merge request'),
|
||||
bodyMessage: s__(
|
||||
|
|
|
@ -159,7 +159,7 @@ export default {
|
|||
)
|
||||
}}</span>
|
||||
<template #modal-footer>
|
||||
<gl-button variant="secondary" @click="handleCancel">{{ s__('Cancel') }}</gl-button>
|
||||
<gl-button variant="secondary" @click="handleCancel">{{ __('Cancel') }}</gl-button>
|
||||
<template v-if="confirmCleanup">
|
||||
<gl-button
|
||||
:disabled="!canSubmit"
|
||||
|
|
|
@ -91,7 +91,7 @@ export const I18N_INSTALL_AGENT_MODAL = {
|
|||
),
|
||||
|
||||
basicInstallTitle: s__('ClusterAgents|Recommended installation method'),
|
||||
basicInstallBody: s__(
|
||||
basicInstallBody: __(
|
||||
`Open a CLI and connect to the cluster you want to install the Agent in. Use this installation method to minimize any manual steps. The token is already included in the command.`,
|
||||
),
|
||||
|
||||
|
@ -100,7 +100,7 @@ export const I18N_INSTALL_AGENT_MODAL = {
|
|||
'ClusterAgents|For alternative installation methods %{linkStart}go to the documentation%{linkEnd}.',
|
||||
),
|
||||
|
||||
registrationErrorTitle: s__('Failed to register Agent'),
|
||||
registrationErrorTitle: __('Failed to register Agent'),
|
||||
unknownError: s__('ClusterAgents|An unknown error occurred. Please try again.'),
|
||||
};
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ export default {
|
|||
</gl-sprintf>
|
||||
{{ s__('DeployTokens|This action cannot be undone.') }}
|
||||
<template #modal-footer>
|
||||
<gl-button category="secondary" @click="cancelHandler">{{ s__('Cancel') }}</gl-button>
|
||||
<gl-button category="secondary" @click="cancelHandler">{{ __('Cancel') }}</gl-button>
|
||||
<gl-button
|
||||
category="primary"
|
||||
variant="danger"
|
||||
|
|
|
@ -50,7 +50,7 @@ export default {
|
|||
mixins: [glFeatureFlagsMixin(), IdState({ idProp: (vm) => vm.diffFile.file_hash })],
|
||||
i18n: {
|
||||
...DIFF_FILE_HEADER,
|
||||
compareButtonLabel: s__('Compare submodule commit revisions'),
|
||||
compareButtonLabel: __('Compare submodule commit revisions'),
|
||||
},
|
||||
props: {
|
||||
discussionPath: {
|
||||
|
@ -130,7 +130,7 @@ export default {
|
|||
const truncatedOldSha = escape(truncateSha(this.diffFile.submodule_compare.old_sha));
|
||||
const truncatedNewSha = escape(truncateSha(this.diffFile.submodule_compare.new_sha));
|
||||
return sprintf(
|
||||
s__('Compare %{oldCommitId}...%{newCommitId}'),
|
||||
__('Compare %{oldCommitId}...%{newCommitId}'),
|
||||
{
|
||||
oldCommitId: `<span class="commit-sha">${truncatedOldSha}</span>`,
|
||||
newCommitId: `<span class="commit-sha">${truncatedNewSha}</span>`,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { GlTooltipDirective, GlModal } from '@gitlab/ui';
|
||||
import { s__, sprintf } from '~/locale';
|
||||
import { __, s__, sprintf } from '~/locale';
|
||||
import eventHub from '../event_hub';
|
||||
|
||||
export default {
|
||||
|
@ -27,7 +27,7 @@ export default {
|
|||
},
|
||||
cancelProps() {
|
||||
return {
|
||||
text: s__('Cancel'),
|
||||
text: __('Cancel'),
|
||||
};
|
||||
},
|
||||
confirmDeleteMessage() {
|
||||
|
|
|
@ -6,7 +6,7 @@ import Visibility from 'visibilityjs';
|
|||
import createFlash from '~/flash';
|
||||
import Poll from '../../lib/utils/poll';
|
||||
import { getParameterByName } from '../../lib/utils/url_utility';
|
||||
import { s__ } from '../../locale';
|
||||
import { s__, __ } from '../../locale';
|
||||
import tabs from '../../vue_shared/components/navigation_tabs.vue';
|
||||
import tablePagination from '../../vue_shared/components/pagination/table_pagination.vue';
|
||||
import container from '../components/container.vue';
|
||||
|
@ -207,13 +207,13 @@ export default {
|
|||
tabs() {
|
||||
return [
|
||||
{
|
||||
name: s__('Available'),
|
||||
name: __('Available'),
|
||||
scope: 'available',
|
||||
count: this.state.availableCounter,
|
||||
isActive: this.scope === 'available',
|
||||
},
|
||||
{
|
||||
name: s__('Stopped'),
|
||||
name: __('Stopped'),
|
||||
scope: 'stopped',
|
||||
count: this.state.stoppedCounter,
|
||||
isActive: this.scope === 'stopped',
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { GlModal, GlButton } from '@gitlab/ui';
|
||||
import { mapActions, mapState, mapGetters } from 'vuex';
|
||||
import createFlash from '~/flash';
|
||||
import { __, sprintf, s__ } from '~/locale';
|
||||
import { __, sprintf } from '~/locale';
|
||||
import { modalTypes } from '../../constants';
|
||||
import { trimPathComponents, getPathParent } from '../../utils';
|
||||
|
||||
|
@ -58,7 +58,7 @@ export default {
|
|||
if (this.modalType === modalTypes.rename) {
|
||||
if (this.entries[this.entryName] && !this.entries[this.entryName].deleted) {
|
||||
createFlash({
|
||||
message: sprintf(s__('The name "%{name}" is already taken in this directory.'), {
|
||||
message: sprintf(__('The name "%{name}" is already taken in this directory.'), {
|
||||
name: this.entryName,
|
||||
}),
|
||||
fadeTransition: false,
|
||||
|
|
|
@ -517,7 +517,7 @@ export default {
|
|||
<gl-empty-state
|
||||
v-else-if="!hasGroups"
|
||||
:title="s__('BulkImport|You have no groups to import')"
|
||||
:description="s__('Check your source instance permissions.')"
|
||||
:description="__('Check your source instance permissions.')"
|
||||
/>
|
||||
<template v-else>
|
||||
<div
|
||||
|
|
|
@ -166,7 +166,7 @@ export default {
|
|||
</gl-sprintf>
|
||||
</p>
|
||||
<template #modal-footer>
|
||||
<gl-button category="secondary" @click="cancelHandler">{{ s__('Cancel') }}</gl-button>
|
||||
<gl-button category="secondary" @click="cancelHandler">{{ __('Cancel') }}</gl-button>
|
||||
</template>
|
||||
</gl-modal>
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,7 @@ import Visibility from 'visibilityjs';
|
|||
import createFlash from '~/flash';
|
||||
import Poll from '~/lib/utils/poll';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import { __, s__, sprintf } from '~/locale';
|
||||
import { __, sprintf } from '~/locale';
|
||||
import {
|
||||
IssuableStatus,
|
||||
IssuableStatusText,
|
||||
|
@ -250,7 +250,7 @@ export default {
|
|||
return false;
|
||||
},
|
||||
defaultErrorMessage() {
|
||||
return sprintf(s__('Error updating %{issuableType}'), { issuableType: this.issuableType });
|
||||
return sprintf(__('Error updating %{issuableType}'), { issuableType: this.issuableType });
|
||||
},
|
||||
isClosed() {
|
||||
return this.issuableStatus === IssuableStatus.Closed;
|
||||
|
@ -437,7 +437,7 @@ export default {
|
|||
})
|
||||
.catch(() => {
|
||||
createFlash({
|
||||
message: sprintf(s__('Error deleting %{issuableType}'), {
|
||||
message: sprintf(__('Error deleting %{issuableType}'), {
|
||||
issuableType: this.issuableType,
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
|
||||
import $ from 'jquery';
|
||||
import createFlash from '~/flash';
|
||||
import { s__, sprintf } from '~/locale';
|
||||
import { __, sprintf } from '~/locale';
|
||||
import TaskList from '../../task_list';
|
||||
import animateMixin from '../mixins/animate';
|
||||
|
||||
|
@ -104,7 +104,7 @@ export default {
|
|||
taskListUpdateError() {
|
||||
createFlash({
|
||||
message: sprintf(
|
||||
s__(
|
||||
__(
|
||||
'Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again.',
|
||||
),
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { s__ } from '~/locale';
|
||||
import { __, s__ } from '~/locale';
|
||||
|
||||
/**
|
||||
* Generates empty state messages for Service Desk issues list.
|
||||
|
@ -25,7 +25,7 @@ export function generateMessages(emptyStateMeta) {
|
|||
|
||||
const commonDescription = `
|
||||
<span>${serviceDeskSupportedMessage}</span>
|
||||
<a href="${serviceDeskHelpPage}">${s__('Learn more.')}</a>`;
|
||||
<a href="${serviceDeskHelpPage}">${__('Learn more.')}</a>`;
|
||||
|
||||
return {
|
||||
serviceDeskEnabledAndCanEditProjectSettings: {
|
||||
|
@ -60,7 +60,7 @@ export function generateMessages(emptyStateMeta) {
|
|||
'ServiceDesk|To enable Service Desk on this instance, an instance administrator must first set up incoming email.',
|
||||
),
|
||||
primaryLink: incomingEmailHelpPage,
|
||||
primaryText: s__('Learn more.'),
|
||||
primaryText: __('Learn more.'),
|
||||
},
|
||||
serviceDeskIsNotEnabled: {
|
||||
title: s__('ServiceDesk|Service Desk is not enabled'),
|
||||
|
|
|
@ -1,55 +1,43 @@
|
|||
<script>
|
||||
import { GlAlert, GlButton, GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
|
||||
import { mapState, mapMutations } from 'vuex';
|
||||
import { retrieveAlert, getLocation } from '~/jira_connect/subscriptions/utils';
|
||||
import { retrieveAlert } from '~/jira_connect/subscriptions/utils';
|
||||
import { SET_ALERT } from '../store/mutation_types';
|
||||
import SubscriptionsList from './subscriptions_list.vue';
|
||||
import AddNamespaceButton from './add_namespace_button.vue';
|
||||
import SignInButton from './sign_in_button.vue';
|
||||
|
||||
export default {
|
||||
name: 'JiraConnectApp',
|
||||
components: {
|
||||
GlAlert,
|
||||
GlButton,
|
||||
GlLink,
|
||||
GlSprintf,
|
||||
SubscriptionsList,
|
||||
AddNamespaceButton,
|
||||
SignInButton,
|
||||
},
|
||||
inject: {
|
||||
usersPath: {
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
location: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['alert']),
|
||||
usersPathWithReturnTo() {
|
||||
if (this.location) {
|
||||
return `${this.usersPath}?return_to=${this.location}`;
|
||||
}
|
||||
|
||||
return this.usersPath;
|
||||
},
|
||||
shouldShowAlert() {
|
||||
return Boolean(this.alert?.message);
|
||||
},
|
||||
userSignedIn() {
|
||||
return Boolean(!this.usersPath);
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.setInitialAlert();
|
||||
this.setLocation();
|
||||
},
|
||||
methods: {
|
||||
...mapMutations({
|
||||
setAlert: SET_ALERT,
|
||||
}),
|
||||
async setLocation() {
|
||||
this.location = await getLocation();
|
||||
},
|
||||
setInitialAlert() {
|
||||
const { linkUrl, title, message, variant } = retrieveAlert() || {};
|
||||
this.setAlert({ linkUrl, title, message, variant });
|
||||
|
@ -82,15 +70,7 @@ export default {
|
|||
|
||||
<div class="jira-connect-app-body gl-my-7 gl-px-5 gl-pb-4">
|
||||
<div class="gl-display-flex gl-justify-content-end">
|
||||
<gl-button
|
||||
v-if="usersPath"
|
||||
category="primary"
|
||||
variant="info"
|
||||
class="gl-align-self-center"
|
||||
:href="usersPathWithReturnTo"
|
||||
target="_blank"
|
||||
>{{ s__('Integrations|Sign in to add namespaces') }}</gl-button
|
||||
>
|
||||
<sign-in-button v-if="!userSignedIn" :users-path="usersPath" />
|
||||
<add-namespace-button v-else />
|
||||
</div>
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<script>
|
||||
import { GlButton } from '@gitlab/ui';
|
||||
import { getLocation } from '~/jira_connect/subscriptions/utils';
|
||||
import { objectToQuery } from '~/lib/utils/url_utility';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlButton,
|
||||
},
|
||||
props: {
|
||||
usersPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
location: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
usersPathWithReturnTo() {
|
||||
if (this.location) {
|
||||
const queryParams = {
|
||||
return_to: this.location,
|
||||
};
|
||||
|
||||
return `${this.usersPath}?${objectToQuery(queryParams)}`;
|
||||
}
|
||||
|
||||
return this.usersPath;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.setLocation();
|
||||
},
|
||||
methods: {
|
||||
async setLocation() {
|
||||
this.location = await getLocation();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-button category="primary" variant="info" :href="usersPathWithReturnTo" target="_blank">
|
||||
<slot>
|
||||
{{ s__('Integrations|Sign in to add namespaces') }}
|
||||
</slot>
|
||||
</gl-button>
|
||||
</template>
|
|
@ -11,6 +11,9 @@ import { getLocation, sizeToParent } from './utils';
|
|||
|
||||
const store = createStore();
|
||||
|
||||
/**
|
||||
* Add `return_to` query param to all HAML-defined GitLab sign in links.
|
||||
*/
|
||||
const updateSignInLinks = async () => {
|
||||
const location = await getLocation();
|
||||
Array.from(document.querySelectorAll('.js-jira-connect-sign-in')).forEach((el) => {
|
||||
|
|
|
@ -14,33 +14,33 @@ import { s__, n__, __, sprintf } from '../../../locale';
|
|||
export const getMonthNames = (abbreviated) => {
|
||||
if (abbreviated) {
|
||||
return [
|
||||
s__('Jan'),
|
||||
s__('Feb'),
|
||||
s__('Mar'),
|
||||
s__('Apr'),
|
||||
s__('May'),
|
||||
s__('Jun'),
|
||||
s__('Jul'),
|
||||
s__('Aug'),
|
||||
s__('Sep'),
|
||||
s__('Oct'),
|
||||
s__('Nov'),
|
||||
s__('Dec'),
|
||||
__('Jan'),
|
||||
__('Feb'),
|
||||
__('Mar'),
|
||||
__('Apr'),
|
||||
__('May'),
|
||||
__('Jun'),
|
||||
__('Jul'),
|
||||
__('Aug'),
|
||||
__('Sep'),
|
||||
__('Oct'),
|
||||
__('Nov'),
|
||||
__('Dec'),
|
||||
];
|
||||
}
|
||||
return [
|
||||
s__('January'),
|
||||
s__('February'),
|
||||
s__('March'),
|
||||
s__('April'),
|
||||
s__('May'),
|
||||
s__('June'),
|
||||
s__('July'),
|
||||
s__('August'),
|
||||
s__('September'),
|
||||
s__('October'),
|
||||
s__('November'),
|
||||
s__('December'),
|
||||
__('January'),
|
||||
__('February'),
|
||||
__('March'),
|
||||
__('April'),
|
||||
__('May'),
|
||||
__('June'),
|
||||
__('July'),
|
||||
__('August'),
|
||||
__('September'),
|
||||
__('October'),
|
||||
__('November'),
|
||||
__('December'),
|
||||
];
|
||||
};
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ export default {
|
|||
}}
|
||||
<a :href="clusterApplicationsDocumentationPath">
|
||||
<strong>
|
||||
{{ s__('View Documentation') }}
|
||||
{{ __('View Documentation') }}
|
||||
</strong>
|
||||
</a>
|
||||
</gl-alert>
|
||||
|
|
|
@ -63,7 +63,7 @@ export default {
|
|||
return !(this.form.fileName && !this.form.fileName.endsWith('.yml'));
|
||||
},
|
||||
fileNameFeedback() {
|
||||
return !this.fileNameState ? s__('The file name should have a .yml extension') : '';
|
||||
return !this.fileNameState ? __('The file name should have a .yml extension') : '';
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
|
|
@ -78,8 +78,8 @@ export default {
|
|||
v-if="resolveAllDiscussionsIssuePath && !allResolved"
|
||||
v-gl-tooltip
|
||||
:href="resolveAllDiscussionsIssuePath"
|
||||
:title="s__('Create issue to resolve all threads')"
|
||||
:aria-label="s__('Create issue to resolve all threads')"
|
||||
:title="__('Create issue to resolve all threads')"
|
||||
:aria-label="__('Create issue to resolve all threads')"
|
||||
class="new-issue-for-discussion discussion-create-issue-btn"
|
||||
icon="issue-new"
|
||||
/>
|
||||
|
|
|
@ -70,7 +70,7 @@ export default {
|
|||
},
|
||||
primaryProps() {
|
||||
return {
|
||||
text: s__('Delete project'),
|
||||
text: __('Delete project'),
|
||||
attributes: [{ variant: 'danger' }, { category: 'primary' }, { disabled: !this.canSubmit }],
|
||||
};
|
||||
},
|
||||
|
|
|
@ -47,7 +47,7 @@ export default {
|
|||
<template>
|
||||
<new-namespace-page
|
||||
:jump-to-last-persisted-panel="hasErrors"
|
||||
:initial-breadcrumb="s__('New group')"
|
||||
:initial-breadcrumb="__('New group')"
|
||||
:panels="$options.PANELS"
|
||||
:title="s__('GroupsNew|Create new group')"
|
||||
persistence-key="new_group_last_active_tab"
|
||||
|
|
|
@ -3,7 +3,7 @@ import { GlModal } from '@gitlab/ui';
|
|||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { visitUrl } from '~/lib/utils/url_utility';
|
||||
import { s__, sprintf } from '~/locale';
|
||||
import { __, s__, sprintf } from '~/locale';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -83,7 +83,7 @@ export default {
|
|||
attributes: [{ variant: 'warning' }],
|
||||
},
|
||||
cancelAction: {
|
||||
text: s__('Cancel'),
|
||||
text: __('Cancel'),
|
||||
attributes: [],
|
||||
},
|
||||
};
|
||||
|
|
|
@ -72,18 +72,18 @@ export default {
|
|||
return [
|
||||
{
|
||||
value: KEY_EVERY_DAY,
|
||||
text: sprintf(s__(`Every day (at %{time})`), { time: this.formattedTime }),
|
||||
text: sprintf(__(`Every day (at %{time})`), { time: this.formattedTime }),
|
||||
},
|
||||
{
|
||||
value: KEY_EVERY_WEEK,
|
||||
text: sprintf(s__('Every week (%{weekday} at %{time})'), {
|
||||
text: sprintf(__('Every week (%{weekday} at %{time})'), {
|
||||
weekday: this.weekday,
|
||||
time: this.formattedTime,
|
||||
}),
|
||||
},
|
||||
{
|
||||
value: KEY_EVERY_MONTH,
|
||||
text: sprintf(s__('Every month (Day %{day} at %{time})'), {
|
||||
text: sprintf(__('Every month (Day %{day} at %{time})'), {
|
||||
day: this.randomDay,
|
||||
time: this.formattedTime,
|
||||
}),
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import { GlIcon, GlSprintf, GlLink, GlFormCheckbox, GlToggle } from '@gitlab/ui';
|
||||
|
||||
import settingsMixin from 'ee_else_ce/pages/projects/shared/permissions/mixins/settings_pannel_mixin';
|
||||
import { s__ } from '~/locale';
|
||||
import { __, s__ } from '~/locale';
|
||||
import {
|
||||
visibilityOptions,
|
||||
visibilityLevelDescriptions,
|
||||
|
@ -31,7 +31,7 @@ export default {
|
|||
operationsLabel: s__('ProjectSettings|Operations'),
|
||||
packagesLabel: s__('ProjectSettings|Packages'),
|
||||
pagesLabel: s__('ProjectSettings|Pages'),
|
||||
ciCdLabel: s__('CI/CD'),
|
||||
ciCdLabel: __('CI/CD'),
|
||||
repositoryLabel: s__('ProjectSettings|Repository'),
|
||||
requirementsLabel: s__('ProjectSettings|Requirements'),
|
||||
securityAndComplianceLabel: s__('ProjectSettings|Security & Compliance'),
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
import csrf from '~/lib/utils/csrf';
|
||||
import { setUrlFragment } from '~/lib/utils/url_utility';
|
||||
import { s__, sprintf } from '~/locale';
|
||||
import { __, s__, sprintf } from '~/locale';
|
||||
import Tracking from '~/tracking';
|
||||
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
|
||||
import {
|
||||
|
@ -83,7 +83,7 @@ export default {
|
|||
),
|
||||
},
|
||||
},
|
||||
feedbackTip: s__(
|
||||
feedbackTip: __(
|
||||
'Tell us your experiences with the new Markdown editor %{linkStart}in this feedback issue%{linkEnd}.',
|
||||
),
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { GlButton, GlModal, GlModalDirective, GlSegmentedControl } from '@gitlab/ui';
|
||||
|
||||
import { s__ } from '~/locale';
|
||||
import { __, s__ } from '~/locale';
|
||||
import { sortOrders, sortOrderOptions } from '../constants';
|
||||
import RequestWarning from './request_warning.vue';
|
||||
|
||||
|
@ -55,7 +55,7 @@ export default {
|
|||
const summary = {};
|
||||
|
||||
if (!this.metricDetails.summaryOptions?.hideTotal) {
|
||||
summary[s__('Total')] = this.metricDetails.calls;
|
||||
summary[__('Total')] = this.metricDetails.calls;
|
||||
}
|
||||
|
||||
if (!this.metricDetails.summaryOptions?.hideDuration) {
|
||||
|
|
|
@ -12,7 +12,7 @@ import { produce } from 'immer';
|
|||
import { fetchPolicies } from '~/lib/graphql';
|
||||
import { historyPushState } from '~/lib/utils/common_utils';
|
||||
import { setUrlParams } from '~/lib/utils/url_utility';
|
||||
import { s__ } from '~/locale';
|
||||
import { __ } from '~/locale';
|
||||
import {
|
||||
BRANCH_PAGINATION_LIMIT,
|
||||
BRANCH_SEARCH_DEBOUNCE,
|
||||
|
@ -25,9 +25,9 @@ import getLastCommitBranchQuery from '~/pipeline_editor/graphql/queries/client/l
|
|||
|
||||
export default {
|
||||
i18n: {
|
||||
dropdownHeader: s__('Switch branch'),
|
||||
title: s__('Branches'),
|
||||
fetchError: s__('Unable to fetch branch list for this project.'),
|
||||
dropdownHeader: __('Switch branch'),
|
||||
title: __('Branches'),
|
||||
fetchError: __('Unable to fetch branch list for this project.'),
|
||||
},
|
||||
inputDebounce: BRANCH_SEARCH_DEBOUNCE,
|
||||
components: {
|
||||
|
|
|
@ -37,7 +37,7 @@ export default {
|
|||
},
|
||||
primaryProps() {
|
||||
return {
|
||||
text: s__('Delete account'),
|
||||
text: __('Delete account'),
|
||||
attributes: [
|
||||
{ variant: 'danger', 'data-qa-selector': 'confirm_delete_account_button' },
|
||||
{ category: 'primary' },
|
||||
|
@ -47,7 +47,7 @@ export default {
|
|||
},
|
||||
cancelProps() {
|
||||
return {
|
||||
text: s__('Cancel'),
|
||||
text: __('Cancel'),
|
||||
};
|
||||
},
|
||||
canSubmit() {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { GlSafeHtmlDirective as SafeHtml, GlButton, GlModal, GlModalDirective }
|
|||
import { escape } from 'lodash';
|
||||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { s__, sprintf } from '~/locale';
|
||||
import { __, s__, sprintf } from '~/locale';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -58,7 +58,7 @@ Please update your Git repository remotes as soon as possible.`),
|
|||
},
|
||||
primaryProps() {
|
||||
return {
|
||||
text: s__('Update username'),
|
||||
text: __('Update username'),
|
||||
attributes: [
|
||||
{ variant: 'warning' },
|
||||
{ category: 'primary' },
|
||||
|
@ -68,7 +68,7 @@ Please update your Git repository remotes as soon as possible.`),
|
|||
},
|
||||
cancelProps() {
|
||||
return {
|
||||
text: s__('Cancel'),
|
||||
text: __('Cancel'),
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
|
@ -95,7 +95,7 @@ export default {
|
|||
|
||||
<template>
|
||||
<new-namespace-page
|
||||
:initial-breadcrumb="s__('New project')"
|
||||
:initial-breadcrumb="__('New project')"
|
||||
:panels="availablePanels"
|
||||
:jump-to-last-persisted-panel="hasErrors"
|
||||
:title="s__('ProjectsNew|Create new project')"
|
||||
|
|
|
@ -51,7 +51,7 @@ export const ERROR_MESSAGE = s__(
|
|||
'UsageQuota|Something went wrong while fetching project storage statistics',
|
||||
);
|
||||
|
||||
export const LEARN_MORE_LABEL = s__('Learn more.');
|
||||
export const LEARN_MORE_LABEL = __('Learn more.');
|
||||
export const USAGE_QUOTAS_LABEL = s__('UsageQuota|Usage Quotas');
|
||||
export const HELP_LINK_ARIA_LABEL = s__('UsageQuota|%{linkTitle} help link');
|
||||
export const TOTAL_USAGE_DEFAULT_TEXT = __('N/A');
|
||||
|
|
|
@ -58,7 +58,7 @@ export default {
|
|||
};
|
||||
this.isLoading = false;
|
||||
createFlash({
|
||||
message: s__('Something went wrong on our end'),
|
||||
message: __('Something went wrong on our end'),
|
||||
});
|
||||
},
|
||||
initPolling() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { GlLink, GlLoadingIcon, GlIcon } from '@gitlab/ui';
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
import { sprintf, n__, s__ } from '~/locale';
|
||||
import { sprintf, __, n__ } from '~/locale';
|
||||
import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue';
|
||||
import { parseIssuableData } from '../../issue_show/utils/parse_data';
|
||||
|
||||
|
@ -40,7 +40,7 @@ export default {
|
|||
this.totalCount,
|
||||
);
|
||||
|
||||
return sprintf(s__('%{mrText}, this issue will be closed automatically.'), { mrText });
|
||||
return sprintf(__('%{mrText}, this issue will be closed automatically.'), { mrText });
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import createFlash from '~/flash';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { normalizeHeaders } from '~/lib/utils/common_utils';
|
||||
import { s__ } from '~/locale';
|
||||
import { __ } from '~/locale';
|
||||
import * as types from './mutation_types';
|
||||
|
||||
const REQUEST_PAGE_COUNT = 100;
|
||||
|
@ -30,7 +30,7 @@ export const fetchMergeRequests = ({ state, dispatch }) => {
|
|||
.catch(() => {
|
||||
dispatch('receiveDataError');
|
||||
createFlash({
|
||||
message: s__('Something went wrong while fetching related merge requests.'),
|
||||
message: __('Something went wrong while fetching related merge requests.'),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { GlFormRadioGroup, GlFormRadio } from '@gitlab/ui';
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
import { sprintf, s__ } from '~/locale';
|
||||
import { sprintf, __ } from '~/locale';
|
||||
|
||||
export default {
|
||||
name: 'RadioFilter',
|
||||
|
@ -49,7 +49,7 @@ export default {
|
|||
...mapActions(['setQuery']),
|
||||
radioLabel(filter) {
|
||||
return filter.value === this.ANY.value
|
||||
? sprintf(s__('Any %{header}'), { header: this.filterData.header.toLowerCase() })
|
||||
? sprintf(__('Any %{header}'), { header: this.filterData.header.toLowerCase() })
|
||||
: filter.label;
|
||||
},
|
||||
},
|
||||
|
|
|
@ -30,7 +30,7 @@ export default {
|
|||
updatedFileDescription() {
|
||||
const { sourcePath } = this.appData;
|
||||
|
||||
return sprintf(s__('Update %{sourcePath} file'), { sourcePath });
|
||||
return sprintf(__('Update %{sourcePath} file'), { sourcePath });
|
||||
},
|
||||
},
|
||||
created() {
|
||||
|
|
|
@ -23,7 +23,7 @@ export default {
|
|||
translations: {
|
||||
createdTimeagoLabel: s__('UserList|created %{timeago}'),
|
||||
deleteListTitle: s__('UserList|Delete %{name}?'),
|
||||
deleteListMessage: s__('User list %{name} will be removed. Are you sure?'),
|
||||
deleteListMessage: __('User list %{name} will be removed. Are you sure?'),
|
||||
editUserListLabel: s__('FeatureFlags|Edit User List'),
|
||||
},
|
||||
modal: {
|
||||
|
|
|
@ -67,8 +67,8 @@ export default {
|
|||
<template #targetBranch>
|
||||
<span class="label-branch">{{ targetBranchEscaped }}</span>
|
||||
</template>
|
||||
<template v-if="glFeatures.restructuredMrWidget" #squashedCommits>
|
||||
<template v-if="isSquashEnabled">
|
||||
<template #squashedCommits>
|
||||
<template v-if="glFeatures.restructuredMrWidget && isSquashEnabled">
|
||||
{{ __('(commits will be squashed)') }}</template
|
||||
></template
|
||||
>
|
||||
|
|
|
@ -151,7 +151,7 @@ export default {
|
|||
right
|
||||
data-qa-selector="download_dropdown"
|
||||
>
|
||||
<gl-dropdown-section-header>{{ s__('Download as') }}</gl-dropdown-section-header>
|
||||
<gl-dropdown-section-header>{{ __('Download as') }}</gl-dropdown-section-header>
|
||||
<gl-dropdown-item
|
||||
:href="mr.emailPatchesPath"
|
||||
class="js-download-email-patches"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { s__ } from '~/locale';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
/**
|
||||
* Validation messages will take priority based on the property order.
|
||||
|
@ -12,11 +12,11 @@ import { s__ } from '~/locale';
|
|||
const defaultFeedbackMap = {
|
||||
valueMissing: {
|
||||
isInvalid: (el) => el.validity?.valueMissing,
|
||||
message: s__('Please fill out this field.'),
|
||||
message: __('Please fill out this field.'),
|
||||
},
|
||||
urlTypeMismatch: {
|
||||
isInvalid: (el) => el.type === 'url' && el.validity?.typeMismatch,
|
||||
message: s__('Please enter a valid URL format, ex: http://www.example.com/home'),
|
||||
message: __('Please enter a valid URL format, ex: http://www.example.com/home'),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -6,27 +6,30 @@ module Ci
|
|||
|
||||
def runner_status_icon(runner, size: 16, icon_class: '')
|
||||
status = runner.status
|
||||
active = runner.active
|
||||
|
||||
title = ''
|
||||
icon = 'warning-solid'
|
||||
span_class = ''
|
||||
|
||||
case status
|
||||
when :online
|
||||
if active
|
||||
title = s_("Runners|Runner is online, last contact was %{runner_contact} ago") % { runner_contact: time_ago_in_words(runner.contacted_at) }
|
||||
icon = 'status-active'
|
||||
span_class = 'gl-text-green-500'
|
||||
else
|
||||
title = s_("Runners|Runner is paused, last contact was %{runner_contact} ago") % { runner_contact: time_ago_in_words(runner.contacted_at) }
|
||||
icon = 'status-paused'
|
||||
span_class = 'gl-text-gray-600'
|
||||
end
|
||||
when :not_connected
|
||||
title = s_("Runners|New runner, has not connected yet")
|
||||
icon = 'warning-solid'
|
||||
when :online
|
||||
title = s_("Runners|Runner is online, last contact was %{runner_contact} ago") % { runner_contact: time_ago_in_words(runner.contacted_at) }
|
||||
icon = 'status-active'
|
||||
span_class = 'gl-text-green-500'
|
||||
when :offline
|
||||
title = s_("Runners|Runner is offline, last contact was %{runner_contact} ago") % { runner_contact: time_ago_in_words(runner.contacted_at) }
|
||||
icon = 'status-failed'
|
||||
span_class = 'gl-text-red-500'
|
||||
when :paused
|
||||
title = s_("Runners|Runner is paused, last contact was %{runner_contact} ago") % { runner_contact: time_ago_in_words(runner.contacted_at) }
|
||||
icon = 'status-paused'
|
||||
span_class = 'gl-text-gray-600'
|
||||
end
|
||||
|
||||
content_tag(:span, class: span_class, title: title, data: { toggle: 'tooltip', container: 'body', testid: 'runner_status_icon', qa_selector: "runner_status_#{status}_content" }) do
|
||||
|
|
|
@ -274,6 +274,14 @@ module Ci
|
|||
end
|
||||
|
||||
def status
|
||||
return :not_connected unless contacted_at
|
||||
|
||||
online? ? :online : :offline
|
||||
end
|
||||
|
||||
# DEPRECATED
|
||||
# TODO Remove in %15.0 in favor of `status` for REST calls, see https://gitlab.com/gitlab-org/gitlab/-/issues/344648
|
||||
def deprecated_rest_status
|
||||
if contacted_at.nil?
|
||||
:not_connected
|
||||
elsif active?
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: use_model_load_balancing
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73631
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344797
|
||||
milestone: '14.5'
|
||||
type: development
|
||||
group: group::sharding
|
||||
default_enabled: false
|
|
@ -0,0 +1,16 @@
|
|||
- name: "REST API Runner will not contain 'paused'"
|
||||
announcement_milestone: "14.5" # The milestone when this feature was first announced as deprecated.
|
||||
removal_milestone: "15.0" # the milestone when this feature is planned to be removed
|
||||
body: | # Do not modify this line, instead modify the lines below.
|
||||
Runner REST API will not return "paused" as a status in GitLab 15.0.
|
||||
|
||||
REST API: Paused runners' status will only relate to runner contact status, such as:
|
||||
"online", "offline", "not_connected". Status "paused" will not appear when the runner is
|
||||
not active.
|
||||
|
||||
When checking if a runner is "paused", API users are advised to check the boolean attribute
|
||||
"active" to be `false` instead.
|
||||
stage: Verify
|
||||
tiers: [Core, Premium, Ultimate]
|
||||
issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344648
|
||||
documentation_url: https://docs.gitlab.com/ee/api/runners.html
|
|
@ -1585,11 +1585,29 @@ all state associated with a given repository including:
|
|||
sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml remove-repository -virtual-storage <virtual-storage> -repository <repository>
|
||||
```
|
||||
|
||||
- `-virtual-storage` is the virtual storage the repository is located in.
|
||||
- `-repository` is the repository's relative path in the storage.
|
||||
- `-virtual-storage` is the virtual storage the repository is located in. Virtual storages are configured in `/etc/gitlab/gitlab.rb` under `praefect['virtual_storages]` and looks like the following:
|
||||
|
||||
Sometimes parts of the repository continue to exist after running `remove-repository`. This can be caused
|
||||
because of:
|
||||
```ruby
|
||||
praefect['virtual_storages'] = {
|
||||
'default' => {
|
||||
...
|
||||
},
|
||||
'storage-1' => {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the virtual storage to specify is `default` or `storage-1`.
|
||||
|
||||
- `-repository` is the repository's relative path in the storage [beginning with `@hashed`](../repository_storage_types.md#hashed-storage).
|
||||
For example:
|
||||
|
||||
```plaintext
|
||||
@hashed/f5/ca/f5ca38f748a1d6eaf726b8a42fb575c3c71f1864a8143301782de13da2d9202b.git
|
||||
```
|
||||
|
||||
Parts of the repository can continue to exist after running `remove-repository`. This can be because of:
|
||||
|
||||
- A deletion error.
|
||||
- An in-flight RPC call targeting the repository.
|
||||
|
@ -1613,8 +1631,53 @@ The command outputs:
|
|||
Each entry is a complete JSON string with a newline at the end (configurable using the
|
||||
`-delimiter` flag). For example:
|
||||
|
||||
```shell
|
||||
```plaintext
|
||||
sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml list-untracked-repositories
|
||||
{"virtual_storage":"default","storage":"gitaly-1","relative_path":"@hashed/ab/cd/abcd123456789012345678901234567890123456789012345678901234567890.git"}
|
||||
{"virtual_storage":"default","storage":"gitaly-1","relative_path":"@hashed/ab/cd/abcd123456789012345678901234567890123456789012345678901234567891.git"}
|
||||
```
|
||||
|
||||
### Manually track repositories
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/omnibus-gitlab/-/merge_requests/5658) in GitLab 14.4.
|
||||
|
||||
The `track-repository` Praefect sub-command adds repositories on disk to the Praefect database to be tracked.
|
||||
|
||||
```shell
|
||||
sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml track-repository -virtual-storage <virtual-storage> -repository <repository>
|
||||
```
|
||||
|
||||
- `-virtual-storage` is the virtual storage the repository is located in. Virtual storages are configured in `/etc/gitlab/gitlab.rb` under `praefect['virtual_storages]` and looks like the following:
|
||||
|
||||
```ruby
|
||||
praefect['virtual_storages'] = {
|
||||
'default' => {
|
||||
...
|
||||
},
|
||||
'storage-1' => {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the virtual storage to specify is `default` or `storage-1`.
|
||||
|
||||
- `-repository` is the repository's relative path in the storage [beginning with `@hashed`](../repository_storage_types.md#hashed-storage).
|
||||
For example:
|
||||
|
||||
```plaintext
|
||||
@hashed/f5/ca/f5ca38f748a1d6eaf726b8a42fb575c3c71f1864a8143301782de13da2d9202b.git
|
||||
```
|
||||
|
||||
- `-authoritative-storage` is the storage we want Praefect to treat as the primary. Required if
|
||||
[per-repository replication](#configure-replication-factor) is set as the replication strategy.
|
||||
|
||||
The command outputs:
|
||||
|
||||
- Results to `STDOUT` and the command's logs.
|
||||
- Errors to `STDERR`.
|
||||
|
||||
This command fails if:
|
||||
|
||||
- The repository is already being tracked by the Praefect database.
|
||||
- The repository does not exist on disk.
|
||||
|
|
|
@ -83,6 +83,24 @@ request is as follows:
|
|||
migrations on a fresh database before the MR is reviewed. If the review leads
|
||||
to large changes in the MR, execute the migrations again once the review is complete.
|
||||
1. Write tests for more complex migrations.
|
||||
1. If your merge request adds new validations to existing models, to make sure the
|
||||
data processing is backwards compatible:
|
||||
|
||||
- Ask in the [`#database`](https://gitlab.slack.com/archives/CNZ8E900G) Slack channel
|
||||
for assistance to execute the database query that checks the existing rows to
|
||||
ensure existing rows aren't impacted by the change.
|
||||
- Add the necessary validation with a feature flag to be gradually rolled out
|
||||
following [the rollout steps](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle/#rollout).
|
||||
|
||||
If this merge request is urgent, the code owners should make the final call on
|
||||
whether reviewing existing rows should be included as an immediate follow-up task
|
||||
to the merge request.
|
||||
|
||||
NOTE:
|
||||
There isn't a way to know anything about our customers' data on their
|
||||
[self-managed instances](../../subscriptions/self_managed/index.md), so keep
|
||||
that in mind for any data implications with your merge request.
|
||||
|
||||
1. Merge requests **must** adhere to the [merge request performance guidelines](../merge_request_performance_guidelines.md).
|
||||
1. For tests that use Capybara, read
|
||||
[how to write reliable, asynchronous integration tests](https://thoughtbot.com/blog/write-reliable-asynchronous-integration-tests-with-capybara).
|
||||
|
|
|
@ -12,7 +12,9 @@ module API
|
|||
expose :runner_type
|
||||
expose :name
|
||||
expose :online?, as: :online
|
||||
expose :status
|
||||
# DEPRECATED
|
||||
# TODO Remove in %15.0 in favor of `status` for REST calls, see https://gitlab.com/gitlab-org/gitlab/-/issues/344648
|
||||
expose :status, as: :deprecated_rest_status
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ module Gitlab
|
|||
module LoadBalancing
|
||||
# Class for setting up load balancing of a specific model.
|
||||
class Setup
|
||||
attr_reader :configuration
|
||||
attr_reader :model, :configuration
|
||||
|
||||
def initialize(model, start_service_discovery: false)
|
||||
@model = model
|
||||
|
@ -15,8 +15,9 @@ module Gitlab
|
|||
|
||||
def setup
|
||||
configure_connection
|
||||
setup_load_balancer
|
||||
setup_connection_proxy
|
||||
setup_service_discovery
|
||||
setup_feature_flag_to_model_load_balancing
|
||||
end
|
||||
|
||||
def configure_connection
|
||||
|
@ -36,28 +37,82 @@ module Gitlab
|
|||
@model.establish_connection(hash_config)
|
||||
end
|
||||
|
||||
def setup_load_balancer
|
||||
lb = LoadBalancer.new(configuration)
|
||||
|
||||
def setup_connection_proxy
|
||||
# We just use a simple `class_attribute` here so we don't need to
|
||||
# inject any modules and/or expose unnecessary methods.
|
||||
@model.class_attribute(:connection)
|
||||
@model.class_attribute(:sticking)
|
||||
setup_class_attribute(:connection, ConnectionProxy.new(load_balancer))
|
||||
setup_class_attribute(:sticking, Sticking.new(load_balancer))
|
||||
end
|
||||
|
||||
@model.connection = ConnectionProxy.new(lb)
|
||||
@model.sticking = Sticking.new(lb)
|
||||
# TODO: This is temporary code to gradually redirect traffic to use
|
||||
# a dedicated DB replicas, or DB primaries (depending on configuration)
|
||||
# This implements a sticky behavior for the current request if enabled.
|
||||
#
|
||||
# This is needed for Phase 3 and Phase 4 of application rollout
|
||||
# https://gitlab.com/groups/gitlab-org/-/epics/6160#progress
|
||||
#
|
||||
# If `GITLAB_USE_MODEL_LOAD_BALANCING` is set, its value is preferred
|
||||
# Otherwise, a `use_model_load_balancing` FF value is used
|
||||
def setup_feature_flag_to_model_load_balancing
|
||||
return if active_record_base?
|
||||
|
||||
@model.singleton_class.prepend(ModelLoadBalancingFeatureFlagMixin)
|
||||
end
|
||||
|
||||
def setup_service_discovery
|
||||
return unless configuration.service_discovery_enabled?
|
||||
|
||||
lb = @model.connection.load_balancer
|
||||
sv = ServiceDiscovery.new(lb, **configuration.service_discovery)
|
||||
sv = ServiceDiscovery.new(load_balancer, **configuration.service_discovery)
|
||||
|
||||
sv.perform_service_discovery
|
||||
|
||||
sv.start if @start_service_discovery
|
||||
end
|
||||
|
||||
def load_balancer
|
||||
@load_balancer ||= LoadBalancer.new(configuration)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def setup_class_attribute(attribute, value)
|
||||
@model.class_attribute(attribute)
|
||||
@model.public_send("#{attribute}=", value) # rubocop:disable GitlabSecurity/PublicSend
|
||||
end
|
||||
|
||||
def active_record_base?
|
||||
@model == ActiveRecord::Base
|
||||
end
|
||||
|
||||
module ModelLoadBalancingFeatureFlagMixin
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def use_model_load_balancing?
|
||||
# Cache environment variable and return env variable first if defined
|
||||
use_model_load_balancing_env = Gitlab::Utils.to_boolean(ENV["GITLAB_USE_MODEL_LOAD_BALANCING"])
|
||||
|
||||
unless use_model_load_balancing_env.nil?
|
||||
return use_model_load_balancing_env
|
||||
end
|
||||
|
||||
# Check a feature flag using RequestStore (if active)
|
||||
return false unless Gitlab::SafeRequestStore.active?
|
||||
|
||||
Gitlab::SafeRequestStore.fetch(:use_model_load_balancing) do
|
||||
Feature.enabled?(:use_model_load_balancing, default_enabled: :yaml)
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:disable Database/MultipleDatabases
|
||||
def connection
|
||||
use_model_load_balancing? ? super : ActiveRecord::Base.connection
|
||||
end
|
||||
|
||||
def sticking
|
||||
use_model_load_balancing? ? super : ActiveRecord::Base.sticking
|
||||
end
|
||||
# rubocop:enable Database/MultipleDatabases
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -44,7 +44,7 @@ module Gitlab
|
|||
|
||||
# We want to use AttributesCleaner for these relations instead, in the future this should be removed to make sure
|
||||
# we are using AttributesPermitter for every imported relation.
|
||||
DISABLED_RELATION_NAMES = %i[user author issuable_sla].freeze
|
||||
DISABLED_RELATION_NAMES = %i[author issuable_sla].freeze
|
||||
|
||||
def initialize(config: ImportExport::Config.new.to_h)
|
||||
@config = config
|
||||
|
|
|
@ -178,17 +178,7 @@ included_attributes:
|
|||
- :project_id
|
||||
- :key
|
||||
- :value
|
||||
label:
|
||||
- :title
|
||||
- :color
|
||||
- :project_id
|
||||
- :group_id
|
||||
- :created_at
|
||||
- :updated_at
|
||||
- :template
|
||||
- :description
|
||||
- :priority
|
||||
labels:
|
||||
label: &label_definition
|
||||
- :title
|
||||
- :color
|
||||
- :project_id
|
||||
|
@ -198,23 +188,13 @@ included_attributes:
|
|||
- :template
|
||||
- :description
|
||||
- :priority
|
||||
labels: *label_definition
|
||||
priorities:
|
||||
- :project_id
|
||||
- :priority
|
||||
- :created_at
|
||||
- :updated_at
|
||||
milestone:
|
||||
- :iid
|
||||
- :title
|
||||
- :project_id
|
||||
- :group_id
|
||||
- :description
|
||||
- :due_date
|
||||
- :created_at
|
||||
- :updated_at
|
||||
- :start_date
|
||||
- :state
|
||||
milestones:
|
||||
milestone: &milestone_definition
|
||||
- :iid
|
||||
- :title
|
||||
- :project_id
|
||||
|
@ -225,6 +205,7 @@ included_attributes:
|
|||
- :updated_at
|
||||
- :start_date
|
||||
- :state
|
||||
milestones: *milestone_definition
|
||||
protected_branches:
|
||||
- :project_id
|
||||
- :name
|
||||
|
@ -315,6 +296,200 @@ included_attributes:
|
|||
- :project_id
|
||||
- :issue_template_key
|
||||
- :project_key
|
||||
snippets:
|
||||
- :title
|
||||
- :content
|
||||
- :author_id
|
||||
- :project_id
|
||||
- :created_at
|
||||
- :updated_at
|
||||
- :file_name
|
||||
- :visibility_level
|
||||
- :description
|
||||
project_members:
|
||||
- :access_level
|
||||
- :source_type
|
||||
- :user_id
|
||||
- :notification_level
|
||||
- :created_at
|
||||
- :updated_at
|
||||
- :created_by_id
|
||||
- :invite_email
|
||||
- :invite_accepted_at
|
||||
- :requested_at
|
||||
- :expires_at
|
||||
- :ldap
|
||||
- :override
|
||||
merge_request: &merge_request_definition
|
||||
- :target_branch
|
||||
- :source_branch
|
||||
- :source_project_id
|
||||
- :author_id
|
||||
- :assignee_id
|
||||
- :title
|
||||
- :created_at
|
||||
- :updated_at
|
||||
- :state
|
||||
- :merge_status
|
||||
- :target_project_id
|
||||
- :iid
|
||||
- :description
|
||||
- :updated_by_id
|
||||
- :merge_error
|
||||
- :merge_params
|
||||
- :merge_when_pipeline_succeeds
|
||||
- :merge_user_id
|
||||
- :merge_commit_sha
|
||||
- :squash_commit_sha
|
||||
- :in_progress_merge_commit_sha
|
||||
- :lock_version
|
||||
- :approvals_before_merge
|
||||
- :rebase_commit_sha
|
||||
- :time_estimate
|
||||
- :squash
|
||||
- :last_edited_at
|
||||
- :last_edited_by_id
|
||||
- :discussion_locked
|
||||
- :allow_maintainer_to_push
|
||||
- :merge_ref_sha
|
||||
- :draft
|
||||
- :diff_head_sha
|
||||
- :source_branch_sha
|
||||
- :target_branch_sha
|
||||
merge_requests: *merge_request_definition
|
||||
award_emoji:
|
||||
- :user_id
|
||||
- :name
|
||||
- :awardable_type
|
||||
- :created_at
|
||||
- :updated_at
|
||||
commit_author:
|
||||
- :name
|
||||
- :email
|
||||
committer:
|
||||
- :name
|
||||
- :email
|
||||
events:
|
||||
- :target_type
|
||||
- :action
|
||||
- :author_id
|
||||
- :fingerprint
|
||||
- :created_at
|
||||
- :updated_at
|
||||
label_links:
|
||||
- :target_type
|
||||
- :created_at
|
||||
- :updated_at
|
||||
merge_request_diff:
|
||||
- :state
|
||||
- :created_at
|
||||
- :updated_at
|
||||
- :base_commit_sha
|
||||
- :real_size
|
||||
- :head_commit_sha
|
||||
- :start_commit_sha
|
||||
- :commits_count
|
||||
- :files_count
|
||||
- :sorted
|
||||
- :diff_type
|
||||
merge_request_diff_commits:
|
||||
- :relative_order
|
||||
- :sha
|
||||
- :authored_date
|
||||
- :committed_date
|
||||
- :message
|
||||
- :trailers
|
||||
merge_request_diff_files:
|
||||
- :relative_order
|
||||
- :new_file
|
||||
- :renamed_file
|
||||
- :deleted_file
|
||||
- :new_path
|
||||
- :old_path
|
||||
- :a_mode
|
||||
- :b_mode
|
||||
- :too_large
|
||||
- :binary
|
||||
- :diff
|
||||
metrics:
|
||||
- :created_at
|
||||
- :updated_at
|
||||
- :latest_closed_by_id
|
||||
- :latest_closed_at
|
||||
- :merged_by_id
|
||||
- :merged_at
|
||||
- :latest_build_started_at
|
||||
- :latest_build_finished_at
|
||||
- :first_deployed_to_production_at
|
||||
- :first_comment_at
|
||||
- :first_commit_at
|
||||
- :last_commit_at
|
||||
- :diff_size
|
||||
- :modified_paths_size
|
||||
- :commits_count
|
||||
- :first_approved_at
|
||||
- :first_reassigned_at
|
||||
- :added_lines
|
||||
- :target_project_id
|
||||
- :removed_lines
|
||||
notes:
|
||||
- :note
|
||||
- :noteable_type
|
||||
- :author_id
|
||||
- :created_at
|
||||
- :updated_at
|
||||
- :project_id
|
||||
- :attachment
|
||||
- :line_code
|
||||
- :commit_id
|
||||
- :system
|
||||
- :st_diff
|
||||
- :updated_by_id
|
||||
- :type
|
||||
- :position
|
||||
- :original_position
|
||||
- :change_position
|
||||
- :resolved_at
|
||||
- :resolved_by_id
|
||||
- :resolved_by_push
|
||||
- :discussion_id
|
||||
- :confidential
|
||||
- :last_edited_at
|
||||
push_event_payload:
|
||||
- :commit_count
|
||||
- :action
|
||||
- :ref_type
|
||||
- :commit_from
|
||||
- :commit_to
|
||||
- :ref
|
||||
- :commit_title
|
||||
- :ref_count
|
||||
resource_label_events:
|
||||
- :action
|
||||
- :user_id
|
||||
- :created_at
|
||||
suggestions:
|
||||
- :relative_order
|
||||
- :applied
|
||||
- :commit_id
|
||||
- :from_content
|
||||
- :to_content
|
||||
- :outdated
|
||||
- :lines_above
|
||||
- :lines_below
|
||||
system_note_metadata:
|
||||
- :commit_count
|
||||
- :action
|
||||
- :created_at
|
||||
- :updated_at
|
||||
timelogs:
|
||||
- :time_spent
|
||||
- :user_id
|
||||
- :project_id
|
||||
- :spent_at
|
||||
- :created_at
|
||||
- :updated_at
|
||||
- :summary
|
||||
|
||||
# Do not include the following attributes for the models specified.
|
||||
excluded_attributes:
|
||||
|
@ -430,16 +605,7 @@ excluded_attributes:
|
|||
- :service_desk_reply_to
|
||||
- :upvotes_count
|
||||
- :work_item_type_id
|
||||
merge_request:
|
||||
- :milestone_id
|
||||
- :sprint_id
|
||||
- :ref_fetched
|
||||
- :merge_jid
|
||||
- :rebase_jid
|
||||
- :latest_merge_request_diff_id
|
||||
- :head_pipeline_id
|
||||
- :state_id
|
||||
merge_requests:
|
||||
merge_request: &merge_request_excluded_definition
|
||||
- :milestone_id
|
||||
- :sprint_id
|
||||
- :ref_fetched
|
||||
|
@ -448,6 +614,7 @@ excluded_attributes:
|
|||
- :latest_merge_request_diff_id
|
||||
- :head_pipeline_id
|
||||
- :state_id
|
||||
merge_requests: *merge_request_excluded_definition
|
||||
award_emoji:
|
||||
- :awardable_id
|
||||
statuses:
|
||||
|
@ -516,10 +683,9 @@ excluded_attributes:
|
|||
- :issue_id
|
||||
zoom_meetings:
|
||||
- :issue_id
|
||||
design:
|
||||
- :issue_id
|
||||
designs:
|
||||
design: &design_excluded_definition
|
||||
- :issue_id
|
||||
designs: *design_excluded_definition
|
||||
design_versions:
|
||||
- :issue_id
|
||||
actions:
|
||||
|
|
|
@ -38128,9 +38128,6 @@ msgstr ""
|
|||
msgid "VulnerabilityChart|Severity"
|
||||
msgstr ""
|
||||
|
||||
msgid "VulnerabilityManagement, Fetching linked Jira issues"
|
||||
msgstr ""
|
||||
|
||||
msgid "VulnerabilityManagement|%{statusStart}Confirmed%{statusEnd} %{timeago} by %{user}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -38158,6 +38155,9 @@ msgstr ""
|
|||
msgid "VulnerabilityManagement|Detected"
|
||||
msgstr ""
|
||||
|
||||
msgid "VulnerabilityManagement|Fetching linked Jira issues"
|
||||
msgstr ""
|
||||
|
||||
msgid "VulnerabilityManagement|Needs triage"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -203,7 +203,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.10.1",
|
||||
"@gitlab/eslint-plugin": "9.4.0",
|
||||
"@gitlab/eslint-plugin": "10.0.0",
|
||||
"@gitlab/stylelint-config": "2.6.0",
|
||||
"@testing-library/dom": "^7.16.2",
|
||||
"@vue/test-utils": "1.2.0",
|
||||
|
|
|
@ -22,20 +22,16 @@ module QA
|
|||
element :labels_block
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value.vue' do
|
||||
element :selected_label_content
|
||||
base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue' do
|
||||
element :dropdown_input_field
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents.vue' do
|
||||
base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue' do
|
||||
element :labels_dropdown_content
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue' do
|
||||
element :labels_edit_button
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue' do
|
||||
element :dropdown_input_field
|
||||
base.view 'app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue' do
|
||||
element :selected_label_content
|
||||
end
|
||||
|
||||
base.view 'app/views/shared/issuable/_sidebar.html.haml' do
|
||||
|
@ -53,7 +49,7 @@ module QA
|
|||
end
|
||||
|
||||
def assign_milestone(milestone)
|
||||
within_element(:milestone_block) do
|
||||
wait_milestone_block_finish_loading do
|
||||
click_element(:edit_link)
|
||||
click_on(milestone.title)
|
||||
end
|
||||
|
@ -70,14 +66,14 @@ module QA
|
|||
end
|
||||
|
||||
def has_assignee?(username)
|
||||
within_element(:assignee_block) do
|
||||
has_text?(username, wait: 1)
|
||||
wait_assignees_block_finish_loading do
|
||||
has_text?(username)
|
||||
end
|
||||
end
|
||||
|
||||
def has_no_assignee?(username)
|
||||
within_element(:assignee_block) do
|
||||
has_no_text?(username, wait: 1)
|
||||
wait_assignees_block_finish_loading do
|
||||
has_no_text?(username)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -88,8 +84,14 @@ module QA
|
|||
end
|
||||
|
||||
def has_label?(label)
|
||||
within_element(:labels_block) do
|
||||
!!has_element?(:selected_label_content, label_name: label)
|
||||
wait_labels_block_finish_loading do
|
||||
has_element?(:selected_label_content, label_name: label)
|
||||
end
|
||||
end
|
||||
|
||||
def has_no_label?(label)
|
||||
wait_labels_block_finish_loading do
|
||||
has_no_element?(:selected_label_content, label_name: label)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -103,27 +105,19 @@ module QA
|
|||
find_element(:more_assignees_link)
|
||||
end
|
||||
|
||||
def select_labels_and_refresh(labels)
|
||||
Support::Retrier.retry_until do
|
||||
click_element(:labels_edit_button)
|
||||
has_element?(:labels_dropdown_content, text: labels.first)
|
||||
end
|
||||
def select_labels(labels)
|
||||
within_element(:labels_block) do
|
||||
click_element(:edit_link)
|
||||
|
||||
labels.each do |label|
|
||||
within_element(:labels_dropdown_content) do
|
||||
send_keys_to_element(:dropdown_input_field, [label, :enter])
|
||||
labels.each do |label|
|
||||
within_element(:labels_dropdown_content) do
|
||||
fill_element(:dropdown_input_field, label)
|
||||
click_button(text: label)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
click_element(:labels_edit_button)
|
||||
|
||||
labels.each do |label|
|
||||
has_element?(:labels_block, text: label, wait: 0)
|
||||
end
|
||||
|
||||
refresh
|
||||
|
||||
wait_for_requests
|
||||
click_element(:title) # to blur dropdown
|
||||
end
|
||||
|
||||
def toggle_more_assignees_link
|
||||
|
@ -141,6 +135,15 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
def wait_labels_block_finish_loading
|
||||
within_element(:labels_block) do
|
||||
wait_until(reload: false, max_duration: 10, sleep_interval: 1) do
|
||||
finished_loading_block?
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def wait_milestone_block_finish_loading
|
||||
within_element(:milestone_block) do
|
||||
wait_until(reload: false, max_duration: 10, sleep_interval: 1) do
|
||||
|
|
|
@ -19,6 +19,7 @@ module QA
|
|||
related_issue_item
|
||||
snippet_description
|
||||
tag
|
||||
label
|
||||
].each do |predicate|
|
||||
RSpec::Matchers.define "have_#{predicate}" do |*args, **kwargs|
|
||||
match do |page_object|
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import { GlAlert, GlButton, GlLink } from '@gitlab/ui';
|
||||
import { GlAlert, GlLink } from '@gitlab/ui';
|
||||
import { mount, shallowMount } from '@vue/test-utils';
|
||||
|
||||
import JiraConnectApp from '~/jira_connect/subscriptions/components/app.vue';
|
||||
import AddNamespaceButton from '~/jira_connect/subscriptions/components/add_namespace_button.vue';
|
||||
import SignInButton from '~/jira_connect/subscriptions/components/sign_in_button.vue';
|
||||
import createStore from '~/jira_connect/subscriptions/store';
|
||||
import { SET_ALERT } from '~/jira_connect/subscriptions/store/mutation_types';
|
||||
import { __ } from '~/locale';
|
||||
|
||||
jest.mock('~/jira_connect/subscriptions/utils', () => ({
|
||||
retrieveAlert: jest.fn().mockReturnValue({ message: 'error message' }),
|
||||
getLocation: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('JiraConnectApp', () => {
|
||||
|
@ -18,7 +18,7 @@ describe('JiraConnectApp', () => {
|
|||
|
||||
const findAlert = () => wrapper.findComponent(GlAlert);
|
||||
const findAlertLink = () => findAlert().findComponent(GlLink);
|
||||
const findGlButton = () => wrapper.findComponent(GlButton);
|
||||
const findSignInButton = () => wrapper.findComponent(SignInButton);
|
||||
const findAddNamespaceButton = () => wrapper.findComponent(AddNamespaceButton);
|
||||
|
||||
const createComponent = ({ provide, mountFn = shallowMount } = {}) => {
|
||||
|
@ -35,28 +35,25 @@ describe('JiraConnectApp', () => {
|
|||
});
|
||||
|
||||
describe('template', () => {
|
||||
describe('when user is not logged in', () => {
|
||||
describe.each`
|
||||
scenario | usersPath | expectSignInButton | expectNamespaceButton
|
||||
${'user is not signed in'} | ${'/users'} | ${true} | ${false}
|
||||
${'user is signed in'} | ${undefined} | ${false} | ${true}
|
||||
`('when $scenario', ({ usersPath, expectSignInButton, expectNamespaceButton }) => {
|
||||
beforeEach(() => {
|
||||
createComponent({
|
||||
provide: {
|
||||
usersPath: '/users',
|
||||
usersPath,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('renders "Sign in" button', () => {
|
||||
expect(findGlButton().text()).toBe('Sign in to add namespaces');
|
||||
expect(findAddNamespaceButton().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when user is logged in', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
it('renders sign in button as expected', () => {
|
||||
expect(findSignInButton().exists()).toBe(expectSignInButton);
|
||||
});
|
||||
|
||||
it('renders "Add namespace" button ', () => {
|
||||
expect(findAddNamespaceButton().exists()).toBe(true);
|
||||
it('renders "Add Namespace" button as expected', () => {
|
||||
expect(findAddNamespaceButton().exists()).toBe(expectNamespaceButton);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
import { GlButton } from '@gitlab/ui';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getLocation } from '~/jira_connect/subscriptions/utils';
|
||||
import SignInButton from '~/jira_connect/subscriptions/components/sign_in_button.vue';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
|
||||
const MOCK_USERS_PATH = '/user';
|
||||
|
||||
jest.mock('~/jira_connect/subscriptions/utils');
|
||||
|
||||
describe('SignInButton', () => {
|
||||
let wrapper;
|
||||
|
||||
const createComponent = () => {
|
||||
wrapper = shallowMount(SignInButton, {
|
||||
propsData: {
|
||||
usersPath: MOCK_USERS_PATH,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const findButton = () => wrapper.findComponent(GlButton);
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('displays a button', () => {
|
||||
createComponent();
|
||||
|
||||
expect(findButton().exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe.each`
|
||||
getLocationValue | expectedHref
|
||||
${''} | ${MOCK_USERS_PATH}
|
||||
${undefined} | ${MOCK_USERS_PATH}
|
||||
${'https://test.jira.com'} | ${`${MOCK_USERS_PATH}?return_to=${encodeURIComponent('https://test.jira.com')}`}
|
||||
`('when getLocation resolves with `$getLocationValue`', ({ getLocationValue, expectedHref }) => {
|
||||
it(`sets button href to ${expectedHref}`, async () => {
|
||||
getLocation.mockResolvedValue(getLocationValue);
|
||||
createComponent();
|
||||
|
||||
expect(getLocation).toHaveBeenCalled();
|
||||
await waitForPromises();
|
||||
|
||||
expect(findButton().attributes('href')).toBe(expectedHref);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -185,15 +185,15 @@ describe('dateInWords', () => {
|
|||
const date = new Date('07/01/2016');
|
||||
|
||||
it('should return date in words', () => {
|
||||
expect(datetimeUtility.dateInWords(date)).toEqual(s__('July 1, 2016'));
|
||||
expect(datetimeUtility.dateInWords(date)).toEqual(__('July 1, 2016'));
|
||||
});
|
||||
|
||||
it('should return abbreviated month name', () => {
|
||||
expect(datetimeUtility.dateInWords(date, true)).toEqual(s__('Jul 1, 2016'));
|
||||
expect(datetimeUtility.dateInWords(date, true)).toEqual(__('Jul 1, 2016'));
|
||||
});
|
||||
|
||||
it('should return date in words without year', () => {
|
||||
expect(datetimeUtility.dateInWords(date, true, true)).toEqual(s__('Jul 1'));
|
||||
expect(datetimeUtility.dateInWords(date, true, true)).toEqual(__('Jul 1'));
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -201,11 +201,11 @@ describe('monthInWords', () => {
|
|||
const date = new Date('2017-01-20');
|
||||
|
||||
it('returns month name from provided date', () => {
|
||||
expect(datetimeUtility.monthInWords(date)).toBe(s__('January'));
|
||||
expect(datetimeUtility.monthInWords(date)).toBe(__('January'));
|
||||
});
|
||||
|
||||
it('returns abbreviated month name from provided date', () => {
|
||||
expect(datetimeUtility.monthInWords(date, true)).toBe(s__('Jan'));
|
||||
expect(datetimeUtility.monthInWords(date, true)).toBe(__('Jan'));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -8,8 +8,9 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
|
|||
setup = described_class.new(ActiveRecord::Base)
|
||||
|
||||
expect(setup).to receive(:configure_connection)
|
||||
expect(setup).to receive(:setup_load_balancer)
|
||||
expect(setup).to receive(:setup_connection_proxy)
|
||||
expect(setup).to receive(:setup_service_discovery)
|
||||
expect(setup).to receive(:setup_feature_flag_to_model_load_balancing)
|
||||
|
||||
setup.setup
|
||||
end
|
||||
|
@ -44,7 +45,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#setup_load_balancer' do
|
||||
describe '#setup_connection_proxy' do
|
||||
it 'sets up the load balancer' do
|
||||
model = Class.new(ActiveRecord::Base)
|
||||
setup = described_class.new(model)
|
||||
|
@ -58,7 +59,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
|
|||
.with(setup.configuration)
|
||||
.and_return(lb)
|
||||
|
||||
setup.setup_load_balancer
|
||||
setup.setup_connection_proxy
|
||||
|
||||
expect(model.connection.load_balancer).to eq(lb)
|
||||
expect(model.sticking)
|
||||
|
@ -81,7 +82,6 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
|
|||
model = ActiveRecord::Base
|
||||
setup = described_class.new(model)
|
||||
sv = instance_spy(Gitlab::Database::LoadBalancing::ServiceDiscovery)
|
||||
lb = model.connection.load_balancer
|
||||
|
||||
allow(setup.configuration)
|
||||
.to receive(:service_discovery_enabled?)
|
||||
|
@ -89,7 +89,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
|
|||
|
||||
allow(Gitlab::Database::LoadBalancing::ServiceDiscovery)
|
||||
.to receive(:new)
|
||||
.with(lb, setup.configuration.service_discovery)
|
||||
.with(setup.load_balancer, setup.configuration.service_discovery)
|
||||
.and_return(sv)
|
||||
|
||||
expect(sv).to receive(:perform_service_discovery)
|
||||
|
@ -102,7 +102,6 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
|
|||
model = ActiveRecord::Base
|
||||
setup = described_class.new(model, start_service_discovery: true)
|
||||
sv = instance_spy(Gitlab::Database::LoadBalancing::ServiceDiscovery)
|
||||
lb = model.connection.load_balancer
|
||||
|
||||
allow(setup.configuration)
|
||||
.to receive(:service_discovery_enabled?)
|
||||
|
@ -110,7 +109,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
|
|||
|
||||
allow(Gitlab::Database::LoadBalancing::ServiceDiscovery)
|
||||
.to receive(:new)
|
||||
.with(lb, setup.configuration.service_discovery)
|
||||
.with(setup.load_balancer, setup.configuration.service_discovery)
|
||||
.and_return(sv)
|
||||
|
||||
expect(sv).to receive(:perform_service_discovery)
|
||||
|
@ -120,4 +119,172 @@ RSpec.describe Gitlab::Database::LoadBalancing::Setup do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#setup_feature_flag_to_model_load_balancing', :reestablished_active_record_base do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where do
|
||||
{
|
||||
"with model LB enabled it picks a dedicated CI connection" => {
|
||||
env_GITLAB_USE_MODEL_LOAD_BALANCING: 'true',
|
||||
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
|
||||
request_store_active: false,
|
||||
ff_use_model_load_balancing: nil,
|
||||
expectations: {
|
||||
main: { read: 'main_replica', write: 'main' },
|
||||
ci: { read: 'ci_replica', write: 'ci' }
|
||||
}
|
||||
},
|
||||
"with model LB enabled and re-use of primary connection it uses CI connection for reads" => {
|
||||
env_GITLAB_USE_MODEL_LOAD_BALANCING: 'true',
|
||||
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: 'main',
|
||||
request_store_active: false,
|
||||
ff_use_model_load_balancing: nil,
|
||||
expectations: {
|
||||
main: { read: 'main_replica', write: 'main' },
|
||||
ci: { read: 'ci_replica', write: 'main' }
|
||||
}
|
||||
},
|
||||
"with model LB disabled it fallbacks to use main" => {
|
||||
env_GITLAB_USE_MODEL_LOAD_BALANCING: 'false',
|
||||
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
|
||||
request_store_active: false,
|
||||
ff_use_model_load_balancing: nil,
|
||||
expectations: {
|
||||
main: { read: 'main_replica', write: 'main' },
|
||||
ci: { read: 'main_replica', write: 'main' }
|
||||
}
|
||||
},
|
||||
"with model LB disabled, but re-use configured it fallbacks to use main" => {
|
||||
env_GITLAB_USE_MODEL_LOAD_BALANCING: 'false',
|
||||
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: 'main',
|
||||
request_store_active: false,
|
||||
ff_use_model_load_balancing: nil,
|
||||
expectations: {
|
||||
main: { read: 'main_replica', write: 'main' },
|
||||
ci: { read: 'main_replica', write: 'main' }
|
||||
}
|
||||
},
|
||||
"with FF disabled without RequestStore it uses main" => {
|
||||
env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
|
||||
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
|
||||
request_store_active: false,
|
||||
ff_use_model_load_balancing: false,
|
||||
expectations: {
|
||||
main: { read: 'main_replica', write: 'main' },
|
||||
ci: { read: 'main_replica', write: 'main' }
|
||||
}
|
||||
},
|
||||
"with FF enabled without RequestStore sticking of FF does not work, so it fallbacks to use main" => {
|
||||
env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
|
||||
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
|
||||
request_store_active: false,
|
||||
ff_use_model_load_balancing: true,
|
||||
expectations: {
|
||||
main: { read: 'main_replica', write: 'main' },
|
||||
ci: { read: 'main_replica', write: 'main' }
|
||||
}
|
||||
},
|
||||
"with FF disabled with RequestStore it uses main" => {
|
||||
env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
|
||||
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
|
||||
request_store_active: true,
|
||||
ff_use_model_load_balancing: false,
|
||||
expectations: {
|
||||
main: { read: 'main_replica', write: 'main' },
|
||||
ci: { read: 'main_replica', write: 'main' }
|
||||
}
|
||||
},
|
||||
"with FF enabled with RequestStore it sticks FF and uses CI connection" => {
|
||||
env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
|
||||
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: nil,
|
||||
request_store_active: true,
|
||||
ff_use_model_load_balancing: true,
|
||||
expectations: {
|
||||
main: { read: 'main_replica', write: 'main' },
|
||||
ci: { read: 'ci_replica', write: 'ci' }
|
||||
}
|
||||
},
|
||||
"with re-use and FF enabled with RequestStore it sticks FF and uses CI connection for reads" => {
|
||||
env_GITLAB_USE_MODEL_LOAD_BALANCING: nil,
|
||||
env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci: 'main',
|
||||
request_store_active: true,
|
||||
ff_use_model_load_balancing: true,
|
||||
expectations: {
|
||||
main: { read: 'main_replica', write: 'main' },
|
||||
ci: { read: 'ci_replica', write: 'main' }
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:ci_class) do
|
||||
Class.new(ActiveRecord::Base) do
|
||||
def self.name
|
||||
'Ci::ApplicationRecordTemporary'
|
||||
end
|
||||
|
||||
establish_connection ActiveRecord::DatabaseConfigurations::HashConfig.new(
|
||||
Rails.env,
|
||||
'ci',
|
||||
ActiveRecord::Base.connection_db_config.configuration_hash
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
let(:models) do
|
||||
{
|
||||
main: ActiveRecord::Base,
|
||||
ci: ci_class
|
||||
}
|
||||
end
|
||||
|
||||
around do |example|
|
||||
if request_store_active
|
||||
Gitlab::WithRequestStore.with_request_store do
|
||||
RequestStore.clear!
|
||||
|
||||
example.run
|
||||
end
|
||||
else
|
||||
example.run
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
# Rewrite `class_attribute` to use rspec mocking and prevent modifying the objects
|
||||
allow_next_instance_of(described_class) do |setup|
|
||||
allow(setup).to receive(:configure_connection)
|
||||
|
||||
allow(setup).to receive(:setup_class_attribute) do |attribute, value|
|
||||
allow(setup.model).to receive(attribute) { value }
|
||||
end
|
||||
end
|
||||
|
||||
stub_env('GITLAB_USE_MODEL_LOAD_BALANCING', env_GITLAB_USE_MODEL_LOAD_BALANCING)
|
||||
stub_env('GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci', env_GITLAB_LOAD_BALANCING_REUSE_PRIMARY_ci)
|
||||
stub_feature_flags(use_model_load_balancing: ff_use_model_load_balancing)
|
||||
end
|
||||
|
||||
it 'results match expectations' do
|
||||
result = models.transform_values do |model|
|
||||
# Make load balancer to force init with a dedicated replicas connections
|
||||
described_class.new(model).tap do |subject|
|
||||
subject.configuration.hosts = [subject.configuration.replica_db_config.host]
|
||||
subject.setup
|
||||
end
|
||||
|
||||
load_balancer = model.connection.load_balancer
|
||||
|
||||
{
|
||||
read: load_balancer.read { |connection| connection.pool.db_config.name },
|
||||
write: load_balancer.read_write { |connection| connection.pool.db_config.name }
|
||||
}
|
||||
end
|
||||
|
||||
expect(result).to eq(expectations)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -80,8 +80,8 @@ RSpec.describe Gitlab::ImportExport::AttributesPermitter do
|
|||
|
||||
let(:attributes_permitter) { described_class.new }
|
||||
|
||||
where(:relation_name, :permitted_attributes_defined) do
|
||||
:user | false
|
||||
where(:relation_name, :permitted_attributes_defined ) do
|
||||
:user | true
|
||||
:author | false
|
||||
:ci_cd_settings | true
|
||||
:metrics_setting | true
|
||||
|
@ -91,6 +91,7 @@ RSpec.describe Gitlab::ImportExport::AttributesPermitter do
|
|||
:auto_devops | true
|
||||
:boards | true
|
||||
:custom_attributes | true
|
||||
:label | true
|
||||
:labels | true
|
||||
:protected_branches | true
|
||||
:protected_tags | true
|
||||
|
@ -99,6 +100,28 @@ RSpec.describe Gitlab::ImportExport::AttributesPermitter do
|
|||
:push_access_levels | true
|
||||
:releases | true
|
||||
:links | true
|
||||
:priorities | true
|
||||
:milestone | true
|
||||
:milestones | true
|
||||
:snippets | true
|
||||
:project_members | true
|
||||
:merge_request | true
|
||||
:merge_requests | true
|
||||
:award_emoji | true
|
||||
:commit_author | true
|
||||
:committer | true
|
||||
:events | true
|
||||
:label_links | true
|
||||
:merge_request_diff | true
|
||||
:merge_request_diff_commits | true
|
||||
:merge_request_diff_files | true
|
||||
:metrics | true
|
||||
:notes | true
|
||||
:push_event_payload | true
|
||||
:resource_label_events | true
|
||||
:suggestions | true
|
||||
:system_note_metadata | true
|
||||
:timelogs | true
|
||||
:container_expiration_policy | true
|
||||
:project_feature | true
|
||||
:prometheus_metrics | true
|
||||
|
@ -113,9 +136,11 @@ RSpec.describe Gitlab::ImportExport::AttributesPermitter do
|
|||
describe 'included_attributes for Project' do
|
||||
subject { described_class.new }
|
||||
|
||||
additional_attributes = { user: %w[id] }
|
||||
|
||||
Gitlab::ImportExport::Config.new.to_h[:included_attributes].each do |relation_sym, permitted_attributes|
|
||||
context "for #{relation_sym}" do
|
||||
it_behaves_like 'a permitted attribute', relation_sym, permitted_attributes
|
||||
it_behaves_like 'a permitted attribute', relation_sym, permitted_attributes, additional_attributes[relation_sym]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -626,7 +626,7 @@ RSpec.describe Ci::Runner do
|
|||
end
|
||||
|
||||
describe '#status' do
|
||||
let(:runner) { create(:ci_runner, :instance, contacted_at: 1.second.ago) }
|
||||
let(:runner) { build(:ci_runner, :instance) }
|
||||
|
||||
subject { runner.status }
|
||||
|
||||
|
@ -638,6 +638,45 @@ RSpec.describe Ci::Runner do
|
|||
it { is_expected.to eq(:not_connected) }
|
||||
end
|
||||
|
||||
context 'inactive but online' do
|
||||
before do
|
||||
runner.contacted_at = 1.second.ago
|
||||
runner.active = false
|
||||
end
|
||||
|
||||
it { is_expected.to eq(:online) }
|
||||
end
|
||||
|
||||
context 'contacted 1s ago' do
|
||||
before do
|
||||
runner.contacted_at = 1.second.ago
|
||||
end
|
||||
|
||||
it { is_expected.to eq(:online) }
|
||||
end
|
||||
|
||||
context 'contacted long time ago' do
|
||||
before do
|
||||
runner.contacted_at = 1.year.ago
|
||||
end
|
||||
|
||||
it { is_expected.to eq(:offline) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#deprecated_rest_status' do
|
||||
let(:runner) { build(:ci_runner, :instance, contacted_at: 1.second.ago) }
|
||||
|
||||
subject { runner.deprecated_rest_status }
|
||||
|
||||
context 'never connected' do
|
||||
before do
|
||||
runner.contacted_at = nil
|
||||
end
|
||||
|
||||
it { is_expected.to eq(:not_connected) }
|
||||
end
|
||||
|
||||
context 'contacted 1s ago' do
|
||||
before do
|
||||
runner.contacted_at = 1.second.ago
|
||||
|
|
|
@ -162,12 +162,12 @@ RSpec.describe Clusters::Applications::Runner do
|
|||
it 'pauses associated runner' do
|
||||
active_runner = create(:ci_runner, contacted_at: 1.second.ago)
|
||||
|
||||
expect(active_runner.status).to eq(:online)
|
||||
expect(active_runner.active).to be_truthy
|
||||
|
||||
application_runner = create(:clusters_applications_runner, :scheduled, runner: active_runner)
|
||||
application_runner.prepare_uninstall
|
||||
|
||||
expect(active_runner.status).to eq(:paused)
|
||||
expect(active_runner.active).to be_falsey
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
RSpec.shared_examples 'a permitted attribute' do |relation_sym, permitted_attributes|
|
||||
RSpec.shared_examples 'a permitted attribute' do |relation_sym, permitted_attributes, additional_attributes = []|
|
||||
let(:prohibited_attributes) { %i[remote_url my_attributes my_ids token my_id test] }
|
||||
|
||||
let(:import_export_config) { Gitlab::ImportExport::Config.new.to_h }
|
||||
|
@ -26,7 +26,7 @@ RSpec.shared_examples 'a permitted attribute' do |relation_sym, permitted_attrib
|
|||
end
|
||||
|
||||
it 'does not contain attributes that would be cleaned with AttributeCleaner' do
|
||||
expect(cleaned_hash.keys).to include(*permitted_hash.keys)
|
||||
expect(cleaned_hash.keys + additional_attributes.to_a).to include(*permitted_hash.keys)
|
||||
end
|
||||
|
||||
it 'does not contain prohibited attributes that are not related to given relation' do
|
||||
|
|
|
@ -873,10 +873,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@gitlab/at.js/-/at.js-1.5.7.tgz#1ee6f838cc4410a1d797770934df91d90df8179e"
|
||||
integrity sha512-c6ySRK/Ma7lxwpIVbSAF3P+xiTLrNTGTLRx4/pHK111AdFxwgUwrYF6aVZFXvmG65jHOJHoa0eQQ21RW6rm0Rg==
|
||||
|
||||
"@gitlab/eslint-plugin@9.4.0":
|
||||
version "9.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/eslint-plugin/-/eslint-plugin-9.4.0.tgz#cad8f63b7985c22865859cc7d2688eb446ad0bbb"
|
||||
integrity sha512-llPypEQrm9/6Xas5GCoSPAK7W/DgO7CKhzDvAk/Ea9BP0rI2+t8Wg4PFhE1XDctYnnUIS/GrdqVKQkpODk24hQ==
|
||||
"@gitlab/eslint-plugin@10.0.0":
|
||||
version "10.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/eslint-plugin/-/eslint-plugin-10.0.0.tgz#83430fb4d0a2467bb54975d0b5b9dc8016005722"
|
||||
integrity sha512-frCYzjQQaZ5kW1on3XwuVGhvYa6XjD6Q1POTbxDpzl6tNxSeTwOJohC6Joyw76e0Kw4fPQd/fHAfKQAB0AVQ7A==
|
||||
dependencies:
|
||||
babel-eslint "^10.0.3"
|
||||
eslint-config-airbnb-base "^14.2.1"
|
||||
|
|
Loading…
Reference in New Issue