Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
d872c89ce4
commit
e684f438e6
60 changed files with 786 additions and 169 deletions
|
@ -5,17 +5,17 @@ import { clamp } from '../services/utils';
|
|||
|
||||
export const tableContentType = 'table';
|
||||
|
||||
const MIN_ROWS = 3;
|
||||
const MIN_COLS = 3;
|
||||
const MAX_ROWS = 8;
|
||||
const MAX_COLS = 8;
|
||||
const MIN_ROWS = 5;
|
||||
const MIN_COLS = 5;
|
||||
const MAX_ROWS = 10;
|
||||
const MAX_COLS = 10;
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlButton,
|
||||
GlDropdown,
|
||||
GlDropdownDivider,
|
||||
GlDropdownForm,
|
||||
GlButton,
|
||||
},
|
||||
inject: ['tiptapEditor'],
|
||||
data() {
|
||||
|
@ -62,22 +62,22 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-dropdown size="small" category="tertiary" icon="table">
|
||||
<gl-dropdown-form class="gl-px-3! gl-w-auto!">
|
||||
<div class="gl-w-auto!">
|
||||
<div v-for="r of list(maxRows)" :key="r" class="gl-display-flex">
|
||||
<gl-button
|
||||
v-for="c of list(maxCols)"
|
||||
:key="c"
|
||||
:data-testid="`table-${r}-${c}`"
|
||||
:class="{ 'gl-bg-blue-50!': r <= rows && c <= cols }"
|
||||
:aria-label="getButtonLabel(r, c)"
|
||||
class="gl-display-inline! gl-px-0! gl-w-5! gl-h-5! gl-rounded-0!"
|
||||
@mouseover="setRowsAndCols(r, c)"
|
||||
@click="insertTable()"
|
||||
/>
|
||||
</div>
|
||||
<gl-dropdown-divider />
|
||||
<gl-dropdown size="small" category="tertiary" icon="table" class="table-dropdown">
|
||||
<gl-dropdown-form class="gl-px-3!">
|
||||
<div v-for="r of list(maxRows)" :key="r" class="gl-display-flex">
|
||||
<gl-button
|
||||
v-for="c of list(maxCols)"
|
||||
:key="c"
|
||||
:data-testid="`table-${r}-${c}`"
|
||||
:class="{ 'active gl-bg-blue-50!': r <= rows && c <= cols }"
|
||||
:aria-label="getButtonLabel(r, c)"
|
||||
class="table-creator-grid-item gl-display-inline gl-rounded-0! gl-w-6! gl-h-6! gl-p-0!"
|
||||
@mouseover="setRowsAndCols(r, c)"
|
||||
@click="insertTable()"
|
||||
/>
|
||||
</div>
|
||||
<gl-dropdown-divider class="gl-my-3! gl-mx-n3!" />
|
||||
<div class="gl-px-1">
|
||||
{{ getButtonLabel(rows, cols) }}
|
||||
</div>
|
||||
</gl-dropdown-form>
|
||||
|
|
10
app/assets/javascripts/integrations/overrides/api.js
Normal file
10
app/assets/javascripts/integrations/overrides/api.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
export const fetchOverrides = (overridesPath, { page, perPage }) => {
|
||||
return axios.get(overridesPath, {
|
||||
params: {
|
||||
page,
|
||||
per_page: perPage,
|
||||
},
|
||||
});
|
||||
};
|
|
@ -1,15 +1,127 @@
|
|||
<script>
|
||||
import { GlLink, GlLoadingIcon, GlPagination, GlTable } from '@gitlab/ui';
|
||||
|
||||
import { DEFAULT_PER_PAGE } from '~/api';
|
||||
import createFlash from '~/flash';
|
||||
import { fetchOverrides } from '~/integrations/overrides/api';
|
||||
import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
|
||||
import { truncateNamespace } from '~/lib/utils/text_utility';
|
||||
import { __, s__ } from '~/locale';
|
||||
import ProjectAvatar from '~/vue_shared/components/project_avatar.vue';
|
||||
|
||||
export default {
|
||||
name: 'IntegrationOverrides',
|
||||
components: {
|
||||
GlLink,
|
||||
GlLoadingIcon,
|
||||
GlPagination,
|
||||
GlTable,
|
||||
ProjectAvatar,
|
||||
},
|
||||
props: {
|
||||
overridesPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
key: 'name',
|
||||
label: __('Project'),
|
||||
},
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
isLoading: true,
|
||||
overrides: [],
|
||||
page: 1,
|
||||
totalItems: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showPagination() {
|
||||
return this.totalItems > this.$options.DEFAULT_PER_PAGE && this.overrides.length > 0;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadOverrides();
|
||||
},
|
||||
methods: {
|
||||
loadOverrides(page = this.page) {
|
||||
this.isLoading = true;
|
||||
|
||||
fetchOverrides(this.overridesPath, {
|
||||
page,
|
||||
perPage: this.$options.DEFAULT_PER_PAGE,
|
||||
})
|
||||
.then(({ data, headers }) => {
|
||||
const { page: newPage, total } = parseIntPagination(normalizeHeaders(headers));
|
||||
this.page = newPage;
|
||||
this.totalItems = total;
|
||||
this.overrides = data;
|
||||
})
|
||||
.catch((error) => {
|
||||
createFlash({
|
||||
message: this.$options.i18n.defaultErrorMessage,
|
||||
error,
|
||||
captureError: true,
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
},
|
||||
truncateNamespace,
|
||||
},
|
||||
DEFAULT_PER_PAGE,
|
||||
i18n: {
|
||||
defaultErrorMessage: s__(
|
||||
'Integrations|An error occurred while loading projects using custom settings.',
|
||||
),
|
||||
tableEmptyText: s__('Integrations|There are no projects using custom settings'),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div></div>
|
||||
<div>
|
||||
<gl-table
|
||||
:items="overrides"
|
||||
:fields="$options.fields"
|
||||
:busy="isLoading"
|
||||
show-empty
|
||||
:empty-text="$options.i18n.tableEmptyText"
|
||||
>
|
||||
<template #cell(name)="{ item }">
|
||||
<gl-link
|
||||
class="gl-display-inline-flex gl-align-items-center gl-hover-text-decoration-none gl-text-body!"
|
||||
:href="item.full_path"
|
||||
>
|
||||
<project-avatar
|
||||
class="gl-mr-3"
|
||||
:project-avatar-url="item.avatar_url"
|
||||
:project-name="item.name"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{{ truncateNamespace(item.full_name) }} /
|
||||
|
||||
<strong>{{ item.name }}</strong>
|
||||
</gl-link>
|
||||
</template>
|
||||
|
||||
<template #table-busy>
|
||||
<gl-loading-icon size="md" class="gl-my-2" />
|
||||
</template>
|
||||
</gl-table>
|
||||
<div class="gl-display-flex gl-justify-content-center gl-mt-5">
|
||||
<gl-pagination
|
||||
v-if="showPagination"
|
||||
:per-page="$options.DEFAULT_PER_PAGE"
|
||||
:total-items="totalItems"
|
||||
:value="page"
|
||||
:disabled="isLoading"
|
||||
@input="loadOverrides"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
import { GlLink, GlDropdown, GlDropdownItem } from '@gitlab/ui';
|
||||
import { isEmpty } from 'lodash';
|
||||
import CiIcon from '~/vue_shared/components/ci_icon.vue';
|
||||
import clipboardButton from '~/vue_shared/components/clipboard_button.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CiIcon,
|
||||
clipboardButton,
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlLink,
|
||||
|
@ -45,7 +47,7 @@ export default {
|
|||
<template>
|
||||
<div class="dropdown">
|
||||
<div class="js-pipeline-info" data-testid="pipeline-info">
|
||||
<ci-icon :status="pipeline.details.status" class="vertical-align-middle" />
|
||||
<ci-icon :status="pipeline.details.status" />
|
||||
|
||||
<span class="font-weight-bold">{{ s__('Job|Pipeline') }}</span>
|
||||
<gl-link
|
||||
|
@ -85,7 +87,14 @@ export default {
|
|||
</template>
|
||||
<gl-link v-else :href="pipeline.ref.path" class="link-commit ref-name">{{
|
||||
pipeline.ref.name
|
||||
}}</gl-link>
|
||||
}}</gl-link
|
||||
><clipboard-button
|
||||
:text="pipeline.ref.name"
|
||||
:title="__('Copy reference')"
|
||||
category="tertiary"
|
||||
size="small"
|
||||
data-testid="copy-source-ref-link"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -14,6 +14,11 @@ export default {
|
|||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
noteIsConfidential: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
noteableType: {
|
||||
type: String,
|
||||
required: false,
|
||||
|
@ -38,6 +43,9 @@ export default {
|
|||
emailParticipants() {
|
||||
return this.noteableData.issue_email_participants?.map(({ email }) => email) || [];
|
||||
},
|
||||
showEmailParticipantsWarning() {
|
||||
return this.emailParticipants.length && !this.noteIsConfidential;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -61,7 +69,7 @@ export default {
|
|||
/>
|
||||
<slot></slot>
|
||||
<email-participants-warning
|
||||
v-if="emailParticipants.length"
|
||||
v-if="showEmailParticipantsWarning"
|
||||
class="gl-border-t-1 gl-border-t-solid gl-border-t-gray-100 gl-rounded-base gl-rounded-top-left-none! gl-rounded-top-right-none!"
|
||||
:emails="emailParticipants"
|
||||
/>
|
||||
|
|
|
@ -380,6 +380,7 @@ export default {
|
|||
<comment-field-layout
|
||||
:with-alert-container="true"
|
||||
:noteable-data="getNoteableData"
|
||||
:note-is-confidential="noteIsConfidential"
|
||||
:noteable-type="noteableType"
|
||||
>
|
||||
<markdown-field
|
||||
|
|
|
@ -326,7 +326,10 @@ export default {
|
|||
></div>
|
||||
<div class="flash-container timeline-content"></div>
|
||||
<form :data-line-code="lineCode" class="edit-note common-note-form js-quick-submit gfm-form">
|
||||
<comment-field-layout :noteable-data="getNoteableData">
|
||||
<comment-field-layout
|
||||
:noteable-data="getNoteableData"
|
||||
:note-is-confidential="discussion.confidential"
|
||||
>
|
||||
<markdown-field
|
||||
:markdown-preview-path="markdownPreviewPath"
|
||||
:markdown-docs-path="markdownDocsPath"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
/* eslint-disable vue/no-v-html */
|
||||
import { s__ } from '~/locale';
|
||||
import { s__, n__ } from '~/locale';
|
||||
|
||||
export default {
|
||||
name: 'MRWidgetRelatedLinks',
|
||||
|
@ -24,7 +24,8 @@ export default {
|
|||
if (this.state === 'closed') {
|
||||
return s__('mrWidget|Did not close');
|
||||
}
|
||||
return s__('mrWidget|Closes');
|
||||
|
||||
return n__('mrWidget|Closes issue', 'mrWidget|Closes issues', this.relatedLinks.closingCount);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -33,7 +34,8 @@ export default {
|
|||
<section class="mr-info-list gl-ml-7 gl-pb-5">
|
||||
<p v-if="relatedLinks.closing">{{ closesText }} <span v-html="relatedLinks.closing"></span></p>
|
||||
<p v-if="relatedLinks.mentioned">
|
||||
{{ s__('mrWidget|Mentions') }} <span v-html="relatedLinks.mentioned"></span>
|
||||
{{ n__('mrWidget|Mentions issue', 'mrWidget|Mentions issues', relatedLinks.mentionedCount) }}
|
||||
<span v-html="relatedLinks.mentioned"></span>
|
||||
</p>
|
||||
<p v-if="relatedLinks.assignToMe"><span v-html="relatedLinks.assignToMe"></span></p>
|
||||
</section>
|
||||
|
|
|
@ -72,7 +72,13 @@ export default class MergeRequestStore {
|
|||
const assignToMe = links.assign_to_closing;
|
||||
|
||||
if (closing || mentioned || assignToMe) {
|
||||
this.relatedLinks = { closing, mentioned, assignToMe };
|
||||
this.relatedLinks = {
|
||||
closing,
|
||||
mentioned,
|
||||
assignToMe,
|
||||
closingCount: links.closing_count,
|
||||
mentionedCount: links.mentioned_count,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,3 +35,22 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-creator-grid-item {
|
||||
box-shadow: inset 0 0 0 $gl-spacing-scale-2 $white,
|
||||
inset $gl-spacing-scale-1 $gl-spacing-scale-1 0 #{$gl-spacing-scale-2 * 3 / 4} $gray-100,
|
||||
inset #{-$gl-spacing-scale-1} #{-$gl-spacing-scale-1} 0 #{$gl-spacing-scale-2 * 3 / 4} $gray-100 !important;
|
||||
|
||||
&.active {
|
||||
box-shadow: inset 0 0 0 $gl-spacing-scale-2 $white,
|
||||
inset $gl-spacing-scale-1 $gl-spacing-scale-1 0 $gl-spacing-scale-2 $blue-500,
|
||||
inset #{-$gl-spacing-scale-1} #{-$gl-spacing-scale-1} 0 $gl-spacing-scale-2 $blue-500 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.table-dropdown .dropdown-menu {
|
||||
@include gl-min-w-0;
|
||||
@include gl-w-auto;
|
||||
|
||||
@include gl-white-space-nowrap;
|
||||
}
|
||||
|
|
|
@ -26,8 +26,4 @@ class Admin::IntegrationsController < Admin::ApplicationController
|
|||
def find_or_initialize_non_project_specific_integration(name)
|
||||
Integration.find_or_initialize_non_project_specific_integration(name, instance: true)
|
||||
end
|
||||
|
||||
def instance_level_integration_overrides?
|
||||
Feature.enabled?(:instance_level_integration_overrides, default_enabled: :yaml)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -24,7 +24,8 @@ module ClustersHelper
|
|||
agent_docs_url: help_page_path('user/clusters/agent/index'),
|
||||
install_docs_url: help_page_path('administration/clusters/kas'),
|
||||
get_started_docs_url: help_page_path('user/clusters/agent/index', anchor: 'define-a-configuration-repository'),
|
||||
integration_docs_url: help_page_path('user/clusters/agent/index', anchor: 'get-started-with-gitops-and-the-gitlab-agent')
|
||||
integration_docs_url: help_page_path('user/clusters/agent/index', anchor: 'get-started-with-gitops-and-the-gitlab-agent'),
|
||||
kas_address: Gitlab::Kas.external_url
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -125,6 +125,17 @@ module IntegrationsHelper
|
|||
!Gitlab.com?
|
||||
end
|
||||
|
||||
def integration_tabs(integration:)
|
||||
[
|
||||
{ key: 'edit', text: _('Settings'), href: scoped_edit_integration_path(integration) },
|
||||
({ key: 'overrides', text: s_('Integrations|Projects using custom settings'), href: scoped_overrides_integration_path(integration) } if instance_level_integration_overrides?)
|
||||
].compact
|
||||
end
|
||||
|
||||
def instance_level_integration_overrides?
|
||||
Feature.enabled?(:instance_level_integration_overrides, default_enabled: :yaml)
|
||||
end
|
||||
|
||||
def jira_issue_breadcrumb_link(issue_reference)
|
||||
link_to '', { class: 'gl-display-flex gl-align-items-center gl-white-space-nowrap' } do
|
||||
icon = image_tag image_path('illustrations/logos/jira.svg'), width: 15, height: 15, class: 'gl-mr-2'
|
||||
|
|
|
@ -139,7 +139,6 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
|
|||
end
|
||||
|
||||
def mentioned_issues_links
|
||||
mentioned_issues = issues_mentioned_but_not_closing(current_user)
|
||||
markdown(
|
||||
issues_sentence(project, mentioned_issues),
|
||||
pipeline: :gfm,
|
||||
|
@ -239,6 +238,18 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
|
|||
APPROVALS_WIDGET_BASE_TYPE
|
||||
end
|
||||
|
||||
def closing_issues
|
||||
strong_memoize(:closing_issues) do
|
||||
visible_closing_issues_for(current_user)
|
||||
end
|
||||
end
|
||||
|
||||
def mentioned_issues
|
||||
strong_memoize(:mentioned_issues) do
|
||||
issues_mentioned_but_not_closing(current_user)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def cached_can_be_reverted?
|
||||
|
@ -253,10 +264,6 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
|
|||
# rubocop: enable CodeReuse/ServiceClass
|
||||
end
|
||||
|
||||
def closing_issues
|
||||
@closing_issues ||= visible_closing_issues_for(current_user)
|
||||
end
|
||||
|
||||
def pipeline
|
||||
@pipeline ||= actual_head_pipeline
|
||||
end
|
||||
|
|
|
@ -110,9 +110,17 @@ class MergeRequestWidgetEntity < Grape::Entity
|
|||
presenter(merge_request).closing_issues_links
|
||||
end
|
||||
|
||||
expose :closing_count do |merge_request|
|
||||
presenter(merge_request).closing_issues.size
|
||||
end
|
||||
|
||||
expose :mentioned_but_not_closing do |merge_request|
|
||||
presenter(merge_request).mentioned_issues_links
|
||||
end
|
||||
|
||||
expose :mentioned_count do |merge_request|
|
||||
presenter(merge_request).mentioned_issues.size
|
||||
end
|
||||
end
|
||||
|
||||
expose :codeclimate, if: -> (mr, _) { head_pipeline_downloadable_path_for_report_type(:codequality) } do
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
- if group_sidebar_link?(:wiki)
|
||||
= render 'layouts/nav/sidebar/wiki_link', wiki_url: @group.wiki.web_url
|
||||
|
||||
- if group_sidebar_link?(:settings)
|
||||
= nav_link(path: group_settings_nav_link_paths) do
|
||||
= link_to edit_group_path(@group), class: 'has-sub-items' do
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
= nav_link(controller: :wikis) do
|
||||
= link_to wiki_url, class: 'shortcuts-wiki', data: { qa_selector: 'wiki_link' } do
|
||||
.nav-icon-container
|
||||
= sprite_icon('book')
|
||||
%span.nav-item-name
|
||||
= _('Wiki')
|
||||
%ul.sidebar-sub-level-items.is-fly-out-only
|
||||
= nav_link(controller: :wikis, html_options: { class: "fly-out-top-item" } ) do
|
||||
= link_to wiki_url do
|
||||
%strong.fly-out-top-item-name
|
||||
= _('Wiki')
|
|
@ -1,7 +1,4 @@
|
|||
- integration = local_assigns.fetch(:integration)
|
||||
|
||||
%h3.page-title
|
||||
= integration.title
|
||||
|
||||
= form_for integration, as: :service, url: scoped_integration_path(integration), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'test-url' => scoped_test_integration_path(integration) } } do |form|
|
||||
= render 'shared/service_settings', form: form, integration: integration
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
.tabs.gl-tabs
|
||||
%div
|
||||
%ul.nav.gl-tabs-nav{ role: 'tablist' }
|
||||
%li.nav-item{ role: 'presentation' }
|
||||
%a.nav-link.gl-tab-nav-item{ role: 'tab', href: scoped_edit_integration_path(integration) }
|
||||
= _('Settings')
|
||||
- active_tab = local_assigns.fetch(:active_tab, 'edit')
|
||||
- active_classes = 'gl-tab-nav-item-active gl-tab-nav-item-active-indigo active'
|
||||
- tabs = integration_tabs(integration: integration)
|
||||
|
||||
%li.nav-item{ role: 'presentation' }
|
||||
%a.nav-link.gl-tab-nav-item.gl-tab-nav-item-active.gl-tab-nav-item-active-indigo.active{ role: 'tab', href: scoped_overrides_integration_path(integration) }
|
||||
= s_('Integrations|Projects using custom settings')
|
||||
- if tabs.length <= 1
|
||||
= yield
|
||||
- else
|
||||
.tabs.gl-tabs
|
||||
%div
|
||||
%ul.nav.gl-tabs-nav{ role: 'tablist' }
|
||||
- tabs.each do |tab|
|
||||
%li.nav-item{ role: 'presentation' }
|
||||
%a.nav-link.gl-tab-nav-item{ role: 'tab', class: (active_classes if tab[:key] == active_tab), href: tab[:href] }
|
||||
= tab[:text]
|
||||
|
||||
.tab-content.gl-tab-content
|
||||
.tab-pane.active{ role: 'tabpanel' }
|
||||
= yield
|
||||
.tab-content.gl-tab-content
|
||||
.tab-pane.gl-pt-3.active{ role: 'tabpanel' }
|
||||
= yield
|
||||
|
|
|
@ -3,4 +3,8 @@
|
|||
- page_title @integration.title, _('Integrations')
|
||||
- @content_class = 'limit-container-width' unless fluid_layout
|
||||
|
||||
= render 'shared/integrations/form', integration: @integration
|
||||
%h3.page-title
|
||||
= @integration.title
|
||||
|
||||
= render 'shared/integrations/tabs', integration: @integration, active_tab: 'edit' do
|
||||
= render 'shared/integrations/form', integration: @integration
|
||||
|
|
|
@ -6,5 +6,5 @@
|
|||
%h3.page-title
|
||||
= @integration.title
|
||||
|
||||
= render 'shared/integrations/tabs', integration: @integration do
|
||||
= render 'shared/integrations/tabs', integration: @integration, active_tab: 'overrides' do
|
||||
.js-vue-integration-overrides{ data: integration_overrides_data(@integration) }
|
||||
|
|
|
@ -434,6 +434,21 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
# Load JH initializers under JH. Load ordering is:
|
||||
# 1. prepend_helpers_path
|
||||
# 2. before_zeitwerk
|
||||
# 3. let_zeitwerk_take_over
|
||||
# 4. move_initializers
|
||||
# 5. load_config_initializers
|
||||
# 6. load_jh_config_initializers
|
||||
Gitlab.jh do
|
||||
initializer :load_jh_config_initializers, after: :load_config_initializers do
|
||||
Dir[Rails.root.join('jh/config/initializers/*.rb')].sort.each do |initializer|
|
||||
load_config_initializer(initializer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Add assets for variants of GitLab. They should take precedence over CE.
|
||||
# This means if multiple files exist, e.g.:
|
||||
#
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class UpdateTrialPlansCiDailyPipelineScheduleTriggers < ActiveRecord::Migration[6.1]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
PREMIUM_TRIAL = 'premium_trial'
|
||||
ULTIMATE_TRIAL = 'ultimate_trial'
|
||||
EVERY_5_MINUTES = (1.day.in_minutes / 5).to_i
|
||||
|
||||
class Plan < ActiveRecord::Base
|
||||
self.table_name = 'plans'
|
||||
self.inheritance_column = :_type_disabled
|
||||
|
||||
has_one :limits, class_name: 'PlanLimits'
|
||||
end
|
||||
|
||||
class PlanLimits < ActiveRecord::Base
|
||||
self.table_name = 'plan_limits'
|
||||
self.inheritance_column = :_type_disabled
|
||||
|
||||
belongs_to :plan
|
||||
end
|
||||
|
||||
def plan_limits_present?
|
||||
premium_trial_plan = Plan.find_by(name: PREMIUM_TRIAL)
|
||||
ultimate_trial_plan = Plan.find_by(name: ULTIMATE_TRIAL)
|
||||
|
||||
premium_trial_plan && premium_trial_plan.limits && ultimate_trial_plan && ultimate_trial_plan.limits
|
||||
end
|
||||
|
||||
def up
|
||||
return unless Gitlab.dev_env_or_com?
|
||||
|
||||
if plan_limits_present?
|
||||
create_or_update_plan_limit('ci_daily_pipeline_schedule_triggers', PREMIUM_TRIAL, EVERY_5_MINUTES)
|
||||
create_or_update_plan_limit('ci_daily_pipeline_schedule_triggers', ULTIMATE_TRIAL, EVERY_5_MINUTES)
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
return unless Gitlab.dev_env_or_com?
|
||||
|
||||
if plan_limits_present?
|
||||
create_or_update_plan_limit('ci_daily_pipeline_schedule_triggers', PREMIUM_TRIAL, 0)
|
||||
create_or_update_plan_limit('ci_daily_pipeline_schedule_triggers', ULTIMATE_TRIAL, 0)
|
||||
end
|
||||
end
|
||||
end
|
1
db/schema_migrations/20210805192450
Normal file
1
db/schema_migrations/20210805192450
Normal file
|
@ -0,0 +1 @@
|
|||
a63f878d89269eb8a2a3cc3b0c81d700861031a079a4a69b56d45d73c4c7946e
|
|
@ -14,6 +14,7 @@ anonymized
|
|||
Ansible
|
||||
Anthos
|
||||
Apdex
|
||||
Apparmor
|
||||
approvers
|
||||
architected
|
||||
architecting
|
||||
|
@ -28,6 +29,7 @@ Atlassian
|
|||
auditability
|
||||
Auth0
|
||||
Authentiq
|
||||
Authy
|
||||
autocomplete
|
||||
autocompleted
|
||||
autocompletes
|
||||
|
@ -186,6 +188,7 @@ enum
|
|||
enums
|
||||
ETag
|
||||
Excon
|
||||
exfiltration
|
||||
expirable
|
||||
Facebook
|
||||
failover
|
||||
|
@ -202,6 +205,7 @@ Filebeat
|
|||
Fio
|
||||
firewalled
|
||||
firewalling
|
||||
fixup
|
||||
Flawfinder
|
||||
Flowdock
|
||||
Fluentd
|
||||
|
@ -246,6 +250,7 @@ Haswell
|
|||
heatmap
|
||||
heatmaps
|
||||
Helm
|
||||
Helmfile
|
||||
Heroku
|
||||
Herokuish
|
||||
Hexo
|
||||
|
|
|
@ -35,6 +35,7 @@ to do that.
|
|||
|
||||
To remove the **primary** site:
|
||||
|
||||
1. [Remove all secondary Geo sites](remove_geo_site.md)
|
||||
1. On the top bar, select **Menu >** **{admin}** **Admin**.
|
||||
1. On the left sidebar, select **Geo > Nodes**.
|
||||
1. Select **Remove** for the **primary** node.
|
||||
|
|
|
@ -11,6 +11,16 @@ Review this page for update instructions for your version. These steps
|
|||
accompany the [general steps](updating_the_geo_nodes.md#general-update-steps)
|
||||
for updating Geo nodes.
|
||||
|
||||
## Updating to GitLab 14.0/14.1
|
||||
|
||||
We found an issue where [Primary sites can not be removed from the UI](https://gitlab.com/gitlab-org/gitlab/-/issues/338231).
|
||||
|
||||
This bug only exists in the UI and does not block the removal of Primary sites using any other method.
|
||||
|
||||
### If you have already updated to an affected version and need to remove your Primary site
|
||||
|
||||
You can manually remove the Primary site by using the [Geo Nodes API](../../../api/geo_nodes.md#delete-a-geo-node).
|
||||
|
||||
## Updating to GitLab 13.12
|
||||
|
||||
We found an issue where [secondary nodes re-download all LFS files](https://gitlab.com/gitlab-org/gitlab/-/issues/334550) upon update. This bug:
|
||||
|
|
|
@ -146,7 +146,7 @@ Backups of GitLab databases and filesystems are taken every 24 hours, and are ke
|
|||
- GitLab SaaS creates backups to ensure your data is secure, but you can't use these methods to export or back up your data yourself.
|
||||
- Issues are stored in the database. They can't be stored in Git itself.
|
||||
- You can use the project export option in:
|
||||
- [The UI](../user/project/settings/import_export.md#exporting-a-project-and-its-data).
|
||||
- [The UI](../user/project/settings/import_export.md#export-a-project-and-its-data).
|
||||
- [The API](../api/project_import_export.md#schedule-an-export).
|
||||
- [Group export](../user/group/settings/import_export.md) does *not* export the projects in it, but does export:
|
||||
- Epics
|
||||
|
|
|
@ -466,7 +466,7 @@ sudo gitlab-rake cache:clear
|
|||
|
||||
### Export a repository
|
||||
|
||||
It's typically recommended to export a project through [the web interface](../../user/project/settings/import_export.md#exporting-a-project-and-its-data) or through [the API](../../api/project_import_export.md). In situations where this is not working as expected, it may be preferable to export a project directly via the Rails console:
|
||||
It's typically recommended to export a project through [the web interface](../../user/project/settings/import_export.md#export-a-project-and-its-data) or through [the API](../../api/project_import_export.md). In situations where this is not working as expected, it may be preferable to export a project directly via the Rails console:
|
||||
|
||||
```ruby
|
||||
user = User.find_by_username('USERNAME')
|
||||
|
|
|
@ -112,8 +112,8 @@ Supported attributes:
|
|||
|
||||
| Attribute | Type | Required | Description |
|
||||
| :-------- | :------- | :------- | :---------- |
|
||||
| `id` | datatype | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `sha` | datatype | yes | The blob SHA. |
|
||||
| `id` | integer or string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding) owned by the authenticated user. |
|
||||
| `sha` | string | yes | The blob SHA. |
|
||||
|
||||
## Get file archive
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ There are several ways to import a project.
|
|||
|
||||
### Importing via UI
|
||||
|
||||
The first option is to simply [import the Project tarball file via the GitLab UI](../user/project/settings/import_export.md#importing-the-project):
|
||||
The first option is to simply [import the Project tarball file via the GitLab UI](../user/project/settings/import_export.md#import-the-project):
|
||||
|
||||
1. Create the group `qa-perf-testing`
|
||||
1. Import the [GitLab FOSS project tarball](https://gitlab.com/gitlab-org/quality/performance-data/-/blob/master/projects_export/gitlabhq_export.tar.gz) into the Group.
|
||||
|
|
|
@ -27,10 +27,11 @@ Email notifications are available in projects for triggered alerts. Project
|
|||
members with the **Owner** or **Maintainer** roles have the option to receive
|
||||
a single email notification for new alerts.
|
||||
|
||||
1. Navigate to **Settings > Monitor**.
|
||||
1. Expand the **Alerts** section.
|
||||
1. In the **Integration settings** tab, select the checkbox
|
||||
**Send a single email notification to Owners and Maintainers for new alerts**.
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Settings > Monitor**.
|
||||
1. Expand **Alerts**.
|
||||
1. On the **Alert settings** tab, select the
|
||||
**Send a single email notification to Owners and Maintainers for new alerts** checkbox.
|
||||
1. Select **Save changes**.
|
||||
|
||||
## Paging **(PREMIUM)**
|
||||
|
|
|
@ -39,19 +39,23 @@ To configure a GitLab Status Page you must:
|
|||
|
||||
Only AWS S3 is supported as a deploy target.
|
||||
|
||||
Prerequisite:
|
||||
|
||||
- You must have at least the Maintainer [role](../../user/permissions.md).
|
||||
|
||||
To provide GitLab with the AWS account information needed to push content to your Status Page:
|
||||
|
||||
1. Sign into GitLab as a user with Maintainer or greater [permissions](../../user/permissions.md).
|
||||
1. Navigate to **{settings}** **Settings > Monitor**. Next to **Status Page**,
|
||||
click **Expand**.
|
||||
1. Click **Active** to enable the Status Page feature.
|
||||
1. In **Status Page URL**, provide the URL to your external status page.
|
||||
1. Provide the **S3 Bucket name**. For more information, see
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Settings > Monitor**.
|
||||
1. Expand **Status Page**.
|
||||
1. Select the **Active** checkbox.
|
||||
1. In the **Status Page URL** box, provide the URL for your external status page.
|
||||
1. In the **S3 Bucket name** box, type the name of your S3 bucket. For more information, see
|
||||
[Bucket configuration documentation](https://docs.aws.amazon.com/AmazonS3/latest/dev/HostingWebsiteOnS3Setup.html).
|
||||
1. Provide the **AWS region** for your bucket. For more information, see the
|
||||
1. In the **AWS region** box, type the region for your bucket. For more information, see the
|
||||
[AWS documentation](https://github.com/aws/aws-sdk-ruby#configuration).
|
||||
1. Provide your **AWS access key ID** and **AWS Secret access key**.
|
||||
1. Click **Save changes**.
|
||||
1. Enter your **AWS access key ID** and **AWS Secret access key**.
|
||||
1. Select **Save changes**.
|
||||
|
||||
### Configure your AWS account
|
||||
|
||||
|
@ -69,8 +73,8 @@ the necessary CI/CD variables to deploy the Status Page to AWS S3:
|
|||
1. Fork the [Status Page](https://gitlab.com/gitlab-org/status-page) project.
|
||||
You can do this through [Repository Mirroring](https://gitlab.com/gitlab-org/status-page#repository-mirroring),
|
||||
which ensures you get the up-to-date Status Page features.
|
||||
1. Navigate to **{settings}** **Settings > CI/CD**.
|
||||
1. Scroll to **Variables**, and click **Expand**.
|
||||
1. On the left sidebar, select **Settings > CI/CD**.
|
||||
1. Expand **Variables**.
|
||||
1. Add the following variables from your Amazon Console:
|
||||
- `S3_BUCKET_NAME` - The name of the Amazon S3 bucket.
|
||||
If no bucket with the provided name exists, the first pipeline run creates
|
||||
|
@ -80,8 +84,8 @@ the necessary CI/CD variables to deploy the Status Page to AWS S3:
|
|||
- `AWS_DEFAULT_REGION` - The AWS region.
|
||||
- `AWS_ACCESS_KEY_ID` - The AWS access key ID.
|
||||
- `AWS_SECRET_ACCESS_KEY` - The AWS secret.
|
||||
1. Navigate to **CI/CD > Pipelines > Run Pipeline**, and run the pipeline to
|
||||
deploy the Status Page to S3.
|
||||
1. On the left sidebar, select **CI/CD > Pipelines**.
|
||||
1. To deploy the Status Page to S3, select **Run pipeline**.
|
||||
|
||||
WARNING:
|
||||
Consider limiting who can access issues in this project, as any user who can view
|
||||
|
@ -92,7 +96,9 @@ the issue can potentially [publish comments to your GitLab Status Page](#publish
|
|||
After creating the CI/CD variables, configure the Project you want to use for
|
||||
Incident issues:
|
||||
|
||||
1. To view the Status Page settings, navigate to **{settings}** **Settings > Monitor > Status Page**.
|
||||
1. On the top bar, select **Menu > Projects** and find your project.
|
||||
1. On the left sidebar, select **Settings > Monitor**.
|
||||
1. Expand **Status page**.
|
||||
1. Fill in your cloud provider's credentials and make sure to select the **Active** checkbox.
|
||||
1. Select **Save changes**.
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ feature is available.
|
|||
> - DAST and SAST metrics [added](https://gitlab.com/gitlab-org/gitlab/-/issues/328033) in GitLab 14.1.
|
||||
> - Fuzz Testing metrics [added](https://gitlab.com/gitlab-org/gitlab/-/issues/330398) in GitLab 14.2.
|
||||
> - Dependency Scanning metrics [added](https://gitlab.com/gitlab-org/gitlab/-/issues/328034) in GitLab 14.2.
|
||||
> - Multiselect [added](https://gitlab.com/gitlab-org/gitlab/-/issues/333586) in GitLab 14.2.
|
||||
> - Multi-select [added](https://gitlab.com/gitlab-org/gitlab/-/issues/333586) in GitLab 14.2.
|
||||
|
||||
DevOps Adoption shows you which groups in your organization are using the most essential features of GitLab:
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60933) in GitLab Free 13.12.
|
||||
|
||||
Federated Learning of Conhorts (FLoC) is a feature that the Chrome browser has
|
||||
rolled out, where users are categorized into different cohorts, so that
|
||||
Federated Learning of Cohorts (FLoC) is a new feature of the Chrome browser.
|
||||
It works by categorizing users into different cohorts, so that
|
||||
advertisers can use this data to uniquely target and track users. For more
|
||||
information, visit the [FLoC repository](https://github.com/WICG/floc).
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ To change the note creation rate limit:
|
|||
1. On the left sidebar, select **Settings > Network**.
|
||||
1. Expand **Notes Rate Limits**.
|
||||
1. Under **Max requests per minute per user**, enter the new value.
|
||||
1. Optional. Under **List of users to be excluded from the limit**, list users to be excluded fromt the limit.
|
||||
1. Optional. Under **List of users to be excluded from the limit**, list users to be excluded from the limit.
|
||||
1. Select **Save changes**.
|
||||
|
||||
This limit is:
|
||||
|
|
|
@ -211,7 +211,7 @@ You can specify from which hosting sites users can [import their projects](../..
|
|||
## Enable project export
|
||||
|
||||
To enable the export of
|
||||
[projects and their data](../../../user/project/settings/import_export.md#exporting-a-project-and-its-data):
|
||||
[projects and their data](../../../user/project/settings/import_export.md#export-a-project-and-its-data):
|
||||
|
||||
1. Sign in to GitLab as a user with [Administrator role](../../permissions.md).
|
||||
1. On the top bar, select **Menu >** **{admin}** **Admin**.
|
||||
|
|
|
@ -124,9 +124,9 @@ From the previous example we see the time used for each stage:
|
|||
- **Issue**: 2 hrs (09:00 to 11:00)
|
||||
- **Plan**: 1 hr (11:00 to 12:00)
|
||||
- **Code**: 2 hrs (12:00 to 14:00)
|
||||
- **Test**: 5 mins
|
||||
- **Test**: 5 minutes
|
||||
- **Review**: 5 hrs (14:00 to 19:00)
|
||||
- **Staging**: 30 mins (19:00 to 19:30)
|
||||
- **Staging**: 30 minutes (19:00 to 19:30)
|
||||
|
||||
More information:
|
||||
|
||||
|
|
|
@ -53,13 +53,13 @@ project. This image consists of a set of Bash utility scripts to support [Helm v
|
|||
This file has a list of paths to other Helmfiles for each app. They're all commented out by default, so you must uncomment
|
||||
the paths for the apps that you would like to manage.
|
||||
|
||||
By default, each `helmfile.yaml` in these sub-paths will have the attribute `installed: true`, which signifies that everytime
|
||||
the pipeline runs, Helmfile will try to either install or update your apps according to the current state of your
|
||||
cluster and Helm releases. If you change this attribute to `installed: false`, Helmfile will try to uninstall this app
|
||||
By default, each `helmfile.yaml` in these sub-paths have the attribute `installed: true`. This signifies that every time
|
||||
the pipeline runs, Helmfile tries to either install or update your apps according to the current state of your
|
||||
cluster and Helm releases. If you change this attribute to `installed: false`, Helmfile tries try to uninstall this app
|
||||
from your cluster. [Read more](https://github.com/roboll/helmfile) about how Helmfile works.
|
||||
|
||||
Furthermore, each app has an `applications/{app}/values.yaml` file. This is the
|
||||
place where you can define some default values for your app's Helm chart. Some apps will already have defaults
|
||||
place where you can define some default values for your app's Helm chart. Some apps already have defaults
|
||||
pre-defined by GitLab.
|
||||
|
||||
#### Built-in applications
|
||||
|
@ -80,7 +80,7 @@ The [built-in supported applications](https://gitlab.com/gitlab-org/project-temp
|
|||
- [Sentry](../infrastructure/clusters/manage/management_project_applications/sentry.md)
|
||||
- [Vault](../infrastructure/clusters/manage/management_project_applications/vault.md)
|
||||
|
||||
### Migrating from GitLab Managed Apps
|
||||
### Migrate from GitLab Managed Apps
|
||||
|
||||
If you had GitLab Managed Apps, either One-Click or CI/CD install, read the docs on how to
|
||||
[migrate from GitLab Managed Apps to project template](migrating_from_gma_to_project_template.md)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
stage: Monitor
|
||||
group: Health
|
||||
group: Monitor
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
|
||||
---
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
# Infrastructure management **(FREE)**
|
||||
|
||||
GitLab provides you with great solutions to help you manage your
|
||||
infrastrucure:
|
||||
infrastructure:
|
||||
|
||||
- [Infrastructure as Code and GitOps](iac/index.md)
|
||||
- [Kubernetes clusters](../project/clusters/index.md)
|
||||
|
|
|
@ -42,8 +42,8 @@ providers. To host them on premises and with other providers,
|
|||
use either the EKS or GKE method to guide you through and enter your cluster's
|
||||
settings manually:
|
||||
|
||||
- [New cluster hosted on Google Kubernetes Engine (GKE)](add_eks_clusters.md).
|
||||
- [New cluster hosted on Amazon Elastic Kubernetes Service (EKS)](add_gke_clusters.md).
|
||||
- [New cluster hosted on Google Kubernetes Engine (GKE)](add_gke_clusters.md).
|
||||
- [New cluster hosted on Amazon Elastic Kubernetes Service (EKS)](add_eks_clusters.md).
|
||||
|
||||
## Add existing cluster
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ To get to the importer page you need to go to "New project" page.
|
|||
|
||||
NOTE:
|
||||
If you are interested in importing Wiki and Merge Request data to your new instance,
|
||||
you'll need to follow the instructions for [exporting a project](../settings/import_export.md#exporting-a-project-and-its-data)
|
||||
you'll need to follow the instructions for [exporting a project](../settings/import_export.md#export-a-project-and-its-data)
|
||||
|
||||
![New project page](img/gitlab_new_project_page_v12_2.png)
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ Kubernetes, Slack, and a lot more.
|
|||
- [Bitbucket to GitLab](import/bitbucket.md)
|
||||
- [Gitea to GitLab](import/gitea.md)
|
||||
- [FogBugz to GitLab](import/fogbugz.md)
|
||||
- [Export a project from GitLab](settings/import_export.md#exporting-a-project-and-its-data)
|
||||
- [Export a project from GitLab](settings/import_export.md#export-a-project-and-its-data)
|
||||
- [Importing and exporting projects between GitLab instances](settings/import_export.md)
|
||||
|
||||
## GitLab Workflow - VS Code extension
|
||||
|
|
|
@ -23,7 +23,7 @@ over [`git filter-branch`](https://git-scm.com/docs/git-filter-branch) and
|
|||
WARNING:
|
||||
Rewriting repository history is a destructive operation. Make sure to back up your repository before
|
||||
you begin. The best way back up a repository is to
|
||||
[export the project](../settings/import_export.md#exporting-a-project-and-its-data).
|
||||
[export the project](../settings/import_export.md#export-a-project-and-its-data).
|
||||
|
||||
## Purge files from repository history
|
||||
|
||||
|
@ -44,7 +44,7 @@ To purge files from a GitLab repository:
|
|||
using a supported package manager or from source.
|
||||
|
||||
1. Generate a fresh [export from the
|
||||
project](../settings/import_export.html#exporting-a-project-and-its-data) and download it.
|
||||
project](../settings/import_export.html#export-a-project-and-its-data) and download it.
|
||||
This project export contains a backup copy of your repository *and* refs
|
||||
we can use to purge files from your repository.
|
||||
|
||||
|
|
|
@ -32,8 +32,8 @@ To set up a project import/export:
|
|||
|
||||
Note the following:
|
||||
|
||||
- Before you can import a project, you need to export the data first.
|
||||
See [Exporting a project and its data](#exporting-a-project-and-its-data)
|
||||
- Before you can import a project, you must export the data first.
|
||||
See [Export a project and its data](#export-a-project-and-its-data)
|
||||
for how you can export a project through the UI.
|
||||
- Imports from a newer version of GitLab are not supported.
|
||||
The Importing GitLab version must be greater than or equal to the Exporting GitLab version.
|
||||
|
@ -42,7 +42,7 @@ Note the following:
|
|||
- Exports are generated in your configured `shared_path`, a temporary shared directory,
|
||||
and are moved to your configured `uploads_directory`. Every 24 hours, a specific worker deletes these export files.
|
||||
- Group members are exported as project members, as long as the user has
|
||||
maintainer or administrator access to the group where the exported project lives.
|
||||
a maintainer or administrator role in the group where the exported project lives.
|
||||
- Project members with the [Owner role](../../permissions.md) are imported as Maintainers.
|
||||
- Imported users can be mapped by their primary email on self-managed instances, if an administrative user (not an owner) does the import.
|
||||
Otherwise, a supplementary comment is left to mention that the original author and
|
||||
|
@ -51,10 +51,10 @@ Note the following:
|
|||
possible through a [professional services engagement](https://about.gitlab.com/services/migration/).
|
||||
- If an imported project contains merge requests originating from forks,
|
||||
then new branches associated with such merge requests are created
|
||||
within a project during the import/export. Thus, the number of branches
|
||||
in a project during the import/export. Thus, the number of branches
|
||||
in the exported project could be bigger than in the original project.
|
||||
- Deploy keys allowed to push to protected branches are not exported. Therefore,
|
||||
you need to recreate this association by first enabling these deploy keys in your
|
||||
you must recreate this association by first enabling these deploy keys in your
|
||||
imported project and then updating your protected branches accordingly.
|
||||
|
||||
## Version history
|
||||
|
@ -141,7 +141,7 @@ NOTE:
|
|||
For more details on the specific data persisted in a project export, see the
|
||||
[`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/project/import_export.yml) file.
|
||||
|
||||
## Exporting a project and its data
|
||||
## Export a project and its data
|
||||
|
||||
Full project export functionality is limited to project maintainers and owners.
|
||||
You can configure such functionality through [project settings](index.md):
|
||||
|
@ -156,18 +156,18 @@ To export a project and its data, follow these steps:
|
|||
|
||||
![Export button](img/import_export_export_button.png)
|
||||
|
||||
1. Once the export is generated, you should receive an email with a link to
|
||||
1. After the export is generated, you should receive an email with a link to
|
||||
download the file:
|
||||
|
||||
![Email download link](img/import_export_mail_link.png)
|
||||
|
||||
1. Alternatively, you can come back to the project settings and download the
|
||||
file from there, or generate a new export. Once the file is available, the page
|
||||
file from there, or generate a new export. After the file is available, the page
|
||||
should show the **Download export** button:
|
||||
|
||||
![Download export](img/import_export_download_export.png)
|
||||
|
||||
## Importing the project
|
||||
## Import the project
|
||||
|
||||
1. The GitLab project import feature is the first import option when creating a
|
||||
new project. Click on **GitLab export**:
|
||||
|
@ -215,7 +215,7 @@ GitLab.com may have [different settings](../../gitlab_com/index.md#importexport)
|
|||
|
||||
### Import workaround for large repositories
|
||||
|
||||
[Maximum import size limitations](#importing-the-project)
|
||||
[Maximum import size limitations](#import-the-project)
|
||||
can prevent an import from being successful.
|
||||
If changing the import limits is not possible,
|
||||
the following local workflow can be used to temporarily
|
||||
|
|
|
@ -271,7 +271,7 @@ Enable [Service Desk](../service_desk.md) for your project to offer customer sup
|
|||
|
||||
### Export project
|
||||
|
||||
Learn how to [export a project](import_export.md#importing-the-project) in GitLab.
|
||||
Learn how to [export a project](import_export.md#import-the-project) in GitLab.
|
||||
|
||||
### Advanced settings
|
||||
|
||||
|
|
|
@ -7181,6 +7181,9 @@ msgstr ""
|
|||
msgid "ClusterAgents|Go to the repository"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Install a new GitLab Agent"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Install new Agent"
|
||||
msgstr ""
|
||||
|
||||
|
@ -7196,9 +7199,6 @@ msgstr ""
|
|||
msgid "ClusterAgents|Learn how to create an agent access token"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Learn more about installing the GitLab Agent"
|
||||
msgstr ""
|
||||
|
||||
msgid "ClusterAgents|Name"
|
||||
msgstr ""
|
||||
|
||||
|
@ -14966,13 +14966,10 @@ msgstr ""
|
|||
msgid "Geo|Remove node"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geo|Remove secondary node"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geo|Remove tracking database entry"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geo|Removing a Geo secondary node stops the synchronization to that node. Are you sure?"
|
||||
msgid "Geo|Removing a Geo node stops the synchronization to and from that node. Are you sure?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Geo|Replicated data is verified with the secondary node(s) using checksums"
|
||||
|
@ -17912,6 +17909,9 @@ msgstr ""
|
|||
msgid "Integrations|All projects inheriting these settings will also be reset."
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|An error occurred while loading projects using custom settings."
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|Browser limitations"
|
||||
msgstr ""
|
||||
|
||||
|
@ -18032,6 +18032,9 @@ msgstr ""
|
|||
msgid "Integrations|Standard"
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|There are no projects using custom settings"
|
||||
msgstr ""
|
||||
|
||||
msgid "Integrations|This integration, and inheriting projects were reset."
|
||||
msgstr ""
|
||||
|
||||
|
@ -39694,8 +39697,10 @@ msgstr ""
|
|||
msgid "mrWidget|Closed by"
|
||||
msgstr ""
|
||||
|
||||
msgid "mrWidget|Closes"
|
||||
msgstr ""
|
||||
msgid "mrWidget|Closes issue"
|
||||
msgid_plural "mrWidget|Closes issues"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "mrWidget|Delete source branch"
|
||||
msgstr ""
|
||||
|
@ -39730,8 +39735,10 @@ msgstr ""
|
|||
msgid "mrWidget|Members who can merge are allowed to add commits."
|
||||
msgstr ""
|
||||
|
||||
msgid "mrWidget|Mentions"
|
||||
msgstr ""
|
||||
msgid "mrWidget|Mentions issue"
|
||||
msgid_plural "mrWidget|Mentions issues"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "mrWidget|Merge"
|
||||
msgstr ""
|
||||
|
|
|
@ -125,7 +125,7 @@
|
|||
"dateformat": "^4.5.1",
|
||||
"deckar01-task_list": "^2.3.1",
|
||||
"diff": "^3.4.0",
|
||||
"dompurify": "^2.3.0",
|
||||
"dompurify": "^2.3.1",
|
||||
"dropzone": "^4.2.0",
|
||||
"editorconfig": "^0.15.3",
|
||||
"emoji-regex": "^7.0.3",
|
||||
|
|
|
@ -11,6 +11,24 @@ RSpec.describe 'User activates the instance-level Mattermost Slash Command integ
|
|||
end
|
||||
|
||||
let(:edit_path) { edit_admin_application_settings_integration_path(:mattermost_slash_commands) }
|
||||
let(:overrides_path) { overrides_admin_application_settings_integration_path(:mattermost_slash_commands) }
|
||||
|
||||
include_examples 'user activates the Mattermost Slash Command integration'
|
||||
|
||||
it 'displays navigation tabs' do
|
||||
expect(page).to have_link('Settings', href: edit_path)
|
||||
expect(page).to have_link('Projects using custom settings', href: overrides_path)
|
||||
end
|
||||
|
||||
context 'when instance_level_integration_overrides is disabled' do
|
||||
before do
|
||||
stub_feature_flags(instance_level_integration_overrides: false)
|
||||
visit_instance_integration('Mattermost slash commands')
|
||||
end
|
||||
|
||||
it 'does not display the overrides tab' do
|
||||
expect(page).not_to have_link('Settings', href: edit_path)
|
||||
expect(page).not_to have_link('Projects using custom settings', href: overrides_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31,7 +31,7 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do
|
|||
let(:merge_request_description) { "Description\n\nclosing #{issue_1.to_reference}, #{issue_2.to_reference}" }
|
||||
|
||||
it 'does not display closing issue message' do
|
||||
expect(page).to have_content("Closes #{issue_1.to_reference} and #{issue_2.to_reference}")
|
||||
expect(page).to have_content("Closes issues #{issue_1.to_reference} and #{issue_2.to_reference}")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -39,7 +39,7 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do
|
|||
let(:merge_request_description) { "Description\n\nRefers to #{issue_1.to_reference} and #{issue_2.to_reference}" }
|
||||
|
||||
it 'does not display closing issue message' do
|
||||
expect(page).to have_content("Mentions #{issue_1.to_reference} and #{issue_2.to_reference}")
|
||||
expect(page).to have_content("Mentions issues #{issue_1.to_reference} and #{issue_2.to_reference}")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -47,8 +47,8 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do
|
|||
let(:merge_request_title) { "closes #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" }
|
||||
|
||||
it 'does not display closing issue message' do
|
||||
expect(page).to have_content("Closes #{issue_1.to_reference}")
|
||||
expect(page).to have_content("Mentions #{issue_2.to_reference}")
|
||||
expect(page).to have_content("Closes issue #{issue_1.to_reference}")
|
||||
expect(page).to have_content("Mentions issue #{issue_2.to_reference}")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -56,7 +56,7 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do
|
|||
let(:merge_request_title) { "closing #{issue_1.to_reference}, #{issue_2.to_reference}" }
|
||||
|
||||
it 'does not display closing issue message' do
|
||||
expect(page).to have_content("Closes #{issue_1.to_reference} and #{issue_2.to_reference}")
|
||||
expect(page).to have_content("Closes issues #{issue_1.to_reference} and #{issue_2.to_reference}")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -64,7 +64,7 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do
|
|||
let(:merge_request_title) { "Refers to #{issue_1.to_reference} and #{issue_2.to_reference}" }
|
||||
|
||||
it 'does not display closing issue message' do
|
||||
expect(page).to have_content("Mentions #{issue_1.to_reference} and #{issue_2.to_reference}")
|
||||
expect(page).to have_content("Mentions issues #{issue_1.to_reference} and #{issue_2.to_reference}")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -72,8 +72,8 @@ RSpec.describe 'Merge request > User sees closing issues message', :js do
|
|||
let(:merge_request_title) { "closes #{issue_1.to_reference}\n\n refers to #{issue_2.to_reference}" }
|
||||
|
||||
it 'does not display closing issue message' do
|
||||
expect(page).to have_content("Closes #{issue_1.to_reference}")
|
||||
expect(page).to have_content("Mentions #{issue_2.to_reference}")
|
||||
expect(page).to have_content("Closes issue #{issue_1.to_reference}")
|
||||
expect(page).to have_content("Mentions issue #{issue_2.to_reference}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,17 +29,17 @@ describe('content_editor/components/toolbar_table_button', () => {
|
|||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('renders a grid of 3x3 buttons to create a table', () => {
|
||||
expect(getNumButtons()).toBe(9); // 3 x 3
|
||||
it('renders a grid of 5x5 buttons to create a table', () => {
|
||||
expect(getNumButtons()).toBe(25); // 5x5
|
||||
});
|
||||
|
||||
describe.each`
|
||||
row | col | numButtons | tableSize
|
||||
${1} | ${2} | ${9} | ${'1x2'}
|
||||
${2} | ${2} | ${9} | ${'2x2'}
|
||||
${2} | ${3} | ${12} | ${'2x3'}
|
||||
${3} | ${2} | ${12} | ${'3x2'}
|
||||
${3} | ${3} | ${16} | ${'3x3'}
|
||||
${3} | ${4} | ${25} | ${'3x4'}
|
||||
${4} | ${4} | ${25} | ${'4x4'}
|
||||
${4} | ${5} | ${30} | ${'4x5'}
|
||||
${5} | ${4} | ${30} | ${'5x4'}
|
||||
${5} | ${5} | ${36} | ${'5x5'}
|
||||
`('button($row, $col) in the table creator grid', ({ row, col, numButtons, tableSize }) => {
|
||||
describe('on mouse over', () => {
|
||||
beforeEach(async () => {
|
||||
|
@ -50,9 +50,7 @@ describe('content_editor/components/toolbar_table_button', () => {
|
|||
it('marks all rows and cols before it as active', () => {
|
||||
const prevRow = Math.max(1, row - 1);
|
||||
const prevCol = Math.max(1, col - 1);
|
||||
expect(wrapper.findByTestId(`table-${prevRow}-${prevCol}`).element).toHaveClass(
|
||||
'gl-bg-blue-50!',
|
||||
);
|
||||
expect(wrapper.findByTestId(`table-${prevRow}-${prevCol}`).element).toHaveClass('active');
|
||||
});
|
||||
|
||||
it('shows a help text indicating the size of the table being inserted', () => {
|
||||
|
@ -89,8 +87,8 @@ describe('content_editor/components/toolbar_table_button', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('does not create more buttons than a 8x8 grid', async () => {
|
||||
for (let i = 3; i < 8; i += 1) {
|
||||
it('does not create more buttons than a 10x10 grid', async () => {
|
||||
for (let i = 5; i < 10; i += 1) {
|
||||
expect(getNumButtons()).toBe(i * i);
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
|
@ -98,6 +96,6 @@ describe('content_editor/components/toolbar_table_button', () => {
|
|||
expect(findDropdown().element).toHaveText(`Insert a ${i}x${i} table.`);
|
||||
}
|
||||
|
||||
expect(getNumButtons()).toBe(64); // 8x8 (and not 9x9)
|
||||
expect(getNumButtons()).toBe(100); // 10x10 (and not 11x11)
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
import { GlTable, GlLink, GlPagination } from '@gitlab/ui';
|
||||
import { shallowMount, mount } from '@vue/test-utils';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { DEFAULT_PER_PAGE } from '~/api';
|
||||
import createFlash from '~/flash';
|
||||
import IntegrationOverrides from '~/integrations/overrides/components/integration_overrides.vue';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import httpStatus from '~/lib/utils/http_status';
|
||||
import ProjectAvatar from '~/vue_shared/components/project_avatar.vue';
|
||||
|
||||
jest.mock('~/flash');
|
||||
|
||||
const mockOverrides = Array(DEFAULT_PER_PAGE * 3)
|
||||
.fill(1)
|
||||
.map((_, index) => ({
|
||||
name: `test-proj-${index}`,
|
||||
avatar_url: `avatar-${index}`,
|
||||
full_path: `test-proj-${index}`,
|
||||
full_name: `test-proj-${index}`,
|
||||
}));
|
||||
|
||||
describe('IntegrationOverrides', () => {
|
||||
let wrapper;
|
||||
let mockAxios;
|
||||
|
||||
const defaultProps = {
|
||||
overridesPath: 'mock/overrides',
|
||||
};
|
||||
|
||||
const createComponent = ({ mountFn = shallowMount } = {}) => {
|
||||
wrapper = mountFn(IntegrationOverrides, {
|
||||
propsData: defaultProps,
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockAxios = new MockAdapter(axios);
|
||||
mockAxios.onGet(defaultProps.overridesPath).reply(httpStatus.OK, mockOverrides, {
|
||||
'X-TOTAL': mockOverrides.length,
|
||||
'X-PAGE': 1,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mockAxios.restore();
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
const findGlTable = () => wrapper.findComponent(GlTable);
|
||||
const findPagination = () => wrapper.findComponent(GlPagination);
|
||||
const findRowsAsModel = () =>
|
||||
findGlTable()
|
||||
.findAllComponents(GlLink)
|
||||
.wrappers.map((link) => {
|
||||
const avatar = link.findComponent(ProjectAvatar);
|
||||
|
||||
return {
|
||||
href: link.attributes('href'),
|
||||
avatarUrl: avatar.props('projectAvatarUrl'),
|
||||
avatarName: avatar.props('projectName'),
|
||||
text: link.text(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('while loading', () => {
|
||||
it('sets GlTable `busy` attribute to `true`', () => {
|
||||
createComponent();
|
||||
|
||||
const table = findGlTable();
|
||||
expect(table.exists()).toBe(true);
|
||||
expect(table.attributes('busy')).toBe('true');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when initial request is successful', () => {
|
||||
it('sets GlTable `busy` attribute to `false`', async () => {
|
||||
createComponent();
|
||||
await waitForPromises();
|
||||
|
||||
const table = findGlTable();
|
||||
expect(table.exists()).toBe(true);
|
||||
expect(table.attributes('busy')).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('table template', () => {
|
||||
beforeEach(async () => {
|
||||
createComponent({ mountFn: mount });
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('renders overrides as rows in table', () => {
|
||||
expect(findRowsAsModel()).toEqual(
|
||||
mockOverrides.map((x) => ({
|
||||
href: x.full_path,
|
||||
avatarUrl: x.avatar_url,
|
||||
avatarName: x.name,
|
||||
text: expect.stringContaining(x.full_name),
|
||||
})),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when request fails', () => {
|
||||
beforeEach(async () => {
|
||||
mockAxios.onGet(defaultProps.overridesPath).reply(httpStatus.INTERNAL_SERVER_ERROR);
|
||||
createComponent();
|
||||
await waitForPromises();
|
||||
});
|
||||
|
||||
it('calls createFlash', () => {
|
||||
expect(createFlash).toHaveBeenCalledTimes(1);
|
||||
expect(createFlash).toHaveBeenCalledWith({
|
||||
message: IntegrationOverrides.i18n.defaultErrorMessage,
|
||||
captureError: true,
|
||||
error: expect.any(Error),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('pagination', () => {
|
||||
it('triggers fetch when `input` event is emitted', async () => {
|
||||
createComponent();
|
||||
jest.spyOn(axios, 'get');
|
||||
await waitForPromises();
|
||||
|
||||
await findPagination().vm.$emit('input', 2);
|
||||
expect(axios.get).toHaveBeenCalledWith(defaultProps.overridesPath, {
|
||||
params: { page: 2, per_page: DEFAULT_PER_PAGE },
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render with <=1 page', async () => {
|
||||
mockAxios.onGet(defaultProps.overridesPath).reply(httpStatus.OK, [mockOverrides[0]], {
|
||||
'X-TOTAL': 1,
|
||||
'X-PAGE': 1,
|
||||
});
|
||||
|
||||
createComponent();
|
||||
await waitForPromises();
|
||||
|
||||
expect(findPagination().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -20,6 +20,7 @@ describe('Stages Dropdown', () => {
|
|||
const findPipelineInfoText = () => wrapper.findByTestId('pipeline-info').text();
|
||||
const findPipelinePath = () => wrapper.findByTestId('pipeline-path').attributes('href');
|
||||
const findMRLinkPath = () => wrapper.findByTestId('mr-link').attributes('href');
|
||||
const findCopySourceBranchBtn = () => wrapper.findByTestId('copy-source-ref-link');
|
||||
const findSourceBranchLinkPath = () =>
|
||||
wrapper.findByTestId('source-branch-link').attributes('href');
|
||||
const findTargetBranchLinkPath = () =>
|
||||
|
@ -70,6 +71,10 @@ describe('Stages Dropdown', () => {
|
|||
|
||||
expect(actual).toBe(expected);
|
||||
});
|
||||
|
||||
it(`renders the source ref copy button`, () => {
|
||||
expect(findCopySourceBranchBtn().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with an "attached" merge request pipeline', () => {
|
||||
|
@ -103,6 +108,10 @@ describe('Stages Dropdown', () => {
|
|||
mockPipelineWithAttachedMR.merge_request.target_branch_path,
|
||||
);
|
||||
});
|
||||
|
||||
it(`renders the source ref copy button`, () => {
|
||||
expect(findCopySourceBranchBtn().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with a detached merge request pipeline', () => {
|
||||
|
@ -130,5 +139,9 @@ describe('Stages Dropdown', () => {
|
|||
mockPipelineDetached.merge_request.source_branch_path,
|
||||
);
|
||||
});
|
||||
|
||||
it(`renders the source ref copy button`, () => {
|
||||
expect(findCopySourceBranchBtn().exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -134,4 +134,18 @@ describe('Comment Field Layout Component', () => {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('issue has email participants, but note is confidential', () => {
|
||||
it('does not show EmailParticipantsWarning', () => {
|
||||
createWrapper({
|
||||
noteableData: {
|
||||
...noteableDataMock,
|
||||
issue_email_participants: [{ email: 'someone@gitlab.com' }],
|
||||
},
|
||||
noteIsConfidential: true,
|
||||
});
|
||||
|
||||
expect(findEmailParticipantsWarning().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,7 +17,7 @@ describe('MRWidgetRelatedLinks', () => {
|
|||
it('returns Closes text for open merge request', () => {
|
||||
createComponent({ state: 'open', relatedLinks: {} });
|
||||
|
||||
expect(wrapper.vm.closesText).toBe('Closes');
|
||||
expect(wrapper.vm.closesText).toBe('Closes issues');
|
||||
});
|
||||
|
||||
it('returns correct text for closed merge request', () => {
|
||||
|
@ -38,6 +38,7 @@ describe('MRWidgetRelatedLinks', () => {
|
|||
createComponent({
|
||||
relatedLinks: {
|
||||
closing: '<a href="#">#23</a> and <a>#42</a>',
|
||||
closingCount: 2,
|
||||
},
|
||||
});
|
||||
const content = wrapper
|
||||
|
@ -45,7 +46,7 @@ describe('MRWidgetRelatedLinks', () => {
|
|||
.replace(/\n(\s)+/g, ' ')
|
||||
.trim();
|
||||
|
||||
expect(content).toContain('Closes #23 and #42');
|
||||
expect(content).toContain('Closes issues #23 and #42');
|
||||
expect(content).not.toContain('Mentions');
|
||||
});
|
||||
|
||||
|
@ -53,11 +54,17 @@ describe('MRWidgetRelatedLinks', () => {
|
|||
createComponent({
|
||||
relatedLinks: {
|
||||
mentioned: '<a href="#">#7</a>',
|
||||
mentionedCount: 1,
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.text().trim()).toContain('Mentions #7');
|
||||
expect(wrapper.text().trim()).not.toContain('Closes');
|
||||
const content = wrapper
|
||||
.text()
|
||||
.replace(/\n(\s)+/g, ' ')
|
||||
.trim();
|
||||
|
||||
expect(content).toContain('Mentions issue #7');
|
||||
expect(content).not.toContain('Closes issues');
|
||||
});
|
||||
|
||||
it('should have closing and mentioned issues at the same time', () => {
|
||||
|
@ -65,6 +72,8 @@ describe('MRWidgetRelatedLinks', () => {
|
|||
relatedLinks: {
|
||||
closing: '<a href="#">#7</a>',
|
||||
mentioned: '<a href="#">#23</a> and <a>#42</a>',
|
||||
closingCount: 1,
|
||||
mentionedCount: 2,
|
||||
},
|
||||
});
|
||||
const content = wrapper
|
||||
|
@ -72,8 +81,8 @@ describe('MRWidgetRelatedLinks', () => {
|
|||
.replace(/\n(\s)+/g, ' ')
|
||||
.trim();
|
||||
|
||||
expect(content).toContain('Closes #7');
|
||||
expect(content).toContain('Mentions #23 and #42');
|
||||
expect(content).toContain('Closes issue #7');
|
||||
expect(content).toContain('Mentions issues #23 and #42');
|
||||
});
|
||||
|
||||
it('should have assing issues link', () => {
|
||||
|
|
|
@ -82,6 +82,10 @@ RSpec.describe ClustersHelper do
|
|||
expect(subject[:get_started_docs_url]).to eq(help_page_path('user/clusters/agent/index', anchor: 'define-a-configuration-repository'))
|
||||
expect(subject[:integration_docs_url]).to eq(help_page_path('user/clusters/agent/index', anchor: 'get-started-with-gitops-and-the-gitlab-agent'))
|
||||
end
|
||||
|
||||
it 'displays kas address' do
|
||||
expect(subject[:kas_address]).to eq(Gitlab::Kas.external_url)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#js_clusters_list_data' do
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
require_migration!('update_trial_plans_ci_daily_pipeline_schedule_triggers')
|
||||
|
||||
RSpec.describe UpdateTrialPlansCiDailyPipelineScheduleTriggers, :migration do
|
||||
let!(:plans) { table(:plans) }
|
||||
let!(:plan_limits) { table(:plan_limits) }
|
||||
let!(:premium_trial_plan) { plans.create!(name: 'premium_trial', title: 'Premium Trial') }
|
||||
let!(:ultimate_trial_plan) { plans.create!(name: 'ultimate_trial', title: 'Ultimate Trial') }
|
||||
|
||||
describe '#up' do
|
||||
let!(:premium_trial_plan_limits) { plan_limits.create!(plan_id: premium_trial_plan.id, ci_daily_pipeline_schedule_triggers: 0) }
|
||||
let!(:ultimate_trial_plan_limits) { plan_limits.create!(plan_id: ultimate_trial_plan.id, ci_daily_pipeline_schedule_triggers: 0) }
|
||||
|
||||
context 'when the environment is dev or com' do
|
||||
before do
|
||||
allow(Gitlab).to receive(:dev_env_or_com?).and_return(true)
|
||||
end
|
||||
|
||||
it 'sets the trial plan limits for ci_daily_pipeline_schedule_triggers' do
|
||||
disable_migrations_output { migrate! }
|
||||
|
||||
expect(ultimate_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(288)
|
||||
expect(premium_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(288)
|
||||
end
|
||||
|
||||
it 'does not change the plan limits if the ultimate trial plan is missing' do
|
||||
ultimate_trial_plan.destroy!
|
||||
|
||||
expect { disable_migrations_output { migrate! } }.not_to change { plan_limits.count }
|
||||
expect(premium_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(0)
|
||||
end
|
||||
|
||||
it 'does not change the plan limits if the ultimate trial plan limits is missing' do
|
||||
ultimate_trial_plan_limits.destroy!
|
||||
|
||||
expect { disable_migrations_output { migrate! } }.not_to change { plan_limits.count }
|
||||
expect(premium_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(0)
|
||||
end
|
||||
|
||||
it 'does not change the plan limits if the premium trial plan is missing' do
|
||||
premium_trial_plan.destroy!
|
||||
|
||||
expect { disable_migrations_output { migrate! } }.not_to change { plan_limits.count }
|
||||
expect(ultimate_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(0)
|
||||
end
|
||||
|
||||
it 'does not change the plan limits if the premium trial plan limits is missing' do
|
||||
premium_trial_plan_limits.destroy!
|
||||
|
||||
expect { disable_migrations_output { migrate! } }.not_to change { plan_limits.count }
|
||||
expect(ultimate_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the environment is anything other than dev or com' do
|
||||
before do
|
||||
allow(Gitlab).to receive(:dev_env_or_com?).and_return(false)
|
||||
end
|
||||
|
||||
it 'does not update the plan limits' do
|
||||
disable_migrations_output { migrate! }
|
||||
|
||||
expect(premium_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(0)
|
||||
expect(ultimate_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#down' do
|
||||
let!(:premium_trial_plan_limits) { plan_limits.create!(plan_id: premium_trial_plan.id, ci_daily_pipeline_schedule_triggers: 288) }
|
||||
let!(:ultimate_trial_plan_limits) { plan_limits.create!(plan_id: ultimate_trial_plan.id, ci_daily_pipeline_schedule_triggers: 288) }
|
||||
|
||||
context 'when the environment is dev or com' do
|
||||
before do
|
||||
allow(Gitlab).to receive(:dev_env_or_com?).and_return(true)
|
||||
end
|
||||
|
||||
it 'sets the trial plan limits ci_daily_pipeline_schedule_triggers to zero' do
|
||||
migrate_down!
|
||||
|
||||
expect(ultimate_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(0)
|
||||
expect(premium_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(0)
|
||||
end
|
||||
|
||||
it 'does not change the plan limits if the ultimate trial plan is missing' do
|
||||
ultimate_trial_plan.destroy!
|
||||
|
||||
expect { migrate_down! }.not_to change { plan_limits.count }
|
||||
expect(premium_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(288)
|
||||
end
|
||||
|
||||
it 'does not change the plan limits if the ultimate trial plan limits is missing' do
|
||||
ultimate_trial_plan_limits.destroy!
|
||||
|
||||
expect { migrate_down! }.not_to change { plan_limits.count }
|
||||
expect(premium_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(288)
|
||||
end
|
||||
|
||||
it 'does not change the plan limits if the premium trial plan is missing' do
|
||||
premium_trial_plan.destroy!
|
||||
|
||||
expect { migrate_down! }.not_to change { plan_limits.count }
|
||||
expect(ultimate_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(288)
|
||||
end
|
||||
|
||||
it 'does not change the plan limits if the premium trial plan limits is missing' do
|
||||
premium_trial_plan_limits.destroy!
|
||||
|
||||
expect { migrate_down! }.not_to change { plan_limits.count }
|
||||
expect(ultimate_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(288)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the environment is anything other than dev or com' do
|
||||
before do
|
||||
allow(Gitlab).to receive(:dev_env_or_com?).and_return(false)
|
||||
end
|
||||
|
||||
it 'does not change the ultimate trial plan limits' do
|
||||
migrate_down!
|
||||
|
||||
expect(ultimate_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(288)
|
||||
expect(premium_trial_plan_limits.reload.ci_daily_pipeline_schedule_triggers).to eq(288)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def migrate_down!
|
||||
disable_migrations_output do
|
||||
migrate!
|
||||
described_class.new.down
|
||||
end
|
||||
end
|
||||
end
|
|
@ -58,7 +58,7 @@ RSpec.describe MergeRequestWidgetEntity do
|
|||
data = described_class.new(resource, request: request, issues_links: true).as_json
|
||||
|
||||
expect(data).to include(:issues_links)
|
||||
expect(data[:issues_links]).to include(:assign_to_closing, :closing, :mentioned_but_not_closing)
|
||||
expect(data[:issues_links]).to include(:assign_to_closing, :closing, :mentioned_but_not_closing, :closing_count, :mentioned_count)
|
||||
end
|
||||
|
||||
it 'omits issue links by default' do
|
||||
|
|
|
@ -4568,10 +4568,10 @@ domhandler@^4.0.0, domhandler@^4.2.0:
|
|||
dependencies:
|
||||
domelementtype "^2.2.0"
|
||||
|
||||
dompurify@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.0.tgz#07bb39515e491588e5756b1d3e8375b5964814e2"
|
||||
integrity sha512-VV5C6Kr53YVHGOBKO/F86OYX6/iLTw2yVSI721gKetxpHCK/V5TaLEf9ODjRgl1KLSWRMY6cUhAbv/c+IUnwQw==
|
||||
dompurify@^2.3.0, dompurify@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.1.tgz#a47059ca21fd1212d3c8f71fdea6943b8bfbdf6a"
|
||||
integrity sha512-xGWt+NHAQS+4tpgbOAI08yxW0Pr256Gu/FNE2frZVTbgrBUn8M7tz7/ktS/LZ2MHeGqz6topj0/xY+y8R5FBFw==
|
||||
|
||||
domutils@^1.5.1:
|
||||
version "1.7.0"
|
||||
|
|
Loading…
Reference in a new issue