Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-09-30 12:08:43 +00:00
parent 14846d722e
commit e99d2e196c
69 changed files with 824 additions and 240 deletions

View File

@ -165,7 +165,7 @@ gem 'html-pipeline', '~> 2.13.2'
gem 'deckar01-task_list', '2.3.1'
gem 'gitlab-markup', '~> 1.8.0'
gem 'github-markup', '~> 1.7.0', require: 'github/markup'
gem 'commonmarker', '~> 0.23.4'
gem 'commonmarker', '~> 0.23.6'
gem 'kramdown', '~> 2.3.1'
gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~> 6.3.2'

View File

@ -79,7 +79,7 @@
{"name":"claide-plugins","version":"0.9.2","platform":"ruby","checksum":"c7ea78bc067ab23bce8515497cdcdcb8f01c86dadfbe13c44644e382922c1c2e"},
{"name":"coderay","version":"1.1.3","platform":"ruby","checksum":"dc530018a4684512f8f38143cd2a096c9f02a1fc2459edcfe534787a7fc77d4b"},
{"name":"colored2","version":"3.1.2","platform":"ruby","checksum":"b13c2bd7eeae2cf7356a62501d398e72fde78780bd26aec6a979578293c28b4a"},
{"name":"commonmarker","version":"0.23.4","platform":"ruby","checksum":"95d9cb050576376374a66d71a4feab3562e0955aab9d80a3e8606a5cf5e9c7ce"},
{"name":"commonmarker","version":"0.23.6","platform":"ruby","checksum":"c8aeaaaff4ba497bf180f762db63a0069794fafb6eff221224c9c8199d337b38"},
{"name":"concurrent-ruby","version":"1.1.10","platform":"ruby","checksum":"244cb1ca0d91ec2c15ca2209507c39fb163336994428e16fbd3f465c87bd8e68"},
{"name":"connection_pool","version":"2.2.5","platform":"ruby","checksum":"13a8fc3921ce4df8e04fb65f1037251decb08d74757b41163688bd1c1feccd39"},
{"name":"contracts","version":"0.11.0","platform":"ruby","checksum":"df6e438efa89c31dd3095851c3f7a25dfdae36b35ff1d4547f1d92941b3c7286"},

View File

@ -282,7 +282,7 @@ GEM
open4 (~> 1.3)
coderay (1.1.3)
colored2 (3.1.2)
commonmarker (0.23.4)
commonmarker (0.23.6)
concurrent-ruby (1.1.10)
connection_pool (2.2.5)
contracts (0.11.0)
@ -1557,7 +1557,7 @@ DEPENDENCIES
capybara-screenshot (~> 1.0.22)
carrierwave (~> 1.3)
charlock_holmes (~> 0.7.7)
commonmarker (~> 0.23.4)
commonmarker (~> 0.23.6)
concurrent-ruby (~> 1.1)
connection_pool (~> 2.0)
countries (~> 3.0)

View File

@ -1,5 +1,5 @@
import { DEFAULT_PER_PAGE } from '~/api';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
import axios from '../lib/utils/axios_utils';
import { buildApiUrl } from './api_utils';
@ -55,7 +55,7 @@ export function getUserProjects(userId, query, options, callback) {
})
.then(({ data }) => callback(data))
.catch(() =>
createFlash({
createAlert({
message: __('Something went wrong while fetching projects'),
}),
);

View File

@ -7,7 +7,7 @@ import { getEmojiScoreWithIntent } from '~/emoji/utils';
import { getCookie, setCookie, scrollToElement } from '~/lib/utils/common_utils';
import * as Emoji from '~/emoji';
import { dispose, fixTitle } from '~/tooltips';
import createFlash from './flash';
import { createAlert } from '~/flash';
import axios from './lib/utils/axios_utils';
import { isInVueNoteablePage } from './lib/utils/dom_utils';
import { __ } from './locale';
@ -491,7 +491,7 @@ export class AwardsHandler {
}
})
.catch(() =>
createFlash({
createAlert({
message: __('Something went wrong on our end.'),
}),
);

View File

@ -2,7 +2,7 @@
import { GlLoadingIcon, GlFormInput, GlFormGroup, GlButton, GlSafeHtmlDirective } from '@gitlab/ui';
import { escape, debounce } from 'lodash';
import { mapActions, mapState } from 'vuex';
import createFlash from '~/flash';
import { createAlert, VARIANT_INFO } from '~/flash';
import { s__, sprintf } from '~/locale';
import createEmptyBadge from '../empty_badge';
import Badge from './badge.vue';
@ -136,14 +136,14 @@ export default {
if (this.isEditing) {
return this.saveBadge()
.then(() => {
createFlash({
createAlert({
message: s__('Badges|Badge saved.'),
type: 'notice',
variant: VARIANT_INFO,
});
this.wasValidated = false;
})
.catch((error) => {
createFlash({
createAlert({
message: s__(
'Badges|Saving the badge failed, please check the entered URLs and try again.',
),
@ -154,14 +154,14 @@ export default {
return this.addBadge()
.then(() => {
createFlash({
createAlert({
message: s__('Badges|New badge added.'),
type: 'notice',
variant: VARIANT_INFO,
});
this.wasValidated = false;
})
.catch((error) => {
createFlash({
createAlert({
message: s__(
'Badges|Adding the badge failed, please check the entered URLs and try again.',
),

View File

@ -1,7 +1,7 @@
<script>
import { GlSprintf, GlModal } from '@gitlab/ui';
import { mapState, mapActions } from 'vuex';
import createFlash from '~/flash';
import { createAlert, VARIANT_INFO } from '~/flash';
import { __, s__ } from '~/locale';
import Badge from './badge.vue';
import BadgeForm from './badge_form.vue';
@ -40,13 +40,13 @@ export default {
onSubmitModal() {
this.deleteBadge(this.badgeInModal)
.then(() => {
createFlash({
createAlert({
message: s__('Badges|The badge was deleted.'),
type: 'notice',
variant: VARIANT_INFO,
});
})
.catch((error) => {
createFlash({
createAlert({
message: s__('Badges|Deleting the badge failed, please try again.'),
});
throw error;

View File

@ -1,5 +1,5 @@
import { isEmpty } from 'lodash';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import { scrollToElement } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
import { CHANGES_TAB, DISCUSSION_TAB, SHOW_TAB } from '../../../constants';
@ -18,7 +18,7 @@ export const addDraftToDiscussion = ({ commit }, { endpoint, data }) =>
return res;
})
.catch(() => {
createFlash({
createAlert({
message: __('An error occurred adding a draft to the thread.'),
});
});
@ -32,7 +32,7 @@ export const createNewDraft = ({ commit }, { endpoint, data }) =>
return res;
})
.catch(() => {
createFlash({
createAlert({
message: __('An error occurred adding a new draft.'),
});
});
@ -44,7 +44,7 @@ export const deleteDraft = ({ commit, getters }, draft) =>
commit(types.DELETE_DRAFT, draft.id);
})
.catch(() =>
createFlash({
createAlert({
message: __('An error occurred while deleting the comment'),
}),
);
@ -62,7 +62,7 @@ export const fetchDrafts = ({ commit, getters, state, dispatch }) =>
});
})
.catch(() =>
createFlash({
createAlert({
message: __('An error occurred while fetching pending comments'),
}),
);
@ -122,7 +122,7 @@ export const updateDraft = (
.then((data) => commit(types.RECEIVE_DRAFT_UPDATE_SUCCESS, data))
.then(callback)
.catch(() =>
createFlash({
createAlert({
message: __('An error occurred while updating the comment'),
}),
);

View File

@ -1,7 +1,7 @@
/* eslint-disable func-names */
import $ from 'jquery';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
@ -80,7 +80,7 @@ MarkdownPreview.prototype.fetchMarkdownPreview = function (text, url, success) {
success(data);
})
.catch(() =>
createFlash({
createAlert({
message: __('An error occurred while fetching Markdown preview'),
}),
);

View File

@ -2,7 +2,7 @@ import $ from 'jquery';
import Api from '~/api';
import initPopover from '~/blob/suggest_gitlab_ci_yml';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
import toast from '~/vue_shared/plugins/global_toast';
@ -155,7 +155,7 @@ export default class FileTemplateMediator {
}
})
.catch((err) =>
createFlash({
createAlert({
message: __(`An error occurred while fetching the template: ${err}`),
}),
);

View File

@ -1,5 +1,5 @@
import { SwaggerUIBundle } from 'swagger-ui-dist';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
export default () => {
@ -15,7 +15,7 @@ export default () => {
});
})
.catch((error) => {
createFlash({
createAlert({
message: __('Something went wrong while initializing the OpenAPI viewer'),
});
throw error;

View File

@ -1,6 +1,6 @@
import $ from 'jquery';
import '~/behaviors/markdown/render_gfm';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
import {
REPO_BLOB_LOAD_VIEWER_START,
@ -69,7 +69,7 @@ export const handleBlobRichViewer = (viewer, type) => {
loadRichBlobViewer(type)
.then((module) => module?.default(viewer))
.catch((error) => {
createFlash({
createAlert({
message: __('Error loading file viewer.'),
});
throw error;
@ -221,7 +221,7 @@ export class BlobViewer {
});
})
.catch(() =>
createFlash({
createAlert({
message: __('Error loading viewer'),
}),
);

View File

@ -2,7 +2,7 @@
import $ from 'jquery';
import initPopover from '~/blob/suggest_gitlab_ci_yml';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import { disableButtonIfEmptyField, setCookie } from '~/lib/utils/common_utils';
import Tracking from '~/tracking';
import BlobFileDropzone from '../blob/blob_file_dropzone';
@ -79,7 +79,7 @@ export default () => {
initPopovers();
})
.catch((e) =>
createFlash({
createAlert({
message: e,
}),
);

View File

@ -3,7 +3,7 @@ import { SourceEditorExtension } from '~/editor/extensions/source_editor_extensi
import { FileTemplateExtension } from '~/editor/extensions/source_editor_file_template_ext';
import { ToolbarExtension } from '~/editor/extensions/source_editor_toolbar_ext';
import SourceEditor from '~/editor/source_editor';
import createFlash from '~/flash';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { addEditorMarkdownListeners } from '~/lib/utils/text_markdown';
import { insertFinalNewline } from '~/lib/utils/text_utility';
@ -44,7 +44,7 @@ export default class EditBlob {
},
]);
} catch (e) {
createFlash({
createAlert({
message: `${BLOB_EDITOR_ERROR}: ${e}`,
});
}
@ -130,7 +130,7 @@ export default class EditBlob {
currentPane.renderGFM();
})
.catch(() =>
createFlash({
createAlert({
message: BLOB_PREVIEW_ERROR,
}),
);

View File

@ -0,0 +1,332 @@
<script>
import {
GlFormGroup,
GlFormInput,
GlFormCheckbox,
GlButton,
GlDatepicker,
GlFormInputGroup,
GlSprintf,
GlLink,
} from '@gitlab/ui';
import { createAlert, VARIANT_INFO } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { formatDate } from '~/lib/utils/datetime_utility';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import { s__ } from '~/locale';
function defaultData() {
return {
expiresAt: null,
name: '',
newTokenDetails: null,
readRepository: false,
writeRepository: false,
readRegistry: false,
writeRegistry: false,
readPackageRegistry: false,
writePackageRegistry: false,
username: '',
placeholders: {
link: { link: ['link_start', 'link_end'] },
i: { i: ['i_start', 'i_end'] },
code: { code: ['code_start', 'code_end'] },
},
};
}
export default {
components: {
GlFormGroup,
GlFormInput,
GlDatepicker,
GlFormCheckbox,
GlButton,
GlFormInputGroup,
ClipboardButton,
GlSprintf,
GlLink,
},
props: {
createNewTokenPath: {
type: String,
required: true,
},
deployTokensHelpUrl: {
type: String,
required: true,
},
containerRegistryEnabled: {
type: Boolean,
required: true,
},
packagesRegistryEnabled: {
type: Boolean,
required: true,
},
tokenType: {
type: String,
required: true,
},
},
data() {
return defaultData();
},
translations: {
addTokenButton: s__('DeployTokens|Create deploy token'),
addTokenExpiryLabel: s__('DeployTokens|Expiration date (optional)'),
addTokenExpiryDescription: s__(
'DeployTokens|Enter an expiration date for your token. Defaults to never expire.',
),
addTokenHeader: s__('DeployTokens|New deploy token'),
addTokenDescription: s__(
'DeployTokens|Create a new deploy token for all projects in this group. %{link_start}What are deploy tokens?%{link_end}',
),
addTokenNameLabel: s__('DeployTokens|Name'),
addTokenNameDescription: s__('DeployTokens|Enter a unique name for your deploy token.'),
addTokenScopesLabel: s__('DeployTokens|Scopes (select at least one)'),
addTokenUsernameDescription: s__(
'DeployTokens|Enter a username for your token. Defaults to %{code_start}gitlab+deploy-token-{n}%{code_end}.',
),
addTokenUsernameLabel: s__('DeployTokens|Username (optional)'),
newTokenCopyMessage: s__('DeployTokens|Copy deploy token'),
newProjectTokenCreated: s__('DeployTokens|Your new project deploy token has been created.'),
newGroupTokenCreated: s__('DeployTokens|Your new group deploy token has been created.'),
newTokenDescription: s__(
'DeployTokens|Use this token as a password. Save it. This password can %{i_start}not%{i_end} be recovered.',
),
newTokenMessage: s__('DeployTokens|Your New Deploy Token'),
newTokenUsernameCopy: s__('DeployTokens|Copy username'),
newTokenUsernameDescription: s__(
'DeployTokens|This username supports access. %{link_start}What kind of access?%{link_end}',
),
readRepositoryHelp: s__('DeployTokens|Allows read-only access to the repository.'),
readRegistryHelp: s__('DeployTokens|Allows read-only access to registry images.'),
writeRegistryHelp: s__('DeployTokens|Allows read and write access to registry images.'),
readPackageRegistryHelp: s__('DeployTokens|Allows read-only access to the package registry.'),
writePackageRegistryHelp: s__(
'DeployTokens|Allows read and write access to the package registry.',
),
},
computed: {
formattedExpiryDate() {
return formatDate(this.expiresAt, 'yyyy-mm-dd');
},
newTokenCreatedMessage() {
return this.tokenType === 'group'
? this.$options.translations.newGroupTokenCreated
: this.$options.translations.newProjectTokenCreated;
},
},
methods: {
createDeployToken() {
return axios
.post(this.createNewTokenPath, {
deploy_token: {
expires_at: this.expiresAt,
name: this.name,
read_repository: this.readRepository,
read_registry: this.readRegistry,
username: this.username,
},
})
.then((response) => {
this.newTokenDetails = response.data;
this.resetData();
createAlert({
variant: VARIANT_INFO,
message: this.newTokenCreatedMessage,
});
})
.catch((error) => {
createAlert({
message: error.response.data.message,
});
});
},
resetData() {
const newData = defaultData();
delete newData.newTokenDetails;
Object.keys(newData).forEach((k) => {
this[k] = newData[k];
});
},
},
};
</script>
<template>
<div>
<div v-if="newTokenDetails" class="created-deploy-token-container info-well">
<div class="well-segment">
<h5>{{ $options.translations.newTokenMessage }}</h5>
<gl-form-group>
<template #description>
<div class="deploy-token-help-block gl-mt-2 text-success">
<gl-sprintf
:message="$options.translations.newTokenUsernameDescription"
:placeholders="placeholders.link"
>
<template #link="{ content }">
<gl-link :href="deployTokensHelpUrl" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</div>
</template>
<gl-form-input-group
name="deploy-token-user"
:value="newTokenDetails.username"
select-on-click
readonly
>
<template #append>
<clipboard-button
:text="newTokenDetails.username"
:title="$options.translations.newTokenUsernameCopy"
/>
</template>
</gl-form-input-group>
</gl-form-group>
<gl-form-group>
<template #description>
<div class="deploy-token-help-block gl-mt-2 text-danger">
<gl-sprintf
:message="$options.translations.newTokenDescription"
:placeholders="placeholders.i"
>
<template #i="{ content }">
<i>{{ content }}</i>
</template>
</gl-sprintf>
</div>
</template>
<gl-form-input-group :value="newTokenDetails.token" name="deploy-token" readonly>
<template #append>
<clipboard-button
:text="newTokenDetails.token"
:title="$options.translations.newTokenCopyMessage"
/>
</template>
</gl-form-input-group>
</gl-form-group>
</div>
</div>
<h5>{{ $options.translations.addTokenHeader }}</h5>
<p class="profile-settings-content">
<gl-sprintf
:message="$options.translations.addTokenDescription"
:placeholders="placeholders.link"
>
<template #link="{ content }">
<gl-link :href="deployTokensHelpUrl" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
<gl-form-group
:label="$options.translations.addTokenNameLabel"
:description="$options.translations.addTokenNameDescription"
label-for="deploy_token_name"
>
<gl-form-input
id="deploy_token_name"
v-model="name"
name="deploy_token_name"
class="qa-deploy-token-name"
data-qa-selector="deploy_token_name_field"
/>
</gl-form-group>
<gl-form-group
:label="$options.translations.addTokenExpiryLabel"
:description="$options.translations.addTokenExpiryDescription"
label-for="deploy_token_expires_at"
>
<gl-form-input
id="deploy_token_expires_at"
name="deploy_token_expires_at"
:value="formattedExpiryDate"
data-qa-selector="deploy_token_expires_at_field"
/>
</gl-form-group>
<gl-form-group
:label="$options.translations.addTokenUsernameLabel"
label-for="deploy_token_username"
>
<template #description>
<gl-sprintf
:message="$options.translations.addTokenUsernameDescription"
:placeholders="placeholders.code"
>
<template #code="{ content }">
<code>{{ content }}</code>
</template>
</gl-sprintf>
</template>
<gl-form-input id="deploy_token_username" v-model="username" />
</gl-form-group>
<gl-form-group
:label="$options.translations.addTokenScopesLabel"
label-for="deploy-token-scopes"
>
<div id="deploy-token-scopes">
<!-- eslint-disable @gitlab/vue-require-i18n-strings -->
<gl-form-checkbox
id="deploy_token_read_repository"
v-model="readRepository"
name="deploy_token_read_repository"
data-qa-selector="deploy_token_read_repository_checkbox"
>
read_repository
<template #help>{{ $options.translations.readRepositoryHelp }}</template>
</gl-form-checkbox>
<gl-form-checkbox
v-if="containerRegistryEnabled"
id="deploy_token_read_registry"
v-model="readRegistry"
name="deploy_token_read_registry"
data-qa-selector="deploy_token_read_registry_checkbox"
>
read_registry
<template #help>{{ $options.translations.readRegistryHelp }}</template>
</gl-form-checkbox>
<gl-form-checkbox
v-if="containerRegistryEnabled"
id="deploy_token_write_registry"
v-model="writeRegistry"
name="deploy_token_write_registry"
data-qa-selector="deploy_token_write_registry_checkbox"
>
write_registry
<template #help>{{ $options.translations.writeRegistryHelp }}</template>
</gl-form-checkbox>
<gl-form-checkbox
v-if="packagesRegistryEnabled"
id="deploy_token_read_package_registry"
v-model="readPackageRegistry"
name="deploy_token_read_package_registry"
data-qa-selector="deploy_token_read_package_registry_checkbox"
>
read_package_registry
<template #help>{{ $options.translations.readPackageRegistryHelp }}</template>
</gl-form-checkbox>
<gl-form-checkbox
v-if="packagesRegistryEnabled"
id="deploy_token_write_package_registry"
v-model="writePackageRegistry"
name="deploy_token_write_package_registry"
data-qa-selector="deploy_token_write_package_registry_checkbox"
>
write_package_registry
<template #help>{{ $options.translations.writePackageRegistryHelp }}</template>
</gl-form-checkbox>
<!-- eslint-enable @gitlab/vue-require-i18n-strings -->
</div>
</gl-form-group>
<div>
<gl-button variant="success" @click="createDeployToken">
{{ $options.translations.addTokenButton }}
</gl-button>
</div>
<gl-datepicker v-model="expiresAt" target="#deploy_token_expires_at" container="body" />
</div>
</template>

View File

@ -0,0 +1,33 @@
import Vue from 'vue';
import NewDeployToken from './components/new_deploy_token.vue';
export default function initDeployTokens() {
const el = document.getElementById('js-new-deploy-token');
if (el == null) return null;
const {
createNewTokenPath,
deployTokensHelpUrl,
containerRegistryEnabled,
packagesRegistryEnabled,
tokenType,
} = el.dataset;
return new Vue({
el,
components: {
NewDeployToken,
},
render(createElement) {
return createElement(NewDeployToken, {
props: {
createNewTokenPath,
deployTokensHelpUrl,
containerRegistryEnabled: containerRegistryEnabled !== undefined,
packagesRegistryEnabled: packagesRegistryEnabled !== undefined,
tokenType,
},
});
},
});
}

View File

@ -81,16 +81,18 @@ export default {
:class="{
'gl-bg-orange-50': blocksMerge && !allResolved,
'gl-bg-gray-50': !blocksMerge || allResolved,
'gl-pr-2': !allResolved,
}"
data-testid="discussions-counter-text"
>
<template v-if="allResolved">
{{ __('All threads resolved!') }}
<gl-dropdown
v-gl-tooltip:discussionCounter.hover.bottom
size="small"
category="tertiary"
right
:title="__('Thread options')"
:aria-label="__('Thread options')"
toggle-class="btn-icon"
class="gl-pt-0! gl-px-2 gl-h-full gl-ml-2"
>
@ -133,9 +135,12 @@ export default {
@click="jumpNext"
/>
<gl-dropdown
v-gl-tooltip:discussionCounter.hover.bottom
size="small"
category="tertiary"
right
:title="__('Thread options')"
:aria-label="__('Thread options')"
toggle-class="btn-icon"
class="gl-pt-0! gl-px-2"
>

View File

@ -57,6 +57,9 @@ export default {
isEnabled() {
return this.containerExpirationPolicy || this.enableHistoricEntries;
},
isLoading() {
return this.$apollo.queries.containerExpirationPolicy.loading;
},
showDisabledFormMessage() {
return !this.isEnabled && !this.fetchSettingsError;
},
@ -86,10 +89,10 @@ export default {
<container-expiration-policy-form
v-if="isEnabled"
v-model="workingCopy"
:is-loading="$apollo.queries.containerExpirationPolicy.loading"
:is-loading="isLoading"
:is-edited="isEdited"
/>
<template v-else>
<template v-if="!isLoading">
<gl-alert
v-if="showDisabledFormMessage"
:dismissible="false"

View File

@ -110,7 +110,7 @@ export default {
{{ cleanupRulesButtonText }}
</gl-button>
</gl-card>
<template v-else>
<template v-if="!$apollo.queries.containerExpirationPolicy.loading">
<gl-alert
v-if="showDisabledFormMessage"
:dismissible="false"

View File

@ -2,9 +2,11 @@ import initStaleRunnerCleanupSetting from 'ee_else_ce/group_settings/stale_runne
import initVariableList from '~/ci_variable_list';
import initSharedRunnersForm from '~/group_settings/mount_shared_runners';
import initSettingsPanels from '~/settings_panels';
import initDeployTokens from '~/deploy_tokens';
// Initialize expandable settings panels
initSettingsPanels();
initDeployTokens();
initSharedRunnersForm();
initStaleRunnerCleanupSetting();

View File

@ -1,5 +1,7 @@
import initRevokeButton from '~/deploy_tokens/init_revoke_button';
import initSearchSettings from '~/search_settings';
import initDeployTokens from '~/deploy_tokens';
initDeployTokens();
initSearchSettings();
initRevokeButton();

View File

@ -10,6 +10,7 @@ import initSharedRunnersToggle from '~/projects/settings/mount_shared_runners_to
import initSettingsPanels from '~/settings_panels';
import { initTokenAccess } from '~/token_access';
import { initCiSecureFiles } from '~/ci_secure_files';
import initDeployTokens from '~/deploy_tokens';
// Initialize expandable settings panels
initSettingsPanels();
@ -34,6 +35,7 @@ document.querySelector('.js-toggle-extra-settings').addEventListener('click', (e
});
registrySettingsApp();
initDeployTokens();
initDeployFreeze();
initSettingsPipelinesTriggers();

View File

@ -1,5 +1,7 @@
import initRevokeButton from '~/deploy_tokens/init_revoke_button';
import initSearchSettings from '~/search_settings';
import initDeployTokens from '~/deploy_tokens';
initDeployTokens();
initSearchSettings();
initRevokeButton();

View File

@ -315,7 +315,6 @@ export default {
data-qa-selector="mr_widget_extension"
>
<state-container
:mr="mr"
:status="statusIconName"
:is-loading="isLoadingSummary"
:class="{ 'gl-cursor-pointer': isCollapsible }"

View File

@ -16,7 +16,8 @@ export default {
props: {
mr: {
type: Object,
required: true,
required: false,
default: null,
},
isLoading: {
type: Boolean,
@ -80,6 +81,7 @@ export default {
</div>
</div>
<div
v-if="mr"
class="gl-md-display-none gl-border-l-1 gl-border-l-solid gl-border-gray-100 gl-ml-3 gl-pl-3 gl-h-6 gl-mt-1"
>
<gl-button

View File

@ -1,9 +1,11 @@
- save_endpoint = local_assigns.fetch(:save_endpoint, nil)
- if ci_variable_protected_by_default?
%p.settings-message.text-center
- link_start = '<a href="%{url}">'.html_safe % { url: help_page_path('ci/variables/index', anchor: 'protected-cicd-variables') }
= s_('Environment variables are configured by your administrator to be %{link_start}protected%{link_end} by default.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
= render Pajamas::AlertComponent.new(variant: :warning, show_icon: false, dismissible: false,
alert_options: { class: 'gl-mb-3'}) do |c|
= c.body do
- link_start = '<a href="%{url}">'.html_safe % { url: help_page_path('ci/variables/index', anchor: 'protected-cicd-variables') }
= _('Environment variables are configured by your administrator to be %{link_start}protected%{link_end} by default.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe }
- is_group = !@group.nil?
- is_project = !@project.nil?

View File

@ -1,8 +1,7 @@
- display_issuable_type = issuable_display_type(@merge_request)
.float-left.btn-group.gl-md-ml-3.gl-display-flex.dropdown.gl-new-dropdown.gl-md-w-auto.gl-w-full
= button_tag type: 'button', class: "btn dropdown-toggle btn-default btn-md gl-button gl-dropdown-toggle btn-default-tertiary dropdown-icon-only dropdown-toggle-no-caret gl-display-none! gl-md-display-inline-flex!", data: { 'toggle' => 'dropdown' } do
%span.gl-sr-only= _('Toggle dropdown')
= button_tag type: 'button', class: "btn dropdown-toggle btn-default btn-md gl-button gl-dropdown-toggle btn-default-tertiary dropdown-icon-only dropdown-toggle-no-caret has-tooltip gl-display-none! gl-md-display-inline-flex!", data: { 'toggle' => 'dropdown' }, title: _('Merge request actions') , 'aria-label': _('Merge request actions') do
= sprite_icon "ellipsis_v", size: 16, css_class: "dropdown-icon gl-icon"
= button_tag type: 'button', class: "btn dropdown-toggle btn-default btn-md btn-block gl-button gl-dropdown-toggle gl-md-display-none!", data: { 'toggle' => 'dropdown' } do
%span.gl-new-dropdown-button-text= _('Merge request actions')

View File

@ -8,10 +8,20 @@
%p
= description
.settings-content
- if @created_deploy_token
= render 'shared/deploy_tokens/new_deploy_token', deploy_token: @created_deploy_token
%h5.gl-mt-0
= s_('DeployTokens|New deploy token')
= render 'shared/deploy_tokens/form', group_or_project: group_or_project, token: @new_deploy_token, presenter: @deploy_tokens
- if Feature.enabled?(:ajax_new_deploy_token, group_or_project)
#js-new-deploy-token{ data: {
container_registry_enabled: container_registry_enabled?(group_or_project),
packages_registry_enabled: packages_registry_enabled?(group_or_project),
create_new_token_path: create_deploy_token_path(group_or_project),
token_type: group_or_project.is_a?(Group) ? 'group' : 'project',
deploy_tokens_help_url: help_page_path('user/project/deploy_tokens/index.md')
}
}
- else
- if @created_deploy_token
= render 'shared/deploy_tokens/new_deploy_token', deploy_token: @created_deploy_token
%h5.gl-mt-0
= s_('DeployTokens|New deploy token')
= render 'shared/deploy_tokens/form', group_or_project: group_or_project, token: @new_deploy_token, presenter: @deploy_tokens
%hr
= render 'shared/deploy_tokens/table', group_or_project: group_or_project, active_tokens: @deploy_tokens

View File

@ -1,8 +0,0 @@
---
name: gitaly_simplify_find_local_branches_response
introduced_by_url: https://gitlab.com/gitlab-org/gitaly/-/merge_requests/4850
rollout_issue_url: https://gitlab.com/gitlab-org/gitaly/-/issues/4452
milestone: '15.4'
type: undefined
group: group::gitaly
default_enabled: false

View File

@ -24,6 +24,16 @@ module SawyerClassPatch
end
else
define_method attribute do
Gitlab::Import::Logger.warn(
Gitlab::ApplicationContext.current.merge(
{
message: 'Sawyer attribute called',
attribute: attribute,
caller: Gitlab::BacktraceCleaner.clean_backtrace(caller)
}
)
)
@attrs[attribute.to_sym]
end

View File

@ -8,35 +8,36 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Amazon Web Services Cognito **(FREE SELF)**
Amazon Cognito lets you add user sign-up, sign-in, and access control to your GitLab instance.
The following documentation enables Cognito as an OAuth2 provider.
The following documentation enables Cognito as an OAuth 2.0 provider.
## Configure AWS Cognito
To enable the [AWS Cognito](https://aws.amazon.com/cognito/) OAuth2 OmniAuth provider, register your application with Cognito. This process generates a Client ID and Client Secret for your application.
Any settings you configure in the following procedure can be modified later.
The following steps enable AWS Cognito as an authentication provider:
To enable the [AWS Cognito](https://aws.amazon.com/cognito/) OAuth 2.0 OmniAuth provider, register your application with Cognito. This process generates a Client ID and Client Secret for your application.
To enable AWS Cognito as an authentication provider, complete the following steps. You can modify any settings you configure later.
1. Sign in to the [AWS console](https://console.aws.amazon.com/console/home).
1. Select **Cognito** from the **Services** menu.
1. Select **Manage User Pools**, and select the **Create a user pool** button in the top right corner.
1. Enter the pool name and then select the **Step through settings** button.
1. From the **Services** menu, select **Cognito**.
1. Select **Manage User Pools** and then select **Create a user pool** in the top right corner.
1. Enter the user pool name and then select **Step through settings**.
1. Under **How do you want your end users to sign in?**, select **Email address or phone number** and **Allow email addresses**.
1. Under **Which standard attributes do you want to require?**, select **email**.
1. Go to the next steps of configuration and set the rest of the settings to suit your needs - in the basic setup they are not related to GitLab configuration.
1. In the **App clients** settings, select **Add an app client**, add **App client name** and select the **Enable username password based authentication** checkbox.
1. Configure the remaining settings to suit your needs. In the basic setup, these settings do not affect GitLab configuration.
1. In the **App clients** settings:
1. Select **Add an app client**.
1. Add the **App client name**.
1. Select the **Enable username password based authentication** checkbox.
1. Select **Create app client**.
1. In the next step, you can set up AWS Lambda functions for sending emails. You can then finish creating the pool.
1. Set up the AWS Lambda functions for sending emails and finish creating the user pool.
1. After creating the user pool, go to **App client settings** and provide the required information:
- **Enabled Identity Providers** - select all
- **Callback URL** - `https://gitlab.example.com/users/auth/cognito/callback`
- Substitute the URL of your GitLab instance for `gitlab.example.com`
- **Callback URL** - `https://<your_gitlab_instance_url>/users/auth/cognito/callback`
- **Allowed OAuth Flows** - Authorization code grant
- **Allowed OAuth2 Scopes** - `email`, `openid`, and `profile`
1. Save changes for the app client settings.
1. Under **Domain name** include the AWS domain name for your AWS Cognito application.
1. Under **App Clients**, find your app client ID and app client secret. These values correspond to the OAuth2 Client ID and Client Secret. Save these values.
1. Under **Domain name**, include the AWS domain name for your AWS Cognito application.
1. Under **App Clients**, find your app client ID. Select **Show details* to display the app client secret. These values correspond to the OAuth 2.0 Client ID and Client Secret. Save these values.
## Configure GitLab
@ -49,8 +50,13 @@ The following steps enable AWS Cognito as an authentication provider:
sudo editor /etc/gitlab/gitlab.rb
```
1. In the following code block, substitute the Client ID (`app_id`), Client Secret (`app_secret`), and the Amazon domain name (`site`) for your AWS Cognito application.
Include the code block in the `/etc/gitlab/gitlab.rb` file:
1. In the following code block, enter your AWS Cognito application information in the following parameters:
- `app_id`: Your client ID.
- `app_secret`: Your client secret.
- `site`: Your Amazon domain and region.
Include the code block in the `/etc/gitlab/gitlab.rb` file:
```ruby
gitlab_rails['omniauth_allow_single_sign_on'] = ['cognito']
@ -59,12 +65,12 @@ Include the code block in the `/etc/gitlab/gitlab.rb` file:
name: "cognito",
label: "Provider name", # optional label for login button, defaults to "Cognito"
icon: nil, # Optional icon URL
app_id: "CLIENT ID",
app_secret: "CLIENT SECRET",
app_id: "<client_id>",
app_secret: "<client_secret>",
args: {
scope: "openid profile email",
client_options: {
site: "https://your_domain.auth.your_region.amazoncognito.com",
site: "https://<your_domain>.auth.<your_region>.amazoncognito.com",
authorize_url: "/oauth2/authorize",
token_url: "/oauth2/token",
user_info_url: "/oauth2/userInfo"
@ -84,8 +90,9 @@ Include the code block in the `/etc/gitlab/gitlab.rb` file:
1. Save the configuration file.
1. Save the file and [reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab for the changes to take effect.
Your sign-in page should now display a Cognito button below the regular sign-in form.
To begin the authentication process, select the icon, and AWS Cognito asks the user to sign in and authorize the GitLab application.
If successful, the user is redirected and signed in to your GitLab instance.
Your sign-in page should now display a Cognito option below the regular sign-in form.
Select this option to begin the authentication process.
AWS Cognito then asks you to sign in and authorize the GitLab application.
If the authorization is successful, you're redirected and signed in to your GitLab instance.
For more information, see [Configure initial settings](../../integration/omniauth.md#configure-initial-settings).

View File

@ -229,12 +229,12 @@ To set the busy status indicator, either:
- Set it directly:
1. On the top bar, in the top-right corner, select your avatar.
1. Select **Set status** or, if you have already set a status, **Edit status**.
1. Select the **Busy** checkbox.
1. Select the **Set yourself as busy** checkbox.
- Set it on your profile:
1. On the top bar, in the top-right corner, select your avatar.
1. Select **Edit profile**.
1. In the **Current status** section, select the **Busy** checkbox.
1. In the **Current status** section, select the **Set yourself as busy** checkbox.
The busy status is displayed in the user interface.

View File

@ -1,27 +0,0 @@
# frozen_string_literal: true
module Banzai
module Filter
class PathologicalMarkdownFilter < HTML::Pipeline::TextFilter
# It's not necessary for this to be precise - we just need to detect
# when there are a non-trivial number of unclosed image links.
# So we don't really care about code blocks, etc.
# See https://gitlab.com/gitlab-org/gitlab/-/issues/370428
REGEX = /!\[(?:[^\]])+?!\[/.freeze
DETECTION_MAX = 10
def call
count = 0
@text.scan(REGEX) do |_match|
count += 1
break if count > DETECTION_MAX
end
return @text if count <= DETECTION_MAX
"_Unable to render markdown - too many unclosed markdown image links detected._"
end
end
end
end

View File

@ -5,7 +5,6 @@ module Banzai
class PlainMarkdownPipeline < BasePipeline
def self.filters
FilterArray[
Filter::PathologicalMarkdownFilter,
Filter::MarkdownPreEscapeFilter,
Filter::MarkdownFilter,
Filter::MarkdownPostEscapeFilter

View File

@ -270,15 +270,13 @@ module Gitlab
end
def consume_find_local_branches_response(response)
if Feature.enabled?(:gitaly_simplify_find_local_branches_response, type: :undefined)
response.flat_map do |message|
response.flat_map do |message|
if message.local_branches.present?
message.local_branches.map do |branch|
target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit)
Gitlab::Git::Branch.new(@repository, branch.name, branch.target_commit.id, target_commit)
end
end
else
response.flat_map do |message|
else
message.branches.map do |gitaly_branch|
Gitlab::Git::Branch.new(
@repository,

View File

@ -69,7 +69,7 @@ module Gitlab
#
# username - The username of the user.
def user(username)
with_rate_limit { octokit.user(username) }
with_rate_limit { octokit.user(username).to_h }
end
def pull_request_reviews(repo_name, iid)
@ -88,7 +88,7 @@ module Gitlab
end
def pull_request(repo_name, iid)
with_rate_limit { octokit.pull_request(repo_name, iid) }
with_rate_limit { octokit.pull_request(repo_name, iid).to_h }
end
def labels(*args)
@ -150,7 +150,7 @@ module Gitlab
each_page(method, *args) do |page|
page.objects.each do |object|
yield object
yield object.to_h
end
end
end

View File

@ -11,9 +11,9 @@ module Gitlab
def each_object_to_import
repo = project.import_source
protected_branches = client.branches(repo).select { |branch| branch.protection&.enabled }
protected_branches = client.branches(repo).select { |branch| branch.dig(:protection, :enabled) }
protected_branches.each do |protected_branch|
object = client.branch_protection(repo, protected_branch.name)
object = client.branch_protection(repo, protected_branch[:name])
next if object.nil? || already_imported?(object)
yield object
@ -44,7 +44,7 @@ module Gitlab
end
def id_for_already_imported_cache(protected_branch)
protected_branch.name
protected_branch[:name]
end
end
end

View File

@ -48,6 +48,8 @@ module Gitlab
def each_object_to_import(&block)
each_review_page do |page, merge_request|
page.objects.each do |review|
review = review.to_h
next if already_imported?(review)
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)

View File

@ -125,6 +125,8 @@ module Gitlab
next unless page_counter.set(page.number)
page.objects.each do |object|
object = object.to_h
next if already_imported?(object)
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)

View File

@ -19,7 +19,7 @@ module Gitlab
# Builds a diff note from a GitHub API response.
#
# note - An instance of `Sawyer::Resource` containing the note details.
# note - An instance of `Hash` containing the note details.
def self.from_api_response(note, additional_data = {})
matches = note[:html_url].match(NOTEABLE_ID_REGEX)

View File

@ -15,7 +15,7 @@ module Gitlab
# Builds an issue from a GitHub API response.
#
# issue - An instance of `Sawyer::Resource` containing the issue
# issue - An instance of `Hash` containing the issue
# details.
def self.from_api_response(issue, additional_data = {})
user =

View File

@ -34,7 +34,7 @@ module Gitlab
class << self
# Builds an event from a GitHub API response.
#
# event - An instance of `Sawyer::Resource` containing the event details.
# event - An instance of `Hash` containing the event details.
def from_api_response(event, additional_data = {})
new(
id: event[:id],

View File

@ -16,7 +16,7 @@ module Gitlab
# Builds a note from a GitHub API response.
#
# note - An instance of `Sawyer::Resource` containing the note details.
# note - An instance of `Hash` containing the note details.
def self.from_api_response(note, additional_data = {})
matches = note[:html_url].match(NOTEABLE_TYPE_REGEX)

View File

@ -16,12 +16,12 @@ module Gitlab
# https://docs.github.com/en/rest/branches/branch-protection#get-branch-protection
# branch_protection - An instance of `Sawyer::Resource` containing the protection details.
def self.from_api_response(branch_protection, _additional_object_data = {})
branch_name = branch_protection.url.match(%r{/branches/(\S{1,255})/protection$})[1]
branch_name = branch_protection[:url].match(%r{/branches/(\S{1,255})/protection$})[1]
hash = {
id: branch_name,
allow_force_pushes: branch_protection.allow_force_pushes.enabled,
required_conversation_resolution: branch_protection.required_conversation_resolution.enabled
allow_force_pushes: branch_protection.dig(:allow_force_pushes, :enabled),
required_conversation_resolution: branch_protection.dig(:required_conversation_resolution, :enabled)
}
new(hash)

View File

@ -17,7 +17,7 @@ module Gitlab
# Builds a PR from a GitHub API response.
#
# issue - An instance of `Sawyer::Resource` containing the PR details.
# issue - An instance of `Hash` containing the PR details.
def self.from_api_response(pr, additional_data = {})
assignee = Representation::User.from_api_response(pr[:assignee]) if pr[:assignee]
user = Representation::User.from_api_response(pr[:user]) if pr[:user]

View File

@ -13,7 +13,7 @@ module Gitlab
# Builds a PullRequestReview from a GitHub API response.
#
# review - An instance of `Sawyer::Resource` containing the note details.
# review - An instance of `Hash` containing the note details.
def self.from_api_response(review, additional_data = {})
user = Representation::User.from_api_response(review[:user]) if review[:user]

View File

@ -13,7 +13,7 @@ module Gitlab
# Builds a user from a GitHub API response.
#
# user - An instance of `Sawyer::Resource` containing the user details.
# user - An instance of `Hash` containing the user details.
def self.from_api_response(user, additional_data = {})
new(
id: user[:id],

View File

@ -39,7 +39,7 @@ module Gitlab
#
# If the object has no author ID we'll use the ID of the GitLab ghost
# user.
# object - An instance of `Sawyer::Resource` or a `Github::Representer`
# object - An instance of `Hash` or a `Github::Representer`
def author_id_for(object, author_key: :author)
user_info = case author_key
when :actor
@ -70,7 +70,7 @@ module Gitlab
# Returns the GitLab user ID for a GitHub user.
#
# user - An instance of `Gitlab::GithubImport::Representation::User` or `Sawyer::Resource`.
# user - An instance of `Gitlab::GithubImport::Representation::User` or `Hash`.
def user_id_for(user)
find(user[:id], user[:login]) if user.present?
end

View File

@ -13130,6 +13130,9 @@ msgstr ""
msgid "DeployTokens|Active Deploy Tokens (%{active_tokens})"
msgstr ""
msgid "DeployTokens|Allows read and write access to registry images."
msgstr ""
msgid "DeployTokens|Allows read and write access to the package registry."
msgstr ""
@ -13175,6 +13178,9 @@ msgstr ""
msgid "DeployTokens|Enter an expiration date for your token. Defaults to never expire."
msgstr ""
msgid "DeployTokens|Expiration date (optional)"
msgstr ""
msgid "DeployTokens|Expires"
msgstr ""
@ -13196,6 +13202,9 @@ msgstr ""
msgid "DeployTokens|Scopes"
msgstr ""
msgid "DeployTokens|Scopes (select at least one)"
msgstr ""
msgid "DeployTokens|This %{entity_type} has no active Deploy Tokens."
msgstr ""
@ -13211,6 +13220,12 @@ msgstr ""
msgid "DeployTokens|Username"
msgstr ""
msgid "DeployTokens|Username (optional)"
msgstr ""
msgid "DeployTokens|Your New Deploy Token"
msgstr ""
msgid "DeployTokens|Your new Deploy Token username"
msgstr ""
@ -41346,6 +41361,9 @@ msgstr ""
msgid "This will remove the fork relationship between this project and other projects in the fork network."
msgstr ""
msgid "Thread options"
msgstr ""
msgid "Thread to reply to cannot be found"
msgstr ""
@ -41944,9 +41962,6 @@ msgstr ""
msgid "Toggle commit list"
msgstr ""
msgid "Toggle dropdown"
msgstr ""
msgid "Toggle emoji award"
msgstr ""
@ -42211,9 +42226,6 @@ msgstr ""
msgid "Trials|Go back to GitLab"
msgstr ""
msgid "Trials|Hey there"
msgstr ""
msgid "Trials|Skip Trial"
msgstr ""
@ -42229,6 +42241,11 @@ msgstr ""
msgid "Trials|You won't get a free trial right now but you can always resume this process by selecting your avatar and choosing 'Start an Ultimate trial'"
msgstr ""
msgid "Trials|You've got %{daysRemaining} day remaining on GitLab %{planName}!"
msgid_plural "Trials|You've got %{daysRemaining} days remaining on GitLab %{planName}!"
msgstr[0] ""
msgstr[1] ""
msgid "Trials|Your trial ends on %{boldStart}%{trialEndDate}%{boldEnd}. We hope youre enjoying the features of GitLab %{planName}. To keep those features after your trial ends, youll need to buy a subscription. (You can also choose GitLab Premium if it meets your needs.)"
msgstr ""

View File

@ -2,14 +2,17 @@
require 'spec_helper'
RSpec.describe 'Group Repository settings' do
RSpec.describe 'Group Repository settings', :js do
include WaitForRequests
let(:user) { create(:user) }
let(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:group, reload: true) { create(:group) }
before_all do
group.add_owner(user)
end
before do
group.add_owner(user)
sign_in(user)
end
@ -20,9 +23,26 @@ RSpec.describe 'Group Repository settings' do
stub_container_registry_config(enabled: true)
end
it_behaves_like 'a deploy token in settings' do
let(:entity_type) { 'group' }
let(:page_path) { group_settings_repository_path(group) }
context 'when ajax deploy tokens is enabled' do
before do
stub_feature_flags(ajax_new_deploy_token: true)
end
it_behaves_like 'a deploy token in settings' do
let(:entity_type) { 'group' }
let(:page_path) { group_settings_repository_path(group) }
end
end
context 'when ajax deploy tokens is disabled' do
before do
stub_feature_flags(ajax_new_deploy_token: false)
end
it_behaves_like 'a deploy token in settings' do
let(:entity_type) { 'group' }
let(:page_path) { group_settings_repository_path(group) }
end
end
end

View File

@ -24,14 +24,14 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
context 'close/reopen/report toggle' do
it 'opens a dropdown when toggle is clicked' do
click_button 'Toggle dropdown'
click_button 'Merge request actions'
expect(container).to have_link("Close merge request")
expect(container).to have_link('Report abuse')
end
it 'links to Report Abuse' do
click_button 'Toggle dropdown'
click_button 'Merge request actions'
click_link 'Report abuse'
expect(page).to have_content('Report abuse to admin')
@ -42,7 +42,7 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
let(:issuable) { create(:merge_request, :opened, source_project: project) }
it 'shows the `Edit` and `Mark as draft` buttons' do
click_button 'Toggle dropdown'
click_button 'Merge request actions'
expect(container).to have_link('Edit')
expect(container).to have_link('Mark as draft')
@ -56,7 +56,7 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
let(:issuable) { create(:merge_request, :closed, source_project: project) }
it 'shows both the `Edit` and `Reopen` button' do
click_button 'Toggle dropdown'
click_button 'Merge request actions'
expect(container).to have_link('Edit')
expect(container).to have_link('Report abuse')
@ -68,7 +68,7 @@ RSpec.describe 'Issuables Close/Reopen/Report toggle' do
let(:issuable) { create(:merge_request, :closed, source_project: project, author: user) }
it 'shows both the `Edit` and `Reopen` button' do
click_button 'Toggle dropdown'
click_button 'Merge request actions'
expect(container).to have_link('Edit')
expect(container).to have_link('Reopen merge request')

View File

@ -90,7 +90,7 @@ RSpec.describe 'Merge Request Discussion Lock', :js do
end
it 'the user can lock the merge_request' do
click_button 'Toggle dropdown'
click_button 'Merge request actions'
expect(page).to have_content('Lock merge request')
end
@ -103,7 +103,7 @@ RSpec.describe 'Merge Request Discussion Lock', :js do
end
it 'the user can unlock the merge_request' do
click_button 'Toggle dropdown'
click_button 'Merge request actions'
expect(page).to have_content('Unlock merge request')
end

View File

@ -43,7 +43,7 @@ RSpec.describe 'User manages subscription', :js do
it 'toggles subscription' do
wait_for_requests
click_button 'Toggle dropdown'
click_button 'Merge request actions'
expect(page).to have_selector('.gl-toggle:not(.is-checked)')
find('[data-testid="notifications-toggle"] .gl-toggle').click

View File

@ -16,12 +16,12 @@ RSpec.describe 'Merge request > User marks merge request as draft', :js do
end
it 'toggles draft status' do
click_button 'Toggle dropdown'
click_button 'Merge request actions'
click_link 'Mark as draft'
expect(page).to have_content("Draft: #{merge_request.title}")
click_button 'Toggle dropdown'
click_button 'Merge request actions'
page.within('.detail-page-header-actions') do
click_link 'Mark as ready'

View File

@ -25,7 +25,7 @@ RSpec.describe 'Projects > Settings > Repository settings' do
context 'for maintainer' do
let(:role) { :maintainer }
context 'Deploy tokens' do
context 'Deploy tokens', :js do
let!(:deploy_token) { create(:deploy_token, projects: [project]) }
before do

View File

@ -0,0 +1,103 @@
import { shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { GlButton, GlFormCheckbox, GlFormInput, GlFormInputGroup, GlDatepicker } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import { TEST_HOST } from 'helpers/test_constants';
import NewDeployToken from '~/deploy_tokens/components/new_deploy_token.vue';
import waitForPromises from 'helpers/wait_for_promises';
const createNewTokenPath = `${TEST_HOST}/create`;
const deployTokensHelpUrl = `${TEST_HOST}/help`;
describe('New Deploy Token', () => {
let wrapper;
const factory = (options = {}) => {
const defaults = {
containerRegistryEnabled: true,
packagesRegistryEnabled: true,
tokenType: 'project',
};
const { containerRegistryEnabled, packagesRegistryEnabled, tokenType } = {
...defaults,
...options,
};
return shallowMount(NewDeployToken, {
propsData: {
deployTokensHelpUrl,
containerRegistryEnabled,
packagesRegistryEnabled,
createNewTokenPath,
tokenType,
},
});
};
afterEach(() => {
wrapper.destroy();
});
describe('without a container registry', () => {
beforeEach(() => {
wrapper = factory({ containerRegistryEnabled: false });
});
it('should not show the read registry scope', () => {
wrapper
.findAllComponents(GlFormCheckbox)
.wrappers.forEach((checkbox) => expect(checkbox.text()).not.toBe('read_registry'));
});
});
describe('with a container registry', () => {
beforeEach(() => {
wrapper = factory();
});
it('should show the read registry scope', () => {
const checkbox = wrapper.findAllComponents(GlFormCheckbox).at(1);
expect(checkbox.text()).toBe('read_registry');
});
it('should make a request to create a token on submit', () => {
const mockAxios = new MockAdapter(axios);
const date = new Date();
const formInputs = wrapper.findAllComponents(GlFormInput);
const name = formInputs.at(0);
const username = formInputs.at(2);
name.vm.$emit('input', 'test name');
username.vm.$emit('input', 'test username');
const datepicker = wrapper.findAllComponents(GlDatepicker).at(0);
datepicker.vm.$emit('input', date);
const [readRepo, readRegistry] = wrapper.findAllComponents(GlFormCheckbox).wrappers;
readRepo.vm.$emit('input', true);
readRegistry.vm.$emit('input', true);
mockAxios
.onPost(createNewTokenPath, {
deploy_token: {
name: 'test name',
expires_at: date.toISOString(),
username: 'test username',
read_repository: true,
read_registry: true,
},
})
.replyOnce(200, { username: 'test token username', token: 'test token' });
wrapper.findAllComponents(GlButton).at(0).vm.$emit('click');
return waitForPromises()
.then(() => nextTick())
.then(() => {
const [tokenUsername, tokenValue] = wrapper.findAllComponents(GlFormInputGroup).wrappers;
expect(tokenUsername.props('value')).toBe('test token username');
expect(tokenValue.props('value')).toBe('test token');
});
});
});
});

View File

@ -19,6 +19,7 @@ import {
expirationPolicyPayload,
emptyExpirationPolicyPayload,
containerExpirationPolicyData,
nullExpirationPolicyPayload,
} from '../mock_data';
describe('Cleanup image tags project settings', () => {
@ -98,15 +99,30 @@ describe('Cleanup image tags project settings', () => {
expect(findDescription().text()).toMatchInterpolatedText(CONTAINER_CLEANUP_POLICY_DESCRIPTION);
});
it('when loading does not render form or alert components', () => {
mountComponentWithApollo({
resolver: jest.fn().mockResolvedValue(),
});
expect(findFormComponent().exists()).toBe(false);
expect(findAlert().exists()).toBe(false);
});
describe('the form is disabled', () => {
it('hides the form', () => {
mountComponent();
it('hides the form', async () => {
mountComponentWithApollo({
resolver: jest.fn().mockResolvedValue(nullExpirationPolicyPayload()),
});
await waitForPromises();
expect(findFormComponent().exists()).toBe(false);
});
it('shows an alert', () => {
mountComponent();
it('shows an alert', async () => {
mountComponentWithApollo({
resolver: jest.fn().mockResolvedValue(nullExpirationPolicyPayload()),
});
await waitForPromises();
const text = findAlert().text();
expect(text).toContain(UNAVAILABLE_FEATURE_INTRO_TEXT);
@ -114,8 +130,12 @@ describe('Cleanup image tags project settings', () => {
});
describe('an admin is visiting the page', () => {
it('shows the admin part of the alert message', () => {
mountComponent({ ...defaultProvidedValues, isAdmin: true });
it('shows the admin part of the alert message', async () => {
mountComponentWithApollo({
provide: { ...defaultProvidedValues, isAdmin: true },
resolver: jest.fn().mockResolvedValue(nullExpirationPolicyPayload()),
});
await waitForPromises();
const sprintf = findAlert().findComponent(GlSprintf);
expect(sprintf.text()).toBe('administration settings');

View File

@ -16,7 +16,11 @@ import {
import expirationPolicyQuery from '~/packages_and_registries/settings/project/graphql/queries/get_expiration_policy.query.graphql';
import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
import { expirationPolicyPayload, emptyExpirationPolicyPayload } from '../mock_data';
import {
expirationPolicyPayload,
emptyExpirationPolicyPayload,
nullExpirationPolicyPayload,
} from '../mock_data';
describe('Container expiration policy project settings', () => {
let wrapper;
@ -78,15 +82,30 @@ describe('Container expiration policy project settings', () => {
expect(findButton().attributes('href')).toBe(defaultProvidedValues.cleanupSettingsPath);
});
it('when loading does not render form or alert components', () => {
mountComponentWithApollo({
resolver: jest.fn().mockResolvedValue(),
});
expect(findFormComponent().exists()).toBe(false);
expect(findAlert().exists()).toBe(false);
});
describe('the form is disabled', () => {
it('the form is hidden', () => {
mountComponent();
it('hides the form', async () => {
mountComponentWithApollo({
resolver: jest.fn().mockResolvedValue(nullExpirationPolicyPayload()),
});
await waitForPromises();
expect(findFormComponent().exists()).toBe(false);
});
it('shows an alert', () => {
mountComponent();
it('shows an alert', async () => {
mountComponentWithApollo({
resolver: jest.fn().mockResolvedValue(nullExpirationPolicyPayload()),
});
await waitForPromises();
const text = findAlert().text();
expect(text).toContain(UNAVAILABLE_FEATURE_INTRO_TEXT);
@ -94,8 +113,12 @@ describe('Container expiration policy project settings', () => {
});
describe('an admin is visiting the page', () => {
it('shows the admin part of the alert message', () => {
mountComponent({ ...defaultProvidedValues, isAdmin: true });
it('shows the admin part of the alert message', async () => {
mountComponentWithApollo({
provide: { ...defaultProvidedValues, isAdmin: true },
resolver: jest.fn().mockResolvedValue(nullExpirationPolicyPayload()),
});
await waitForPromises();
const sprintf = findAlert().findComponent(GlSprintf);
expect(sprintf.text()).toBe('administration settings');

View File

@ -29,6 +29,15 @@ export const emptyExpirationPolicyPayload = () => ({
},
});
export const nullExpirationPolicyPayload = () => ({
data: {
project: {
id: '1',
containerExpirationPolicy: null,
},
},
});
export const expirationPolicyMutationPayload = ({ override, errors = [] } = {}) => ({
data: {
updateContainerExpirationPolicy: {

View File

@ -1,5 +1,5 @@
# frozen_string_literal: true
require 'fast_spec_helper'
require 'spec_helper'
require 'sawyer'
require_relative '../../config/initializers/sawyer_patch'
@ -64,6 +64,28 @@ RSpec.describe 'sawyer_patch' do
expect(sawyer_resource.count_total).to eq(1)
expect(sawyer_resource.count_total?).to eq(true)
expect(sawyer_resource.count_total + 1).to eq(2)
sawyer_resource.count_total = 3
expect(sawyer_resource.count_total).to eq(3)
expect(sawyer_resource.user.name).to eq('User name')
end
it 'logs when a sawyer resource dynamic method is called' do
sawyer_resource = Sawyer::Resource.new(
Sawyer::Agent.new(''),
{
count_total: 1,
user: { name: 'User name' }
}
)
expected_attributes = []
allow(Gitlab::Import::Logger).to receive(:warn) do |params|
expected_attributes.push(params[:attribute])
end
sawyer_resource.count_total
sawyer_resource.user
sawyer_resource.user.name
expect(expected_attributes).to match_array(%i[count_total user user name])
end
end

View File

@ -1,27 +0,0 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Banzai::Filter::PathologicalMarkdownFilter do
include FilterSpecHelper
let_it_be(:short_text) { '![a' * 5 }
let_it_be(:long_text) { ([short_text] * 10).join(' ') }
let_it_be(:with_images_text) { "![One ![one](one.jpg) #{'and\n' * 200} ![two ![two](two.jpg)" }
it 'detects a significat number of unclosed image links' do
msg = <<~TEXT
_Unable to render markdown - too many unclosed markdown image links detected._
TEXT
expect(filter(long_text)).to eq(msg.strip)
end
it 'does nothing when there are only a few unclosed image links' do
expect(filter(short_text)).to eq(short_text)
end
it 'does nothing when there are only a few unclosed image links and images' do
expect(filter(with_images_text)).to eq(with_images_text)
end
end

View File

@ -168,15 +168,13 @@ RSpec.describe Banzai::Pipeline::FullPipeline do
end
end
describe 'unclosed image links' do
it 'detects a significat number of unclosed image links' do
markdown = '![a ' * 30
msg = <<~TEXT
Unable to render markdown - too many unclosed markdown image links detected.
TEXT
output = described_class.to_html(markdown, project: nil)
describe 'cmark-gfm and autlolinks' do
it 'does not hang with significant number of unclosed image links' do
markdown = '![a ' * 300000
expect(output).to include(msg.strip)
expect do
Timeout.timeout(2.seconds) { described_class.to_html(markdown, project: nil) }
end.not_to raise_error
end
end
end

View File

@ -11,7 +11,7 @@ RSpec.describe Gitlab::ConfigChecker::ExternalDatabaseChecker do
let(:new_database) { instance_double(Gitlab::Database::Reflection) }
before do
allow(Gitlab::Database::Reflection).to receive(:new).and_call_original
allow(Gitlab::Database::Reflection).to receive(:new).and_return(new_database)
allow(old_database).to receive(:postgresql_minimum_supported_version?).and_return(false)
allow(old_database).to receive(:version).and_return(old_database_version)
allow(new_database).to receive(:postgresql_minimum_supported_version?).and_return(true)

View File

@ -3,7 +3,8 @@
require 'spec_helper'
RSpec.describe Gitlab::GitalyClient::RefService do
let(:project) { create(:project, :repository) }
let_it_be(:project) { create(:project, :repository) }
let(:storage_name) { project.repository_storage }
let(:relative_path) { project.disk_path + '.git' }
let(:repository) { project.repository }
@ -179,13 +180,22 @@ RSpec.describe Gitlab::GitalyClient::RefService do
)
)
end
local_branches = target_commits.each_with_index.map do |gitaly_commit, i|
Gitaly::Branch.new(name: "#{remote_name}/#{i}", target_commit: gitaly_commit)
end
response = [
Gitaly::FindLocalBranchesResponse.new(branches: branches[0, 2], local_branches: local_branches[0, 2]),
Gitaly::FindLocalBranchesResponse.new(branches: branches[2, 2], local_branches: local_branches[2, 2])
]
response = if set_local_branches
[
Gitaly::FindLocalBranchesResponse.new(local_branches: local_branches[0, 2]),
Gitaly::FindLocalBranchesResponse.new(local_branches: local_branches[2, 2])
]
else
[
Gitaly::FindLocalBranchesResponse.new(branches: branches[0, 2]),
Gitaly::FindLocalBranchesResponse.new(branches: branches[2, 2])
]
end
expect_any_instance_of(Gitaly::RefService::Stub)
.to receive(:find_local_branches)
@ -220,18 +230,14 @@ RSpec.describe Gitlab::GitalyClient::RefService do
end
end
context 'when feature flag :gitaly_simplify_find_local_branches_response is enabled' do
before do
stub_feature_flags(gitaly_simplify_find_local_branches_response: true)
end
context 'when local_branches variable is not set' do
let(:set_local_branches) { false }
it_behaves_like 'common examples'
end
context 'when feature flag :gitaly_simplify_find_local_branches_response is disabled' do
before do
stub_feature_flags(gitaly_simplify_find_local_branches_response: false)
end
context 'when local_branches variable is set' do
let(:set_local_branches) { true }
it_behaves_like 'common examples'
end

View File

@ -152,6 +152,22 @@ RSpec.describe Gitlab::GithubImport::Client do
end
end
describe '#each_object' do
it 'converts each object into a hash' do
client = described_class.new('foo')
stub_request(:get, 'https://api.github.com/rate_limit')
.to_return(status: 200, headers: { 'X-RateLimit-Limit' => 5000, 'X-RateLimit-Remaining' => 5000 })
stub_request(:get, 'https://api.github.com/repos/foo/bar/releases?per_page=100')
.to_return(status: 200, body: [{ id: 1 }].to_json, headers: { 'Content-Type' => 'application/json' })
client.each_object(:releases, 'foo/bar') do |release|
expect(release).to eq({ id: 1 })
end
end
end
describe '#each_page' do
let(:client) { described_class.new('foo') }
let(:object1) { double(:object1) }

View File

@ -309,7 +309,7 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
describe '#each_object_to_import' do
let(:importer) { importer_class.new(project, client) }
let(:object) { double(:object) }
let(:object) { {} }
before do
expect(importer)

View File

@ -14,32 +14,32 @@ RSpec.shared_examples 'a deploy token in settings' do
end
end
it 'add a new deploy token' do
it 'add a new deploy token', :js do
visit page_path
fill_in 'deploy_token_name', with: 'new_deploy_key'
fill_in 'deploy_token_expires_at', with: (Date.today + 1.month).to_s
fill_in 'deploy_token_username', with: 'deployer'
check 'deploy_token_read_repository'
check 'deploy_token_read_registry'
fill_in _('Name'), with: 'new_deploy_key'
fill_in _('Expiration date (optional)'), with: (Date.today + 1.month).to_s
fill_in _('Username (optional)'), with: 'deployer'
check 'read_repository'
check 'read_registry'
click_button 'Create deploy token'
expect(page).to have_content("Your new #{entity_type} deploy token has been created")
within('.created-deploy-token-container') do
expect(page).to have_selector("input[name='deploy-token-user'][value='deployer']")
expect(page).to have_selector("input[name='deploy-token'][readonly='readonly']")
expect(find("input[name='deploy-token-user']").value).to eq("deployer")
expect(find("input[name='deploy-token'][readonly='readonly']")).to be_visible
end
expect(find("input#deploy_token_name").value).to eq nil
expect(find("input#deploy_token_name").value).to be_empty
expect(find("input#deploy_token_read_repository").checked?).to eq false
end
context "with form errors" do
context "with form errors", :js do
before do
visit page_path
fill_in "deploy_token_name", with: "new_deploy_key"
fill_in "deploy_token_username", with: "deployer"
fill_in _('Name'), with: "new_deploy_key"
fill_in _('Username (optional)'), with: "deployer"
click_button "Create deploy token"
end

View File

@ -7,7 +7,7 @@ require (
github.com/BurntSushi/toml v1.2.0
github.com/FZambia/sentinel v1.1.1
github.com/alecthomas/chroma/v2 v2.3.0
github.com/aws/aws-sdk-go v1.43.31
github.com/aws/aws-sdk-go v1.44.107
github.com/disintegration/imaging v1.6.2
github.com/getsentry/raven-go v0.2.0
github.com/golang-jwt/jwt/v4 v4.4.2

View File

@ -178,8 +178,9 @@ github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevB
github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.17.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.43.31 h1:yJZIr8nMV1hXjAvvOLUFqZRJcHV7udPQBfhJqawDzI0=
github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go v1.44.107 h1:VP7Rq3wzsOV7wrfHqjAAKRksD4We58PaoVSDPKhm8nw=
github.com/aws/aws-sdk-go v1.44.107/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go-v2 v1.16.2 h1:fqlCk6Iy3bnCumtrLz9r3mJ/2gUT0pJ0wLFVIdWh+JA=
github.com/aws/aws-sdk-go-v2 v1.16.2/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1 h1:SdK4Ppk5IzLs64ZMvr6MrSficMtjY2oS0WOORXTlxwU=