Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
4df10dee37
commit
495c2f372a
|
|
@ -58,9 +58,6 @@ export default {
|
|||
'app/assets/javascripts/ci/pipelines_page/components/pipelines_artifacts.vue',
|
||||
'app/assets/javascripts/ci/pipelines_page/pipelines.vue',
|
||||
'app/assets/javascripts/ci/reports/components/report_section.vue',
|
||||
'app/assets/javascripts/clusters_list/components/agents.vue',
|
||||
'app/assets/javascripts/clusters_list/components/delete_agent_button.vue',
|
||||
'app/assets/javascripts/clusters_list/components/install_agent_modal.vue',
|
||||
'app/assets/javascripts/commit/pipelines/legacy_pipelines_table_wrapper.vue',
|
||||
'app/assets/javascripts/content_editor/components/bubble_menus/media_bubble_menu.vue',
|
||||
'app/assets/javascripts/content_editor/components/content_editor.vue',
|
||||
|
|
@ -76,7 +73,6 @@ export default {
|
|||
'app/assets/javascripts/custom_emoji/pages/index.vue',
|
||||
'app/assets/javascripts/deploy_freeze/components/deploy_freeze_modal.vue',
|
||||
'app/assets/javascripts/deploy_freeze/components/deploy_freeze_table.vue',
|
||||
'app/assets/javascripts/deployments/components/deployment_header.vue',
|
||||
'app/assets/javascripts/design_management/components/design_description/description_form.vue',
|
||||
'app/assets/javascripts/design_management/components/design_notes/design_discussion.vue',
|
||||
'app/assets/javascripts/design_management/components/design_notes/design_note.vue',
|
||||
|
|
@ -92,15 +88,6 @@ export default {
|
|||
'app/assets/javascripts/diffs/components/diff_view.vue',
|
||||
'app/assets/javascripts/diffs/components/image_diff_overlay.vue',
|
||||
'app/assets/javascripts/emoji/components/picker.vue',
|
||||
'app/assets/javascripts/environments/components/canary_update_modal.vue',
|
||||
'app/assets/javascripts/environments/components/deployment.vue',
|
||||
'app/assets/javascripts/environments/components/empty_state.vue',
|
||||
'app/assets/javascripts/environments/components/enable_review_app_modal.vue',
|
||||
'app/assets/javascripts/environments/components/environment_flux_resource_selector.vue',
|
||||
'app/assets/javascripts/environments/components/environment_form.vue',
|
||||
'app/assets/javascripts/environments/environment_details/components/deployment_actions.vue',
|
||||
'app/assets/javascripts/environments/environment_details/components/deployment_history.vue',
|
||||
'app/assets/javascripts/environments/environment_details/components/kubernetes/kubernetes_overview.vue',
|
||||
'app/assets/javascripts/error_tracking/components/error_details.vue',
|
||||
'app/assets/javascripts/error_tracking/components/error_tracking_list.vue',
|
||||
'app/assets/javascripts/error_tracking_settings/components/project_dropdown.vue',
|
||||
|
|
@ -144,8 +131,6 @@ export default {
|
|||
'app/assets/javascripts/invite_members/components/user_limit_notification.vue',
|
||||
'app/assets/javascripts/jira_connect/branches/components/new_branch_form.vue',
|
||||
'app/assets/javascripts/jira_import/components/jira_import_form.vue',
|
||||
'app/assets/javascripts/kubernetes_dashboard/components/workload_details.vue',
|
||||
'app/assets/javascripts/kubernetes_dashboard/components/workload_details_drawer.vue',
|
||||
'app/assets/javascripts/members/components/filter_sort/sort_dropdown.vue',
|
||||
'app/assets/javascripts/merge_request_dashboard/components/status_badge.vue',
|
||||
'app/assets/javascripts/merge_requests/components/reviewers/reviewer_drawer.vue',
|
||||
|
|
@ -448,9 +433,6 @@ export default {
|
|||
'ee/app/assets/javascripts/projects/components/move_personal_project_to_group_modal.vue',
|
||||
'ee/app/assets/javascripts/projects/merge_requests/blocking_mr_input_root.vue',
|
||||
'ee/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue',
|
||||
'ee/app/assets/javascripts/protected_environments/create_protected_environment.vue',
|
||||
'ee/app/assets/javascripts/protected_environments/edit_protected_environment_rules_card.vue',
|
||||
'ee/app/assets/javascripts/protected_environments/protected_environments.vue',
|
||||
'ee/app/assets/javascripts/related_items_tree/components/create_epic_form.vue',
|
||||
'ee/app/assets/javascripts/related_items_tree/components/related_items_tree_app.vue',
|
||||
'ee/app/assets/javascripts/related_items_tree/components/related_items_tree_header_actions.vue',
|
||||
|
|
|
|||
|
|
@ -162,3 +162,5 @@ config:
|
|||
]
|
||||
ignores:
|
||||
- "doc/architecture"
|
||||
customRules:
|
||||
- "./doc/.markdownlint/rules/unnecessary_traversal.js"
|
||||
|
|
|
|||
|
|
@ -69,11 +69,6 @@ export default {
|
|||
required: true,
|
||||
type: Array,
|
||||
},
|
||||
defaultBranchName: {
|
||||
default: '.noBranch',
|
||||
required: false,
|
||||
type: String,
|
||||
},
|
||||
maxAgents: {
|
||||
default: null,
|
||||
required: false,
|
||||
|
|
@ -423,7 +418,6 @@ export default {
|
|||
v-if="action.name === 'delete-agent'"
|
||||
:key="action.name"
|
||||
:agent="item"
|
||||
:default-branch-name="defaultBranchName"
|
||||
/>
|
||||
<gl-disclosure-dropdown-item
|
||||
v-else
|
||||
|
|
|
|||
|
|
@ -119,7 +119,6 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
folderList: {},
|
||||
feedbackBannerDismissed: false,
|
||||
queryErrored: false,
|
||||
sharedAgentsQueryErrored: false,
|
||||
|
|
@ -292,12 +291,7 @@ export default {
|
|||
{{ $options.i18n.error }}
|
||||
</gl-alert>
|
||||
|
||||
<agent-table
|
||||
v-else
|
||||
:agents="tab.agents"
|
||||
:default-branch-name="defaultBranchName"
|
||||
:max-agents="limit"
|
||||
/>
|
||||
<agent-table v-else :agents="tab.agents" :max-agents="limit" />
|
||||
</gl-tab>
|
||||
|
||||
<gl-tab v-if="availableConfigs.length" :title="$options.i18n.availableConfigs">
|
||||
|
|
|
|||
|
|
@ -36,11 +36,6 @@ export default {
|
|||
type: Object,
|
||||
validator: (value) => ['id', 'name'].every((prop) => value[prop]),
|
||||
},
|
||||
defaultBranchName: {
|
||||
default: '.noBranch',
|
||||
required: false,
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -194,6 +194,8 @@ export default {
|
|||
}
|
||||
this.registerAgent();
|
||||
},
|
||||
// This method is triggered from outside of the component
|
||||
// eslint-disable-next-line vue/no-unused-properties
|
||||
showModalForAgent(name) {
|
||||
this.agentName = name;
|
||||
this.$refs.modal?.show();
|
||||
|
|
|
|||
|
|
@ -55,9 +55,6 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
iid() {
|
||||
return this.deployment.iid;
|
||||
},
|
||||
status() {
|
||||
return this.deployment.status?.toLowerCase() ?? '';
|
||||
},
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ export default {
|
|||
static: true,
|
||||
},
|
||||
data() {
|
||||
return { error: '', dismissed: true };
|
||||
return { error: '' };
|
||||
},
|
||||
computed: {
|
||||
stableWeight() {
|
||||
|
|
|
|||
|
|
@ -71,9 +71,6 @@ export default {
|
|||
const dateTime = new Date(this.deploymentTime);
|
||||
return localeDateFormat.asDateTimeFull.format(dateTime);
|
||||
},
|
||||
createdAt() {
|
||||
return this.deployment?.createdAt;
|
||||
},
|
||||
commit() {
|
||||
return this.deployment?.commit;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -24,14 +24,6 @@ export default {
|
|||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return this.hasTerm ? this.$options.i18n.searchingTitle : this.$options.i18n.title;
|
||||
},
|
||||
content() {
|
||||
return this.hasTerm ? this.$options.i18n.searchingContent : this.$options.i18n.content;
|
||||
},
|
||||
},
|
||||
i18n: {
|
||||
title: s__('Environments|Get started with environments'),
|
||||
content: s__(
|
||||
|
|
|
|||
|
|
@ -47,11 +47,6 @@ export default {
|
|||
- if: $CI_PIPELINE_SOURCE == "merge_request_event"`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
commaOrPeriod(index, length) {
|
||||
return index + 1 === length ? '.' : ',';
|
||||
},
|
||||
},
|
||||
i18n,
|
||||
configuringReviewAppsPath: helpPagePath('ci/review_apps/_index.md', {
|
||||
anchor: 'configure-review-apps',
|
||||
|
|
|
|||
|
|
@ -61,10 +61,7 @@ export default {
|
|||
fluxKustomizations: {
|
||||
query: fluxKustomizationsQuery,
|
||||
variables() {
|
||||
return {
|
||||
configuration: this.configuration,
|
||||
namespace: this.namespace,
|
||||
};
|
||||
return this.variables;
|
||||
},
|
||||
skip() {
|
||||
return !this.namespace;
|
||||
|
|
@ -85,10 +82,7 @@ export default {
|
|||
fluxHelmReleases: {
|
||||
query: fluxHelmReleasesQuery,
|
||||
variables() {
|
||||
return {
|
||||
configuration: this.configuration,
|
||||
namespace: this.namespace,
|
||||
};
|
||||
return this.variables;
|
||||
},
|
||||
skip() {
|
||||
return !this.namespace;
|
||||
|
|
|
|||
|
|
@ -103,13 +103,9 @@ export default {
|
|||
selectedAgentId: this.environment.clusterAgentId,
|
||||
agentSearchTerm: '',
|
||||
selectedNamespace: this.environment.kubernetesNamespace,
|
||||
kubernetesError: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
loadingNamespacesList() {
|
||||
return this.$apollo.queries.k8sNamespaces.loading;
|
||||
},
|
||||
isNameDisabled() {
|
||||
return Boolean(this.environment.id);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -80,9 +80,6 @@ export default {
|
|||
isActionsShown() {
|
||||
return this.actions.length > 0;
|
||||
},
|
||||
deploymentIid() {
|
||||
return this.approvalEnvironment.deploymentIid;
|
||||
},
|
||||
environment() {
|
||||
return this.approvalEnvironment.environment;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -104,9 +104,6 @@ export default {
|
|||
isPaginationDisabled() {
|
||||
return this.isLoading || this.isPrefetchingPages;
|
||||
},
|
||||
pollingInterval() {
|
||||
return this.graphqlEtagKey ? ENVIRONMENT_DETAILS_QUERY_POLLING_INTERVAL : null;
|
||||
},
|
||||
isDirectionAscending() {
|
||||
return this.sortDirection === DIRECTION_ASCENDING;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -132,7 +132,6 @@ export default {
|
|||
podsLoading: false,
|
||||
activeTab: k8sResourceType.k8sPods,
|
||||
fluxApiError: '',
|
||||
focusedElement: null,
|
||||
podToDelete: {},
|
||||
fluxHelmRelease: {},
|
||||
fluxKustomization: {},
|
||||
|
|
|
|||
|
|
@ -179,13 +179,13 @@ export default {
|
|||
</gl-badge>
|
||||
</div>
|
||||
</workload-details-item>
|
||||
<workload-details-item v-if="item.status && !item.fullStatus" :label="$options.i18n.status">
|
||||
<workload-details-item v-if="item.status && !hasFullStatus" :label="$options.i18n.status">
|
||||
<gl-badge :variant="$options.WORKLOAD_STATUS_BADGE_VARIANTS[item.status]">{{
|
||||
$options.STATUS_LABELS[item.status]
|
||||
}}</gl-badge>
|
||||
</workload-details-item>
|
||||
<workload-details-item
|
||||
v-if="item.fullStatus"
|
||||
v-if="hasFullStatus"
|
||||
:label="$options.i18n.status"
|
||||
:is-expanded="expanded.status"
|
||||
collapsible
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ export default {
|
|||
onDeletePod(pod) {
|
||||
this.$emit('delete-pod', pod);
|
||||
},
|
||||
// This method is triggered from outside of the component
|
||||
// eslint-disable-next-line vue/no-unused-properties
|
||||
toggle(item, section) {
|
||||
if (!isEqual(item, this.selectedItem)) {
|
||||
this.open(item, section);
|
||||
|
|
|
|||
|
|
@ -15,11 +15,9 @@ import {
|
|||
import Tracking from '~/tracking';
|
||||
import {
|
||||
TODO_TARGET_TYPE_ISSUE,
|
||||
TODO_TARGET_TYPE_WORK_ITEM,
|
||||
TODO_TARGET_TYPE_MERGE_REQUEST,
|
||||
TODO_TARGET_TYPE_DESIGN,
|
||||
TODO_TARGET_TYPE_ALERT,
|
||||
TODO_TARGET_TYPE_EPIC,
|
||||
TODO_TARGET_TYPE_SSH_KEY,
|
||||
TODO_TARGET_TYPE_WIKI_PAGE,
|
||||
TODO_ACTION_TYPE_ASSIGNED,
|
||||
|
|
@ -65,12 +63,7 @@ export const TARGET_TYPES = [
|
|||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
id: 'Issue',
|
||||
value: TODO_TARGET_TYPE_ISSUE,
|
||||
title: s__('Todos|Issue'),
|
||||
},
|
||||
{
|
||||
id: 'WorkItem',
|
||||
value: TODO_TARGET_TYPE_WORK_ITEM,
|
||||
title: s__('Todos|Work item'),
|
||||
title: s__('Todos|Issue or Epic'),
|
||||
},
|
||||
{
|
||||
id: 'MergeRequest',
|
||||
|
|
@ -87,12 +80,6 @@ export const TARGET_TYPES = [
|
|||
value: TODO_TARGET_TYPE_ALERT,
|
||||
title: s__('Todos|Alert'),
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
id: 'Epic',
|
||||
value: TODO_TARGET_TYPE_EPIC,
|
||||
title: s__('Todos|Epic'),
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
id: 'Key',
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ export const TODO_STATE_DONE = 'done';
|
|||
export const TODO_STATE_PENDING = 'pending';
|
||||
|
||||
export const TODO_TARGET_TYPE_ISSUE = 'ISSUE';
|
||||
export const TODO_TARGET_TYPE_WORK_ITEM = 'WORKITEM';
|
||||
export const TODO_TARGET_TYPE_MERGE_REQUEST = 'MERGEREQUEST';
|
||||
export const TODO_TARGET_TYPE_DESIGN = 'DESIGN';
|
||||
export const TODO_TARGET_TYPE_ALERT = 'ALERT';
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ export default {
|
|||
v-if="isNewNote"
|
||||
name="image-comment-dark"
|
||||
:size="24"
|
||||
class="gl-rounded-full gl-border-2 gl-border-solid gl-border-white gl-bg-white"
|
||||
class="gl-rounded-full gl-border-2 gl-border-solid gl-border-neutral-0 gl-bg-neutral-0 gl-text-neutral-950"
|
||||
/>
|
||||
<template v-else>
|
||||
{{ label }}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<script>
|
||||
import { GlButton, GlIcon, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { __ } from '~/locale';
|
||||
import {
|
||||
GROUP_VISIBILITY_TYPE,
|
||||
PROJECT_VISIBILITY_TYPE,
|
||||
|
|
@ -32,17 +31,10 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
isBannedProject() {
|
||||
return !this.isGroup && this.visibilityLevel === 'banned';
|
||||
},
|
||||
visibilityIcon() {
|
||||
return this.isBannedProject ? 'spam' : VISIBILITY_TYPE_ICON[this.visibilityLevel];
|
||||
return VISIBILITY_TYPE_ICON[this.visibilityLevel];
|
||||
},
|
||||
visibilityTooltip() {
|
||||
if (this.isBannedProject) {
|
||||
return __('This project is hidden because its creator has been banned');
|
||||
}
|
||||
|
||||
if (this.isGroup) {
|
||||
return GROUP_VISIBILITY_TYPE[this.visibilityLevel];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@
|
|||
.rd-hunk-header {
|
||||
// this is used when a hunk header doesn't have any text, only expand buttons
|
||||
min-height: calc(1em * $code-line-height);
|
||||
border-top: 1px solid var(--rd-hunk-header-border-color, $gray-100);
|
||||
border-bottom: 1px solid var(--rd-hunk-header-border-color, $gray-100);
|
||||
border-top: 1px solid var(--rd-hunk-header-border-color, var(--gl-border-color-default));
|
||||
border-bottom: 1px solid var(--rd-hunk-header-border-color, var(--gl-border-color-default));
|
||||
background-color: var(--rd-hunk-header-background-color, $gray-50);
|
||||
color: var(--rd-hunk-header-color, $gray-400);
|
||||
|
||||
|
|
|
|||
|
|
@ -125,8 +125,7 @@ html {
|
|||
padding-left: $gl-padding;
|
||||
padding-right: $gl-padding;
|
||||
box-shadow: none;
|
||||
background-color: $gray-10;
|
||||
border-left: 1px solid $gray-100;
|
||||
@apply gl-bg-subtle gl-border-l;
|
||||
|
||||
@include media-breakpoint-down(sm) {
|
||||
min-width: unset;
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@
|
|||
@apply gl-transition-width;
|
||||
height: $toggle-sidebar-height;
|
||||
padding: 0 $gl-padding;
|
||||
background-color: $gray-10;
|
||||
@apply gl-bg-subtle;
|
||||
border: 0;
|
||||
@apply gl-text-subtle;
|
||||
display: flex;
|
||||
|
|
@ -252,7 +252,7 @@
|
|||
@apply gl-border-b;
|
||||
|
||||
&:hover {
|
||||
background-color: $gray-100;
|
||||
@apply gl-bg-strong;
|
||||
@apply gl-text-default;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,21 +19,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.gl-sortable {
|
||||
.header {
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: $gray-100;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 1px solid $blue-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-dragging {
|
||||
// Important because plugin sets inline CSS
|
||||
opacity: 1 !important;
|
||||
|
|
|
|||
|
|
@ -783,9 +783,7 @@ pre {
|
|||
font-size: $gl-font-size;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
@apply gl-text-default;
|
||||
background-color: $gray-10;
|
||||
border: 1px solid $gray-100;
|
||||
@apply gl-text-default gl-bg-subtle gl-border;
|
||||
border-radius: $border-radius-small;
|
||||
|
||||
// Select only code elements that will have the copy code button
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ blockquote,
|
|||
color: $gl-text-color-subtle;
|
||||
padding: 0 0 0 15px;
|
||||
margin: 0;
|
||||
border-left: 3px solid $gray-100;
|
||||
border-left: 3px solid $gl-border-color-default;
|
||||
}
|
||||
|
||||
span.highlight_word {
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ tr td {
|
|||
}
|
||||
|
||||
tr.border-top td {
|
||||
border-top: 2px solid $gray-100;
|
||||
border-top: 2px solid $gl-border-color-default;
|
||||
}
|
||||
|
||||
tr.line td {
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
.markdown-code-block pre.code {
|
||||
padding: $gl-padding-8 $input-horizontal-padding;
|
||||
margin: 0 0 $gl-padding-8;
|
||||
border: 1px solid $gray-100;
|
||||
border: 1px solid $gl-border-color-default;
|
||||
border-radius: $border-radius-small;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@
|
|||
|
||||
.margin {
|
||||
background-color: $white;
|
||||
border-right: 1px solid $gray-100;
|
||||
@apply gl-border-r;
|
||||
|
||||
.line-insert {
|
||||
border-right: 1px solid $line-added-dark;
|
||||
|
|
|
|||
|
|
@ -60,17 +60,17 @@
|
|||
&,
|
||||
.badge.badge-pill {
|
||||
color: var(--ide-text-color, $black);
|
||||
border-color: var(--ide-input-border, $gray-200);
|
||||
border-color: var(--ide-input-border, var(--gl-border-color-strong));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.drag-handle:hover {
|
||||
background-color: var(--ide-dropdown-hover-background, $gray-50);
|
||||
background-color: var(--ide-dropdown-hover-background, var(--gl-background-color-strong));
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background-color: var(--ide-background, $white);
|
||||
background-color: var(--ide-background, var(--gl-background-color-default));
|
||||
|
||||
.badge.badge-pill {
|
||||
background-color: var(--ide-dropdown-hover-background, $badge-bg);
|
||||
|
|
@ -97,12 +97,12 @@
|
|||
}
|
||||
|
||||
code {
|
||||
background-color: var(--ide-background, $gray-100);
|
||||
background-color: var(--ide-background, var(--gl-background-color-strong));
|
||||
}
|
||||
|
||||
.ide-pipeline .top-bar,
|
||||
.ide-terminal .top-bar {
|
||||
background-color: var(--ide-background, $gray-10);
|
||||
background-color: var(--ide-background, var(--gl-background-color-subtle));
|
||||
}
|
||||
|
||||
.common-note-form .md-area {
|
||||
|
|
@ -110,13 +110,13 @@
|
|||
}
|
||||
|
||||
.md table:not(.code) tr th {
|
||||
background-color: var(--ide-highlight-background, $gray-100);
|
||||
background-color: var(--ide-highlight-background, var(--gl-background-color-strong));
|
||||
}
|
||||
|
||||
&,
|
||||
.card,
|
||||
.common-note-form .md-area {
|
||||
background-color: var(--ide-highlight-background, $white);
|
||||
background-color: var(--ide-highlight-background, var(--gl-background-color-default));
|
||||
}
|
||||
|
||||
.card,
|
||||
|
|
@ -142,7 +142,7 @@
|
|||
}
|
||||
|
||||
pre {
|
||||
border-color: var(--ide-border-color-alt, $gray-100);
|
||||
border-color: var(--ide-border-color-alt, var(--gl-border-color-default));
|
||||
|
||||
code {
|
||||
background-color: var(--ide-empty-state-background, inherit);
|
||||
|
|
@ -179,7 +179,7 @@
|
|||
&,
|
||||
.avatar {
|
||||
color: var(--ide-text-color, var(--gl-text-color-default));
|
||||
background-color: var(--ide-highlight-background, $white);
|
||||
background-color: var(--ide-highlight-background, var(--gl-background-color-default));
|
||||
border-color: var(--ide-highlight-background, var(--gl-avatar-border-color-default));
|
||||
}
|
||||
}
|
||||
|
|
@ -189,7 +189,7 @@
|
|||
input[type='search'],
|
||||
.filtered-search-box {
|
||||
border-color: var(--ide-input-border, var(--gl-border-color-default));
|
||||
background: var(--ide-input-background, $white) !important;
|
||||
background: var(--ide-input-background, var(--gl-background-color-default)) !important;
|
||||
}
|
||||
|
||||
input[type='text']:not([disabled]):not([readonly]):focus,
|
||||
|
|
@ -212,11 +212,11 @@
|
|||
|
||||
.filtered-search-token .value-container,
|
||||
.filtered-search-term .value-container {
|
||||
background-color: var(--ide-dropdown-hover-background, $gray-50);
|
||||
background-color: var(--ide-dropdown-hover-background, var(--gl-background-color-strong));
|
||||
color: var(--ide-text-color, var(--gl-text-color-default));
|
||||
|
||||
&:hover {
|
||||
background-color: var(--ide-input-border, $gray-100);
|
||||
background-color: var(--ide-input-border, var(--gl-border-color-default));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -245,25 +245,25 @@
|
|||
}
|
||||
|
||||
.dropdown-menu-toggle {
|
||||
border-color: var(--ide-btn-default-border, $gray-200);
|
||||
border-color: var(--ide-btn-default-border, var(--gl-border-color-strong));
|
||||
background-color: var(--ide-input-background, transparent);
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--ide-dropdown-btn-hover-background, $gray-50) !important;
|
||||
border-color: var(--ide-dropdown-btn-hover-border, $gray-200) !important;
|
||||
background-color: var(--ide-dropdown-btn-hover-background, var(--gl-background-color-strong)) !important;
|
||||
border-color: var(--ide-dropdown-btn-hover-border, var(--gl-border-color-strong)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
// todo: remove this block after all default buttons have been migrated to gl-button
|
||||
.btn-default:not(.gl-button) {
|
||||
background-color: var(--ide-btn-default-background, $white) !important;
|
||||
background-color: var(--ide-btn-default-background, var(--gl-background-color-default)) !important;
|
||||
border-color: var(--ide-btn-default-border, var(--gl-border-color-default));
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
border-color: var(--ide-btn-default-hover-border, var(--gl-border-color-default)) !important;
|
||||
background-color: var(--ide-btn-default-background, $gray-50) !important;
|
||||
background-color: var(--ide-btn-default-background, var(--gl-background-color-strong)) !important;
|
||||
}
|
||||
|
||||
&:active,
|
||||
|
|
@ -276,21 +276,21 @@
|
|||
.dropdown-menu {
|
||||
color: var(--ide-text-color, var(--gl-text-color-default));
|
||||
border-color: var(--ide-background, var(--gl-border-color-default));
|
||||
background-color: var(--ide-dropdown-background, $white);
|
||||
background-color: var(--ide-dropdown-background, var(--gl-background-color-default));
|
||||
|
||||
.nav-links {
|
||||
background-color: var(--ide-dropdown-hover-background, $white);
|
||||
background-color: var(--ide-dropdown-hover-background, var(--gl-background-color-default));
|
||||
border-color: var(--ide-dropdown-hover-background, var(--gl-border-color-default));
|
||||
}
|
||||
|
||||
.gl-tabs-nav {
|
||||
background-color: var(--ide-dropdown-hover-background, $white);
|
||||
background-color: var(--ide-dropdown-hover-background, var(--gl-background-color-default));
|
||||
box-shadow: inset 0 -2px 0 0 var(--ide-dropdown-hover-background, var(--gl-border-color-default));
|
||||
}
|
||||
|
||||
.divider {
|
||||
background-color: var(--ide-dropdown-hover-background, $gray-100);
|
||||
border-color: var(--ide-dropdown-hover-background, $gray-100);
|
||||
background-color: var(--ide-dropdown-hover-background, var(--gl-border-color-default));
|
||||
border-color: var(--ide-dropdown-hover-background, var(--gl-border-color-default));
|
||||
}
|
||||
|
||||
li > a:not(.disable-hover):hover,
|
||||
|
|
@ -298,37 +298,37 @@
|
|||
li button:not(.disable-hover):hover,
|
||||
li button:not(.disable-hover):focus,
|
||||
li button.is-focused {
|
||||
background-color: var(--ide-dropdown-hover-background, $gray-50);
|
||||
background-color: var(--ide-dropdown-hover-background, var(--gl-background-color-strong));
|
||||
color: var(--ide-text-color, var(--gl-text-color-default));
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-title,
|
||||
.dropdown-input {
|
||||
border-color: var(--ide-dropdown-hover-background, $gray-100) !important;
|
||||
border-color: var(--ide-dropdown-hover-background, var(--gl-border-color-default)) !important;
|
||||
}
|
||||
|
||||
// todo: remove this block after all disabled buttons have been migrated to gl-button
|
||||
.btn[disabled]:not(.gl-button) {
|
||||
background-color: var(--ide-btn-default-background, $gray-10) !important;
|
||||
border: 1px solid var(--ide-btn-disabled-border, $gray-100) !important;
|
||||
background-color: var(--ide-btn-default-background, var(--gl-background-color-subtle)) !important;
|
||||
border: 1px solid var(--ide-btn-disabled-border, var(--gl-border-color-default)) !important;
|
||||
color: var(--ide-btn-disabled-color, var(--gl-text-color-disabled)) !important;
|
||||
}
|
||||
|
||||
.md table:not(.code) tbody {
|
||||
background-color: var(--ide-empty-state-background, $white);
|
||||
background-color: var(--ide-empty-state-background, var(--gl-background-color-default));
|
||||
}
|
||||
|
||||
.animation-container {
|
||||
[class^='skeleton-line-'] {
|
||||
background-color: var(--ide-animation-gradient-1, $gray-100);
|
||||
background-color: var(--ide-animation-gradient-1, var(--gl-border-color-default));
|
||||
|
||||
&::after {
|
||||
background-image: linear-gradient(to right,
|
||||
var(--ide-animation-gradient-1, $gray-100) 0%,
|
||||
var(--ide-animation-gradient-2, $gray-10) 20%,
|
||||
var(--ide-animation-gradient-1, $gray-100) 40%,
|
||||
var(--ide-animation-gradient-1, $gray-100) 100%);
|
||||
var(--ide-animation-gradient-1, var(--gl-border-color-default)) 0%,
|
||||
var(--ide-animation-gradient-2, var(--gl-background-color-subtle)) 20%,
|
||||
var(--ide-animation-gradient-1, var(--gl-border-color-default)) 40%,
|
||||
var(--ide-animation-gradient-1, var(--gl-border-color-default)) 100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,8 +62,8 @@ $design-pin-diameter-sm: 24px;
|
|||
height: $design-pin-diameter-sm;
|
||||
width: $design-pin-diameter-sm;
|
||||
box-sizing: content-box;
|
||||
@apply gl-bg-purple-500 dark:gl-bg-purple-700;
|
||||
color: var(--white, $white);
|
||||
@apply gl-text-neutral-0;
|
||||
background-color: var(--gl-status-brand-icon-color);
|
||||
font-weight: $gl-font-weight-bold;
|
||||
border-radius: 50%;
|
||||
z-index: 1;
|
||||
|
|
@ -71,16 +71,16 @@ $design-pin-diameter-sm: 24px;
|
|||
border: 0;
|
||||
|
||||
&.draft {
|
||||
background-color: var(--orange-500, $orange-500);
|
||||
background-color: var(--gl-status-warning-icon-color);
|
||||
}
|
||||
|
||||
&.resolved {
|
||||
background-color: var(--gray-500, $gray-500);
|
||||
@apply gl-bg-strong gl-text-strong;
|
||||
}
|
||||
|
||||
&.on-image {
|
||||
box-shadow: 0 2px 4px var(--gl-color-alpha-dark-8), 0 0 1px var(--gl-color-alpha-dark-24);
|
||||
border: var(--white, $white) 2px solid;
|
||||
@apply gl-border-2 gl-border-solid gl-border-neutral-0;
|
||||
will-change: transform, box-shadow, opacity;
|
||||
// NOTE: verbose transition property required for Safari
|
||||
transition: transform $general-hover-transition-duration linear, box-shadow $general-hover-transition-duration linear, opacity $general-hover-transition-duration linear;
|
||||
|
|
@ -106,7 +106,7 @@ $design-pin-diameter-sm: 24px;
|
|||
|
||||
&.small {
|
||||
position: absolute;
|
||||
border: 1px solid var(--white, $white);
|
||||
@apply gl-border-1 gl-border-solid gl-border-neutral-0;
|
||||
height: $design-pin-diameter-sm;
|
||||
width: $design-pin-diameter-sm;
|
||||
}
|
||||
|
|
@ -161,7 +161,7 @@ $design-pin-diameter-sm: 24px;
|
|||
|
||||
&::before {
|
||||
content: '';
|
||||
border-left: 1px solid var(--gray-100, $gray-100);
|
||||
@apply gl-border-l;
|
||||
position: absolute;
|
||||
left: 11px;
|
||||
top: -17px;
|
||||
|
|
|
|||
|
|
@ -1,36 +1,10 @@
|
|||
@import 'page_bundles/mixins_and_variables_and_functions';
|
||||
|
||||
.x-axis path,
|
||||
.y-axis path,
|
||||
.label-x-axis-line,
|
||||
.label-y-axis-line {
|
||||
fill: none;
|
||||
stroke-width: 1;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.x-axis path,
|
||||
.y-axis path {
|
||||
stroke: var(--gray-300, $gray-300);
|
||||
}
|
||||
|
||||
.label-x-axis-line,
|
||||
.label-y-axis-line {
|
||||
stroke: var(--gray-100, $gray-100);
|
||||
}
|
||||
|
||||
.y-axis {
|
||||
line {
|
||||
stroke: var(--gray-300, $gray-300);
|
||||
stroke-width: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy boards
|
||||
*/
|
||||
.deploy-board {
|
||||
background-color: var(--gray-50, $gray-50);
|
||||
@apply gl-bg-strong;
|
||||
min-height: 20px;
|
||||
|
||||
> .loading-icon,
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ $ide-commit-header-height: 48px;
|
|||
}
|
||||
|
||||
&:not([disabled]):hover {
|
||||
background-color: var(--ide-input-border, $gray-100);
|
||||
background-color: var(--ide-input-border, var(--gl-border-color-default));
|
||||
}
|
||||
|
||||
&:not([disabled]):focus {
|
||||
|
|
|
|||
|
|
@ -621,7 +621,7 @@
|
|||
border: 1px solid var(--gl-border-color-default);
|
||||
|
||||
.circle-icon-container {
|
||||
color: var(--gray-100, $gray-100);
|
||||
color: var(--gl-border-color-default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -857,8 +857,7 @@
|
|||
}
|
||||
|
||||
.memory-graph-container {
|
||||
background: var(--white, $white);
|
||||
border: 1px solid var(--gray-100, $gray-100);
|
||||
@apply gl-bg-default gl-border;
|
||||
}
|
||||
|
||||
.review-bar-component {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ $item-height: 40px;
|
|||
$details-cell-width: 180px;
|
||||
$timeline-cell-height: 32px;
|
||||
$timeline-cell-width: 180px;
|
||||
$border-style: 1px solid var(--gray-100, $gray-100);
|
||||
$gradient-dark-gray: rgba(0, 0, 0, 0.15);
|
||||
$gradient-gray: rgba(255, 255, 255, 0.001);
|
||||
$scroll-top-gradient: linear-gradient(to bottom, $gradient-dark-gray 0%, $gradient-gray 100%);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@
|
|||
|
||||
// Pipeline mini graph
|
||||
.pipeline-mini-graph-stage-container {
|
||||
// Temoorarily sets a max height for the dropdown
|
||||
position: relative;
|
||||
// Temporarily sets a max height for the dropdown
|
||||
// until GlDisclosureDropdown supports adding
|
||||
// a max-height value
|
||||
// see https://gitlab.com/gitlab-org/gitlab-ui/-/issues/3010
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ $disclosure-hierarchy-chevron-dimension: 1.2rem;
|
|||
display: flex;
|
||||
position: relative;
|
||||
font-size: $gl-font-size-sm;
|
||||
border: 1px solid var(--gray-100, $gray-100);
|
||||
@apply gl-border;
|
||||
border-right-style: none;
|
||||
border-left-style: none;
|
||||
line-height: $gl-line-height-16;
|
||||
|
|
@ -177,7 +177,7 @@ $disclosure-hierarchy-chevron-dimension: 1.2rem;
|
|||
width: $disclosure-hierarchy-chevron-dimension;
|
||||
height: $disclosure-hierarchy-chevron-dimension;
|
||||
transform: rotate(45deg) skew(14deg, 14deg);
|
||||
border: 1px solid var(--gray-100, $gray-100);
|
||||
@apply gl-border;
|
||||
border-color: inherit;
|
||||
border-bottom-color: transparent;
|
||||
border-left-color: transparent;
|
||||
|
|
@ -197,7 +197,7 @@ $disclosure-hierarchy-chevron-dimension: 1.2rem;
|
|||
|
||||
.disclosure-hierarchy-item:first-child & {
|
||||
padding-left: $gl-spacing-scale-3;
|
||||
border-left: 1px solid var(--gray-100, $gray-100);
|
||||
@apply gl-border-l;
|
||||
border-top-left-radius: $gl-border-radius-base;
|
||||
border-bottom-left-radius: $gl-border-radius-base;
|
||||
|
||||
|
|
@ -215,7 +215,7 @@ $disclosure-hierarchy-chevron-dimension: 1.2rem;
|
|||
|
||||
.disclosure-hierarchy-item:last-child & {
|
||||
padding-right: $gl-spacing-scale-4;
|
||||
border-right: 1px solid var(--gray-100, $gray-100);
|
||||
@apply gl-border-r;
|
||||
border-top-right-radius: $gl-border-radius-base;
|
||||
border-bottom-right-radius: $gl-border-radius-base;
|
||||
|
||||
|
|
@ -349,7 +349,7 @@ $disclosure-hierarchy-chevron-dimension: 1.2rem;
|
|||
.work-item-design-grid {
|
||||
@apply gl-grid-cols-3;
|
||||
}
|
||||
|
||||
|
||||
.work-item-design-show-sm {
|
||||
@apply gl-block;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@
|
|||
}
|
||||
|
||||
&:first-child {
|
||||
border-top: 1px solid $gray-100;
|
||||
@apply gl-border-t;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
content: ' ';
|
||||
height: 100%;
|
||||
width: 4px;
|
||||
background-color: $gray-100;
|
||||
background-color: $gl-border-color-default;
|
||||
}
|
||||
|
||||
position: relative;
|
||||
|
|
|
|||
|
|
@ -59,10 +59,6 @@ class ProjectsFinder < UnionFinder
|
|||
init_collection
|
||||
end
|
||||
|
||||
if Feature.enabled?(:hide_projects_of_banned_users)
|
||||
collection = without_created_and_owned_by_banned_user(collection)
|
||||
end
|
||||
|
||||
use_cte = params.delete(:use_cte)
|
||||
collection = Project.wrap_with_cte(collection) if use_cte
|
||||
collection = filter_projects(collection)
|
||||
|
|
@ -300,12 +296,6 @@ class ProjectsFinder < UnionFinder
|
|||
{ min_access_level: params[:min_access_level] }
|
||||
end
|
||||
|
||||
def without_created_and_owned_by_banned_user(projects)
|
||||
return projects if current_user&.can?(:admin_all_resources)
|
||||
|
||||
projects.without_created_and_owned_by_banned_user
|
||||
end
|
||||
|
||||
# Returns the available organizations to filter topics
|
||||
def topic_organization_ids
|
||||
@topic_organization_ids ||= begin
|
||||
|
|
|
|||
|
|
@ -508,8 +508,6 @@ module ApplicationHelper
|
|||
title = format(issuable_title, issuable: _('issue'))
|
||||
when MergeRequest
|
||||
title = format(issuable_title, issuable: _('merge request'))
|
||||
when Project
|
||||
title = _('This project is hidden because its creator has been banned')
|
||||
end
|
||||
|
||||
return unless title
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@ module ProjectsHelper
|
|||
include CompareHelper
|
||||
include Gitlab::Allowable
|
||||
|
||||
BANNED = 'banned'
|
||||
|
||||
def project_incident_management_setting
|
||||
@project_incident_management_setting ||= @project.incident_management_setting ||
|
||||
@project.build_incident_management_setting
|
||||
|
|
@ -573,7 +571,7 @@ module ProjectsHelper
|
|||
project_avatar: project.avatar_url,
|
||||
project_name: project.name,
|
||||
project_id: project.id,
|
||||
project_visibility_level: visibility_level_name(project)
|
||||
project_visibility_level: Gitlab::VisibilityLevel.string_level(project.visibility_level)
|
||||
}.merge(
|
||||
dropdown_attributes,
|
||||
fork_button_attributes,
|
||||
|
|
@ -672,10 +670,6 @@ module ProjectsHelper
|
|||
end
|
||||
|
||||
def visibility_level_content(project, css_class: nil, icon_css_class: nil, icon_variant: nil)
|
||||
if project.created_and_owned_by_banned_user? && Feature.enabled?(:hide_projects_of_banned_users)
|
||||
return hidden_resource_icon(project, css_class: css_class, variant: icon_variant)
|
||||
end
|
||||
|
||||
title = visibility_icon_description(project)
|
||||
container_class = [
|
||||
'has-tooltip gl-border-0 gl-bg-transparent gl-p-0 gl-leading-0 gl-text-inherit',
|
||||
|
|
@ -791,14 +785,6 @@ module ProjectsHelper
|
|||
}
|
||||
end
|
||||
|
||||
def visibility_level_name(project)
|
||||
if project.created_and_owned_by_banned_user? && Feature.enabled?(:hide_projects_of_banned_users)
|
||||
BANNED
|
||||
else
|
||||
Gitlab::VisibilityLevel.string_level(project.visibility_level)
|
||||
end
|
||||
end
|
||||
|
||||
def can_admin_project_clusters?(project)
|
||||
project.clusters.any? && can?(current_user, :admin_cluster, project)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1072,26 +1072,6 @@ class Project < ApplicationRecord
|
|||
scope :for_group_and_its_ancestor_groups, ->(group) { where(namespace_id: group.self_and_ancestors.select(:id)) }
|
||||
scope :is_importing, -> { with_import_state.where(import_state: { status: %w[started scheduled] }) }
|
||||
|
||||
scope :without_created_and_owned_by_banned_user, -> do
|
||||
where_not_exists(
|
||||
Users::BannedUser.joins(
|
||||
'INNER JOIN project_authorizations ON project_authorizations.user_id = banned_users.user_id'
|
||||
).where('projects.creator_id = banned_users.user_id')
|
||||
.where('project_authorizations.project_id = projects.id')
|
||||
.where(project_authorizations: { access_level: Gitlab::Access::OWNER })
|
||||
)
|
||||
end
|
||||
|
||||
scope :with_created_and_owned_by_banned_user, -> do
|
||||
where_exists(
|
||||
Users::BannedUser.joins(
|
||||
'INNER JOIN project_authorizations ON project_authorizations.user_id = banned_users.user_id'
|
||||
).where('projects.creator_id = banned_users.user_id')
|
||||
.where('project_authorizations.project_id = projects.id')
|
||||
.where(project_authorizations: { access_level: Gitlab::Access::OWNER })
|
||||
)
|
||||
end
|
||||
|
||||
class << self
|
||||
# Searches for a list of projects based on the query given in `query`.
|
||||
#
|
||||
|
|
@ -3367,12 +3347,6 @@ class Project < ApplicationRecord
|
|||
pending_delete? || hidden?
|
||||
end
|
||||
|
||||
def created_and_owned_by_banned_user?
|
||||
return false unless creator
|
||||
|
||||
creator.banned? && team.max_member_access(creator.id) == Gitlab::Access::OWNER
|
||||
end
|
||||
|
||||
def work_items_feature_flag_enabled?
|
||||
group&.work_items_feature_flag_enabled? || Feature.enabled?(:work_items, self)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -309,10 +309,6 @@ class ProjectPolicy < BasePolicy
|
|||
|
||||
condition(:namespace_catalog_available) { namespace_catalog_available? }
|
||||
|
||||
condition(:created_and_owned_by_banned_user, scope: :subject) do
|
||||
Feature.enabled?(:hide_projects_of_banned_users) && @subject.created_and_owned_by_banned_user?
|
||||
end
|
||||
|
||||
desc "User has either planner or reporter access"
|
||||
condition(:planner_or_reporter_access) do
|
||||
can?(:reporter_access) || can?(:planner_access)
|
||||
|
|
@ -1100,10 +1096,6 @@ class ProjectPolicy < BasePolicy
|
|||
enable :write_model_experiments
|
||||
end
|
||||
|
||||
rule { ~admin & ~organization_owner & created_and_owned_by_banned_user }.policy do
|
||||
prevent :read_project
|
||||
end
|
||||
|
||||
rule { ~private_project & guest & external_user }.enable :read_container_image
|
||||
|
||||
rule { can?(:create_pipeline_schedule) }.policy do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Groups # rubocop:disable Gitlab/BoundedContexts -- existing top-level module
|
||||
class RestoreService < Groups::BaseService
|
||||
def execute
|
||||
return error(_('You are not authorized to perform this action')) unless can?(current_user, :remove_group, group)
|
||||
return error(_('Group has not been marked for deletion')) unless group.marked_for_deletion?
|
||||
return error(_('Group deletion is in progress')) if group.deleted?
|
||||
|
||||
result = remove_deletion_schedule
|
||||
|
||||
group.reset
|
||||
|
||||
log_event if result[:status] == :success
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def remove_deletion_schedule
|
||||
deletion_schedule = group.deletion_schedule
|
||||
|
||||
if deletion_schedule.destroy
|
||||
success
|
||||
else
|
||||
error(_('Could not restore the group'))
|
||||
end
|
||||
end
|
||||
|
||||
def log_event
|
||||
log_info("User #{current_user.id} restored group #{group.full_path}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Groups::RestoreService.prepend_mod
|
||||
|
|
@ -9,7 +9,7 @@ module Projects
|
|||
end
|
||||
|
||||
def self.query(project_ids)
|
||||
MergeRequest.opened.of_projects(project_ids)
|
||||
MergeRequest.opened.without_hidden.of_projects(project_ids)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Projects
|
||||
class RestoreService < BaseService
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
DELETED_SUFFIX_REGEX = /-deleted-[a-zA-Z0-9]+\z/
|
||||
|
||||
def execute
|
||||
return error(_('Project already deleted')) if project.pending_delete?
|
||||
|
||||
result = ::Projects::UpdateService.new(
|
||||
project,
|
||||
current_user,
|
||||
{ archived: false,
|
||||
hidden: false,
|
||||
marked_for_deletion_at: nil,
|
||||
deleting_user: nil,
|
||||
name: updated_value(project.name),
|
||||
path: updated_value(project.path) }
|
||||
).execute
|
||||
|
||||
if result[:status] == :success
|
||||
log_event
|
||||
|
||||
## Trigger root namespace statistics refresh, to add project_statistics of
|
||||
## projects restored from deletion
|
||||
Namespaces::ScheduleAggregationWorker.perform_async(project.namespace_id)
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def log_event
|
||||
log_info("User #{current_user.id} restored project #{project.full_path}")
|
||||
end
|
||||
|
||||
def suffix
|
||||
original_path_taken?(project) ? "-#{SecureRandom.alphanumeric(5)}" : ""
|
||||
end
|
||||
strong_memoize_attr :suffix
|
||||
|
||||
def original_path_taken?(project)
|
||||
existing_project = ::Project.find_by_full_path(original_value(project.full_path))
|
||||
|
||||
existing_project.present? && existing_project.id != project.id
|
||||
end
|
||||
|
||||
def original_value(value)
|
||||
value.sub(DELETED_SUFFIX_REGEX, '')
|
||||
end
|
||||
|
||||
def updated_value(value)
|
||||
"#{original_value(value)}#{suffix}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Projects::RestoreService.prepend_mod
|
||||
|
|
@ -129,10 +129,7 @@
|
|||
= _('access:')
|
||||
%strong
|
||||
= visibility_level_content(@project, css_class: visibility_level_color(@project.visibility_level))
|
||||
- if @project.created_and_owned_by_banned_user? && Feature.enabled?(:hide_projects_of_banned_users)
|
||||
= _('This project is hidden because its creator has been banned')
|
||||
- else
|
||||
= visibility_level_label(@project.visibility_level)
|
||||
= visibility_level_label(@project.visibility_level)
|
||||
|
||||
= render 'shared/custom_attributes', custom_attributes: @project.custom_attributes
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: hide_projects_of_banned_users
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121488
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/412621
|
||||
milestone: '16.2'
|
||||
type: development
|
||||
group: group::anti-abuse
|
||||
default_enabled: false
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
- title: "GitLab.com certificate-based integration with Kubernetes"
|
||||
announcement_milestone: "14.5"
|
||||
removal_milestone: "15.9"
|
||||
removal_milestone: "18.0"
|
||||
window: 3
|
||||
breaking_change: true
|
||||
body: |
|
||||
The certificate-based integration with Kubernetes will be [deprecated and removed](https://about.gitlab.com/blog/2021/11/15/deprecating-the-cert-based-kubernetes-integration/). As a GitLab.com user, on new namespaces, you will no longer be able to integrate GitLab and your cluster using the certificate-based approach as of GitLab 15.0. The integration for current users will be enabled per namespace.
|
||||
|
|
@ -8,9 +9,6 @@
|
|||
For a more robust, secure, forthcoming, and reliable integration with Kubernetes, we recommend you use the
|
||||
[agent for Kubernetes](https://docs.gitlab.com/user/clusters/agent/) to connect Kubernetes clusters with GitLab. [How do I migrate?](https://docs.gitlab.com/user/infrastructure/clusters/migrate_to_gitlab_agent/)
|
||||
|
||||
Although an explicit removal date is set, we don't plan to remove this feature until the new solution has feature parity.
|
||||
For more information about the blockers to removal, see [this issue](https://gitlab.com/gitlab-org/configure/general/-/issues/199).
|
||||
|
||||
For updates and details about this deprecation, follow [this epic](https://gitlab.com/groups/gitlab-org/configure/-/epics/8).
|
||||
|
||||
GitLab Self-Managed customers can still use the feature [with a feature flag](https://docs.gitlab.com/update/deprecations/#self-managed-certificate-based-integration-with-kubernetes).
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
migration_job_name: BackfillAmazonInstanceAuditEventDestinations
|
||||
description: Backfill the audit_events_instance_amazon_s3_configurations table to audit_events_instance_external_streaming_destinations
|
||||
feature_category: audit_events
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/184039
|
||||
milestone: '17.11'
|
||||
queued_migration_version: 20250310151216
|
||||
finalized_by: # version of the migration that finalized this BBM
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class QueueBackfillAmazonInstanceAuditEventDestinations < Gitlab::Database::Migration[2.2]
|
||||
milestone '17.11'
|
||||
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
MIGRATION = "BackfillAmazonInstanceAuditEventDestinations"
|
||||
DELAY_INTERVAL = 2.minutes
|
||||
BATCH_SIZE = 1000
|
||||
SUB_BATCH_SIZE = 100
|
||||
|
||||
def up
|
||||
queue_batched_background_migration(
|
||||
MIGRATION,
|
||||
:audit_events_instance_amazon_s3_configurations,
|
||||
:id,
|
||||
job_interval: DELAY_INTERVAL,
|
||||
batch_size: BATCH_SIZE,
|
||||
sub_batch_size: SUB_BATCH_SIZE
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
delete_batched_background_migration(MIGRATION, :audit_events_instance_amazon_s3_configurations, :id, [])
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
3d5a619453f9db8266fc1a8534a64286f16037a686b9e6a54623061a20ba6e64
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
names: ['Custom rule/unnecessary-traversal'],
|
||||
description: 'Links should not traverse out and back into the same directory',
|
||||
tags: ['gitlab-docs', 'links'],
|
||||
function: (params, onError) => {
|
||||
// Get the current file directory name
|
||||
const { name: filePath = '', lines = [] } = params;
|
||||
const dirName = path.basename(path.dirname(filePath));
|
||||
|
||||
if (!filePath) return;
|
||||
// Process each line
|
||||
lines.forEach((line, i) => {
|
||||
// Skip lines that don't contain markdown links with relative paths
|
||||
if (!line.includes('](../')) return;
|
||||
|
||||
// Regular expression to find markdown links with potential traversal issues
|
||||
const linkRegex = /\[([^\]]+)\]\((\.\.\/([^/]+)\/)(.*?)(?:\s+"[^"]*")?\)/g;
|
||||
|
||||
let match;
|
||||
while ((match = linkRegex.exec(line)) !== null) {
|
||||
/*
|
||||
Destructure regex match into:
|
||||
- fullMatch: the entire link
|
||||
- linkText: the link text
|
||||
- traversalPart: the '../dir/' part
|
||||
- traversalDir: just the 'dir' part
|
||||
- targetPath: the rest of the path
|
||||
*/
|
||||
const [fullMatch, linkText, traversalPart, traversalDir, targetPath] = match;
|
||||
|
||||
// Check if traversal directory matches current directory
|
||||
if (traversalDir === dirName) {
|
||||
// Calculate positions for precise highlighting
|
||||
const linkStart = match.index;
|
||||
const traversalStart = fullMatch.indexOf(traversalPart);
|
||||
|
||||
onError({
|
||||
lineNumber: i + 1,
|
||||
range: [linkStart + traversalStart, traversalPart.length],
|
||||
detail: `Link path does not need: '../${traversalDir}/'. Shorten link path to '[${linkText}](${targetPath})'`,
|
||||
fixInfo: {
|
||||
editColumn: linkStart + 1,
|
||||
deleteCount: fullMatch.length,
|
||||
insertText: `[${linkText}](${targetPath})`,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
---
|
||||
# Error: gitlab_docs.InternalLinkFormat
|
||||
#
|
||||
# Checks that internal link paths don't start with '/' or './'.
|
||||
# Checks that internal link paths don't use `//`, or start with '/' or './'.
|
||||
#
|
||||
# For a list of all options, see https://vale.sh/docs/topics/styles/
|
||||
extends: existence
|
||||
message: "Edit the link so it does not start with '/' or './'."
|
||||
message: "Edit the link so it does not use `//`, or start with '/' or './'."
|
||||
link: https://docs.gitlab.com/development/documentation/styleguide/#links
|
||||
vocab: false
|
||||
level: error
|
||||
scope: raw
|
||||
raw:
|
||||
- '\[[^\]]+\]\(\.?\/(?!uploads|documentation).*?\)'
|
||||
- '\[[^\]]+\]\((\.?\/(?!uploads|documentation)|[^:)]*\/\/)[^)]*\)'
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
---
|
||||
# Error: gitlab_docs.RelativeLinksDoubleSlashes
|
||||
#
|
||||
# Checks for the presence of double slashes in relative URLs.
|
||||
#
|
||||
# For a list of all options, see https://vale.sh/docs/topics/styles/
|
||||
extends: existence
|
||||
message: "Do not use double slashes '//' or '../doc' in the link path"
|
||||
link: https://docs.gitlab.com/development/documentation/styleguide/#links
|
||||
vocab: false
|
||||
level: error
|
||||
scope: raw
|
||||
raw:
|
||||
- '(\.//)|(\.\.\/doc\/)'
|
||||
|
|
@ -531,7 +531,7 @@ To set this limit to `2000` on your instance, run the following command in the G
|
|||
Plan.default.actual_limits.update!(pipeline_hierarchy_size: 2000)
|
||||
```
|
||||
|
||||
You can also set this limit by using the GitLab UI in the [Admin area](../administration/settings/continuous_integration.md#set-cicd-limits).
|
||||
You can also set this limit by using the GitLab UI in the [Admin area](settings/continuous_integration.md#set-cicd-limits).
|
||||
|
||||
This limit is enabled on GitLab.com and cannot be changed.
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ Verify the presence of report on the base commit by obtaining the `base_sha` usi
|
|||
|
||||
## No Code Quality symbol in the changes view
|
||||
|
||||
If no symbol is displayed in the [changes view](../testing/code_quality.md#merge-request-changes-view), ensure that the `location.path` in the code quality report:
|
||||
If no symbol is displayed in the [changes view](code_quality.md#merge-request-changes-view), ensure that the `location.path` in the code quality report:
|
||||
|
||||
- Is using a relative path to the file containing the code quality violation.
|
||||
- Is not prefixed with `./`. For example, the `path` should be `somedir/file1.rb` instead of `./somedir/file1.rb`.
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ writes, but should be set to a low value, such as 10 seconds.
|
|||
|
||||
`Rails.cache` uses Redis as the store.
|
||||
GitLab instances, like GitLab.com, can configure Redis for [key eviction](https://redis.io/docs/latest/develop/reference/eviction/).
|
||||
See the [Redis development guide](../development/redis.md#caching).
|
||||
See the [Redis development guide](redis.md#caching).
|
||||
|
||||
### When to use HTTP caching
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ enhancement. They are responsible for:
|
|||
|
||||
The first merge request where a feature can be tested should include the
|
||||
documentation, even if the feature is behind a feature flag.
|
||||
For more information, see the [guidelines](../documentation/feature_flags.md).
|
||||
For more information, see the [guidelines](feature_flags.md).
|
||||
|
||||
The author of this MR, either a frontend or backend developer, should write the documentation.
|
||||
|
||||
|
|
@ -101,7 +101,7 @@ otherwise agreed with the product manager and technical writer:
|
|||
If the new or changed documentation requires extensive collaboration or
|
||||
conversation, a separate, linked issue can be used for the planning process.
|
||||
|
||||
- Use the [Documentation guidelines](../documentation/_index.md),
|
||||
- Use the [Documentation guidelines](_index.md),
|
||||
and other resources linked from there, including:
|
||||
- [Documentation folder structure](site_architecture/folder_structure.md).
|
||||
- [Documentation Style Guide](styleguide/_index.md).
|
||||
|
|
@ -114,7 +114,7 @@ otherwise agreed with the product manager and technical writer:
|
|||
- Want to request any other help.
|
||||
- If you are working on documentation in a separate merge request, ensure the
|
||||
documentation is merged as close as possible to the code merge.
|
||||
- If the feature has a feature flag, [follow the policy for documenting feature-flagged issues](../documentation/feature_flags.md).
|
||||
- If the feature has a feature flag, [follow the policy for documenting feature-flagged issues](feature_flags.md).
|
||||
|
||||
#### Review
|
||||
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ Refer to [Track and Propose Sessions for Python Learning Group](https://gitlab.c
|
|||
|
||||
### Communication
|
||||
|
||||
- Stay updated by following the [learning group issue](<https://gitlab.com/gitlab-org/gitlab/-/issues/517449>)
|
||||
- Stay updated by following the [learning group issue](https://gitlab.com/gitlab-org/gitlab/-/issues/517449)
|
||||
- Join the discussion on Slack: **#python_getting_started**
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ This window takes place on May 5 - 7, 2025 from 09:00 UTC to 22:00 UTC.
|
|||
|
||||
| Deprecation | Impact | Stage | Scope |
|
||||
|-------------|--------|-------|-------|
|
||||
| [GitLab.com certificate-based integration with Kubernetes](https://gitlab.com/groups/gitlab-org/configure/-/epics/8) | | Configure | |
|
||||
| [Runner `active` GraphQL fields replaced by `paused`](https://gitlab.com/gitlab-org/gitlab/-/issues/351109) | Low | Verify | Instance, group, project |
|
||||
| [ZenTao integration](https://gitlab.com/gitlab-org/gitlab/-/issues/377825) | Low | Foundations | Instance |
|
||||
| [GraphQL deprecation of `dependencyProxyTotalSizeInBytes` field](https://gitlab.com/gitlab-org/gitlab/-/issues/414236) | Low | Package | Group |
|
||||
|
|
|
|||
|
|
@ -1329,6 +1329,29 @@ You can read more about it in the [charts release page](https://docs.gitlab.com/
|
|||
|
||||
</div>
|
||||
|
||||
<div class="deprecation breaking-change" data-milestone="18.0">
|
||||
|
||||
### GitLab.com certificate-based integration with Kubernetes
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
||||
- Announced in GitLab <span class="milestone">14.5</span>
|
||||
- Removal in GitLab <span class="milestone">18.0</span> ([breaking change](https://docs.gitlab.com/update/terminology/#breaking-change))
|
||||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/groups/gitlab-org/configure/-/epics/8).
|
||||
|
||||
</div>
|
||||
|
||||
The certificate-based integration with Kubernetes will be [deprecated and removed](https://about.gitlab.com/blog/2021/11/15/deprecating-the-cert-based-kubernetes-integration/). As a GitLab.com user, on new namespaces, you will no longer be able to integrate GitLab and your cluster using the certificate-based approach as of GitLab 15.0. The integration for current users will be enabled per namespace.
|
||||
|
||||
For a more robust, secure, forthcoming, and reliable integration with Kubernetes, we recommend you use the
|
||||
[agent for Kubernetes](https://docs.gitlab.com/user/clusters/agent/) to connect Kubernetes clusters with GitLab. [How do I migrate?](https://docs.gitlab.com/user/infrastructure/clusters/migrate_to_gitlab_agent/)
|
||||
|
||||
For updates and details about this deprecation, follow [this epic](https://gitlab.com/groups/gitlab-org/configure/-/epics/8).
|
||||
|
||||
GitLab Self-Managed customers can still use the feature [with a feature flag](https://docs.gitlab.com/update/deprecations/#self-managed-certificate-based-integration-with-kubernetes).
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation " data-milestone="18.0">
|
||||
|
||||
### Gitaly rate limiting
|
||||
|
|
@ -6331,32 +6354,6 @@ We are deprecating support for **uploading backups to remote storage** using Ope
|
|||
|
||||
<div class="deprecation breaking-change" data-milestone="15.9">
|
||||
|
||||
### GitLab.com certificate-based integration with Kubernetes
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
||||
- Announced in GitLab <span class="milestone">14.5</span>
|
||||
- Removal in GitLab <span class="milestone">15.9</span> ([breaking change](https://docs.gitlab.com/update/terminology/#breaking-change))
|
||||
- To discuss this change or learn more, see the [deprecation issue](https://gitlab.com/groups/gitlab-org/configure/-/epics/8).
|
||||
|
||||
</div>
|
||||
|
||||
The certificate-based integration with Kubernetes will be [deprecated and removed](https://about.gitlab.com/blog/2021/11/15/deprecating-the-cert-based-kubernetes-integration/). As a GitLab.com user, on new namespaces, you will no longer be able to integrate GitLab and your cluster using the certificate-based approach as of GitLab 15.0. The integration for current users will be enabled per namespace.
|
||||
|
||||
For a more robust, secure, forthcoming, and reliable integration with Kubernetes, we recommend you use the
|
||||
[agent for Kubernetes](https://docs.gitlab.com/user/clusters/agent/) to connect Kubernetes clusters with GitLab. [How do I migrate?](https://docs.gitlab.com/user/infrastructure/clusters/migrate_to_gitlab_agent/)
|
||||
|
||||
Although an explicit removal date is set, we don't plan to remove this feature until the new solution has feature parity.
|
||||
For more information about the blockers to removal, see [this issue](https://gitlab.com/gitlab-org/configure/general/-/issues/199).
|
||||
|
||||
For updates and details about this deprecation, follow [this epic](https://gitlab.com/groups/gitlab-org/configure/-/epics/8).
|
||||
|
||||
GitLab Self-Managed customers can still use the feature [with a feature flag](https://docs.gitlab.com/update/deprecations/#self-managed-certificate-based-integration-with-kubernetes).
|
||||
|
||||
</div>
|
||||
|
||||
<div class="deprecation breaking-change" data-milestone="15.9">
|
||||
|
||||
### Live Preview no longer available in the Web IDE
|
||||
|
||||
<div class="deprecation-notes">
|
||||
|
|
|
|||
|
|
@ -266,7 +266,7 @@ Prerequisites:
|
|||
[enable the extension marketplace](../../administration/settings/vscode_extension_marketplace.md).
|
||||
|
||||
If you have the Owner role for a top-level group, you can enable the
|
||||
[extension marketplace](../enterprise_user/_index.md#enable-the-extension-marketplace-for-the-web-ide-and-workspaces) for enterprise users.
|
||||
[extension marketplace](_index.md#enable-the-extension-marketplace-for-the-web-ide-and-workspaces) for enterprise users.
|
||||
|
||||
To enable the extension marketplace for the
|
||||
[Web IDE](../project/web_ide/_index.md) and [workspaces](../workspace/_index.md):
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ Alternatively, you can remove a linked item using the `/unlink` [quick action](.
|
|||
|
||||
## Related topics
|
||||
|
||||
- [Work items](../work_items/_index.md)
|
||||
- [Work items](_index.md)
|
||||
- [Issues](../project/issues/_index.md)
|
||||
- [Epics](../group/epics/_index.md)
|
||||
- [Tasks](../tasks.md)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Gitlab
|
||||
module BackgroundMigration
|
||||
class BackfillAmazonInstanceAuditEventDestinations < BatchedMigrationJob
|
||||
feature_category :audit_events
|
||||
|
||||
def perform
|
||||
# CE implementation is a no-op
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Gitlab::BackgroundMigration::BackfillAmazonInstanceAuditEventDestinations.prepend_mod
|
||||
|
|
@ -60792,9 +60792,6 @@ msgstr ""
|
|||
msgid "This project is archived and read-only. To resume pull mirroring, unarchive the project."
|
||||
msgstr ""
|
||||
|
||||
msgid "This project is hidden because its creator has been banned"
|
||||
msgstr ""
|
||||
|
||||
msgid "This project is licensed under the %{strong_start}%{license_name}%{strong_end}."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -61643,9 +61640,6 @@ msgstr ""
|
|||
msgid "Todos|Due today"
|
||||
msgstr ""
|
||||
|
||||
msgid "Todos|Epic"
|
||||
msgstr ""
|
||||
|
||||
msgid "Todos|Failed adding todo. Try again later."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -61679,7 +61673,7 @@ msgstr ""
|
|||
msgid "Todos|Isn't an empty To-Do List beautiful?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Todos|Issue"
|
||||
msgid "Todos|Issue or Epic"
|
||||
msgstr ""
|
||||
|
||||
msgid "Todos|It's how you always know what to work on next."
|
||||
|
|
@ -61885,9 +61879,6 @@ msgstr ""
|
|||
msgid "Todos|Wiki page"
|
||||
msgstr ""
|
||||
|
||||
msgid "Todos|Work item"
|
||||
msgstr ""
|
||||
|
||||
msgid "Todos|You"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@
|
|||
"@gitlab/favicon-overlay": "2.0.0",
|
||||
"@gitlab/fonts": "^1.3.0",
|
||||
"@gitlab/query-language-rust": "0.5.2",
|
||||
"@gitlab/svgs": "3.123.0",
|
||||
"@gitlab/svgs": "3.126.0",
|
||||
"@gitlab/ui": "111.9.1",
|
||||
"@gitlab/vue-router-vue3": "npm:vue-router@4.5.0",
|
||||
"@gitlab/vuex-vue3": "npm:vuex@4.1.0",
|
||||
|
|
|
|||
|
|
@ -25,12 +25,6 @@ RSpec.describe ProjectsFinder, feature_category: :groups_and_projects do
|
|||
create(:project, :private, name: 'D', path: 'D')
|
||||
end
|
||||
|
||||
let_it_be(:banned_user_project) do
|
||||
create(:project, :public, name: 'Project created by a banned user', creator: create(:user, :banned)).tap do |p|
|
||||
create(:project_authorization, :owner, user: p.creator, project: p)
|
||||
end
|
||||
end
|
||||
|
||||
let(:params) { {} }
|
||||
let(:current_user) { user }
|
||||
let(:project_ids_relation) { nil }
|
||||
|
|
@ -554,22 +548,13 @@ RSpec.describe ProjectsFinder, feature_category: :groups_and_projects do
|
|||
public_project,
|
||||
internal_project,
|
||||
private_project,
|
||||
shared_project,
|
||||
banned_user_project
|
||||
shared_project
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with admin mode disabled' do
|
||||
it { is_expected.to match_array([public_project, internal_project]) }
|
||||
|
||||
context 'when hide_projects_of_banned_users FF is disabled' do
|
||||
before do
|
||||
stub_feature_flags(hide_projects_of_banned_users: false)
|
||||
end
|
||||
|
||||
it { is_expected.to match_array([public_project, internal_project, banned_user_project]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -29,9 +29,6 @@ Vue.use(VueApollo);
|
|||
describe('Agents', () => {
|
||||
let wrapper;
|
||||
|
||||
const defaultProps = {
|
||||
defaultBranchName: 'default',
|
||||
};
|
||||
const provideData = {
|
||||
fullPath: 'path/to/project/group',
|
||||
};
|
||||
|
|
@ -94,7 +91,6 @@ describe('Agents', () => {
|
|||
wrapper = shallowMount(Agents, {
|
||||
apolloProvider,
|
||||
propsData: {
|
||||
...defaultProps,
|
||||
...props,
|
||||
},
|
||||
provide: {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import { clusterAgentsResponse } from './mock_data';
|
|||
Vue.use(VueApollo);
|
||||
|
||||
const fullPath = 'path/to/project';
|
||||
const defaultBranchName = 'default';
|
||||
const agent = {
|
||||
id: 'agent-id',
|
||||
name: 'agent-name',
|
||||
|
|
@ -53,7 +52,6 @@ describe('DeleteAgentButton', () => {
|
|||
query: getAgentsQuery,
|
||||
variables: {
|
||||
fullPath,
|
||||
defaultBranchName,
|
||||
isGroup: false,
|
||||
},
|
||||
data: clusterAgentsResponse.data,
|
||||
|
|
@ -71,7 +69,6 @@ describe('DeleteAgentButton', () => {
|
|||
isGroup: false,
|
||||
};
|
||||
const propsData = {
|
||||
defaultBranchName,
|
||||
agent,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ exports[`Design note pin component should match the snapshot of note without ind
|
|||
type="button"
|
||||
>
|
||||
<gl-icon-stub
|
||||
class="gl-bg-white gl-border-2 gl-border-solid gl-border-white gl-rounded-full"
|
||||
class="gl-bg-neutral-0 gl-border-2 gl-border-neutral-0 gl-border-solid gl-rounded-full gl-text-neutral-950"
|
||||
name="image-comment-dark"
|
||||
size="24"
|
||||
variant="current"
|
||||
|
|
@ -35,7 +35,7 @@ exports[`Design note pin component should match the snapshot when pin is resolve
|
|||
type="button"
|
||||
>
|
||||
<gl-icon-stub
|
||||
class="gl-bg-white gl-border-2 gl-border-solid gl-border-white gl-rounded-full"
|
||||
class="gl-bg-neutral-0 gl-border-2 gl-border-neutral-0 gl-border-solid gl-rounded-full gl-text-neutral-950"
|
||||
name="image-comment-dark"
|
||||
size="24"
|
||||
variant="current"
|
||||
|
|
@ -50,7 +50,7 @@ exports[`Design note pin component should match the snapshot when position is ab
|
|||
type="button"
|
||||
>
|
||||
<gl-icon-stub
|
||||
class="gl-bg-white gl-border-2 gl-border-solid gl-border-white gl-rounded-full"
|
||||
class="gl-bg-neutral-0 gl-border-2 gl-border-neutral-0 gl-border-solid gl-rounded-full gl-text-neutral-950"
|
||||
name="image-comment-dark"
|
||||
size="24"
|
||||
variant="current"
|
||||
|
|
|
|||
|
|
@ -68,11 +68,10 @@ describe('Visibility icon button', () => {
|
|||
|
||||
describe('if item represents project', () => {
|
||||
it.each`
|
||||
visibilityLevel | visibilityTooltip | visibilityIcon | tooltipPlacement
|
||||
${VISIBILITY_LEVEL_PUBLIC_STRING} | ${PROJECT_VISIBILITY_TYPE[VISIBILITY_LEVEL_PUBLIC_STRING]} | ${VISIBILITY_TYPE_ICON[VISIBILITY_LEVEL_PUBLIC_STRING]} | ${'top'}
|
||||
${VISIBILITY_LEVEL_INTERNAL_STRING} | ${PROJECT_VISIBILITY_TYPE[VISIBILITY_LEVEL_INTERNAL_STRING]} | ${VISIBILITY_TYPE_ICON[VISIBILITY_LEVEL_INTERNAL_STRING]} | ${'bottom'}
|
||||
${VISIBILITY_LEVEL_PRIVATE_STRING} | ${PROJECT_VISIBILITY_TYPE[VISIBILITY_LEVEL_PRIVATE_STRING]} | ${VISIBILITY_TYPE_ICON[VISIBILITY_LEVEL_PRIVATE_STRING]} | ${'left'}
|
||||
${'banned'} | ${'This project is hidden because its creator has been banned'} | ${'spam'} | ${'right'}
|
||||
visibilityLevel | visibilityTooltip | visibilityIcon | tooltipPlacement
|
||||
${VISIBILITY_LEVEL_PUBLIC_STRING} | ${PROJECT_VISIBILITY_TYPE[VISIBILITY_LEVEL_PUBLIC_STRING]} | ${VISIBILITY_TYPE_ICON[VISIBILITY_LEVEL_PUBLIC_STRING]} | ${'top'}
|
||||
${VISIBILITY_LEVEL_INTERNAL_STRING} | ${PROJECT_VISIBILITY_TYPE[VISIBILITY_LEVEL_INTERNAL_STRING]} | ${VISIBILITY_TYPE_ICON[VISIBILITY_LEVEL_INTERNAL_STRING]} | ${'bottom'}
|
||||
${VISIBILITY_LEVEL_PRIVATE_STRING} | ${PROJECT_VISIBILITY_TYPE[VISIBILITY_LEVEL_PRIVATE_STRING]} | ${VISIBILITY_TYPE_ICON[VISIBILITY_LEVEL_PRIVATE_STRING]} | ${'left'}
|
||||
`(
|
||||
'should return corresponding text when visibility level is $visibilityLevel',
|
||||
({ visibilityLevel, visibilityTooltip, visibilityIcon, tooltipPlacement }) => {
|
||||
|
|
|
|||
|
|
@ -938,13 +938,6 @@ RSpec.describe ApplicationHelper do
|
|||
it_behaves_like 'returns icon with tooltip'
|
||||
end
|
||||
|
||||
context 'when resource is a project' do
|
||||
let_it_be(:resource) { build(:project) }
|
||||
let(:expected_title) { 'This project is hidden because its creator has been banned' }
|
||||
|
||||
it_behaves_like 'returns icon with tooltip'
|
||||
end
|
||||
|
||||
context 'when css_class is provided' do
|
||||
let_it_be(:resource) { build(:issue) }
|
||||
|
||||
|
|
|
|||
|
|
@ -1294,28 +1294,6 @@ RSpec.describe ProjectsHelper, feature_category: :source_code_management do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#visibility_level_name' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:banned_user, :feature_flag_enabled, :expected) do
|
||||
true | true | 'banned'
|
||||
false | false | 'private'
|
||||
true | false | 'private'
|
||||
false | true | 'private'
|
||||
end
|
||||
|
||||
with_them do
|
||||
before do
|
||||
stub_feature_flags(hide_projects_of_banned_users: feature_flag_enabled)
|
||||
allow(project).to receive(:created_and_owned_by_banned_user?).and_return(banned_user)
|
||||
end
|
||||
|
||||
subject { visibility_level_name(project) }
|
||||
|
||||
it { is_expected.to eq(expected) }
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'configure import method modal' do
|
||||
context 'as a user' do
|
||||
it 'returns a link to contact an administrator' do
|
||||
|
|
@ -1835,35 +1813,6 @@ RSpec.describe ProjectsHelper, feature_category: :source_code_management do
|
|||
end
|
||||
|
||||
it_behaves_like 'returns visibility level content_tag'
|
||||
|
||||
context 'when project creator is banned' do
|
||||
let(:hidden_resource_icon) { '<svg>fake hidden resource icon</svg>' }
|
||||
|
||||
before do
|
||||
allow(project).to receive(:created_and_owned_by_banned_user?).and_return(true)
|
||||
allow(helper).to receive(:hidden_resource_icon).and_return(hidden_resource_icon)
|
||||
end
|
||||
|
||||
it 'returns hidden resource icon' do
|
||||
expect(helper.visibility_level_content(project)).to eq hidden_resource_icon
|
||||
end
|
||||
end
|
||||
|
||||
context 'with hide_projects_of_banned_users feature flag disabled' do
|
||||
before do
|
||||
stub_feature_flags(hide_projects_of_banned_users: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'returns visibility level content_tag'
|
||||
|
||||
context 'when project creator is banned' do
|
||||
before do
|
||||
allow(project).to receive(:created_and_owned_by_banned_user?).and_return(true)
|
||||
end
|
||||
|
||||
it_behaves_like 'returns visibility level content_tag'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#hidden_issue_icon' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe QueueBackfillAmazonInstanceAuditEventDestinations,
|
||||
migration: :gitlab_main,
|
||||
feature_category: :audit_events do
|
||||
let(:batched_migration) { described_class::MIGRATION }
|
||||
|
||||
it 'schedules a new batched migration' do
|
||||
reversible_migration do |migration|
|
||||
migration.before -> {
|
||||
expect(batched_migration).not_to have_scheduled_batched_migration
|
||||
}
|
||||
|
||||
migration.after -> {
|
||||
expect(batched_migration).to have_scheduled_batched_migration(
|
||||
table_name: :audit_events_instance_amazon_s3_configurations,
|
||||
column_name: :id,
|
||||
interval: described_class::DELAY_INTERVAL,
|
||||
batch_size: described_class::BATCH_SIZE,
|
||||
sub_batch_size: described_class::SUB_BATCH_SIZE,
|
||||
gitlab_schema: :gitlab_main
|
||||
)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
it 'removes scheduled migration when rolling back' do
|
||||
disable_migrations_output do
|
||||
migrate!
|
||||
schema_migrate_down!
|
||||
end
|
||||
|
||||
expect(batched_migration).not_to have_scheduled_batched_migration
|
||||
end
|
||||
end
|
||||
|
|
@ -9529,109 +9529,6 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
|
|||
end
|
||||
end
|
||||
|
||||
describe '.without_created_and_owned_by_banned_user' do
|
||||
let_it_be(:other_project) { create(:project) }
|
||||
|
||||
subject(:results) { described_class.without_created_and_owned_by_banned_user }
|
||||
|
||||
context 'when project creator is not banned' do
|
||||
let_it_be(:project_of_active_user) { create(:project, creator: create(:user)) }
|
||||
|
||||
it 'includes the project' do
|
||||
expect(results).to match_array([other_project, project_of_active_user])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project creator is banned' do
|
||||
let_it_be(:banned_user) { create(:user, :banned) }
|
||||
let_it_be(:project_of_banned_user) { create(:project, creator: banned_user) }
|
||||
|
||||
context 'when project creator is also an owner' do
|
||||
let_it_be(:project_auth) do
|
||||
project = project_of_banned_user
|
||||
create(:project_authorization, :owner, user: project.creator, project: project)
|
||||
end
|
||||
|
||||
it 'excludes the project' do
|
||||
expect(results).to match_array([other_project])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project creator is not an owner' do
|
||||
it 'includes the project' do
|
||||
expect(results).to match_array([other_project, project_of_banned_user])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.with_created_and_owned_by_banned_user' do
|
||||
let_it_be(:other_project) { create(:project) }
|
||||
|
||||
subject(:results) { described_class.with_created_and_owned_by_banned_user }
|
||||
|
||||
context 'when project creator is not banned' do
|
||||
let_it_be(:project_of_active_user) { create(:project, creator: create(:user)) }
|
||||
|
||||
it 'does not include the project' do
|
||||
expect(results).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project creator is banned' do
|
||||
let_it_be(:banned_user) { create(:user, :banned) }
|
||||
let_it_be(:project_of_banned_user) { create(:project, creator: banned_user) }
|
||||
|
||||
context 'when project creator is also an owner' do
|
||||
let_it_be(:project_auth) do
|
||||
project = project_of_banned_user
|
||||
create(:project_authorization, :owner, user: project.creator, project: project)
|
||||
end
|
||||
|
||||
it 'includes the banned user project' do
|
||||
expect(results).to match_array([project_of_banned_user])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when project creator is not an owner' do
|
||||
it 'does not include the project' do
|
||||
expect(results).not_to be_present
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#created_and_owned_by_banned_user?' do
|
||||
subject { project.created_and_owned_by_banned_user? }
|
||||
|
||||
context 'when creator is banned' do
|
||||
let_it_be(:creator) { create(:user, :banned) }
|
||||
let_it_be(:project) { create(:project, creator: creator) }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
|
||||
context 'when creator is an owner' do
|
||||
let_it_be(:project_auth) do
|
||||
create(:project_authorization, :owner, user: project.creator, project: project)
|
||||
end
|
||||
|
||||
it { is_expected.to eq true }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when creator is not banned' do
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
|
||||
context 'when there is no creator' do
|
||||
let_it_be(:project) { build_stubbed(:project, creator: nil) }
|
||||
|
||||
it { is_expected.to eq false }
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'something that has web-hooks' do
|
||||
let_it_be_with_reload(:object) { create(:project) }
|
||||
|
||||
|
|
|
|||
|
|
@ -124,31 +124,5 @@ RSpec.describe Namespaces::ProjectNamespacePolicy, feature_category: :groups_and
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when project is created and owned by a banned user' do
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
|
||||
let(:current_user) { developer }
|
||||
|
||||
before do
|
||||
allow(project).to receive(:created_and_owned_by_banned_user?).and_return(true)
|
||||
end
|
||||
|
||||
it { expect_disallowed(:read_project, :read_namespace) }
|
||||
|
||||
context 'when current user is an admin', :enable_admin_mode do
|
||||
let(:current_user) { admin }
|
||||
|
||||
it { expect_allowed(:read_project, :read_namespace) }
|
||||
end
|
||||
|
||||
context 'when hide_projects_of_banned_users FF is disabled' do
|
||||
before do
|
||||
stub_feature_flags(hide_projects_of_banned_users: false)
|
||||
end
|
||||
|
||||
it { expect_allowed(:read_project, :read_namespace) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3978,32 +3978,6 @@ RSpec.describe ProjectPolicy, feature_category: :system_access do
|
|||
end
|
||||
end
|
||||
|
||||
describe 'when project is created and owned by a banned user' do
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
|
||||
let(:current_user) { guest }
|
||||
|
||||
before do
|
||||
allow(project).to receive(:created_and_owned_by_banned_user?).and_return(true)
|
||||
end
|
||||
|
||||
it { expect_disallowed(:read_project) }
|
||||
|
||||
context 'when current user is an admin', :enable_admin_mode do
|
||||
let(:current_user) { admin }
|
||||
|
||||
it { expect_allowed(:read_project) }
|
||||
end
|
||||
|
||||
context 'when hide_projects_of_banned_users FF is disabled' do
|
||||
before do
|
||||
stub_feature_flags(hide_projects_of_banned_users: false)
|
||||
end
|
||||
|
||||
it { expect_allowed(:read_project) }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'webhooks' do
|
||||
context 'when the current_user is a maintainer' do
|
||||
let(:current_user) { maintainer }
|
||||
|
|
|
|||
|
|
@ -218,11 +218,10 @@ RSpec.describe 'getting project information', feature_category: :groups_and_proj
|
|||
end
|
||||
|
||||
context 'for N+1 queries with isCatalogResource' do
|
||||
let_it_be(:project1) { create(:project, group: group) }
|
||||
let_it_be(:project2) { create(:project, group: group) }
|
||||
let_it_be(:project1) { create(:project, group: group, owners: current_user) }
|
||||
let_it_be(:project2) { create(:project, group: group, owners: current_user) }
|
||||
|
||||
it 'avoids N+1 database queries' do
|
||||
pending('See: https://gitlab.com/gitlab-org/gitlab/-/issues/403634')
|
||||
it 'avoids N+1 database queries', :request_store do
|
||||
ctx = { current_user: current_user }
|
||||
|
||||
baseline_query = graphql_query_for(:project, { full_path: project1.full_path }, 'isCatalogResource')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Groups::RestoreService, feature_category: :groups_and_projects do
|
||||
let(:user) { create(:user) }
|
||||
let(:group) do
|
||||
create(:group_with_deletion_schedule,
|
||||
marked_for_deletion_on: 1.day.ago,
|
||||
deleting_user: user)
|
||||
end
|
||||
|
||||
subject(:execute) { described_class.new(group, user, {}).execute }
|
||||
|
||||
context 'when restoring the group' do
|
||||
context 'with a user that can admin the group' do
|
||||
before do
|
||||
group.add_owner(user)
|
||||
end
|
||||
|
||||
context 'for a group that has been marked for deletion' do
|
||||
it 'removes the mark for deletion' do
|
||||
execute
|
||||
|
||||
expect(group.marked_for_deletion_on).to be_nil
|
||||
expect(group.deleting_user).to be_nil
|
||||
end
|
||||
|
||||
it 'returns success' do
|
||||
result = execute
|
||||
|
||||
expect(result).to eq({ status: :success })
|
||||
end
|
||||
|
||||
context 'when restoring fails' do
|
||||
it 'returns error' do
|
||||
allow(group.deletion_schedule).to receive(:destroy).and_return(false)
|
||||
|
||||
result = execute
|
||||
|
||||
expect(result).to eq({ status: :error, message: 'Could not restore the group' })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'for a group that has not been marked for deletion' do
|
||||
let(:group) { create(:group) }
|
||||
|
||||
it 'does not change the attributes associated with delayed deletion' do
|
||||
execute
|
||||
|
||||
expect(group.marked_for_deletion_on).to be_nil
|
||||
expect(group.deleting_user).to be_nil
|
||||
end
|
||||
|
||||
it 'returns error' do
|
||||
result = execute
|
||||
|
||||
expect(result).to eq({ status: :error, message: 'Group has not been marked for deletion' })
|
||||
end
|
||||
end
|
||||
|
||||
it 'logs the restore' do
|
||||
allow(Gitlab::AppLogger).to receive(:info)
|
||||
|
||||
expect(::Gitlab::AppLogger).to receive(:info).with("User #{user.id} restored group #{group.full_path}")
|
||||
|
||||
execute
|
||||
end
|
||||
|
||||
context 'when the group is deletion is in progress' do
|
||||
before do
|
||||
group.namespace_details.update!(deleted_at: Time.current)
|
||||
end
|
||||
|
||||
it { is_expected.to eq({ status: :error, message: 'Group deletion is in progress' }) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a user that cannot admin the group' do
|
||||
it 'does not restore the group' do
|
||||
execute
|
||||
|
||||
expect(group.marked_for_deletion?).to be_truthy
|
||||
end
|
||||
|
||||
it 'returns error' do
|
||||
result = execute
|
||||
|
||||
expect(result).to eq({ status: :error, message: 'You are not authorized to perform this action' })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -15,5 +15,19 @@ RSpec.describe Projects::OpenMergeRequestsCountService, :use_clean_rails_memory_
|
|||
|
||||
expect(subject.count).to eq(1)
|
||||
end
|
||||
|
||||
context 'when there are hidden merge requests' do
|
||||
let(:banned_user) { create(:user, :banned) }
|
||||
|
||||
before do
|
||||
create(:merge_request, :opened, source_project: project, target_project: project, source_branch: 'user-branch')
|
||||
create(:merge_request, :opened, source_project: project, target_project: project,
|
||||
source_branch: 'banned-user-branch', author: banned_user)
|
||||
end
|
||||
|
||||
it 'does not include hidden merge requests in the count' do
|
||||
expect(described_class.new(project).count).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Projects::RestoreService, feature_category: :groups_and_projects do
|
||||
let(:user) { create(:user, :with_namespace) }
|
||||
let(:pending_delete) { nil }
|
||||
let(:project) do
|
||||
create(:project,
|
||||
:repository,
|
||||
path: 'project-1-deleted-177483',
|
||||
name: 'Project1 Name-deleted-177483',
|
||||
namespace: user.namespace,
|
||||
marked_for_deletion_at: 1.day.ago,
|
||||
deleting_user: user,
|
||||
archived: true,
|
||||
hidden: true,
|
||||
pending_delete: pending_delete)
|
||||
end
|
||||
|
||||
context 'when restoring project' do
|
||||
subject(:execute) { described_class.new(project, user).execute }
|
||||
|
||||
it 'marks project as not hidden, unarchived and not marked for deletion' do
|
||||
expect(Namespaces::ScheduleAggregationWorker).to receive(:perform_async)
|
||||
.with(project.namespace.id).and_call_original
|
||||
|
||||
execute
|
||||
|
||||
expect(Project.unscoped.all).to include(project)
|
||||
expect(project.archived).to be(false)
|
||||
expect(project.hidden).to be(false)
|
||||
expect(project.marked_for_deletion_at).to be_nil
|
||||
expect(project.deleting_user).to be_nil
|
||||
end
|
||||
|
||||
context 'when the original project path is not taken' do
|
||||
it 'renames the project back to its original path' do
|
||||
expect { execute }.to change { project.path }.from("project-1-deleted-177483").to("project-1")
|
||||
end
|
||||
|
||||
it 'renames the project back to its original name' do
|
||||
expect { execute }.to change { project.name }.from("Project1 Name-deleted-177483").to("Project1 Name")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the original project name has been taken' do
|
||||
before do
|
||||
create(:project, path: 'project-1', name: 'Project1 Name', namespace: user.namespace, deleting_user: user)
|
||||
end
|
||||
|
||||
it 'renames the project back to its original path with a suffix' do
|
||||
expect { execute }.to change { project.path }.from("project-1-deleted-177483").to(/project-1-[a-zA-Z0-9]{5}/)
|
||||
end
|
||||
|
||||
it 'renames the project back to its original name with a suffix' do
|
||||
expect { execute }.to change { project.name }.from("Project1 Name-deleted-177483")
|
||||
.to(/Project1 Name-[a-zA-Z0-9]{5}/)
|
||||
end
|
||||
|
||||
it 'uses the same suffix for both the path and name' do
|
||||
execute
|
||||
|
||||
path_suffix = project.path.split('-')[-1]
|
||||
name_suffix = project.name.split('-')[-1]
|
||||
|
||||
expect(path_suffix).to eq(name_suffix)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the original project path does not contain the -deleted- suffix' do
|
||||
let(:project) do
|
||||
create(
|
||||
:project,
|
||||
:repository,
|
||||
namespace: user.namespace,
|
||||
marked_for_deletion_at: 1.day.ago,
|
||||
deleting_user: user,
|
||||
archived: true,
|
||||
pending_delete: pending_delete
|
||||
)
|
||||
end
|
||||
|
||||
it 'renames the project back to its original path' do
|
||||
expect { execute }.not_to change { project.path }
|
||||
end
|
||||
|
||||
it 'renames the project back to its original name' do
|
||||
expect { execute }.not_to change { project.name }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when restoring project already in process of removal' do
|
||||
let(:deletion_date) { 2.days.ago }
|
||||
let(:pending_delete) { true }
|
||||
|
||||
it 'does not allow to restore' do
|
||||
expect(described_class.new(project, user).execute).to include(status: :error)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1436,10 +1436,10 @@
|
|||
stylelint-declaration-strict-value "1.10.4"
|
||||
stylelint-scss "6.0.0"
|
||||
|
||||
"@gitlab/svgs@3.123.0":
|
||||
version "3.123.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.123.0.tgz#1fa3b1a709755ff7c8ef67e18c0442101655ebf0"
|
||||
integrity sha512-yjVn+utOTIKk8d9JlvGo6EgJ4TQ+CKpe3RddflAqtsQqQuL/2MlVdtaUePybxYzWIaumFuh5LouQ6BrWyw1niQ==
|
||||
"@gitlab/svgs@3.126.0":
|
||||
version "3.126.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.126.0.tgz#1c0bb95c11de808b78afd05dc95aca258c3b39f0"
|
||||
integrity sha512-7X8uzitNn7NDcVy+FVCw8npMNEUpLGHTO5Z+BJZqVILj/FD+0WveYdPxAEVa9hXYQn5qXWM0ZAknzB9LM6Id8w==
|
||||
|
||||
"@gitlab/ui@111.9.1":
|
||||
version "111.9.1"
|
||||
|
|
|
|||
Loading…
Reference in New Issue