Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
356e3c444d
commit
bf293d4793
|
|
@ -542,7 +542,6 @@ That's all of the required database changes.
|
|||
|
||||
belongs_to :cool_widget, inverse_of: :cool_widget_state
|
||||
|
||||
validates :verification_failure, length: { maximum: 255 }
|
||||
validates :verification_state, :cool_widget, presence: true
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -509,7 +509,6 @@ That's all of the required database changes.
|
|||
|
||||
belongs_to :cool_widget, inverse_of: :cool_widget_state
|
||||
|
||||
validates :verification_failure, length: { maximum: 255 }
|
||||
validates :verification_state, :cool_widget, presence: true
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -133,15 +133,15 @@ export const METRIC_TOOLTIPS = {
|
|||
},
|
||||
[VULNERABILITY_METRICS.CRITICAL]: {
|
||||
description: s__('ValueStreamAnalytics|Critical vulnerabilities over time.'),
|
||||
groupLink: '-/security/vulnerabilities',
|
||||
projectLink: '-/security/vulnerability_report',
|
||||
docsLink: helpPagePath('user/application_security/vulnerability_report/index'),
|
||||
groupLink: '-/security/vulnerabilities?severity=CRITICAL',
|
||||
projectLink: '-/security/vulnerability_report?severity=CRITICAL',
|
||||
docsLink: helpPagePath('user/application_security/vulnerabilities/severities.html'),
|
||||
},
|
||||
[VULNERABILITY_METRICS.HIGH]: {
|
||||
description: s__('ValueStreamAnalytics|High vulnerabilities over time.'),
|
||||
groupLink: '-/security/vulnerabilities',
|
||||
projectLink: '-/security/vulnerability_report',
|
||||
docsLink: helpPagePath('user/application_security/vulnerability_report/index'),
|
||||
groupLink: '-/security/vulnerabilities?severity=HIGH',
|
||||
projectLink: '-/security/vulnerability_report?severity=HIGH',
|
||||
docsLink: helpPagePath('user/application_security/vulnerabilities/severities.html'),
|
||||
},
|
||||
[MERGE_REQUEST_METRICS.THROUGHPUT]: {
|
||||
description: s__('ValueStreamAnalytics|The number of merge requests merged by month.'),
|
||||
|
|
|
|||
|
|
@ -90,22 +90,6 @@ export default {
|
|||
ALERT_COLLAPSED_FILES,
|
||||
},
|
||||
props: {
|
||||
endpoint: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
endpointMetadata: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
endpointBatch: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
endpointDiffForPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
endpointCoverage: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
|
@ -116,15 +100,6 @@ export default {
|
|||
required: false,
|
||||
default: '',
|
||||
},
|
||||
endpointUpdateUser: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
projectPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
shouldShow: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
|
|
@ -144,51 +119,6 @@ export default {
|
|||
required: false,
|
||||
default: '',
|
||||
},
|
||||
isFluidLayout: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
dismissEndpoint: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
showSuggestPopover: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
fileByFileUserPreference: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
defaultSuggestionCommitMessage: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
rehydratedMrReviews: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => ({}),
|
||||
},
|
||||
sourceProjectDefaultUrl: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
sourceProjectFullPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
isForked: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
const treeWidth =
|
||||
|
|
@ -343,21 +273,6 @@ export default {
|
|||
renderFileTree: 'adjustView',
|
||||
},
|
||||
mounted() {
|
||||
this.setBaseConfig({
|
||||
endpoint: this.endpoint,
|
||||
endpointMetadata: this.endpointMetadata,
|
||||
endpointBatch: this.endpointBatch,
|
||||
endpointDiffForPath: this.endpointDiffForPath,
|
||||
endpointCoverage: this.endpointCoverage,
|
||||
endpointUpdateUser: this.endpointUpdateUser,
|
||||
projectPath: this.projectPath,
|
||||
dismissEndpoint: this.dismissEndpoint,
|
||||
showSuggestPopover: this.showSuggestPopover,
|
||||
viewDiffsFileByFile: this.fileByFileUserPreference || false,
|
||||
defaultSuggestionCommitMessage: this.defaultSuggestionCommitMessage,
|
||||
mrReviews: this.rehydratedMrReviews,
|
||||
});
|
||||
|
||||
if (this.endpointCodequality) {
|
||||
this.setCodequalityEndpoint(this.endpointCodequality);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@ import eventHub from '../notes/event_hub';
|
|||
import DiffsApp from './components/app.vue';
|
||||
|
||||
import { TREE_LIST_STORAGE_KEY, DIFF_WHITESPACE_COOKIE_NAME } from './constants';
|
||||
import { getReviewsForMergeRequest } from './utils/file_reviews';
|
||||
import { getDerivedMergeRequestInformation } from './utils/merge_request';
|
||||
|
||||
export default function initDiffsApp(store = notesStore) {
|
||||
const el = document.getElementById('js-diffs-app');
|
||||
|
|
@ -32,26 +30,13 @@ export default function initDiffsApp(store = notesStore) {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
endpoint: dataset.endpoint,
|
||||
endpointMetadata: dataset.endpointMetadata || '',
|
||||
endpointBatch: dataset.endpointBatch || '',
|
||||
endpointDiffForPath: dataset.endpointDiffForPath || '',
|
||||
endpointCoverage: dataset.endpointCoverage || '',
|
||||
endpointCodequality: dataset.endpointCodequality || '',
|
||||
endpointUpdateUser: dataset.updateCurrentUserPath,
|
||||
projectPath: dataset.projectPath,
|
||||
helpPagePath: dataset.helpPagePath,
|
||||
currentUser: JSON.parse(dataset.currentUserData) || {},
|
||||
changesEmptyStateIllustration: dataset.changesEmptyStateIllustration,
|
||||
isFluidLayout: parseBoolean(dataset.isFluidLayout),
|
||||
dismissEndpoint: dataset.dismissEndpoint,
|
||||
showSuggestPopover: parseBoolean(dataset.showSuggestPopover),
|
||||
showWhitespaceDefault: parseBoolean(dataset.showWhitespaceDefault),
|
||||
viewDiffsFileByFile: parseBoolean(dataset.fileByFileDefault),
|
||||
defaultSuggestionCommitMessage: dataset.defaultSuggestionCommitMessage,
|
||||
sourceProjectDefaultUrl: dataset.sourceProjectDefaultUrl,
|
||||
sourceProjectFullPath: dataset.sourceProjectFullPath,
|
||||
isForked: parseBoolean(dataset.isForked),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -90,31 +75,14 @@ export default function initDiffsApp(store = notesStore) {
|
|||
...mapActions('diffs', ['setRenderTreeList', 'setShowWhitespace']),
|
||||
},
|
||||
render(createElement) {
|
||||
const { mrPath } = getDerivedMergeRequestInformation({ endpoint: this.endpoint });
|
||||
|
||||
return createElement('diffs-app', {
|
||||
props: {
|
||||
endpoint: this.endpoint,
|
||||
endpointMetadata: this.endpointMetadata,
|
||||
endpointBatch: this.endpointBatch,
|
||||
endpointDiffForPath: this.endpointDiffForPath,
|
||||
endpointCoverage: this.endpointCoverage,
|
||||
endpointCodequality: this.endpointCodequality,
|
||||
endpointUpdateUser: this.endpointUpdateUser,
|
||||
currentUser: this.currentUser,
|
||||
projectPath: this.projectPath,
|
||||
helpPagePath: this.helpPagePath,
|
||||
shouldShow: this.activeTab === 'diffs',
|
||||
changesEmptyStateIllustration: this.changesEmptyStateIllustration,
|
||||
isFluidLayout: this.isFluidLayout,
|
||||
dismissEndpoint: this.dismissEndpoint,
|
||||
showSuggestPopover: this.showSuggestPopover,
|
||||
fileByFileUserPreference: this.viewDiffsFileByFile,
|
||||
defaultSuggestionCommitMessage: this.defaultSuggestionCommitMessage,
|
||||
rehydratedMrReviews: getReviewsForMergeRequest(mrPath),
|
||||
sourceProjectDefaultUrl: this.sourceProjectDefaultUrl,
|
||||
sourceProjectFullPath: this.sourceProjectFullPath,
|
||||
isForked: this.isForked,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export const DRAWER_Z_INDEX = 252;
|
|||
|
||||
export const MIN_USERNAME_LENGTH = 2;
|
||||
|
||||
export const BYTES_FORMAT_BYTES = 'Bytes';
|
||||
export const BYTES_FORMAT_BYTES = 'B';
|
||||
export const BYTES_FORMAT_KIB = 'KiB';
|
||||
export const BYTES_FORMAT_MIB = 'MiB';
|
||||
export const BYTES_FORMAT_GIB = 'GiB';
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ export function numberToHumanSize(size, digits = 2) {
|
|||
|
||||
switch (format) {
|
||||
case BYTES_FORMAT_BYTES:
|
||||
return sprintf(__('%{size} bytes'), { size: humanSize });
|
||||
return sprintf(__('%{size} B'), { size: humanSize });
|
||||
case BYTES_FORMAT_KIB:
|
||||
return sprintf(__('%{size} KiB'), { size: humanSize });
|
||||
case BYTES_FORMAT_MIB:
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import store from '~/mr_notes/stores';
|
||||
import mrNotes from '~/mr_notes/stores';
|
||||
import { getLocationHash } from '~/lib/utils/url_utility';
|
||||
import eventHub from '~/notes/event_hub';
|
||||
import { initReviewBar } from '~/batch_comments';
|
||||
import { initDiscussionCounter } from '~/mr_notes/discussion_counter';
|
||||
import { initOverviewTabCounter } from '~/mr_notes/init_count';
|
||||
import { getDerivedMergeRequestInformation } from '~/diffs/utils/merge_request';
|
||||
import { getReviewsForMergeRequest } from '~/diffs/utils/file_reviews';
|
||||
|
||||
function setupMrNotesState(notesDataset) {
|
||||
function setupMrNotesState(store, notesDataset, diffsDataset) {
|
||||
const noteableData = JSON.parse(notesDataset.noteableData);
|
||||
noteableData.noteableType = notesDataset.noteableType;
|
||||
noteableData.targetType = notesDataset.targetType;
|
||||
|
|
@ -15,26 +17,43 @@ function setupMrNotesState(notesDataset) {
|
|||
const currentUserData = JSON.parse(notesDataset.currentUserData);
|
||||
const endpoints = { metadata: notesDataset.endpointMetadata };
|
||||
|
||||
const { mrPath } = getDerivedMergeRequestInformation({ endpoint: diffsDataset.endpoint });
|
||||
|
||||
store.dispatch('setNotesData', notesData);
|
||||
store.dispatch('setNoteableData', noteableData);
|
||||
store.dispatch('setUserData', currentUserData);
|
||||
store.dispatch('setTargetNoteHash', getLocationHash());
|
||||
store.dispatch('setEndpoints', endpoints);
|
||||
store.dispatch('diffs/setBaseConfig', {
|
||||
endpoint: diffsDataset.endpoint,
|
||||
endpointMetadata: diffsDataset.endpointMetadata,
|
||||
endpointBatch: diffsDataset.endpointBatch,
|
||||
endpointDiffForPath: diffsDataset.endpointDiffForPath,
|
||||
endpointCoverage: diffsDataset.endpointCoverage,
|
||||
endpointUpdateUser: diffsDataset.updateCurrentUserPath,
|
||||
projectPath: diffsDataset.projectPath,
|
||||
dismissEndpoint: diffsDataset.dismissEndpoint,
|
||||
showSuggestPopover: parseBoolean(diffsDataset.showSuggestPopover),
|
||||
viewDiffsFileByFile: parseBoolean(diffsDataset.fileByFileDefault),
|
||||
defaultSuggestionCommitMessage: diffsDataset.defaultSuggestionCommitMessage,
|
||||
mrReviews: getReviewsForMergeRequest(mrPath),
|
||||
});
|
||||
}
|
||||
|
||||
export function initMrStateLazyLoad({ reviewBarParams } = {}) {
|
||||
export function initMrStateLazyLoad(store = mrNotes, { reviewBarParams } = {}) {
|
||||
store.dispatch('setActiveTab', window.mrTabs.getCurrentAction());
|
||||
window.mrTabs.eventHub.$on('MergeRequestTabChange', (value) =>
|
||||
store.dispatch('setActiveTab', value),
|
||||
);
|
||||
|
||||
const discussionsEl = document.getElementById('js-vue-mr-discussions');
|
||||
const notesDataset = discussionsEl.dataset;
|
||||
const diffsEl = document.getElementById('js-diffs-app');
|
||||
|
||||
let stop = () => {};
|
||||
stop = store.watch(
|
||||
(state) => state.page.activeTab,
|
||||
(activeTab) => {
|
||||
setupMrNotesState(notesDataset);
|
||||
setupMrNotesState(store, discussionsEl.dataset, diffsEl.dataset);
|
||||
|
||||
// prevent loading MR state on commits and pipelines pages
|
||||
// this is due to them having a shared controller with the Overview page
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export default function initMrNotes(lazyLoadParams) {
|
|||
action: mrShowNode.dataset.mrAction,
|
||||
});
|
||||
|
||||
initMrStateLazyLoad(lazyLoadParams);
|
||||
initMrStateLazyLoad(undefined, lazyLoadParams);
|
||||
|
||||
document.addEventListener('merged:UpdateActions', () => {
|
||||
initRevertCommitModal('i_code_review_post_merge_submit_revert_modal');
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ export const MISSING_MANIFEST_WARNING_TOOLTIP = s__(
|
|||
export const CREATED_AT = s__('ContainerRegistry|Created %{time}');
|
||||
|
||||
export const NOT_AVAILABLE_TEXT = __('Not applicable.');
|
||||
export const NOT_AVAILABLE_SIZE = __('0 bytes');
|
||||
export const NOT_AVAILABLE_SIZE = __('0 B');
|
||||
|
||||
export const CLEANUP_UNSCHEDULED_TEXT = s__('ContainerRegistry|Cleanup will run %{time}');
|
||||
export const CLEANUP_SCHEDULED_TEXT = s__('ContainerRegistry|Cleanup pending');
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export const DIGEST_LABEL = s__('HarborRegistry|Digest: %{imageId}');
|
|||
export const CREATED_AT_LABEL = s__('HarborRegistry|Published %{timeInfo}');
|
||||
|
||||
export const NOT_AVAILABLE_TEXT = __('Not applicable.');
|
||||
export const NOT_AVAILABLE_SIZE = __('0 bytes');
|
||||
export const NOT_AVAILABLE_SIZE = __('0 B');
|
||||
|
||||
export const TOKEN_TYPE_TAG_NAME = 'tag_name';
|
||||
|
||||
|
|
|
|||
|
|
@ -216,7 +216,11 @@ export default {
|
|||
>
|
||||
<template #toggle>
|
||||
<gl-button category="tertiary" icon="question-o" class="btn-with-notification">
|
||||
<span v-if="showWhatsNewNotification" class="notification-dot-info"></span>
|
||||
<span
|
||||
v-if="showWhatsNewNotification"
|
||||
data-testid="notification-dot"
|
||||
class="notification-dot-info"
|
||||
></span>
|
||||
{{ $options.i18n.help }}
|
||||
</gl-button>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -489,14 +489,12 @@
|
|||
padding: 0;
|
||||
|
||||
.issuable-context-form {
|
||||
$issue-sticky-header-height: 76px;
|
||||
|
||||
top: calc(#{$calc-application-header-height} + #{$issue-sticky-header-height});
|
||||
height: calc(#{$calc-application-viewport-height} - #{$issue-sticky-header-height} - var(--mr-review-bar-height) - $content-wrapper-padding);
|
||||
top: calc(#{$calc-application-header-height} + #{$mr-sticky-header-height});
|
||||
height: calc(#{$calc-application-viewport-height} - #{$mr-sticky-header-height} - var(--mr-review-bar-height));
|
||||
position: sticky;
|
||||
overflow: auto;
|
||||
padding: 0 15px;
|
||||
margin-bottom: calc((#{$header-height} + $issue-sticky-header-height) * -1);
|
||||
margin-bottom: calc((#{$content-wrapper-padding} * -1) + var(--mr-review-bar-height));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ class PersonalAccessToken < ApplicationRecord
|
|||
|
||||
validates :scopes, presence: true
|
||||
validate :validate_scopes
|
||||
validates :expires_at, presence: true, on: :create
|
||||
validate :expires_at_before_instance_max_expiry_date, on: :create
|
||||
|
||||
def revoke!
|
||||
|
|
@ -54,14 +55,6 @@ class PersonalAccessToken < ApplicationRecord
|
|||
!revoked? && !expired?
|
||||
end
|
||||
|
||||
# fall back to default value until background migration has updated all
|
||||
# existing PATs and we can add a validation
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/369123
|
||||
def expires_at=(value)
|
||||
datetime = value.presence || MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now
|
||||
super(datetime)
|
||||
end
|
||||
|
||||
override :simple_sorts
|
||||
def self.simple_sorts
|
||||
super.merge(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ module PersonalAccessTokens
|
|||
def execute
|
||||
return ServiceResponse.error(message: 'Not permitted to create') unless creation_permitted?
|
||||
|
||||
token = target_user.personal_access_tokens.create(params.slice(*allowed_params))
|
||||
token = target_user.personal_access_tokens.create(personal_access_token_params)
|
||||
|
||||
if token.persisted?
|
||||
log_event(token)
|
||||
|
|
@ -31,13 +31,17 @@ module PersonalAccessTokens
|
|||
|
||||
attr_reader :target_user, :ip_address
|
||||
|
||||
def allowed_params
|
||||
[
|
||||
:name,
|
||||
:impersonation,
|
||||
:scopes,
|
||||
:expires_at
|
||||
]
|
||||
def personal_access_token_params
|
||||
{
|
||||
name: params[:name],
|
||||
impersonation: params[:impersonation] || false,
|
||||
scopes: params[:scopes],
|
||||
expires_at: pat_expiration
|
||||
}
|
||||
end
|
||||
|
||||
def pat_expiration
|
||||
params[:expires_at].presence || PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now
|
||||
end
|
||||
|
||||
def creation_permitted?
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ module ResourceAccessTokens
|
|||
name: params[:name] || "#{resource_type}_bot",
|
||||
impersonation: false,
|
||||
scopes: params[:scopes] || default_scopes,
|
||||
expires_at: params[:expires_at] || nil
|
||||
expires_at: pat_expiration
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -106,10 +106,10 @@ module ResourceAccessTokens
|
|||
end
|
||||
|
||||
def create_membership(resource, user, access_level)
|
||||
resource.add_member(user, access_level, expires_at: default_pat_expiration)
|
||||
resource.add_member(user, access_level, expires_at: pat_expiration)
|
||||
end
|
||||
|
||||
def default_pat_expiration
|
||||
def pat_expiration
|
||||
params[:expires_at].presence || PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: adherence_report_ui
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/122374
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/414495
|
||||
milestone: '16.1'
|
||||
type: development
|
||||
group: group::compliance
|
||||
default_enabled: false
|
||||
|
|
@ -179,6 +179,7 @@ options:
|
|||
- p_ci_templates_katalon
|
||||
- p_ci_templates_terraform_module_base
|
||||
- p_ci_templates_terraform_module
|
||||
- p_ci_templates_pages_zola
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_zola_monthly
|
||||
description: Count of pipelines using the Zola Pages template
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: "16.1"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121946
|
||||
time_frame: 28d
|
||||
data_source: redis_hll
|
||||
data_category: optional
|
||||
instrumentation_class: RedisHLLMetric
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- p_ci_templates_pages_zola
|
||||
|
|
@ -180,6 +180,7 @@ options:
|
|||
- p_ci_templates_katalon
|
||||
- p_ci_templates_terraform_module_base
|
||||
- p_ci_templates_terraform_module
|
||||
- p_ci_templates_pages_zola
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
key_path: redis_hll_counters.ci_templates.p_ci_templates_pages_zola_weekly
|
||||
description: Count of pipelines using the Zola Pages template
|
||||
product_section: ''
|
||||
product_stage: ''
|
||||
product_group: ''
|
||||
value_type: number
|
||||
status: active
|
||||
milestone: "16.1"
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121946
|
||||
time_frame: 7d
|
||||
data_source: redis_hll
|
||||
data_category: optional
|
||||
instrumentation_class: RedisHLLMetric
|
||||
distribution:
|
||||
- ce
|
||||
- ee
|
||||
tier:
|
||||
- free
|
||||
- premium
|
||||
- ultimate
|
||||
options:
|
||||
events:
|
||||
- p_ci_templates_pages_zola
|
||||
|
|
@ -12,6 +12,7 @@ Gitlab::Seeder.quiet do
|
|||
}
|
||||
|
||||
user.personal_access_tokens.build(params).tap do |pat|
|
||||
pat.expires_at = 365.days.from_now
|
||||
pat.set_token(token)
|
||||
pat.save!
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddNotNullConstraintToPersonalAccessTokensExpiresAt < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_not_null_constraint :personal_access_tokens, :expires_at, validate: false
|
||||
end
|
||||
|
||||
def down
|
||||
remove_not_null_constraint :personal_access_tokens, :expires_at
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
d630b2bbfbb4ac030da8020745005bf7326b337ea9dbf4a57130e95d1824b780
|
||||
|
|
@ -26899,6 +26899,9 @@ ALTER TABLE users
|
|||
ALTER TABLE vulnerability_scanners
|
||||
ADD CONSTRAINT check_37608c9db5 CHECK ((char_length(vendor) <= 255)) NOT VALID;
|
||||
|
||||
ALTER TABLE personal_access_tokens
|
||||
ADD CONSTRAINT check_b8d60815eb CHECK ((expires_at IS NOT NULL)) NOT VALID;
|
||||
|
||||
ALTER TABLE sprints
|
||||
ADD CONSTRAINT check_ccd8a1eae0 CHECK ((start_date IS NOT NULL)) NOT VALID;
|
||||
|
||||
|
|
|
|||
|
|
@ -23733,7 +23733,7 @@ Represents a state transition of a vulnerability.
|
|||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| <a id="vulnerabilitystatetransitiontypeauthor"></a>`author` | [`UserCore!`](#usercore) | User who changed the state of the vulnerability. |
|
||||
| <a id="vulnerabilitystatetransitiontypeauthor"></a>`author` | [`UserCore`](#usercore) | User who changed the state of the vulnerability. |
|
||||
| <a id="vulnerabilitystatetransitiontypecomment"></a>`comment` | [`String`](#string) | Comment for the state change. |
|
||||
| <a id="vulnerabilitystatetransitiontypecreatedat"></a>`createdAt` | [`Time!`](#time) | Time of the state change of the vulnerability. |
|
||||
| <a id="vulnerabilitystatetransitiontypedismissalreason"></a>`dismissalReason` | [`VulnerabilityDismissalReason`](#vulnerabilitydismissalreason) | Reason for the dismissal. |
|
||||
|
|
|
|||
|
|
@ -342,7 +342,7 @@ In the example above:
|
|||
job log, but they are displayed in the raw job log. To see them, in the upper-right corner
|
||||
of the job log, select **Show complete raw** (**{doc-text}**).
|
||||
- `\r`: carriage return.
|
||||
- `\e[0K`: clear line ANSI escape code.
|
||||
- `\e[0K`: clear line ANSI escape sequence (`\e[K` does not work, the `0` must be included).
|
||||
|
||||
Sample raw job log:
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ Here is an example on how to use database helpers to create a new table and fore
|
|||
end
|
||||
|
||||
add_concurrent_partitioned_foreign_key(
|
||||
:p_ci_examples, :ci_builds,
|
||||
:p_ci_examples, :p_ci_builds,
|
||||
column: [:partition_id, :build_id],
|
||||
target_column: [:partition_id, :id],
|
||||
on_update: :cascade,
|
||||
|
|
@ -51,7 +51,7 @@ When creating the routing table:
|
|||
- The table name must start with the `p_` prefix. There are analyzers in place to ensure that all queries go
|
||||
through the routing tables and do not access the partitions directly.
|
||||
- Each new table needs a `partition_id` column and its value must equal
|
||||
the value from the related association. In this example, that is `ci_builds`. All resources
|
||||
the value from the related association. In this example, that is `p_ci_builds`. All resources
|
||||
belonging to a pipeline share the same `partition_id` value.
|
||||
- The primary key must have the columns ordered this way to allow efficient
|
||||
search only by `id`.
|
||||
|
|
@ -74,7 +74,7 @@ the application runs:
|
|||
def up
|
||||
with_lock_retries do
|
||||
connection.execute(<<~SQL)
|
||||
LOCK TABLE ci_builds IN SHARE UPDATE EXCLUSIVE MODE;
|
||||
LOCK TABLE p_ci_builds IN SHARE ROW EXCLUSIVE MODE;
|
||||
LOCK TABLE ONLY p_ci_examples IN ACCESS EXCLUSIVE MODE;
|
||||
SQL
|
||||
|
||||
|
|
@ -92,7 +92,7 @@ Partitions are created in `gitlab_partitions_dynamic` schema.
|
|||
When creating a partition, remember:
|
||||
|
||||
- Partition names do not use the `p_` prefix.
|
||||
- The default value for `partition_id` is `100`.
|
||||
- The starting value for `partition_id` is `100`.
|
||||
|
||||
## Cascade the partition value
|
||||
|
||||
|
|
|
|||
|
|
@ -856,7 +856,7 @@ Sometimes they are more precise and will be maintained more actively.
|
|||
|
||||
For each external link you add, weigh the customer benefit with the maintenance difficulties.
|
||||
|
||||
### Links requiring permissions
|
||||
### Links that require permissions
|
||||
|
||||
Don't link directly to:
|
||||
|
||||
|
|
@ -864,23 +864,26 @@ Don't link directly to:
|
|||
- Project features that require [special permissions](../../../user/permissions.md)
|
||||
to view.
|
||||
|
||||
These fail for:
|
||||
These links fail for:
|
||||
|
||||
- Those without sufficient permissions.
|
||||
- Automated link checkers.
|
||||
|
||||
Instead:
|
||||
If you must use one of these links:
|
||||
|
||||
- To reduce confusion, mention in the text that the information is either:
|
||||
- Contained in a confidential issue.
|
||||
- Requires special permission to a project to view.
|
||||
- Provide a link in back ticks (`` ` ``) so that those with access to the issue
|
||||
can navigate to it.
|
||||
- Mention that the information is confidential or requires specific permissions.
|
||||
- Put the link in backticks, so that it does not cause link checkers to fail.
|
||||
|
||||
Example:
|
||||
Examples:
|
||||
|
||||
```markdown
|
||||
For more information, see the [confidential issue](../../../user/project/issues/confidential_issues.md) `https://gitlab.com/gitlab-org/gitlab-foss/-/issues/<issue_number>`.
|
||||
GitLab team members can view more information in this confidential issue:
|
||||
`https://gitlab.com/gitlab-org/gitlab/-/issues/<issue_number>`
|
||||
```
|
||||
|
||||
```markdown
|
||||
Users with the Maintainer role for the project can use the pipeline editor:
|
||||
`https://gitlab.com/gitlab-org/gitlab/-/ci/editor`
|
||||
```
|
||||
|
||||
### Link to specific lines of code
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ To purge files from a GitLab repository:
|
|||
1. Clone a fresh copy of the repository from the bundle using `--bare` and `--mirror` options:
|
||||
|
||||
```shell
|
||||
git clone --bare /path/to/project.bundle
|
||||
git clone --bare --mirror /path/to/project.bundle
|
||||
```
|
||||
|
||||
1. Go to the `project.git` directory:
|
||||
|
|
@ -134,6 +134,12 @@ To purge files from a GitLab repository:
|
|||
Repeat this step and all following steps (including the [repository cleanup](#repository-cleanup) step)
|
||||
every time you run any `git filter-repo` command.
|
||||
|
||||
1. To allow you to force push the changes you need to unset the mirror flag:
|
||||
|
||||
```shell
|
||||
git config --unset remote.origin.mirror
|
||||
```
|
||||
|
||||
1. Force push your changes to overwrite all branches on GitLab:
|
||||
|
||||
```shell
|
||||
|
|
@ -167,8 +173,6 @@ To purge files from a GitLab repository:
|
|||
|
||||
## Repository cleanup
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/19376) in GitLab 11.6.
|
||||
|
||||
Repository cleanup allows you to upload a text file of objects and GitLab removes internal Git
|
||||
references to these objects. You can use
|
||||
[`git filter-repo`](https://github.com/newren/git-filter-repo) to produce a list of objects (in a
|
||||
|
|
@ -180,6 +184,10 @@ of the operation. This happens automatically, but submitting the cleanup request
|
|||
fails if any writes are ongoing, so cancel any outstanding `git push`
|
||||
operations before continuing.
|
||||
|
||||
WARNING:
|
||||
Removing internal Git references results in associated merge request commits, pipelines, and changes details
|
||||
no longer being available.
|
||||
|
||||
To clean up a repository:
|
||||
|
||||
1. Go to the project for the repository.
|
||||
|
|
|
|||
|
|
@ -92,17 +92,30 @@ module API
|
|||
success Entities::ResourceAccessTokenWithToken
|
||||
end
|
||||
params do
|
||||
requires :id, type: String, desc: "The #{source_type} ID", documentation: { example: 2 }
|
||||
requires :name, type: String, desc: "Resource access token name", documentation: { example: 'test' }
|
||||
requires :scopes, type: Array[String], values: ::Gitlab::Auth.resource_bot_scopes.map(&:to_s),
|
||||
desc: "The permissions of the token",
|
||||
documentation: { example: %w[api read_repository] }
|
||||
optional :access_level, type: Integer,
|
||||
values: ALLOWED_RESOURCE_ACCESS_LEVELS.values,
|
||||
default: Gitlab::Access::MAINTAINER,
|
||||
desc: "The access level of the token in the #{source_type}",
|
||||
documentation: { example: 40 }
|
||||
optional :expires_at, type: Date, desc: "The expiration date of the token", documentation: { example: '"2021-01-31' }
|
||||
requires :id,
|
||||
type: String,
|
||||
desc: "The #{source_type} ID",
|
||||
documentation: { example: 2 }
|
||||
requires :name,
|
||||
type: String,
|
||||
desc: "Resource access token name",
|
||||
documentation: { example: 'test' }
|
||||
requires :scopes,
|
||||
type: Array[String],
|
||||
values: ::Gitlab::Auth.resource_bot_scopes.map(&:to_s),
|
||||
desc: "The permissions of the token",
|
||||
documentation: { example: %w[api read_repository] }
|
||||
requires :expires_at,
|
||||
type: Date,
|
||||
desc: "The expiration date of the token",
|
||||
default: PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now,
|
||||
documentation: { example: '"2021-01-31' }
|
||||
optional :access_level,
|
||||
type: Integer,
|
||||
values: ALLOWED_RESOURCE_ACCESS_LEVELS.values,
|
||||
default: Gitlab::Access::MAINTAINER,
|
||||
desc: "The access level of the token in the #{source_type}",
|
||||
documentation: { example: 40 }
|
||||
end
|
||||
post ':id/access_tokens' do
|
||||
resource = find_source(source_type, params[:id])
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
code_quality:
|
||||
stage: test
|
||||
image: "cirrusci/flutter:1.22.5"
|
||||
image: "ghcr.io/cirruslabs/flutter:3.10.3"
|
||||
before_script:
|
||||
- pub global activate dart_code_metrics
|
||||
- flutter pub global activate dart_code_metrics
|
||||
- export PATH="$PATH:$HOME/.pub-cache/bin"
|
||||
script:
|
||||
- metrics lib -r codeclimate > gl-code-quality-report.json
|
||||
|
|
@ -20,9 +20,9 @@ code_quality:
|
|||
|
||||
test:
|
||||
stage: test
|
||||
image: "cirrusci/flutter:1.22.5"
|
||||
image: "ghcr.io/cirruslabs/flutter:3.10.3"
|
||||
before_script:
|
||||
- pub global activate junitreport
|
||||
- flutter pub global activate junitreport
|
||||
- export PATH="$PATH:$HOME/.pub-cache/bin"
|
||||
script:
|
||||
- flutter test --machine --coverage | tojunit -o report.xml
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
variables:
|
||||
DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.49.0'
|
||||
DAST_AUTO_DEPLOY_IMAGE_VERSION: 'v2.50.0'
|
||||
|
||||
.dast-auto-deploy:
|
||||
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${DAST_AUTO_DEPLOY_IMAGE_VERSION}"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
variables:
|
||||
AUTO_DEPLOY_IMAGE_VERSION: 'v2.49.0'
|
||||
AUTO_DEPLOY_IMAGE_VERSION: 'v2.50.0'
|
||||
|
||||
.auto-deploy:
|
||||
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
variables:
|
||||
AUTO_DEPLOY_IMAGE_VERSION: 'v2.49.0'
|
||||
AUTO_DEPLOY_IMAGE_VERSION: 'v2.50.0'
|
||||
|
||||
.auto-deploy:
|
||||
image: "${CI_TEMPLATE_REGISTRY_HOST}/gitlab-org/cluster-integration/auto-deploy-image:${AUTO_DEPLOY_IMAGE_VERSION}"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
# To contribute improvements to CI/CD templates, please follow the Development guide at:
|
||||
# https://docs.gitlab.com/ee/development/cicd/templates.html
|
||||
# This specific template is located at:
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Pages/Zola.gitlab-ci.yml
|
||||
|
||||
# Prefer to copy-paste this template instead of include it to ensure forward compatibility
|
||||
|
||||
---
|
||||
# From: https://www.getzola.org/documentation/deployment/gitlab-pages/
|
||||
# Source template is slightly modified to be self-contained
|
||||
|
||||
pages:
|
||||
image: alpine:latest
|
||||
variables:
|
||||
# This variable will ensure that the CI runner pulls in your theme from the submodule
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
before_script:
|
||||
# Install the zola package from the alpine community repositories
|
||||
- apk add --update-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/community/ zola
|
||||
script:
|
||||
# Execute zola build
|
||||
- zola build --base-url "$CI_PAGES_URL"
|
||||
artifacts:
|
||||
paths:
|
||||
# Path of our artifacts
|
||||
- public
|
||||
# This config will only publish changes that are pushed on the default branch
|
||||
rules:
|
||||
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
|
||||
environment: production
|
||||
|
|
@ -123,8 +123,10 @@ module Gitlab
|
|||
end
|
||||
|
||||
def tree_entries(repository, revision, path, recursive, skip_flat_paths, pagination_params)
|
||||
pagination_params ||= {}
|
||||
pagination_params[:limit] ||= TREE_ENTRIES_DEFAULT_LIMIT
|
||||
unless pagination_params.nil? && recursive
|
||||
pagination_params ||= {}
|
||||
pagination_params[:limit] ||= TREE_ENTRIES_DEFAULT_LIMIT
|
||||
end
|
||||
|
||||
request = Gitaly::GetTreeEntriesRequest.new(
|
||||
repository: @gitaly_repo,
|
||||
|
|
|
|||
|
|
@ -307,3 +307,5 @@
|
|||
aggregation: weekly
|
||||
- name: p_ci_templates_terraform_module
|
||||
aggregation: weekly
|
||||
- name: p_ci_templates_pages_zola
|
||||
aggregation: weekly
|
||||
|
|
|
|||
|
|
@ -1084,6 +1084,9 @@ msgid_plural "%{selectedProjectsCount} projects"
|
|||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "%{size} B"
|
||||
msgstr ""
|
||||
|
||||
msgid "%{size} GiB"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -1093,9 +1096,6 @@ msgstr ""
|
|||
msgid "%{size} MiB"
|
||||
msgstr ""
|
||||
|
||||
msgid "%{size} bytes"
|
||||
msgstr ""
|
||||
|
||||
msgid "%{sourceBranch} into %{targetBranch}"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -1538,7 +1538,7 @@ msgstr ""
|
|||
msgid "/day"
|
||||
msgstr ""
|
||||
|
||||
msgid "0 bytes"
|
||||
msgid "0 B"
|
||||
msgstr ""
|
||||
|
||||
msgid "1 Code quality finding"
|
||||
|
|
@ -11483,6 +11483,9 @@ msgstr ""
|
|||
msgid "Compliance Report|Frameworks"
|
||||
msgstr ""
|
||||
|
||||
msgid "Compliance Report|Standards Adherence"
|
||||
msgstr ""
|
||||
|
||||
msgid "Compliance Report|Violations"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
image: node:latest
|
||||
|
||||
stages:
|
||||
- install
|
||||
|
||||
install:
|
||||
stage: install
|
||||
script:
|
||||
- "npm config set @<%= registry_scope %>:registry <%= gitlab_address_with_port %>/api/v4/groups/<%= another_project.group.id %>/-/packages/npm/"
|
||||
- "npm install <%= package.name %>"
|
||||
cache:
|
||||
key: ${CI_COMMIT_REF_NAME}
|
||||
paths:
|
||||
- node_modules/
|
||||
artifacts:
|
||||
paths:
|
||||
- node_modules/
|
||||
only:
|
||||
- "<%= another_project.default_branch %>"
|
||||
tags:
|
||||
- "runner-for-<%= another_project.group.name %>"
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
image: node:latest
|
||||
|
||||
stages:
|
||||
- deploy
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
script:
|
||||
- echo "//${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/packages/npm/:_authToken=<%= auth_token %>">.npmrc
|
||||
- npm publish
|
||||
only:
|
||||
- "<%= project.default_branch %>"
|
||||
tags:
|
||||
- "runner-for-<%= project.group.name %>"
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"name": "<%= package.name %>",
|
||||
"version": "1.0.0",
|
||||
"description": "Example package for GitLab npm registry",
|
||||
"publishConfig": {
|
||||
"@<%= registry_scope %>:registry": "<%= gitlab_address_with_port %>/api/v4/projects/<%= project.id %>/packages/npm/"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Package' do
|
||||
describe 'Package Registry', :skip_live_env, :orchestrated, :packages, :object_storage,
|
||||
product_group: :package_registry do
|
||||
describe 'npm group level endpoint' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
include Runtime::Fixtures
|
||||
include Support::Helpers::MaskToken
|
||||
|
||||
let!(:registry_scope) { Runtime::Namespace.sandbox_name }
|
||||
let!(:personal_access_token) do
|
||||
Flow::Login.sign_in unless Page::Main::Menu.perform(&:signed_in?)
|
||||
|
||||
Resource::PersonalAccessToken.fabricate!.token
|
||||
end
|
||||
|
||||
let(:project_deploy_token) do
|
||||
Resource::ProjectDeployToken.fabricate_via_api! do |deploy_token|
|
||||
deploy_token.name = 'npm-deploy-token'
|
||||
deploy_token.project = project
|
||||
deploy_token.scopes = %w[
|
||||
read_repository
|
||||
read_package_registry
|
||||
write_package_registry
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
let(:uri) { URI.parse(Runtime::Scenario.gitlab_address) }
|
||||
let(:gitlab_address_with_port) { "#{uri.scheme}://#{uri.host}:#{uri.port}" }
|
||||
let(:gitlab_host_with_port) { "#{uri.host}:#{uri.port}" }
|
||||
|
||||
let!(:project) do
|
||||
Resource::Project.fabricate_via_api! do |project|
|
||||
project.name = 'npm-group-level-publish'
|
||||
end
|
||||
end
|
||||
|
||||
let!(:another_project) do
|
||||
Resource::Project.fabricate_via_api! do |another_project|
|
||||
another_project.name = 'npm-group-level-install'
|
||||
another_project.group = project.group
|
||||
end
|
||||
end
|
||||
|
||||
let!(:runner) do
|
||||
Resource::GroupRunner.fabricate! do |runner|
|
||||
runner.name = "qa-runner-#{Time.now.to_i}"
|
||||
runner.tags = ["runner-for-#{project.group.name}"]
|
||||
runner.executor = :docker
|
||||
runner.group = project.group
|
||||
end
|
||||
end
|
||||
|
||||
let(:package) do
|
||||
Resource::Package.init do |package|
|
||||
package.name = "@#{registry_scope}/#{project.name}-#{SecureRandom.hex(8)}"
|
||||
package.project = project
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
package.remove_via_api!
|
||||
runner.remove_via_api!
|
||||
project.remove_via_api!
|
||||
another_project.remove_via_api!
|
||||
end
|
||||
|
||||
where(:case_name, :authentication_token_type, :token_name, :testcase) do
|
||||
'using personal access token' | :personal_access_token | 'Personal Access Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/413760'
|
||||
'using ci job token' | :ci_job_token | 'CI Job Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/413761'
|
||||
'using project deploy token' | :project_deploy_token | 'Deploy Token' | 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/413762'
|
||||
end
|
||||
|
||||
with_them do
|
||||
let(:auth_token) do
|
||||
case authentication_token_type
|
||||
when :personal_access_token
|
||||
use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: project)
|
||||
use_ci_variable(name: 'PERSONAL_ACCESS_TOKEN', value: personal_access_token, project: another_project)
|
||||
when :ci_job_token
|
||||
'${CI_JOB_TOKEN}'
|
||||
when :project_deploy_token
|
||||
use_ci_variable(name: 'PROJECT_DEPLOY_TOKEN', value: project_deploy_token.token, project: project)
|
||||
use_ci_variable(name: 'PROJECT_DEPLOY_TOKEN', value: project_deploy_token.token, project: another_project)
|
||||
end
|
||||
end
|
||||
|
||||
it 'push and pull a npm package via CI', testcase: params[:testcase] do
|
||||
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
|
||||
npm_upload_yaml = ERB.new(read_fixture('package_managers/npm',
|
||||
'npm_upload_package_group.yaml.erb')).result(binding)
|
||||
package_json = ERB.new(read_fixture('package_managers/npm', 'package.json.erb')).result(binding)
|
||||
|
||||
Resource::Repository::Commit.fabricate_via_api! do |commit|
|
||||
commit.project = project
|
||||
commit.commit_message = 'Add files'
|
||||
commit.add_files([
|
||||
{
|
||||
file_path: '.gitlab-ci.yml',
|
||||
content: npm_upload_yaml
|
||||
},
|
||||
{
|
||||
file_path: 'package.json',
|
||||
content: package_json
|
||||
}
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
project.visit!
|
||||
Flow::Pipeline.visit_latest_pipeline
|
||||
|
||||
Page::Project::Pipeline::Show.perform do |pipeline|
|
||||
pipeline.click_job('deploy')
|
||||
end
|
||||
|
||||
Page::Project::Job::Show.perform do |job|
|
||||
expect(job).to be_successful(timeout: 800)
|
||||
end
|
||||
|
||||
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
|
||||
Resource::Repository::Commit.fabricate_via_api! do |commit|
|
||||
npm_install_yaml = ERB.new(read_fixture('package_managers/npm',
|
||||
'npm_install_package_group.yaml.erb')).result(binding)
|
||||
|
||||
commit.project = another_project
|
||||
commit.commit_message = 'Add .gitlab-ci.yml'
|
||||
commit.add_files([
|
||||
{
|
||||
file_path: '.gitlab-ci.yml',
|
||||
content: npm_install_yaml
|
||||
}
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
another_project.visit!
|
||||
Flow::Pipeline.visit_latest_pipeline
|
||||
|
||||
Page::Project::Pipeline::Show.perform do |pipeline|
|
||||
pipeline.click_job('install')
|
||||
end
|
||||
|
||||
Page::Project::Job::Show.perform do |job|
|
||||
expect(job).to be_successful(timeout: 800)
|
||||
job.click_browse_button
|
||||
end
|
||||
|
||||
Page::Project::Artifact::Show.perform do |artifacts|
|
||||
artifacts.go_to_directory('node_modules')
|
||||
artifacts.go_to_directory("@#{registry_scope}")
|
||||
expect(artifacts).to have_content(project.name.to_s)
|
||||
end
|
||||
|
||||
project.visit!
|
||||
Page::Project::Menu.perform(&:go_to_package_registry)
|
||||
|
||||
Page::Project::Packages::Index.perform do |index|
|
||||
expect(index).to have_package(package.name)
|
||||
|
||||
index.click_package(package.name)
|
||||
end
|
||||
|
||||
Page::Project::Packages::Show.perform do |show|
|
||||
expect(show).to have_package_info(package.name, "1.0.0")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -90,7 +90,7 @@ module QA
|
|||
it 'push and pull a npm package via CI', testcase: params[:testcase] do
|
||||
Support::Retrier.retry_on_exception(max_attempts: 3, sleep_interval: 2) do
|
||||
npm_upload_yaml = ERB.new(read_fixture('package_managers/npm', 'npm_upload_package_instance.yaml.erb')).result(binding)
|
||||
package_json = ERB.new(read_fixture('package_managers/npm', 'package_instance.json.erb')).result(binding)
|
||||
package_json = ERB.new(read_fixture('package_managers/npm', 'package.json.erb')).result(binding)
|
||||
|
||||
Resource::Repository::Commit.fabricate_via_api! do |commit|
|
||||
commit.project = project
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ module QA
|
|||
it 'push and pull a npm package via CI', testcase: params[:testcase] do
|
||||
Resource::Repository::Commit.fabricate_via_api! do |commit|
|
||||
npm_upload_install_yaml = ERB.new(read_fixture('package_managers/npm', 'npm_upload_install_package_project.yaml.erb')).result(binding)
|
||||
package_json = ERB.new(read_fixture('package_managers/npm', 'package_project.json.erb')).result(binding)
|
||||
package_json = ERB.new(read_fixture('package_managers/npm', 'package.json.erb')).result(binding)
|
||||
|
||||
commit.project = project
|
||||
commit.commit_message = 'Add .gitlab-ci.yml'
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ FactoryBot.define do
|
|||
user
|
||||
sequence(:name) { |n| "PAT #{n}" }
|
||||
revoked { false }
|
||||
expires_at { 5.days.from_now }
|
||||
expires_at { 30.days.from_now }
|
||||
scopes { ['api'] }
|
||||
impersonation { false }
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ describe('diffs/components/app', () => {
|
|||
let wrapper;
|
||||
let mock;
|
||||
|
||||
function createComponent(props = {}, extendStore = () => {}, provisions = {}) {
|
||||
function createComponent(props = {}, extendStore = () => {}, provisions = {}, baseConfig = {}) {
|
||||
const provide = {
|
||||
...provisions,
|
||||
glFeatures: {
|
||||
|
|
@ -57,20 +57,24 @@ describe('diffs/components/app', () => {
|
|||
|
||||
extendStore(store);
|
||||
|
||||
store.dispatch('diffs/setBaseConfig', {
|
||||
endpoint: TEST_ENDPOINT,
|
||||
endpointMetadata: `${TEST_HOST}/diff/endpointMetadata`,
|
||||
endpointBatch: `${TEST_HOST}/diff/endpointBatch`,
|
||||
endpointDiffForPath: TEST_ENDPOINT,
|
||||
projectPath: 'namespace/project',
|
||||
dismissEndpoint: '',
|
||||
showSuggestPopover: true,
|
||||
mrReviews: {},
|
||||
...baseConfig,
|
||||
});
|
||||
|
||||
wrapper = shallowMount(App, {
|
||||
propsData: {
|
||||
endpoint: TEST_ENDPOINT,
|
||||
endpointMetadata: `${TEST_HOST}/diff/endpointMetadata`,
|
||||
endpointBatch: `${TEST_HOST}/diff/endpointBatch`,
|
||||
endpointDiffForPath: TEST_ENDPOINT,
|
||||
endpointCoverage: `${TEST_HOST}/diff/endpointCoverage`,
|
||||
endpointCodequality: '',
|
||||
projectPath: 'namespace/project',
|
||||
currentUser: {},
|
||||
changesEmptyStateIllustration: '',
|
||||
dismissEndpoint: '',
|
||||
showSuggestPopover: true,
|
||||
fileByFileUserPreference: false,
|
||||
...props,
|
||||
},
|
||||
provide,
|
||||
|
|
@ -653,13 +657,18 @@ describe('diffs/components/app', () => {
|
|||
|
||||
describe('file-by-file', () => {
|
||||
it('renders a single diff', async () => {
|
||||
createComponent({ fileByFileUserPreference: true }, ({ state }) => {
|
||||
state.diffs.treeEntries = {
|
||||
123: { type: 'blob', fileHash: '123' },
|
||||
312: { type: 'blob', fileHash: '312' },
|
||||
};
|
||||
state.diffs.diffFiles.push({ file_hash: '312' });
|
||||
});
|
||||
createComponent(
|
||||
undefined,
|
||||
({ state }) => {
|
||||
state.diffs.treeEntries = {
|
||||
123: { type: 'blob', fileHash: '123' },
|
||||
312: { type: 'blob', fileHash: '312' },
|
||||
};
|
||||
state.diffs.diffFiles.push({ file_hash: '312' });
|
||||
},
|
||||
undefined,
|
||||
{ viewDiffsFileByFile: true },
|
||||
);
|
||||
|
||||
await nextTick();
|
||||
|
||||
|
|
@ -671,12 +680,17 @@ describe('diffs/components/app', () => {
|
|||
const paginator = () => fileByFileNav().findComponent(GlPagination);
|
||||
|
||||
it('sets previous button as disabled', async () => {
|
||||
createComponent({ fileByFileUserPreference: true }, ({ state }) => {
|
||||
state.diffs.treeEntries = {
|
||||
123: { type: 'blob', fileHash: '123' },
|
||||
312: { type: 'blob', fileHash: '312' },
|
||||
};
|
||||
});
|
||||
createComponent(
|
||||
undefined,
|
||||
({ state }) => {
|
||||
state.diffs.treeEntries = {
|
||||
123: { type: 'blob', fileHash: '123' },
|
||||
312: { type: 'blob', fileHash: '312' },
|
||||
};
|
||||
},
|
||||
undefined,
|
||||
{ viewDiffsFileByFile: true },
|
||||
);
|
||||
|
||||
await nextTick();
|
||||
|
||||
|
|
@ -685,13 +699,18 @@ describe('diffs/components/app', () => {
|
|||
});
|
||||
|
||||
it('sets next button as disabled', async () => {
|
||||
createComponent({ fileByFileUserPreference: true }, ({ state }) => {
|
||||
state.diffs.treeEntries = {
|
||||
123: { type: 'blob', fileHash: '123' },
|
||||
312: { type: 'blob', fileHash: '312' },
|
||||
};
|
||||
state.diffs.currentDiffFileId = '312';
|
||||
});
|
||||
createComponent(
|
||||
undefined,
|
||||
({ state }) => {
|
||||
state.diffs.treeEntries = {
|
||||
123: { type: 'blob', fileHash: '123' },
|
||||
312: { type: 'blob', fileHash: '312' },
|
||||
};
|
||||
state.diffs.currentDiffFileId = '312';
|
||||
},
|
||||
undefined,
|
||||
{ viewDiffsFileByFile: true },
|
||||
);
|
||||
|
||||
await nextTick();
|
||||
|
||||
|
|
@ -700,10 +719,15 @@ describe('diffs/components/app', () => {
|
|||
});
|
||||
|
||||
it("doesn't display when there's fewer than 2 files", async () => {
|
||||
createComponent({ fileByFileUserPreference: true }, ({ state }) => {
|
||||
state.diffs.treeEntries = { 123: { type: 'blob', fileHash: '123' } };
|
||||
state.diffs.currentDiffFileId = '123';
|
||||
});
|
||||
createComponent(
|
||||
undefined,
|
||||
({ state }) => {
|
||||
state.diffs.treeEntries = { 123: { type: 'blob', fileHash: '123' } };
|
||||
state.diffs.currentDiffFileId = '123';
|
||||
},
|
||||
undefined,
|
||||
{ viewDiffsFileByFile: true },
|
||||
);
|
||||
|
||||
await nextTick();
|
||||
|
||||
|
|
@ -717,13 +741,18 @@ describe('diffs/components/app', () => {
|
|||
`(
|
||||
'calls navigateToDiffFileIndex with $index when $link is clicked',
|
||||
async ({ currentDiffFileId, targetFile }) => {
|
||||
createComponent({ fileByFileUserPreference: true }, ({ state }) => {
|
||||
state.diffs.treeEntries = {
|
||||
123: { type: 'blob', fileHash: '123', filePaths: { old: '1234', new: '123' } },
|
||||
312: { type: 'blob', fileHash: '312', filePaths: { old: '3124', new: '312' } },
|
||||
};
|
||||
state.diffs.currentDiffFileId = currentDiffFileId;
|
||||
});
|
||||
createComponent(
|
||||
undefined,
|
||||
({ state }) => {
|
||||
state.diffs.treeEntries = {
|
||||
123: { type: 'blob', fileHash: '123', filePaths: { old: '1234', new: '123' } },
|
||||
312: { type: 'blob', fileHash: '312', filePaths: { old: '3124', new: '312' } },
|
||||
};
|
||||
state.diffs.currentDiffFileId = currentDiffFileId;
|
||||
},
|
||||
undefined,
|
||||
{ viewDiffsFileByFile: true },
|
||||
);
|
||||
|
||||
await nextTick();
|
||||
|
||||
|
|
|
|||
|
|
@ -109,8 +109,8 @@ describe('Number Utils', () => {
|
|||
|
||||
describe('numberToHumanSize', () => {
|
||||
it('should return bytes', () => {
|
||||
expect(numberToHumanSize(654)).toEqual('654 bytes');
|
||||
expect(numberToHumanSize(-654)).toEqual('-654 bytes');
|
||||
expect(numberToHumanSize(654)).toEqual('654 B');
|
||||
expect(numberToHumanSize(-654)).toEqual('-654 B');
|
||||
});
|
||||
|
||||
it('should return KiB', () => {
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ describe('Harbor artifact list row', () => {
|
|||
},
|
||||
});
|
||||
|
||||
expect(findByTestId('size').text()).toBe('0 bytes');
|
||||
expect(findByTestId('size').text()).toBe('0 B');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ describe('PackageTitle', () => {
|
|||
it('correctly calculates the size', async () => {
|
||||
await createComponent();
|
||||
|
||||
expect(packageSize().props('text')).toBe('300 bytes');
|
||||
expect(packageSize().props('text')).toBe('300 B');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ describe('HelpCenter component', () => {
|
|||
};
|
||||
const withinComponent = () => within(wrapper.element);
|
||||
const findButton = (name) => withinComponent().getByRole('button', { name });
|
||||
const findNotificationDot = () => wrapper.findByTestId('notification-dot');
|
||||
|
||||
// eslint-disable-next-line no-shadow
|
||||
const createWrapper = (sidebarData) => {
|
||||
|
|
@ -203,8 +204,8 @@ describe('HelpCenter component', () => {
|
|||
createWrapper({ ...sidebarData, display_whats_new: false });
|
||||
});
|
||||
|
||||
it('is false', () => {
|
||||
expect(wrapper.vm.showWhatsNewNotification).toBe(false);
|
||||
it('does not render notification dot', () => {
|
||||
expect(findNotificationDot().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -215,8 +216,8 @@ describe('HelpCenter component', () => {
|
|||
createWrapper({ ...sidebarData, display_whats_new: true });
|
||||
});
|
||||
|
||||
it('is true', () => {
|
||||
expect(wrapper.vm.showWhatsNewNotification).toBe(true);
|
||||
it('renders notification dot', () => {
|
||||
expect(findNotificationDot().exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe('when "What\'s new" drawer got opened', () => {
|
||||
|
|
@ -224,8 +225,8 @@ describe('HelpCenter component', () => {
|
|||
findButton("What's new 5").click();
|
||||
});
|
||||
|
||||
it('is false', () => {
|
||||
expect(wrapper.vm.showWhatsNewNotification).toBe(false);
|
||||
it('does not render notification dot', () => {
|
||||
expect(findNotificationDot().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -235,8 +236,8 @@ describe('HelpCenter component', () => {
|
|||
createWrapper({ ...sidebarData, display_whats_new: true });
|
||||
});
|
||||
|
||||
it('is false', () => {
|
||||
expect(wrapper.vm.showWhatsNewNotification).toBe(false);
|
||||
it('does not render notification dot', () => {
|
||||
expect(findNotificationDot().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import setWindowLocation from 'helpers/set_window_location_helper';
|
|||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import { stubPerformanceWebAPI } from 'helpers/performance';
|
||||
import initDiffsApp from '~/diffs';
|
||||
import { initMrStateLazyLoad } from '~/mr_notes/init';
|
||||
import { createStore } from '~/mr_notes/stores';
|
||||
import {
|
||||
getDiffCodePart,
|
||||
|
|
@ -53,23 +54,35 @@ const startDiffsApp = () => {
|
|||
endpointBatch: `${TEST_BASE_URL}diffs_batch.json`,
|
||||
projectPath: TEST_PROJECT_PATH,
|
||||
helpPagePath: '/help',
|
||||
currentUserData: 'null',
|
||||
currentUserData: '{}',
|
||||
changesEmptyStateIllustration: '',
|
||||
isFluidLayout: 'false',
|
||||
dismissEndpoint: '',
|
||||
showSuggestPopover: 'false',
|
||||
showWhitespaceDefault: 'true',
|
||||
viewDiffsFileByFile: 'false',
|
||||
fileByFileDefault: 'false',
|
||||
defaultSuggestionCommitMessage: 'Lorem ipsum',
|
||||
});
|
||||
|
||||
const notesEl = document.createElement('div');
|
||||
notesEl.id = 'js-vue-mr-discussions';
|
||||
document.body.appendChild(notesEl);
|
||||
Object.assign(notesEl.dataset, {
|
||||
noteableData: '{ "current_user": {} }',
|
||||
notesData: '{}',
|
||||
currentUserData: '{}',
|
||||
});
|
||||
|
||||
window.mrTabs = {
|
||||
getCurrentAction: () => 'diffs',
|
||||
eventHub: {
|
||||
$on() {},
|
||||
},
|
||||
};
|
||||
const store = createStore();
|
||||
initMrStateLazyLoad(store);
|
||||
|
||||
const vm = initDiffsApp(store);
|
||||
|
||||
store.dispatch('setActiveTab', 'diffs');
|
||||
|
||||
return vm;
|
||||
return initDiffsApp(store);
|
||||
};
|
||||
|
||||
describe('diffs third party interoperability', () => {
|
||||
|
|
@ -117,7 +130,7 @@ describe('diffs third party interoperability', () => {
|
|||
${'parallel view right side'} | ${'parallel'} | ${'.diff-tr.line_holder'} | ${'.diff-td.line_content.right-side'} | ${EXPECT_PARALLEL_RIGHT_SIDE}
|
||||
`('$desc', ({ view, rowSelector, codeSelector, expectation }) => {
|
||||
beforeEach(async () => {
|
||||
setWindowLocation(`${TEST_HOST}/${TEST_BASE_URL}/diffs?view=${view}`);
|
||||
setWindowLocation(`${TEST_HOST}${TEST_BASE_URL}diffs?view=${view}`);
|
||||
|
||||
vm = startDiffsApp();
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ require 'spec_helper'
|
|||
RSpec.describe API::Entities::PersonalAccessToken do
|
||||
describe '#as_json' do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:token) { create(:personal_access_token, user: user, expires_at: nil) }
|
||||
let_it_be(:token) { create(:personal_access_token, user: user) }
|
||||
|
||||
let(:entity) { described_class.new(token) }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Pages/Zola.gitlab-ci.yml', feature_category: :pages do
|
||||
subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Pages/Zola') }
|
||||
|
||||
describe 'the created pipeline' do
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
|
||||
let(:user) { project.first_owner }
|
||||
let(:service) { Ci::CreatePipelineService.new(project, user, ref: project.default_branch) }
|
||||
let(:pipeline) { service.execute(:push).payload }
|
||||
let(:build_names) { pipeline.builds.pluck(:name) }
|
||||
|
||||
before do
|
||||
stub_ci_pipeline_yaml_file(template.content)
|
||||
allow(Ci::BuildScheduleWorker).to receive(:perform).and_return(true)
|
||||
end
|
||||
|
||||
it 'creates "pages" job' do
|
||||
expect(build_names).to include('pages')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -208,6 +208,19 @@ RSpec.describe Gitlab::GitalyClient::CommitService, feature_category: :gitaly do
|
|||
is_expected.to eq([[], nil])
|
||||
end
|
||||
|
||||
context 'when recursive is "true"' do
|
||||
let(:recursive) { true }
|
||||
|
||||
it 'sends a get_tree_entries message without the limit' do
|
||||
expect_any_instance_of(Gitaly::CommitService::Stub)
|
||||
.to receive(:get_tree_entries)
|
||||
.with(gitaly_request_with_params({ pagination_params: nil }), kind_of(Hash))
|
||||
.and_return([])
|
||||
|
||||
is_expected.to eq([[], nil])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with UTF-8 params strings' do
|
||||
let(:revision) { "branch\u011F" }
|
||||
let(:path) { "foo/\u011F.txt" }
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ RSpec.describe PersonalAccessToken, 'TokenAuthenticatable' do
|
|||
let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) }
|
||||
let(:user) { create(:user) }
|
||||
let(:personal_access_token) do
|
||||
described_class.new(name: 'test-pat-01', user_id: user.id, scopes: [:api], token_digest: token_digest)
|
||||
described_class.new(name: 'test-pat-01', user_id: user.id, scopes: [:api], token_digest: token_digest, expires_at: 30.days.from_now)
|
||||
end
|
||||
|
||||
before do
|
||||
|
|
|
|||
|
|
@ -259,6 +259,13 @@ RSpec.describe PersonalAccessToken, feature_category: :system_access do
|
|||
context 'validates expires_at' do
|
||||
let(:max_expiration_date) { described_class::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now }
|
||||
|
||||
it "can't be blank" do
|
||||
personal_access_token.expires_at = nil
|
||||
|
||||
expect(personal_access_token).not_to be_valid
|
||||
expect(personal_access_token.errors[:expires_at].first).to eq("can't be blank")
|
||||
end
|
||||
|
||||
context 'when expires_in is less than MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS days' do
|
||||
it 'is valid' do
|
||||
personal_access_token.expires_at = max_expiration_date - 1.day
|
||||
|
|
@ -285,11 +292,10 @@ RSpec.describe PersonalAccessToken, feature_category: :system_access do
|
|||
let_it_be(:not_revoked_nil_token) { create(:personal_access_token, revoked: nil) }
|
||||
let_it_be(:expired_token) { create(:personal_access_token, :expired) }
|
||||
let_it_be(:not_expired_token) { create(:personal_access_token) }
|
||||
let_it_be(:never_expires_token) { create(:personal_access_token, expires_at: nil) }
|
||||
|
||||
it 'includes non-revoked and non-expired tokens' do
|
||||
it 'includes non-revoked tokens' do
|
||||
expect(described_class.active)
|
||||
.to match_array([not_revoked_false_token, not_revoked_nil_token, not_expired_token, never_expires_token])
|
||||
.to match_array([not_revoked_false_token, not_revoked_nil_token, not_expired_token])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -414,22 +420,4 @@ RSpec.describe PersonalAccessToken, feature_category: :system_access do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#expires_at=' do
|
||||
let(:personal_access_token) { described_class.new }
|
||||
|
||||
context 'expires_at set to empty value' do
|
||||
[nil, ""].each do |expires_in_value|
|
||||
it 'defaults to PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS' do
|
||||
personal_access_token.expires_at = expires_in_value
|
||||
|
||||
freeze_time do
|
||||
expect(personal_access_token.expires_at).to eq(
|
||||
PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now.to_date
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -225,7 +225,8 @@ RSpec.describe API::Internal::Base, feature_category: :system_access do
|
|||
params: {
|
||||
key_id: key.id,
|
||||
name: 'newtoken',
|
||||
scopes: %w(read_api read_repository)
|
||||
scopes: %w(read_api read_repository),
|
||||
expires_at: 365.days.from_now
|
||||
},
|
||||
headers: gitlab_shell_internal_api_request_header
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ require 'spec_helper'
|
|||
|
||||
RSpec.describe AccessTokenEntityBase do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:token) { create(:personal_access_token, user: user, expires_at: nil) }
|
||||
let_it_be(:token) { create(:personal_access_token, user: user) }
|
||||
|
||||
subject(:json) { described_class.new(token).as_json }
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,13 @@ RSpec.describe PersonalAccessTokens::CreateService, feature_category: :system_ac
|
|||
end
|
||||
end
|
||||
|
||||
context 'with no expires_at set', :freeze_time do
|
||||
let(:params) { { name: 'Test token', impersonation: false, scopes: [:no_valid] } }
|
||||
let(:service) { described_class.new(current_user: user, target_user: user, params: params) }
|
||||
|
||||
it { expect(subject.payload[:personal_access_token].expires_at).to eq PersonalAccessToken::MAX_PERSONAL_ACCESS_TOKEN_LIFETIME_IN_DAYS.days.from_now.to_date }
|
||||
end
|
||||
|
||||
context 'when invalid scope' do
|
||||
let(:params) { { name: 'Test token', impersonation: false, scopes: [:no_valid], expires_at: Date.today + 1.month } }
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue