Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-12-16 12:08:27 +00:00
parent bc9b43904d
commit 377d420e3d
78 changed files with 578 additions and 448 deletions

View File

@ -1,6 +1,5 @@
import $ from 'jquery';
import './autosize';
import './markdown/render_gfm';
import initCollapseSidebarOnWindowResize from './collapse_sidebar_on_window_resize';
import initCopyToClipboard from './copy_to_clipboard';
import installGlEmojiElement from './gl_emoji';

View File

@ -0,0 +1,13 @@
import $ from 'jquery';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
$.fn.renderGFM = function plugin() {
this.get().forEach(renderGFM);
return this;
};
requestIdleCallback(
() => {
renderGFM(document.body);
},
{ timeout: 500 },
);

View File

@ -1,4 +1,3 @@
import $ from 'jquery';
import syntaxHighlight from '~/syntax_highlight';
import highlightCurrentUser from './highlight_current_user';
import { renderKroki } from './render_kroki';
@ -8,40 +7,46 @@ import renderMetrics from './render_metrics';
import renderObservability from './render_observability';
import { renderJSONTable } from './render_json_table';
function initPopovers(elements) {
if (!elements.length) return;
import(/* webpackChunkName: 'IssuablePopoverBundle' */ '~/issuable/popover')
.then(({ default: initIssuablePopovers }) => {
initIssuablePopovers(elements);
})
.catch(() => {});
}
// Render GitLab flavoured Markdown
//
// Delegates to syntax highlight and render math & mermaid diagrams.
//
$.fn.renderGFM = function renderGFM() {
syntaxHighlight(this.find('.js-syntax-highlight').get());
renderKroki(this.find('.js-render-kroki[hidden]').get());
renderMath(this.find('.js-render-math'));
renderSandboxedMermaid(this.find('.js-render-mermaid').get());
renderJSONTable(
Array.from(this.find('[lang="json"][data-lang-params="table"]').get()).map((e) => e.parentNode),
);
export function renderGFM(element) {
const [
highlightEls,
krokiEls,
mathEls,
mermaidEls,
tableEls,
userEls,
popoverEls,
metricsEls,
observabilityEls,
] = [
'.js-syntax-highlight',
'.js-render-kroki[hidden]',
'.js-render-math',
'.js-render-mermaid',
'[lang="json"][data-lang-params="table"]',
'.gfm-project_member',
'.gfm-issue, .gfm-merge_request',
'.js-render-metrics',
'.js-render-observability',
].map((selector) => Array.from(element.querySelectorAll(selector)));
highlightCurrentUser(this.find('.gfm-project_member').get());
const issuablePopoverElements = this.find('.gfm-issue, .gfm-merge_request').get();
if (issuablePopoverElements.length) {
import(/* webpackChunkName: 'IssuablePopoverBundle' */ '~/issuable/popover')
.then(({ default: initIssuablePopovers }) => {
initIssuablePopovers(issuablePopoverElements);
})
.catch(() => {});
}
renderMetrics(this.find('.js-render-metrics').get());
renderObservability(this.find('.js-render-observability').get());
return this;
};
$(() => {
window.requestIdleCallback(
() => {
$('body').renderGFM();
},
{ timeout: 500 },
);
});
syntaxHighlight(highlightEls);
renderKroki(krokiEls);
renderMath(mathEls);
renderSandboxedMermaid(mermaidEls);
renderJSONTable(tableEls.map((e) => e.parentNode));
highlightCurrentUser(userEls);
renderMetrics(metricsEls);
renderObservability(observabilityEls);
initPopovers(popoverEls);
}

View File

@ -175,14 +175,14 @@ class SafeMathRenderer {
}
}
export default function renderMath($els) {
if (!$els.length) return;
export default function renderMath(elements) {
if (!elements.length) return;
Promise.all([
import(/* webpackChunkName: 'katex' */ 'katex'),
import(/* webpackChunkName: 'katex' */ 'katex/dist/katex.min.css'),
])
.then(([katex]) => {
const renderer = new SafeMathRenderer($els.get(), katex);
const renderer = new SafeMathRenderer(elements, katex);
renderer.render();
renderer.attachEvents();
})

View File

@ -4,6 +4,7 @@ import $ from 'jquery';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import '~/behaviors/markdown/init_gfm';
// MarkdownPreview
//

View File

@ -1,5 +1,5 @@
import $ from 'jquery';
import '~/behaviors/markdown/render_gfm';
import '~/behaviors/markdown/init_gfm';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
import {

View File

@ -9,6 +9,7 @@ import { addEditorMarkdownListeners } from '~/lib/utils/text_markdown';
import { insertFinalNewline } from '~/lib/utils/text_utility';
import TemplateSelectorMediator from '../blob/file_template_mediator';
import { BLOB_EDITOR_ERROR, BLOB_PREVIEW_ERROR } from './constants';
import '~/behaviors/markdown/init_gfm';
export default class EditBlob {
// The options object has:

View File

@ -40,6 +40,7 @@ import { localTimeAgo } from './lib/utils/datetime_utility';
import { getLocationHash } from './lib/utils/url_utility';
import { sprintf, s__, __ } from './locale';
import TaskList from './task_list';
import '~/behaviors/markdown/init_gfm';
window.autosize = Autosize;

View File

@ -28,6 +28,7 @@ import {
TASK_TYPE_NAME,
WIDGET_TYPE_DESCRIPTION,
} from '~/work_items/constants';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import animateMixin from '../mixins/animate';
import { convertDescriptionWithNewSort } from '../utils';
@ -178,7 +179,7 @@ export default {
},
methods: {
renderGFM() {
$(this.$refs['gfm-content']).renderGFM();
renderGFM(this.$refs['gfm-content']);
if (this.canUpdate) {
// eslint-disable-next-line no-new

View File

@ -5,6 +5,7 @@ import { createAlert } from '~/flash';
import { getCookie, isMetaClick, parseBoolean, scrollToElement } from '~/lib/utils/common_utils';
import { parseUrlPathname } from '~/lib/utils/url_utility';
import createEventHub from '~/helpers/event_hub_factory';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import BlobForkSuggestion from './blob/blob_fork_suggestion';
import Diff from './diff';
import { initDiffStatsDropdown } from './init_diff_stats_dropdown';
@ -346,7 +347,7 @@ export default class MergeRequestTabs {
this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
}
$('.detail-page-description').renderGFM();
renderGFM(document.querySelector('.detail-page-description'));
if (shouldScroll) this.recallScroll(action);
} else if (action === this.currentAction) {

View File

@ -1,11 +1,10 @@
<script>
import $ from 'jquery';
import { escape } from 'lodash';
import { mapActions, mapGetters, mapState } from 'vuex';
import SafeHtml from '~/vue_shared/directives/safe_html';
import { __ } from '~/locale';
import '~/behaviors/markdown/render_gfm';
import Suggestions from '~/vue_shared/components/markdown/suggestions.vue';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import autosave from '../mixins/autosave';
import NoteAttachment from './note_attachment.vue';
import NoteAwardsList from './note_awards_list.vue';
@ -121,7 +120,7 @@ export default {
'removeSuggestionInfoFromBatch',
]),
renderGFM() {
$(this.$refs['note-body']).renderGFM();
renderGFM(this.$refs['note-body']);
},
handleFormUpdate(noteText, parentElement, callback, resolveDiscussion) {
this.$emit('handleFormUpdate', { noteText, parentElement, callback, resolveDiscussion });

View File

@ -12,6 +12,7 @@ import { ignoreWhilePending } from '~/lib/utils/ignore_while_pending';
import { truncateSha } from '~/lib/utils/text_utility';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import { __, s__, sprintf } from '~/locale';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import eventHub from '../event_hub';
import noteable from '../mixins/noteable';
import resolvable from '../mixins/resolvable';
@ -287,7 +288,7 @@ export default {
this.isEditing = false;
this.isRequesting = false;
this.oldContent = null;
$(this.$refs.noteBody.$el).renderGFM();
renderGFM(this.$refs.noteBody.$el);
this.$refs.noteBody.resetAutoSave();
this.$emit('updateSuccess');
},

View File

@ -5,7 +5,7 @@ import { createAlert } from '~/flash';
import { __ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
import { handleLocationHash } from '~/lib/utils/common_utils';
import { renderGFM } from '../render_gfm_facade';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
export default {
components: {

View File

@ -1,5 +0,0 @@
import $ from 'jquery';
export const renderGFM = (el) => {
return $(el).renderGFM();
};

View File

@ -35,11 +35,6 @@ export default {
required: true,
default: () => [],
},
stagesClass: {
type: [Array, Object, String],
required: false,
default: '',
},
updateDropdown: {
type: Boolean,
required: false,
@ -59,7 +54,7 @@ export default {
};
</script>
<template>
<div class="stage-cell" data-testid="pipeline-mini-graph">
<div data-testid="pipeline-mini-graph">
<linked-pipelines-mini-list
v-if="upstreamPipeline"
:triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
@ -77,7 +72,6 @@ export default {
:is-merge-train="isMergeTrain"
:stages="stages"
:update-dropdown="updateDropdown"
:stages-class="stagesClass"
data-testid="pipeline-stages"
@miniGraphStageClick="$emit('miniGraphStageClick')"
/>

View File

@ -17,11 +17,6 @@ export default {
required: false,
default: false,
},
stagesClass: {
type: [Array, Object, String],
required: false,
default: '',
},
isMergeTrain: {
type: Boolean,
required: false,
@ -35,8 +30,7 @@ export default {
<div
v-for="stage in stages"
:key="stage.name"
:class="stagesClass"
class="dropdown gl-display-inline-block gl-mr-2 gl-my-2 gl-vertical-align-middle stage-container"
class="pipeline-mini-graph-stage-container dropdown gl-display-inline-block gl-mr-2 gl-my-2 gl-vertical-align-middle"
>
<pipeline-stage
:stage="stage"

View File

@ -1,5 +1,4 @@
<script>
import $ from 'jquery';
import { isEmpty } from 'lodash';
import SafeHtml from '~/vue_shared/directives/safe_html';
import { scrollToElement } from '~/lib/utils/common_utils';
@ -7,7 +6,7 @@ import { slugify } from '~/lib/utils/text_utility';
import { getLocationHash } from '~/lib/utils/url_utility';
import { CREATED_ASC } from '~/releases/constants';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import '~/behaviors/markdown/render_gfm';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import EvidenceBlock from './evidence_block.vue';
import ReleaseBlockAssets from './release_block_assets.vue';
import ReleaseBlockFooter from './release_block_footer.vue';
@ -86,7 +85,7 @@ export default {
},
methods: {
renderGFM() {
$(this.$refs['gfm-content']).renderGFM();
renderGFM(this.$refs['gfm-content']);
},
},
safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },

View File

@ -1,9 +1,8 @@
<script>
import { GlIcon, GlLink, GlLoadingIcon } from '@gitlab/ui';
import $ from 'jquery';
import SafeHtml from '~/vue_shared/directives/safe_html';
import '~/behaviors/markdown/render_gfm';
import { handleLocationHash } from '~/lib/utils/common_utils';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import readmeQuery from '../../queries/readme.query.graphql';
export default {
@ -43,7 +42,7 @@ export default {
if (newVal) {
this.$nextTick(() => {
handleLocationHash();
$(this.$refs.readme).renderGFM();
renderGFM(this.$refs.readme);
});
}
},

View File

@ -1,5 +1,4 @@
<script>
import $ from 'jquery';
import { GlButton, GlIntersectionObserver } from '@gitlab/ui';
import SafeHtml from '~/vue_shared/directives/safe_html';
@ -7,8 +6,8 @@ import { FLASH_TYPES, FLASH_CLOSED_EVENT } from '~/flash';
import { isLoggedIn } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
import csrf from '~/lib/utils/csrf';
import '~/behaviors/markdown/render_gfm';
import { trackTrialAcceptTerms } from '~/google_tag_manager';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
export default {
name: 'TermsApp',
@ -55,7 +54,7 @@ export default {
},
methods: {
renderGFM() {
$(this.$refs.gfmContainer).renderGFM();
renderGFM(this.$refs.gfmContainer);
},
handleBottomReached() {
this.acceptDisabled = false;

View File

@ -190,7 +190,7 @@ export default {
</template>
<template v-else-if="hasPipeline">
<a :href="status.details_path" class="gl-align-self-center gl-mr-3">
<ci-icon :status="status" :size="24" />
<ci-icon :status="status" :size="24" class="gl-display-flex" />
</a>
<div class="ci-widget-container d-flex">
<div class="ci-widget-content">
@ -280,7 +280,6 @@ export default {
:pipeline-path="pipeline.path"
:stages="pipeline.details.stages"
:upstream-pipeline="pipeline.triggered_by"
stages-class="mr-widget-pipeline-stages"
/>
<pipeline-artifacts :pipeline-id="pipeline.id" :artifacts="artifacts" class="gl-ml-3" />
</span>

View File

@ -1,11 +1,10 @@
<script>
import { GlSkeletonLoader } from '@gitlab/ui';
import $ from 'jquery';
import '~/behaviors/markdown/render_gfm';
import { forEach, escape } from 'lodash';
import SafeHtml from '~/vue_shared/directives/safe_html';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
const { CancelToken } = axios;
let axiosSource;
@ -97,7 +96,7 @@ export default {
this.isLoading = false;
this.$nextTick(() => {
$(this.$refs.markdownPreview).renderGFM();
renderGFM(this.$refs.markdownPreview);
});
})
.catch(() => {

View File

@ -1,7 +1,6 @@
<script>
import { GlIcon } from '@gitlab/ui';
import $ from 'jquery';
import '~/behaviors/markdown/render_gfm';
import { debounce, unescape } from 'lodash';
import { createAlert } from '~/flash';
import GLForm from '~/gl_form';
@ -11,6 +10,7 @@ import { stripHtml } from '~/lib/utils/text_utility';
import { __, sprintf } from '~/locale';
import Suggestions from '~/vue_shared/components/markdown/suggestions.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import MarkdownHeader from './header.vue';
import MarkdownToolbar from './toolbar.vue';
@ -314,7 +314,9 @@ export default {
this.markdownPreview = data.body || __('Nothing to preview.');
this.$nextTick()
.then(() => $(this.$refs['markdown-preview']).renderGFM())
.then(() => {
renderGFM(this.$refs['markdown-preview']);
})
.catch(() =>
createAlert({
message: __('Error rendering Markdown preview'),

View File

@ -1,15 +1,9 @@
<script>
import $ from 'jquery';
import '~/behaviors/markdown/render_gfm';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
export default {
mounted() {
this.renderGFM();
},
methods: {
renderGFM() {
$(this.$el).renderGFM();
},
renderGFM(this.$el);
},
};
</script>

View File

@ -1,10 +1,9 @@
<script>
import { GlDrawer, GlAlert, GlSkeletonLoader } from '@gitlab/ui';
import $ from 'jquery';
import SafeHtml from '~/vue_shared/directives/safe_html';
import '~/behaviors/markdown/render_gfm';
import { s__ } from '~/locale';
import { contentTop } from '~/lib/utils/common_utils';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import { getRenderedMarkdown } from './utils/fetch';
export const cache = {};
@ -78,7 +77,7 @@ export default {
},
renderGLFM() {
this.$nextTick(() => {
$(this.$refs['content-element']).renderGFM();
renderGFM(this.$refs['content-element']);
});
},
closeDrawer() {

View File

@ -21,12 +21,12 @@ import $ from 'jquery';
import { mapGetters, mapActions, mapState } from 'vuex';
import SafeHtml from '~/vue_shared/directives/safe_html';
import descriptionVersionHistoryMixin from 'ee_else_ce/notes/mixins/description_version_history';
import '~/behaviors/markdown/render_gfm';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import NoteHeader from '~/notes/components/note_header.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { spriteIcon } from '~/lib/utils/common_utils';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import TimelineEntryItem from './timeline_entry_item.vue';
const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
@ -89,7 +89,7 @@ export default {
},
},
mounted() {
$(this.$refs['gfm-content']).renderGFM();
renderGFM(this.$refs['gfm-content']);
},
methods: {
...mapActions(['fetchDescriptionVersion', 'softDeleteDescriptionVersion']),

View File

@ -1,7 +1,6 @@
<script>
import $ from 'jquery';
import SafeHtml from '~/vue_shared/directives/safe_html';
import '~/behaviors/markdown/render_gfm';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
export default {
directives: {
@ -26,12 +25,7 @@ export default {
},
},
mounted() {
this.renderGFM();
},
methods: {
renderGFM() {
$(this.$refs.gfmContainer).renderGFM();
},
renderGFM(this.$refs.gfmContainer);
},
};
</script>

View File

@ -17,9 +17,9 @@
*/
import { GlButton, GlSkeletonLoader, GlTooltipDirective, GlIcon } from '@gitlab/ui';
import $ from 'jquery';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import SafeHtml from '~/vue_shared/directives/safe_html';
import descriptionVersionHistoryMixin from 'ee_else_ce/notes/mixins/description_version_history';
import '~/behaviors/markdown/render_gfm';
import axios from '~/lib/utils/axios_utils';
import { getLocationHash } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
@ -89,7 +89,7 @@ export default {
},
},
mounted() {
$(this.$refs['gfm-content']).renderGFM();
renderGFM(this.$refs['gfm-content']);
},
methods: {
fetchDescriptionVersion() {},

View File

@ -1,8 +1,7 @@
<script>
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import $ from 'jquery';
import '~/behaviors/markdown/render_gfm';
import SafeHtml from '~/vue_shared/directives/safe_html';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
const isCheckbox = (target) => target?.classList.contains('task-list-item-checkbox');
@ -47,7 +46,7 @@ export default {
async renderGFM() {
await this.$nextTick();
$(this.$refs['gfm-content']).renderGFM();
renderGFM(this.$refs['gfm-content']);
if (this.canEdit) {
this.checkboxes = this.$el.querySelectorAll('.task-list-item-checkbox');

View File

@ -70,33 +70,20 @@
}
}
// Mini Pipelines
// Pipeline mini graph
.pipeline-mini-graph-stage-container {
&:last-child {
margin-right: 0;
}
.stage-cell {
.stage-container {
&:last-child {
margin-right: 0;
}
// Hack to show a button tooltip inline
button.has-tooltip + .tooltip {
min-width: 105px;
}
// Bootstrap way of showing the content inline for anchors.
a.has-tooltip {
white-space: nowrap;
}
&:not(:last-child) {
&::after {
content: '';
border-bottom: 2px solid $gray-200;
position: absolute;
right: -4px;
top: 11px;
width: 4px;
}
&:not(:last-child) {
&::after {
content: '';
border-bottom: 2px solid $gray-200;
position: absolute;
right: -4px;
top: 11px;
width: 4px;
}
}
}

View File

@ -135,6 +135,7 @@ class RegistrationsController < Devise::RegistrationsController
return identity_verification_redirect_path if custom_confirmation_enabled?
Gitlab::Tracking.event(self.class.name, 'render', user: resource)
users_almost_there_path(email: resource.email)
end

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
module Ci
class FreezePeriodsFinder
def initialize(project, current_user = nil)
@project = project
@current_user = current_user
end
def execute
return Ci::FreezePeriod.none unless Ability.allowed?(@current_user, :read_freeze_period, @project)
@project.freeze_periods
end
end
end

View File

@ -1,14 +0,0 @@
# frozen_string_literal: true
class FreezePeriodsFinder
def initialize(project, current_user = nil)
@project = project
@current_user = current_user
end
def execute
return Ci::FreezePeriod.none unless Ability.allowed?(@current_user, :read_freeze_period, @project)
@project.freeze_periods
end
end

View File

@ -3,7 +3,5 @@
class DependencyProxy::GroupSetting < ApplicationRecord
belongs_to :group
attribute :enabled, default: true
validates :group, presence: true
end

View File

@ -53,9 +53,7 @@
= ci_label_for_status(@last_pipeline.status)
- if @last_pipeline.stages_count.nonzero?
#{ n_(s_('Pipeline|with stage'), s_('Pipeline|with stages'), @last_pipeline.stages_count) }
.mr-widget-pipeline-graph
.stage-cell
.js-commit-pipeline-mini-graph{ data: { stages: @last_pipeline_stages.to_json.html_safe, full_path: @project.full_path, iid: @last_pipeline.iid, graphql_resource_etag: graphql_etag_pipeline_path(@last_pipeline) } }
.js-commit-pipeline-mini-graph{ data: { stages: @last_pipeline_stages.to_json.html_safe, full_path: @project.full_path, iid: @last_pipeline.iid, graphql_resource_etag: graphql_etag_pipeline_path(@last_pipeline) } }
- if @last_pipeline.duration
in
= time_interval_in_words @last_pipeline.duration

View File

@ -1,8 +0,0 @@
---
name: automated_email_provision
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75872
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/348317
milestone: '14.6'
type: development
group: group::license
default_enabled: true

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class ChangeEnabledDefaultInDependencyProxyGroupSettings < Gitlab::Database::Migration[2.0]
def change
change_column_default :dependency_proxy_group_settings, :enabled, from: false, to: true
end
end

View File

@ -0,0 +1 @@
b14a060e05fc73c9d76d7c8bec3f9e1fa99b33eae6ec0057b4a398b28414a02a

View File

@ -14733,7 +14733,7 @@ CREATE TABLE dependency_proxy_group_settings (
group_id integer NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
enabled boolean DEFAULT false NOT NULL
enabled boolean DEFAULT true NOT NULL
);
CREATE SEQUENCE dependency_proxy_group_settings_id_seq

View File

@ -70,39 +70,6 @@ a single URL used by all Geo sites, including the primary.
In Kubernetes, you can use the same domain under `global.hosts.domain` as for the primary site.
## Disable Geo proxying
You can disable the secondary proxying on each Geo site, separately, by following these steps with Omnibus-based packages:
1. SSH into each application node (serving user traffic directly) on your secondary Geo site
and add the following environment variable:
```shell
sudo editor /etc/gitlab/gitlab.rb
```
```ruby
gitlab_workhorse['env'] = {
"GEO_SECONDARY_PROXY" => "0"
}
```
1. Reconfigure the updated nodes for the change to take effect:
```shell
gitlab-ctl reconfigure
```
In Kubernetes, you can use `--set gitlab.webservice.extraEnv.GEO_SECONDARY_PROXY="0"`,
or specify the following in your values file:
```yaml
gitlab:
webservice:
extraEnv:
GEO_SECONDARY_PROXY: "0"
```
## Geo proxying with Separate URLs
> Geo secondary proxying for separate URLs is [enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/346112) in GitLab 15.1.
@ -192,3 +159,42 @@ It does not cover all data types, more will be added in the future as they are t
1. Git reads are served from the local secondary while pushes get proxied to the primary.
Selective sync or cases where repositories don't exist locally on the Geo secondary throw a "not found" error.
1. Pages can use the same URL (without access control), but must be configured separately and are not proxied.
## Disable Geo proxying
Secondary proxying is enabled by default on a secondary site when it uses a unified URL, meaning, the same `external_url` as the primary site. Disabling proxying in this case tends to not be helpful due to completely different behavior being served at the same URL, depending on routing.
Secondary proxying is enabled by default in GitLab 15.1 on a secondary site even without a unified URL. If proxying needs to be disabled on a secondary site, it is much easier to disable the feature flag in [Geo proxying with Separate URLs](#geo-proxying-with-separate-urls). However, if there are multiple secondary sites, then the instructions in this section can be used to disable secondary proxying per site.
Additionally, the `gitlab-workhorse` service polls `/api/v4/geo/proxy` every 10 seconds. In GitLab 15.2 and later, it is only polled once, if Geo is not enabled. Prior to GitLab 15.2, you can stop this polling by disabling secondary proxying.
You can disable the secondary proxying on each Geo site, separately, by following these steps with Omnibus-based packages:
1. SSH into each application node (serving user traffic directly) on your secondary Geo site
and add the following environment variable:
```shell
sudo editor /etc/gitlab/gitlab.rb
```
```ruby
gitlab_workhorse['env'] = {
"GEO_SECONDARY_PROXY" => "0"
}
```
1. Reconfigure the updated nodes for the change to take effect:
```shell
gitlab-ctl reconfigure
```
In Kubernetes, you can use `--set gitlab.webservice.extraEnv.GEO_SECONDARY_PROXY="0"`,
or specify the following in your values file:
```yaml
gitlab:
webservice:
extraEnv:
GEO_SECONDARY_PROXY: "0"
```

View File

@ -0,0 +1,141 @@
---
stage: Data Stores
group: Pods
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Multiple Databases **(FREE SELF)**
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/6168) in GitLab 15.7.
WARNING:
This feature is not ready for production use
By default, GitLab uses a single application database, referred to as the `main` database.
To scale GitLab, you can configure GitLab to use multiple application databases.
Due to [known issues](#known-issues), configuring GitLab with multiple databases is in [**Alpha**](../../policy/alpha-beta-support.md#alpha-features).
## Known issues
- Migrating data from the `main` database to the `ci` database is not supported or documented yet.
- Once data is migrated to the `ci` database, you cannot migrate it back.
## Set up multiple databases
Use the following content to set up multiple databases with a new GitLab installation.
There is no documentation for existing GitLab installations yet.
After you have set up multiple databases, GitLab uses a second application database for
[CI/CD features](../../ci/index.md), referred to as the `ci` database. For
example, GitLab reads and writes to the `ci_pipelines` table in the `ci`
database.
WARNING:
You must stop GitLab before setting up multiple databases. This prevents
split-brain situations, where `main` data is written to the `ci` database, and
the other way around.
### Installations from source
1. [Back up GitLab](../../raketasks/backup_restore.md)
in case of unforeseen issues.
1. Stop GitLab:
```shell
sudo service gitlab stop
```
1. Open `config/database.yml`, and add a `ci:` section under
`production:`. See `config/database.yml.decomposed-postgresql` for possible
values for this new `ci:` section. Once modified, the `config/database.yml` should
look like:
```yaml
production:
main:
# ...
ci:
adapter: postgresql
encoding: unicode
database: gitlabhq_production_ci
# ...
```
1. Save the `config/database.yml` file.
1. Create the `gitlabhq_production_ci` database:
```shell
sudo -u postgres psql -d template1 -c "CREATE DATABASE gitlabhq_production OWNER git;"
sudo -u git -H bundle exec rake db:schema:load:ci
```
1. Lock writes for `ci` tables in `main` database, and the other way around:
```shell
sudo -u git -H bundle exec rake gitlab:db:lock_writes
```
1. Restart GitLab:
```shell
sudo service gitlab restart
```
### Omnibus GitLab installations
1. [Back up GitLab](../../raketasks/backup_restore.md)
in case of unforeseen issues.
1. Stop GitLab:
```shell
sudo gitlab-ctl stop
```
1. Edit `/etc/gitlab/gitlab.rb` and add the following lines:
```ruby
gitlab_rails['databases']['ci']['enable'] = true
gitlab_rails['databases']['ci']['db_database'] = 'gitlabhq_production_ci'
```
1. Save the `/etc/gitlab/gitlab.rb` file.
1. Reconfigure GitLab:
```shell
sudo gitlab-ctl reconfigure
```
1. Optional. Reconfiguring GitLab should create the `gitlabhq_production_ci`. If it did not, manually create the `gitlabhq_production_ci`:
```shell
sudo gitlab-ctl start postgresql
sudo -u gitlab-psql /opt/gitlab/embedded/bin/psql -h /var/opt/gitlab/postgresql -d template1 -c "CREATE DATABASE gitlabhq_production_ci OWNER gitlab;"
sudo gitlab-rake db:schema:load:ci
1. Lock writes for `ci` tables in `main` database, and the other way around:
```shell
sudo gitlab-ctl start postgresql
sudo gitlab-rake gitlab:db:lock_writes
```
1. Restart GitLab:
```shell
sudo gitlab-ctl restart
```
## Further information
For more information on multiple databases, see [issue 6168](https://gitlab.com/groups/gitlab-org/-/epics/6168).
For more information on how multiple databases work in GitLab, see the [development guide for multiple databases](../../development/database/multiple_databases.md).
Since 2022-07-02, GitLab.com has been running with two separate databases. For more information, see this [blog post](https://about.gitlab.com/blog/2022/06/02/splitting-database-into-main-and-ci/).

View File

@ -199,10 +199,16 @@ You can configure GitLab to use multiple SAML 2.0 identity providers if:
Example multiple providers configuration for Omnibus GitLab:
To allow your users to use SAML to sign up without having to manually create an account from either of the providers, add the following values to your configuration.
```ruby
gitlab_rails['omniauth_allow_single_sign_on'] = ['saml', 'saml1']
```
```ruby
gitlab_rails['omniauth_providers'] = [
{
name: 'saml',
name: 'saml', # This must match the following name configuration parameter
args: {
name: 'saml', # This is mandatory and must match the provider name
strategy_class: 'OmniAuth::Strategies::SAML',
@ -212,7 +218,7 @@ gitlab_rails['omniauth_providers'] = [
label: 'Provider 1' # Differentiate the two buttons and providers in the UI
},
{
name: 'saml1',
name: 'saml1', # This must match the following name configuration parameter
args: {
name: 'saml1', # This is mandatory and must match the provider name
strategy_class: 'OmniAuth::Strategies::SAML',
@ -351,13 +357,19 @@ For a full list of supported assertions, see the [OmniAuth SAML gem](https://git
## Configure users based on SAML group membership
You can require users to be members of a certain group, or assign users [external](../user/admin_area/external_users.md), administrator or [auditor](../administration/auditor_users.md) access levels based on group membership.
These groups are checked on each SAML login and user attributes updated as necessary.
This feature **does not** allow you to
automatically add users to GitLab [Groups](../user/group/index.md).
You can:
Support for these groups depends on your [subscription](https://about.gitlab.com/pricing/)
and whether you've installed [GitLab Enterprise Edition (EE)](https://about.gitlab.com/install/).
- Require users to be members of a certain group.
- Assign users [external](../user/admin_area/external_users.md), administrator or [auditor](../administration/auditor_users.md) roles based on group membership.
GitLab checks these groups on each SAML sign in and updates user attributes as necessary.
This feature **does not** allow you to automatically add users to GitLab
[Groups](../user/group/index.md).
Support for these groups depends on:
- Your [subscription](https://about.gitlab.com/pricing/).
- Whether you've installed [GitLab Enterprise Edition (EE)](https://about.gitlab.com/install/).
| Group | Tier | GitLab Enterprise Edition (EE) Only? |
|------------------------------|--------------------|--------------------------------------|
@ -368,9 +380,9 @@ and whether you've installed [GitLab Enterprise Edition (EE)](https://about.gitl
### Prerequisites
First tell GitLab where to look for group information. For this, you
must make sure that your IdP server sends a specific `AttributeStatement` along
with the regular SAML response. Here is an example:
You must tell GitLab where to look for group information. To do this, make sure
that your IdP server sends a specific `AttributeStatement` along with the regular
SAML response. For example:
```xml
<saml:AttributeStatement>
@ -383,9 +395,9 @@ with the regular SAML response. Here is an example:
</saml:AttributeStatement>
```
The name of the attribute can be anything you like, but it must contain the groups
to which a user belongs. To tell GitLab where to find these groups, you need
to add a `groups_attribute:` element to your SAML settings.
The name of the attribute must contain the groups that a user belongs to.
To tell GitLab where to find these groups, add a `groups_attribute:`
element to your SAML settings.
### Required groups
@ -585,17 +597,13 @@ list.
## Validate response signatures
We require Identity Providers to sign SAML responses to ensure that the assertions are
not tampered with.
IdPs must sign SAML responses to ensure that the assertions are not tampered with.
This prevents user impersonation and prevents privilege escalation when specific group
membership is required. Typically this:
This prevents user impersonation and privilege escalation when specific group
membership is required.
- Is configured using `idp_cert_fingerprint`.
- Includes the full certificate in the response, although if your Identity Provider
doesn't support this, you can directly configure GitLab using the `idp_cert` option.
Example configuration with `idp_cert_fingerprint`:
You configure the response signature validation using `idp_cert_fingerprint`.
An example configuration:
```yaml
args: {
@ -607,7 +615,8 @@ args: {
}
```
Example configuration with `idp_cert`:
If your IdP does not support configuring this using `idp_cert_fingerprint`, you
can instead configure GitLab directly using `idp_cert`. An example configuration:
```yaml
args: {
@ -621,15 +630,14 @@ args: {
}
```
If the response signature validation is configured incorrectly, you can see error messages
such as:
If you have configured the response signature validation incorrectly, you might see
error messages such as:
- A key validation error.
- Digest mismatch.
- Fingerprint mismatch.
Refer to the [troubleshooting section](#troubleshooting) for more information on
solving these errors.
For more information on solving these errors, see the [troubleshooting SAML guide](../user/group/saml_sso/troubleshooting.md).
## Customize SAML settings

View File

@ -76,7 +76,9 @@ The following AWS regions are not available:
- Cape Town (`af-south-1`)
- Milan (`eu-south-1`)
- Paris (`eu-west-3`)
- GovCloud
- Zurich (`eu-central-2`)
- GovCloud (US-East) (`us-gov-east-1`)
- GovCloud (US-West) (`us-gov-west-1`)
## Planned features

View File

@ -18,6 +18,8 @@ You can migrate groups in two ways:
- By direct transfer (recommended).
- By uploading an export file.
If you migrate from GitLab.com to self-managed GitLab, an administrator can create users on the self-managed GitLab instance.
## Migrate groups by direct transfer (recommended)
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/249160) in GitLab 13.7 for group resources [with a flag](../../feature_flags.md) named `bulk_import`. Disabled by default.
@ -68,6 +70,12 @@ GitLab maps users and their contributions correctly provided:
You might need to reconfigure your firewall to prevent blocking the connection on the self-managed
instance.
If you use [SAML SSO for GitLab.com groups](../../group/saml_sso/index.md),
contributing users must have [linked their SAML identity to their GitLab.com account](../../group/saml_sso/index.md#linking-saml-to-your-existing-gitlabcom-account).
When migrating to GitLab.com, you must create users manually unless [SCIM](../../group/saml_sso/scim_setup.md) is used. Creating users with the API is only
available to self-managed instances because it requires administrator access.
### Connect to the source GitLab instance
Create the group you want to import to and connect the source:
@ -252,6 +260,22 @@ import = BulkImports::Entity.where(namespace_id: Group.id).map(&:bulk_import)
import.status #=> 3 means that the import timed out.
```
#### Error: `404 Group Not Found`
If you attempt to import a group that has a path comprised of only numbers (for example, `5000`), GitLab attempts to
find the group by ID instead of the path. This causes a `404 Group Not Found` error in GitLab 15.4 and earlier.
To solve this, you must change the source group path to include a non-numerical character using either:
- The GitLab UI:
1. On the top bar, select **Main menu > Groups** and find your group.
1. On the left sidebar, select **Settings > General**.
1. Expand **Advanced**.
1. Under **Change group URL**, change the group URL to include non-numeric characters.
- The [Groups API](../../../api/groups.md#update-group).
### Provide feedback
Please leave your feedback about migrating groups by direct transfer in
@ -286,7 +310,7 @@ Professional Services team.
Note the following:
- Exports are stored in a temporary directory and are deleted every 24 hours by a specific worker.
- To preserve group-level relationships from imported projects, run the Group Import/Export first, to allow projects to
- To preserve group-level relationships from imported projects, export and import groups first so that projects can
be imported into the desired group structure.
- Imported groups are given a `private` visibility level, unless imported into a parent group.
- If imported into a parent group, a subgroup inherits the same level of visibility unless otherwise restricted.
@ -360,7 +384,7 @@ To export the contents of a group:
1. In the **Advanced** section, select **Download export**.
You can also generate a new file by selecting **Regenerate export**.
You can also use the [group import/export API](../../../api/group_import_export.md).
You can also export a group [using the API](../../../api/group_import_export.md).
### Import the group

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

View File

@ -5,99 +5,59 @@ group: Import
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# Migrate projects to a GitLab instance **(FREE)**
# Import and migrate projects **(FREE)**
See these documents to migrate to GitLab:
If you want to bring existing projects to GitLab or copy GitLab projects to a different location, you can:
- [From Bitbucket Cloud](bitbucket.md)
- [From Bitbucket Server (also known as Stash)](bitbucket_server.md)
- [From ClearCase](clearcase.md)
- [From CVS](cvs.md)
- [From FogBugz](fogbugz.md)
- [From GitHub.com or GitHub Enterprise](github.md)
- [From GitLab.com](gitlab_com.md)
- [From Gitea](gitea.md)
- [From Perforce](perforce.md)
- Import projects from external systems using one of the [available importers](#available-project-importers).
- Migrate GitLab projects:
- Between two GitLab self-managed instances.
- Between a self-managed instance and GitLab.com in both directions.
- In the same GitLab instance.
For any type of source and target, you can migrate projects:
- As part of a [GitLab group migration](../../group/import/index.md). You can't migrate only chosen projects,
but you can migrate many projects at once within a group.
- Using [file exports](../settings/import_export.md). With this method you can migrate projects one by one. No network
connection between instances is required.
If you only need to migrate Git repositories, you can [import each project by URL](repo_by_url.md). However, you can't
import issues and merge requests this way. To retain metadata like issues and merge requests, either:
- [Migrate projects with groups](../../group/import/index.md).
- Use [file exports](../settings/import_export.md) to import projects.
Keep in mind the limitations of [migrating using file exports](../settings/import_export.md#items-that-are-exported).
When migrating from self-managed to GitLab.com, user associations (such as comment author)
are changed to the user who is importing the projects.
## Available project importers
You can import projects from:
- [Bitbucket Cloud](bitbucket.md)
- [Bitbucket Server (also known as Stash)](bitbucket_server.md)
- [ClearCase](clearcase.md)
- [CVS](cvs.md)
- [FogBugz](fogbugz.md)
- [GitHub.com or GitHub Enterprise](github.md)
- [GitLab.com](gitlab_com.md)
- [Gitea](gitea.md)
- [Perforce](perforce.md)
- [From SVN](https://git-scm.com/book/en/v2/Git-and-Other-Systems-Git-as-a-Client)
- [From TFVC](tfvc.md)
- [From repository by URL](repo_by_url.md)
- [By uploading a manifest file (AOSP)](manifest.md)
- [From Phabricator](phabricator.md)
- [From Jira (issues only)](jira.md)
- [TFVC](tfvc.md)
- [Repository by URL](repo_by_url.md)
- [Uloading a manifest file (AOSP)](manifest.md)
- [Phabricator](phabricator.md)
- [Jira (issues only)](jira.md)
You can also import any Git repository through HTTP from the **New Project** page. Note that if the
repository is too large, the import can timeout.
You can also [connect your external repository to get CI/CD benefits](../../../ci/ci_cd_for_external_repos/index.md).
You can then [connect your external repository to get CI/CD benefits](../../../ci/ci_cd_for_external_repos/index.md).
## Project import history
You can view all project imports created by you. This list includes the following:
- Source (without credentials for security reasons)
- Destination
- Status
- Error details if the import failed
To view project import history:
1. Sign in to GitLab.
1. On the top bar, select **New** (**{plus}**).
1. Select **New project/repository**.
1. Select **Import project**.
1. Select **History**.
![Project import history page](img/gitlab_import_history_page_v14_10.png)
The history also includes projects created from [built-in](../working_with_projects.md#create-a-project-from-a-built-in-template)
or [custom](../working_with_projects.md#create-a-project-from-a-built-in-template)
templates. GitLab uses [import repository by URL](repo_by_url.md)
to create a new project from a template.
## LFS authentication
When importing a project that contains LFS objects, if the project has an [`.lfsconfig`](https://github.com/git-lfs/git-lfs/blob/master/docs/man/git-lfs-config.5.ronn)
file with a URL host (`lfs.url`) different from the repository URL host, LFS files are not downloaded.
## Migrate from self-managed GitLab to GitLab.com
Depending on your requirements, there are several ways to migrate from self-managed GitLab to GitLab.com.
### Migrate using GitLab Migration (recommended)
Using [GitLab Migration](../../group/import/index.md), you can migrate top-level groups you are the Owner of, with all their subgroups and projects included.
GitLab Migration maps users and their contributions correctly on GitLab.com provided:
- Contributing users exist on GitLab.com at the time of the import.
- Those users have a public email on the source GitLab instance that matches their primary email on GitLab.com.
If you use [SAML SSO for GitLab.com groups](../../group/saml_sso/index.md),
contributing users must have [linked their SAML identity to their GitLab.com account](../../group/saml_sso/index.md#linking-saml-to-your-existing-gitlabcom-account).
When migrating to GitLab.com, you must create users manually unless [SCIM](../../group/saml_sso/scim_setup.md) is used. Creating users with the API is only
available to self-managed instances because it requires administrator access.
### Migrate specific projects only
If you only need to migrate Git repositories, you can [import each project by URL](repo_by_url.md).
However, you can't import issues and merge requests this way. To retain metadata like issues and
merge requests, use the [import/export feature](../settings/import_export.md)
to export projects from self-managed GitLab and import those projects into GitLab.com.
GitLab maps user contributions correctly when an admin access token is used to perform the import.
As a result, the import/export feature does not map user contributions correctly when you are importing projects from a self-managed instance to GitLab.com.
Instead, all GitLab user associations (such as comment author) are changed to the user importing the project. For more
information, see the prerequisites and important notes in these sections:
- [Export a project and its data](../settings/import_export.md#export-a-project-and-its-data).
- [Import the project](../settings/import_export.md#import-a-project-and-its-data).
To preserve contribution history, [migrate using GitLab Migration](#migrate-using-gitlab-migration-recommended).
### Migrate using the API
## Migrate using the API
To migrate all data from self-managed to GitLab.com, you can leverage the [API](../../../api/index.md).
Migrate the assets in this order:
@ -106,17 +66,9 @@ Migrate the assets in this order:
1. [Projects](../../../api/projects.md)
1. [Project variables](../../../api/project_level_variables.md)
Keep in mind the limitations of the [import/export feature](../settings/import_export.md#items-that-are-exported). As with [Migrating specific projects using the import/export feature](#migrate-specific-projects-only) user associations (such as comment author) are changed to the user importing projects when migrating from self-managed to GitLab.com.
You must still migrate your [Container Registry](../../packages/container_registry/index.md)
over a series of Docker pulls and pushes. Re-run any CI pipelines to retrieve any build artifacts.
## Migrate from GitLab.com to self-managed GitLab
The process is essentially the same as [migrating from self-managed GitLab to GitLab.com](#migrate-from-self-managed-gitlab-to-gitlabcom).
The main difference is that an administrator can create users on the self-managed GitLab instance
through the UI or the [users API](../../../api/users.md#user-creation).
## Migrate between two self-managed GitLab instances
To migrate from an existing self-managed GitLab instance to a new self-managed GitLab instance, it's
@ -128,12 +80,36 @@ The backups produced don't depend on the operating system running GitLab. You ca
the restore method to switch between different operating system distributions or versions, as long
as the same GitLab version [is available for installation](../../../administration/package_information/supported_os.md).
To instead merge two self-managed GitLab instances together, use the instructions in
[Migrate from self-managed GitLab to GitLab.com](#migrate-from-self-managed-gitlab-to-gitlabcom).
This method is useful when both self-managed instances have existing data that must be preserved.
Also note that administrators can use the [Users API](../../../api/users.md) to migrate users.
Also note that administrators can use the [Users API](../../../api/users.md)
to migrate users.
## View project import history
You can view all project imports created by you. This list includes the following:
- Paths of source projects if projects were imported from external systems, or import method if GitLab projects were migrated.
- Paths of destination projects.
- Start date of each import.
- Status of each import.
- Error details if any errors occurred.
To view project import history:
1. Sign in to GitLab.
1. On the top bar, select **Create new...** (**{plus-square}**).
1. Select **New project/repository**.
1. Select **Import project**.
1. Select **History** in the upper right corner.
1. If there are any errors for a particular import, you can see them by selecting **Details**.
The history also includes projects created from [built-in](../working_with_projects.md#create-a-project-from-a-built-in-template)
or [custom](../working_with_projects.md#create-a-project-from-a-built-in-template)
templates. GitLab uses [import repository by URL](repo_by_url.md)
to create a new project from a template.
## LFS authentication
When importing a project that contains LFS objects, if the project has an [`.lfsconfig`](https://github.com/git-lfs/git-lfs/blob/master/docs/man/git-lfs-config.5.ronn)
file with a URL host (`lfs.url`) different from the repository URL host, LFS files are not downloaded.
## Project aliases **(PREMIUM SELF)**

View File

@ -12,7 +12,22 @@ then imported into a new GitLab instance. You can also:
- [Migrate groups](../../group/import/index.md) using the preferred method.
- [Migrate groups using file exports](../../group/settings/import_export.md).
## Set up project import/export
GitLab maps user contributions correctly when an admin access token is used to perform the import.
As a result, migrating projects using file exports does not map user contributions correctly when you are importing
projects from a self-managed instance to GitLab.com.
Instead, all GitLab user associations (such as comment author) are changed to the user importing the project. For more
information, see the prerequisites and important notes in these sections:
- [Export a project and its data](../settings/import_export.md#export-a-project-and-its-data).
- [Import the project](../settings/import_export.md#import-a-project-and-its-data).
To preserve contribution history, [migrate using direct transfer](../../group/import/index.md#migrate-groups-by-direct-transfer-recommended).
If you migrate from GitLab.com to self-managed GitLab, an administrator can create users on the self-managed GitLab instance.
## Set up project to migrate using file exports
Before you can import or export a project and its data, you must set it up.
@ -24,8 +39,7 @@ Before you can import or export a project and its data, you must set it up.
## Between CE and EE
You can export projects from the [Community Edition to the Enterprise Edition](https://about.gitlab.com/install/ce-or-ee/)
and vice versa. This assumes [version history](#version-history)
requirements are met.
and vice versa. This assumes [version history](#version-history) requirements are met.
If you're exporting a project from the Enterprise Edition to the Community Edition, you may lose
data that is retained only in the Enterprise Edition. For more information, see
@ -37,8 +51,7 @@ Before you can import a project, you must export it.
Prerequisites:
- Review the list of [items that are exported](#items-that-are-exported).
Not all items are exported.
- Review the list of [items that are exported](#items-that-are-exported). Not all items are exported.
- You must have at least the Maintainer role for the project.
To export a project and its data, follow these steps:
@ -134,7 +147,7 @@ To import a project:
1. Enter your project name and URL. Then select the file you exported previously.
1. Select **Import project** to begin importing. Your newly imported project page appears shortly.
To get the status of an import, you can query it through the [Project import/export API](../../../api/project_import_export.md#import-status).
To get the status of an import, you can query it through the [API](../../../api/project_import_export.md#import-status).
As described in the API documentation, the query may return an import error or exceptions.
### Changes to imported items
@ -218,36 +231,9 @@ For example:
| 13.0 | 13.0, 12.10, 12.9 |
| 13.1 | 13.1, 13.0, 12.10 |
### 12.x
Prior to 13.0 this was a defined compatibility table:
| Exporting GitLab version | Importing GitLab version |
| -------------------------- | -------------------------- |
| 11.7 to 12.10 | 11.7 to 12.10 |
| 11.1 to 11.6 | 11.1 to 11.6 |
| 10.8 to 11.0 | 10.8 to 11.0 |
| 10.4 to 10.7 | 10.4 to 10.7 |
| 10.3 | 10.3 |
| 10.0 to 10.2 | 10.0 to 10.2 |
| 9.4 to 9.6 | 9.4 to 9.6 |
| 9.2 to 9.3 | 9.2 to 9.3 |
| 8.17 to 9.1 | 8.17 to 9.1 |
| 8.13 to 8.16 | 8.13 to 8.16 |
| 8.12 | 8.12 |
| 8.10.3 to 8.11 | 8.10.3 to 8.11 |
| 8.10.0 to 8.10.2 | 8.10.0 to 8.10.2 |
| 8.9.5 to 8.9.11 | 8.9.5 to 8.9.11 |
| 8.9.0 to 8.9.4 | 8.9.0 to 8.9.4 |
Projects can be exported and imported only between versions of GitLab with matching Import/Export versions.
For example, 8.10.3 and 8.11 have the same Import/Export version (0.1.3)
and the exports between them are compatible.
## Related topics
- [Project import/export API](../../../api/project_import_export.md)
- [Project import/export administration Rake tasks](../../../administration/raketasks/project_import_export.md)
- [Group import/export](../../group/settings/import_export.md)
- [Group import/export API](../../../api/group_import_export.md)
- [Project import and export API](../../../api/project_import_export.md)
- [Project import and export administration Rake tasks](../../../administration/raketasks/project_import_export.md)
- [Migrating GitLab groups](../../group/import/index.md)
- [Group import and export API](../../../api/group_import_export.md)

View File

@ -34,7 +34,7 @@ module API
get ":id/freeze_periods" do
authorize! :read_freeze_period, user_project
freeze_periods = ::FreezePeriodsFinder.new(user_project, current_user).execute
freeze_periods = ::Ci::FreezePeriodsFinder.new(user_project, current_user).execute
present paginate(freeze_periods), with: Entities::FreezePeriod, current_user: current_user
end

View File

@ -750,6 +750,9 @@ msgstr ""
msgid "%{itemsCount} issues with a limit of %{maxIssueCount}"
msgstr ""
msgid "%{key} is not a valid URL."
msgstr ""
msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
msgstr ""
@ -49329,9 +49332,6 @@ msgstr ""
msgid "is not a descendant of the Group owning the template"
msgstr ""
msgid "is not a valid URL."
msgstr ""
msgid "is not a valid X509 certificate."
msgstr ""

View File

@ -43,6 +43,7 @@ class MetricsServer # rubocop:disable Gitlab/NamespacedClass
path = options[:path]&.then { |p| Pathname.new(p) } || Pathname.new('')
cmd = path.join('gitlab-metrics-exporter').to_path
env = {
'GOGC' => '10', # Set Go GC heap goal to 10% to curb memory growth.
'GME_MMAP_METRICS_DIR' => metrics_dir.to_s,
'GME_PROBES' => 'self,mmap',
'GME_SERVER_HOST' => settings['address'],

View File

@ -167,6 +167,16 @@ RSpec.describe RegistrationsController do
expect(controller.current_user).to be_nil
end
it 'tracks an almost there redirect' do
post_create
expect_snowplow_event(
category: described_class.name,
action: 'render',
user: User.find_by(email: base_user_params[:email])
)
end
context 'when registration is triggered from an accepted invite' do
context 'when it is part from the initial invite email', :snowplow do
let_it_be(:member) { create(:project_member, :invited, invite_email: user_params.dig(:user, :email)) }
@ -260,6 +270,16 @@ RSpec.describe RegistrationsController do
expect(response).to redirect_to(users_sign_up_welcome_path)
end
it 'does not track an almost there redirect' do
post_create
expect_no_snowplow_event(
category: described_class.name,
action: 'render',
user: User.find_by(email: base_user_params[:email])
)
end
context 'when invite email matches email used on registration' do
let(:session_params) { { invite_email: user_params.dig(:user, :email) } }

View File

@ -35,7 +35,7 @@ RSpec.describe 'Merge request > User sees pipelines', :js, feature_category: :co
end
wait_for_requests
expect(page).to have_selector('.stage-cell')
expect(page).to have_css('[data-testid="pipeline-mini-graph"]')
end
context 'with a detached merge request pipeline' do

View File

@ -2,7 +2,7 @@
require 'spec_helper'
RSpec.describe FreezePeriodsFinder do
RSpec.describe Ci::FreezePeriodsFinder, feature_category: :release_orchestration do
subject(:finder) { described_class.new(project, user).execute }
let(:project) { create(:project, :private) }

View File

@ -6,9 +6,10 @@ import DraftNote from '~/batch_comments/components/draft_note.vue';
import PublishButton from '~/batch_comments/components/publish_button.vue';
import { createStore } from '~/batch_comments/stores';
import NoteableNote from '~/notes/components/noteable_note.vue';
import '~/behaviors/markdown/render_gfm';
import { createDraft } from '../mock_data';
jest.mock('~/behaviors/markdown/render_gfm');
const NoteableNoteStub = stubComponent(NoteableNote, {
template: `
<div>

View File

@ -3,9 +3,10 @@ import PreviewItem from '~/batch_comments/components/preview_item.vue';
import { createStore } from '~/batch_comments/stores';
import diffsModule from '~/diffs/store/modules';
import notesModule from '~/notes/stores/modules';
import '~/behaviors/markdown/render_gfm';
import { createDraft } from '../mock_data';
jest.mock('~/behaviors/markdown/render_gfm');
describe('Batch comments draft preview item component', () => {
let wrapper;
let draft;

View File

@ -4,9 +4,10 @@ import Vue from 'vue';
import Vuex from 'vuex';
import PreviewDropdown from '~/batch_comments/components/preview_dropdown.vue';
import { createStore } from '~/mr_notes/stores';
import '~/behaviors/markdown/render_gfm';
import { createDraft } from '../mock_data';
jest.mock('~/behaviors/markdown/render_gfm');
Vue.use(Vuex);
describe('Batch comments publish dropdown component', () => {

View File

@ -5,9 +5,10 @@ import { createStore } from '~/mr_notes/stores';
import DiscussionNotes from '~/notes/components/discussion_notes.vue';
import NoteableDiscussion from '~/notes/components/noteable_discussion.vue';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import '~/behaviors/markdown/render_gfm';
import discussionsMockData from '../mock_data/diff_discussions';
jest.mock('~/behaviors/markdown/render_gfm');
describe('DiffDiscussions', () => {
let store;
let wrapper;

View File

@ -4,7 +4,6 @@ import { editor as monacoEditor, Range } from 'monaco-editor';
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
import { shallowMount } from '@vue/test-utils';
import '~/behaviors/markdown/render_gfm';
import waitForPromises from 'helpers/wait_for_promises';
import { stubPerformanceWebAPI } from 'helpers/performance';
import { exampleConfigs, exampleFiles } from 'jest/ide/lib/editorconfig/mock_data';
@ -27,6 +26,7 @@ import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer
import SourceEditorInstance from '~/editor/source_editor_instance';
import { file } from '../helpers';
jest.mock('~/behaviors/markdown/render_gfm');
jest.mock('~/editor/extensions/source_editor_ci_schema_ext');
const PREVIEW_MARKDOWN_PATH = '/foo/bar/preview_markdown';

View File

@ -5,7 +5,6 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import '~/behaviors/markdown/render_gfm';
import { createAlert } from '~/flash';
import { IssuableStatus, IssuableStatusText, IssuableType } from '~/issues/constants';
import IssuableApp from '~/issues/show/components/app.vue';
@ -30,6 +29,7 @@ import {
jest.mock('~/flash');
jest.mock('~/issues/show/event_hub');
jest.mock('~/lib/utils/url_utility');
jest.mock('~/behaviors/markdown/render_gfm');
const REALTIME_REQUEST_STACK = [initialRequest, secondRequest];

View File

@ -1,7 +1,6 @@
import $ from 'jquery';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import '~/behaviors/markdown/render_gfm';
import { GlTooltip, GlModal } from '@gitlab/ui';
import setWindowLocation from 'helpers/set_window_location_helper';
@ -21,6 +20,7 @@ import createWorkItemFromTaskMutation from '~/work_items/graphql/create_work_ite
import TaskList from '~/task_list';
import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
import { TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import {
projectWorkItemTypesQueryResponse,
createWorkItemFromTaskMutationResponse,
@ -37,6 +37,7 @@ jest.mock('~/lib/utils/url_utility', () => ({
updateHistory: jest.fn(),
}));
jest.mock('~/task_list');
jest.mock('~/behaviors/markdown/render_gfm');
const showModal = jest.fn();
const hideModal = jest.fn();
@ -161,7 +162,6 @@ describe('Description component', () => {
});
it('applies syntax highlighting and math when description changed', async () => {
const prototypeSpy = jest.spyOn($.prototype, 'renderGFM');
createComponent();
await wrapper.setProps({
@ -169,7 +169,7 @@ describe('Description component', () => {
});
expect(findGfmContent().exists()).toBe(true);
expect(prototypeSpy).toHaveBeenCalled();
expect(renderGFM).toHaveBeenCalled();
});
it('sets data-update-url', () => {

View File

@ -1,6 +1,5 @@
import { getByRole } from '@testing-library/dom';
import { shallowMount, mount } from '@vue/test-utils';
import '~/behaviors/markdown/render_gfm';
import { nextTick } from 'vue';
import DiscussionNotes from '~/notes/components/discussion_notes.vue';
import NoteableNote from '~/notes/components/noteable_note.vue';
@ -11,6 +10,8 @@ import PlaceholderSystemNote from '~/vue_shared/components/notes/placeholder_sys
import SystemNote from '~/vue_shared/components/notes/system_note.vue';
import { noteableDataMock, discussionMock, notesDataMock } from '../mock_data';
jest.mock('~/behaviors/markdown/render_gfm');
const LINE_RANGE = {};
const DISCUSSION_WITH_LINE_RANGE = {
...discussionMock,

View File

@ -9,7 +9,6 @@ import ResolveWithIssueButton from '~/notes/components/discussion_resolve_with_i
import NoteForm from '~/notes/components/note_form.vue';
import NoteableDiscussion from '~/notes/components/noteable_discussion.vue';
import createStore from '~/notes/stores';
import '~/behaviors/markdown/render_gfm';
import {
noteableDataMock,
discussionMock,
@ -18,6 +17,8 @@ import {
userDataMock,
} from '../mock_data';
jest.mock('~/behaviors/markdown/render_gfm');
describe('noteable_discussion component', () => {
let store;
let wrapper;

View File

@ -14,11 +14,12 @@ import NotesApp from '~/notes/components/notes_app.vue';
import NotesActivityHeader from '~/notes/components/notes_activity_header.vue';
import * as constants from '~/notes/constants';
import createStore from '~/notes/stores';
import '~/behaviors/markdown/render_gfm';
// TODO: use generated fixture (https://gitlab.com/gitlab-org/gitlab-foss/issues/62491)
import OrderedLayout from '~/vue_shared/components/ordered_layout.vue';
import * as mockData from '../mock_data';
jest.mock('~/behaviors/markdown/render_gfm');
const TYPE_COMMENT_FORM = 'comment-form';
const TYPE_NOTES_LIST = 'notes-list';
const TEST_NOTES_FILTER_VALUE = 1;

View File

@ -3,13 +3,13 @@ import { nextTick } from 'vue';
import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import WikiContent from '~/pages/shared/wikis/components/wiki_content.vue';
import { renderGFM } from '~/pages/shared/wikis/render_gfm_facade';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status';
import waitForPromises from 'helpers/wait_for_promises';
import { handleLocationHash } from '~/lib/utils/common_utils';
jest.mock('~/pages/shared/wikis/render_gfm_facade');
jest.mock('~/behaviors/markdown/render_gfm');
jest.mock('~/lib/utils/common_utils');
describe('pages/shared/wikis/components/wiki_content', () => {

View File

@ -48,7 +48,6 @@ describe('Pipeline Mini Graph', () => {
isMergeTrain: false,
pipelinePath: '',
stages: expect.any(Array),
stagesClass: '',
updateDropdown: false,
upstreamPipeline: undefined,
});
@ -83,7 +82,6 @@ describe('Pipeline Mini Graph', () => {
isMergeTrain: false,
pipelinePath: '',
stages: expect.any(Array),
stagesClass: '',
updateDropdown: false,
upstreamPipeline: expect.any(Object),
});
@ -115,7 +113,6 @@ describe('Pipeline Mini Graph', () => {
isMergeTrain: false,
pipelinePath: 'my/pipeline/path',
stages: expect.any(Array),
stagesClass: '',
updateDropdown: false,
upstreamPipeline: undefined,
});

View File

@ -26,12 +26,6 @@ describe('Pipeline Stages', () => {
expect(findPipelineStages()).toHaveLength(mockStages.length);
});
it('renders stages with a custom class', () => {
createComponent({ stagesClass: 'my-class' });
expect(wrapper.findAll('.my-class')).toHaveLength(mockStages.length);
});
it('does not fail when stages are empty', () => {
createComponent({ stages: [] });

View File

@ -1,5 +1,4 @@
import { mount } from '@vue/test-utils';
import $ from 'jquery';
import { nextTick } from 'vue';
import originalOneReleaseQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release.query.graphql.json';
import { convertOneReleaseGraphQLResponse } from '~/releases/util';
@ -10,6 +9,9 @@ import ReleaseBlock from '~/releases/components/release_block.vue';
import ReleaseBlockFooter from '~/releases/components/release_block_footer.vue';
import { BACK_URL_PARAM } from '~/releases/constants';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
jest.mock('~/behaviors/markdown/render_gfm');
describe('Release block', () => {
let wrapper;
@ -34,7 +36,6 @@ describe('Release block', () => {
const editButton = () => wrapper.find('.js-edit-button');
beforeEach(() => {
jest.spyOn($.fn, 'renderGFM');
release = convertOneReleaseGraphQLResponse(originalOneReleaseQueryResponse).data;
});
@ -62,7 +63,7 @@ describe('Release block', () => {
it('renders release description', () => {
expect(wrapper.vm.$refs['gfm-content']).toBeDefined();
expect($.fn.renderGFM).toHaveBeenCalledTimes(1);
expect(renderGFM).toHaveBeenCalledTimes(1);
});
it('renders release date', () => {

View File

@ -1,4 +1,3 @@
import $ from 'jquery';
import { merge } from 'lodash';
import { GlIntersectionObserver } from '@gitlab/ui';
import { nextTick } from 'vue';
@ -7,13 +6,14 @@ import { mountExtended } from 'helpers/vue_test_utils_helper';
import { FLASH_TYPES, FLASH_CLOSED_EVENT } from '~/flash';
import { isLoggedIn } from '~/lib/utils/common_utils';
import TermsApp from '~/terms/components/app.vue';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
jest.mock('~/lib/utils/csrf', () => ({ token: 'mock-csrf-token' }));
jest.mock('~/lib/utils/common_utils');
jest.mock('~/behaviors/markdown/render_gfm');
describe('TermsApp', () => {
let wrapper;
let renderGFMSpy;
const defaultProvide = {
terms: 'foo bar',
@ -35,7 +35,6 @@ describe('TermsApp', () => {
};
beforeEach(() => {
renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
isLoggedIn.mockReturnValue(true);
});
@ -65,7 +64,7 @@ describe('TermsApp', () => {
createComponent();
expect(wrapper.findByText(defaultProvide.terms).exists()).toBe(true);
expect(renderGFMSpy).toHaveBeenCalled();
expect(renderGFM).toHaveBeenCalled();
});
describe('accept button', () => {

View File

@ -1,7 +1,8 @@
import { mount } from '@vue/test-utils';
import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants';
import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
import '~/behaviors/markdown/render_gfm';
jest.mock('~/behaviors/markdown/render_gfm');
describe('ContentViewer', () => {
let wrapper;

View File

@ -1,11 +1,12 @@
import { GlSkeletonLoader } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import $ from 'jquery';
import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
import MarkdownViewer from '~/vue_shared/components/content_viewer/viewers/markdown_viewer.vue';
jest.mock('~/behaviors/markdown/render_gfm');
describe('MarkdownViewer', () => {
let wrapper;
let mock;
@ -26,7 +27,6 @@ describe('MarkdownViewer', () => {
mock = new MockAdapter(axios);
jest.spyOn(axios, 'post');
jest.spyOn($.fn, 'renderGFM');
});
afterEach(() => {

View File

@ -1,12 +1,14 @@
import { nextTick } from 'vue';
import AxiosMockAdapter from 'axios-mock-adapter';
import $ from 'jquery';
import { TEST_HOST, FIXTURES_PATH } from 'spec/test_constants';
import axios from '~/lib/utils/axios_utils';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import MarkdownFieldHeader from '~/vue_shared/components/markdown/header.vue';
import MarkdownToolbar from '~/vue_shared/components/markdown/toolbar.vue';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
jest.mock('~/behaviors/markdown/render_gfm');
const markdownPreviewPath = `${TEST_HOST}/preview`;
const markdownDocsPath = `${TEST_HOST}/docs`;
@ -138,15 +140,13 @@ describe('Markdown field component', () => {
});
it('renders markdown preview and GFM', async () => {
const renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
previewLink = getPreviewLink();
previewLink.vm.$emit('click', { target: {} });
await axios.waitFor(markdownPreviewPath);
expect(subject.find('.md-preview-holder').element.innerHTML).toContain(previewHTML);
expect(renderGFMSpy).toHaveBeenCalled();
expect(renderGFM).toHaveBeenCalled();
});
it('calls video.pause() on comment input when isSubmitting is changed to true', async () => {

View File

@ -1,10 +1,11 @@
import { shallowMount } from '@vue/test-utils';
import $ from 'jquery';
import MarkdownFieldView from '~/vue_shared/components/markdown/field_view.vue';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
jest.mock('~/behaviors/markdown/render_gfm');
describe('Markdown Field View component', () => {
let renderGFMSpy;
let wrapper;
function createComponent() {
@ -12,7 +13,6 @@ describe('Markdown Field View component', () => {
}
beforeEach(() => {
renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
createComponent();
});
@ -21,6 +21,6 @@ describe('Markdown Field View component', () => {
});
it('processes rendering with GFM', () => {
expect(renderGFMSpy).toHaveBeenCalledTimes(1);
expect(renderGFM).toHaveBeenCalledTimes(1);
});
});

View File

@ -1,10 +1,12 @@
import MockAdapter from 'axios-mock-adapter';
import { mount } from '@vue/test-utils';
import $ from 'jquery';
import waitForPromises from 'helpers/wait_for_promises';
import createStore from '~/notes/stores';
import IssueSystemNote from '~/vue_shared/components/notes/system_note.vue';
import axios from '~/lib/utils/axios_utils';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
jest.mock('~/behaviors/markdown/render_gfm');
describe('system note component', () => {
let vm;
@ -75,11 +77,9 @@ describe('system note component', () => {
});
it('should renderGFM onMount', () => {
const renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
createComponent(props);
expect(renderGFMSpy).toHaveBeenCalled();
expect(renderGFM).toHaveBeenCalled();
});
it('renders outdated code lines', async () => {

View File

@ -1,10 +1,12 @@
import { shallowMount } from '@vue/test-utils';
import $ from 'jquery';
import IssuableDescription from '~/vue_shared/issuable/show/components/issuable_description.vue';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import { mockIssuable } from '../mock_data';
jest.mock('~/behaviors/markdown/render_gfm');
const createComponent = ({
issuable = mockIssuable,
enableTaskList = true,
@ -16,11 +18,9 @@ const createComponent = ({
});
describe('IssuableDescription', () => {
let renderGFMSpy;
let wrapper;
beforeEach(() => {
renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
wrapper = createComponent();
});
@ -30,17 +30,7 @@ describe('IssuableDescription', () => {
describe('mounted', () => {
it('calls `renderGFM`', () => {
expect(renderGFMSpy).toHaveBeenCalledTimes(1);
});
});
describe('methods', () => {
describe('renderGFM', () => {
it('calls `renderGFM` on container element', () => {
wrapper.vm.renderGFM();
expect(renderGFMSpy).toHaveBeenCalled();
});
expect(renderGFM).toHaveBeenCalledTimes(1);
});
});

View File

@ -1,12 +1,14 @@
import { GlIcon } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import { shallowMount } from '@vue/test-utils';
import $ from 'jquery';
import waitForPromises from 'helpers/wait_for_promises';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import WorkItemSystemNote from '~/work_items/components/notes/system_note.vue';
import NoteHeader from '~/notes/components/note_header.vue';
import axios from '~/lib/utils/axios_utils';
jest.mock('~/behaviors/markdown/render_gfm');
describe('system note component', () => {
let wrapper;
let props;
@ -84,11 +86,9 @@ describe('system note component', () => {
});
it('should renderGFM onMount', () => {
const renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
createComponent(props);
expect(renderGFMSpy).toHaveBeenCalled();
expect(renderGFM).toHaveBeenCalled();
});
// eslint-disable-next-line jest/no-disabled-tests

View File

@ -1,9 +1,11 @@
import { shallowMount } from '@vue/test-utils';
import $ from 'jquery';
import { nextTick } from 'vue';
import WorkItemDescriptionRendered from '~/work_items/components/work_item_description_rendered.vue';
import { renderGFM } from '~/behaviors/markdown/render_gfm';
import { descriptionTextWithCheckboxes, descriptionHtmlWithCheckboxes } from '../mock_data';
jest.mock('~/behaviors/markdown/render_gfm');
describe('WorkItemDescription', () => {
let wrapper;
@ -32,13 +34,11 @@ describe('WorkItemDescription', () => {
});
it('renders gfm', async () => {
const renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
createComponent();
await nextTick();
expect(renderGFMSpy).toHaveBeenCalled();
expect(renderGFM).toHaveBeenCalled();
});
describe('with checkboxes', () => {

View File

@ -23,6 +23,8 @@ import CreateWorkItem from '~/work_items/pages/create_work_item.vue';
import WorkItemsRoot from '~/work_items/pages/work_item_root.vue';
import { createRouter } from '~/work_items/router';
jest.mock('~/behaviors/markdown/render_gfm');
describe('Work items router', () => {
let wrapper;

View File

@ -4,7 +4,7 @@ require 'spec_helper'
require_relative '../../metrics_server/metrics_server'
RSpec.describe MetricsServer do # rubocop:disable RSpec/FilePath
RSpec.describe MetricsServer, feature_category: :application_performance do # rubocop:disable RSpec/FilePath
let(:prometheus_config) { ::Prometheus::Client.configuration }
let(:metrics_dir) { Dir.mktmpdir }
@ -118,6 +118,7 @@ RSpec.describe MetricsServer do # rubocop:disable RSpec/FilePath
let(:expected_port) { target == 'puma' ? '8083' : '8082' }
let(:expected_env) do
{
'GOGC' => '10',
'GME_MMAP_METRICS_DIR' => metrics_dir,
'GME_PROBES' => 'self,mmap',
'GME_SERVER_HOST' => 'localhost',

View File

@ -183,25 +183,25 @@ RSpec.describe PipelineSerializer do
context 'with triggered pipelines' do
before do
pipeline_1 = create(:ci_pipeline)
pipeline_1 = create(:ci_pipeline, project: project)
build_1 = create(:ci_build, pipeline: pipeline_1)
create(:ci_sources_pipeline, source_job: build_1)
pipeline_2 = create(:ci_pipeline)
build_2 = create(:ci_build, pipeline: pipeline_2)
create(:ci_sources_pipeline, source_job: build_2)
end
it 'verifies number of queries', :request_store do
recorded = ActiveRecord::QueryRecorder.new { subject }
control = ActiveRecord::QueryRecorder.new do
serializer.represent(Ci::Pipeline.all, preload: true)
end
# Existing numbers are high and require performance optimization
# Ongoing issue:
# https://gitlab.com/gitlab-org/gitlab/-/issues/225156
expected_queries = Gitlab.ee? ? 78 : 74
pipeline_2 = create(:ci_pipeline, project: project)
build_2 = create(:ci_build, pipeline: pipeline_2)
create(:ci_sources_pipeline, source_job: build_2)
expect(recorded.count).to be_within(2).of(expected_queries)
expect(recorded.cached_count).to eq(0)
recorded = ActiveRecord::QueryRecorder.new do
serializer.represent(Ci::Pipeline.all, preload: true)
end
expect(recorded).not_to exceed_query_limit(control)
end
end