Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									bc9b43904d
								
							
						
					
					
						commit
						377d420e3d
					
				| 
						 | 
					@ -1,6 +1,5 @@
 | 
				
			||||||
import $ from 'jquery';
 | 
					import $ from 'jquery';
 | 
				
			||||||
import './autosize';
 | 
					import './autosize';
 | 
				
			||||||
import './markdown/render_gfm';
 | 
					 | 
				
			||||||
import initCollapseSidebarOnWindowResize from './collapse_sidebar_on_window_resize';
 | 
					import initCollapseSidebarOnWindowResize from './collapse_sidebar_on_window_resize';
 | 
				
			||||||
import initCopyToClipboard from './copy_to_clipboard';
 | 
					import initCopyToClipboard from './copy_to_clipboard';
 | 
				
			||||||
import installGlEmojiElement from './gl_emoji';
 | 
					import installGlEmojiElement from './gl_emoji';
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 },
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
import syntaxHighlight from '~/syntax_highlight';
 | 
					import syntaxHighlight from '~/syntax_highlight';
 | 
				
			||||||
import highlightCurrentUser from './highlight_current_user';
 | 
					import highlightCurrentUser from './highlight_current_user';
 | 
				
			||||||
import { renderKroki } from './render_kroki';
 | 
					import { renderKroki } from './render_kroki';
 | 
				
			||||||
| 
						 | 
					@ -8,40 +7,46 @@ import renderMetrics from './render_metrics';
 | 
				
			||||||
import renderObservability from './render_observability';
 | 
					import renderObservability from './render_observability';
 | 
				
			||||||
import { renderJSONTable } from './render_json_table';
 | 
					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
 | 
					// Render GitLab flavoured Markdown
 | 
				
			||||||
//
 | 
					export function renderGFM(element) {
 | 
				
			||||||
// Delegates to syntax highlight and render math & mermaid diagrams.
 | 
					  const [
 | 
				
			||||||
//
 | 
					    highlightEls,
 | 
				
			||||||
$.fn.renderGFM = function renderGFM() {
 | 
					    krokiEls,
 | 
				
			||||||
  syntaxHighlight(this.find('.js-syntax-highlight').get());
 | 
					    mathEls,
 | 
				
			||||||
  renderKroki(this.find('.js-render-kroki[hidden]').get());
 | 
					    mermaidEls,
 | 
				
			||||||
  renderMath(this.find('.js-render-math'));
 | 
					    tableEls,
 | 
				
			||||||
  renderSandboxedMermaid(this.find('.js-render-mermaid').get());
 | 
					    userEls,
 | 
				
			||||||
  renderJSONTable(
 | 
					    popoverEls,
 | 
				
			||||||
    Array.from(this.find('[lang="json"][data-lang-params="table"]').get()).map((e) => e.parentNode),
 | 
					    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());
 | 
					  syntaxHighlight(highlightEls);
 | 
				
			||||||
 | 
					  renderKroki(krokiEls);
 | 
				
			||||||
  const issuablePopoverElements = this.find('.gfm-issue, .gfm-merge_request').get();
 | 
					  renderMath(mathEls);
 | 
				
			||||||
  if (issuablePopoverElements.length) {
 | 
					  renderSandboxedMermaid(mermaidEls);
 | 
				
			||||||
    import(/* webpackChunkName: 'IssuablePopoverBundle' */ '~/issuable/popover')
 | 
					  renderJSONTable(tableEls.map((e) => e.parentNode));
 | 
				
			||||||
      .then(({ default: initIssuablePopovers }) => {
 | 
					  highlightCurrentUser(userEls);
 | 
				
			||||||
        initIssuablePopovers(issuablePopoverElements);
 | 
					  renderMetrics(metricsEls);
 | 
				
			||||||
      })
 | 
					  renderObservability(observabilityEls);
 | 
				
			||||||
      .catch(() => {});
 | 
					  initPopovers(popoverEls);
 | 
				
			||||||
  }
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
  renderMetrics(this.find('.js-render-metrics').get());
 | 
					 | 
				
			||||||
  renderObservability(this.find('.js-render-observability').get());
 | 
					 | 
				
			||||||
  return this;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
$(() => {
 | 
					 | 
				
			||||||
  window.requestIdleCallback(
 | 
					 | 
				
			||||||
    () => {
 | 
					 | 
				
			||||||
      $('body').renderGFM();
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    { timeout: 500 },
 | 
					 | 
				
			||||||
  );
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -175,14 +175,14 @@ class SafeMathRenderer {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function renderMath($els) {
 | 
					export default function renderMath(elements) {
 | 
				
			||||||
  if (!$els.length) return;
 | 
					  if (!elements.length) return;
 | 
				
			||||||
  Promise.all([
 | 
					  Promise.all([
 | 
				
			||||||
    import(/* webpackChunkName: 'katex' */ 'katex'),
 | 
					    import(/* webpackChunkName: 'katex' */ 'katex'),
 | 
				
			||||||
    import(/* webpackChunkName: 'katex' */ 'katex/dist/katex.min.css'),
 | 
					    import(/* webpackChunkName: 'katex' */ 'katex/dist/katex.min.css'),
 | 
				
			||||||
  ])
 | 
					  ])
 | 
				
			||||||
    .then(([katex]) => {
 | 
					    .then(([katex]) => {
 | 
				
			||||||
      const renderer = new SafeMathRenderer($els.get(), katex);
 | 
					      const renderer = new SafeMathRenderer(elements, katex);
 | 
				
			||||||
      renderer.render();
 | 
					      renderer.render();
 | 
				
			||||||
      renderer.attachEvents();
 | 
					      renderer.attachEvents();
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@ import $ from 'jquery';
 | 
				
			||||||
import { createAlert } from '~/flash';
 | 
					import { createAlert } from '~/flash';
 | 
				
			||||||
import axios from '~/lib/utils/axios_utils';
 | 
					import axios from '~/lib/utils/axios_utils';
 | 
				
			||||||
import { __ } from '~/locale';
 | 
					import { __ } from '~/locale';
 | 
				
			||||||
 | 
					import '~/behaviors/markdown/init_gfm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MarkdownPreview
 | 
					// MarkdownPreview
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
import $ from 'jquery';
 | 
					import $ from 'jquery';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					import '~/behaviors/markdown/init_gfm';
 | 
				
			||||||
import { createAlert } from '~/flash';
 | 
					import { createAlert } from '~/flash';
 | 
				
			||||||
import { __ } from '~/locale';
 | 
					import { __ } from '~/locale';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@ import { addEditorMarkdownListeners } from '~/lib/utils/text_markdown';
 | 
				
			||||||
import { insertFinalNewline } from '~/lib/utils/text_utility';
 | 
					import { insertFinalNewline } from '~/lib/utils/text_utility';
 | 
				
			||||||
import TemplateSelectorMediator from '../blob/file_template_mediator';
 | 
					import TemplateSelectorMediator from '../blob/file_template_mediator';
 | 
				
			||||||
import { BLOB_EDITOR_ERROR, BLOB_PREVIEW_ERROR } from './constants';
 | 
					import { BLOB_EDITOR_ERROR, BLOB_PREVIEW_ERROR } from './constants';
 | 
				
			||||||
 | 
					import '~/behaviors/markdown/init_gfm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class EditBlob {
 | 
					export default class EditBlob {
 | 
				
			||||||
  // The options object has:
 | 
					  // The options object has:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -40,6 +40,7 @@ import { localTimeAgo } from './lib/utils/datetime_utility';
 | 
				
			||||||
import { getLocationHash } from './lib/utils/url_utility';
 | 
					import { getLocationHash } from './lib/utils/url_utility';
 | 
				
			||||||
import { sprintf, s__, __ } from './locale';
 | 
					import { sprintf, s__, __ } from './locale';
 | 
				
			||||||
import TaskList from './task_list';
 | 
					import TaskList from './task_list';
 | 
				
			||||||
 | 
					import '~/behaviors/markdown/init_gfm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
window.autosize = Autosize;
 | 
					window.autosize = Autosize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,6 +28,7 @@ import {
 | 
				
			||||||
  TASK_TYPE_NAME,
 | 
					  TASK_TYPE_NAME,
 | 
				
			||||||
  WIDGET_TYPE_DESCRIPTION,
 | 
					  WIDGET_TYPE_DESCRIPTION,
 | 
				
			||||||
} from '~/work_items/constants';
 | 
					} from '~/work_items/constants';
 | 
				
			||||||
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
import animateMixin from '../mixins/animate';
 | 
					import animateMixin from '../mixins/animate';
 | 
				
			||||||
import { convertDescriptionWithNewSort } from '../utils';
 | 
					import { convertDescriptionWithNewSort } from '../utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -178,7 +179,7 @@ export default {
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    renderGFM() {
 | 
					    renderGFM() {
 | 
				
			||||||
      $(this.$refs['gfm-content']).renderGFM();
 | 
					      renderGFM(this.$refs['gfm-content']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (this.canUpdate) {
 | 
					      if (this.canUpdate) {
 | 
				
			||||||
        // eslint-disable-next-line no-new
 | 
					        // eslint-disable-next-line no-new
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@ import { createAlert } from '~/flash';
 | 
				
			||||||
import { getCookie, isMetaClick, parseBoolean, scrollToElement } from '~/lib/utils/common_utils';
 | 
					import { getCookie, isMetaClick, parseBoolean, scrollToElement } from '~/lib/utils/common_utils';
 | 
				
			||||||
import { parseUrlPathname } from '~/lib/utils/url_utility';
 | 
					import { parseUrlPathname } from '~/lib/utils/url_utility';
 | 
				
			||||||
import createEventHub from '~/helpers/event_hub_factory';
 | 
					import createEventHub from '~/helpers/event_hub_factory';
 | 
				
			||||||
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
import BlobForkSuggestion from './blob/blob_fork_suggestion';
 | 
					import BlobForkSuggestion from './blob/blob_fork_suggestion';
 | 
				
			||||||
import Diff from './diff';
 | 
					import Diff from './diff';
 | 
				
			||||||
import { initDiffStatsDropdown } from './init_diff_stats_dropdown';
 | 
					import { initDiffStatsDropdown } from './init_diff_stats_dropdown';
 | 
				
			||||||
| 
						 | 
					@ -346,7 +347,7 @@ export default class MergeRequestTabs {
 | 
				
			||||||
        this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
 | 
					        this.commitPipelinesTable = destroyPipelines(this.commitPipelinesTable);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      $('.detail-page-description').renderGFM();
 | 
					      renderGFM(document.querySelector('.detail-page-description'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (shouldScroll) this.recallScroll(action);
 | 
					      if (shouldScroll) this.recallScroll(action);
 | 
				
			||||||
    } else if (action === this.currentAction) {
 | 
					    } else if (action === this.currentAction) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,10 @@
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
import { escape } from 'lodash';
 | 
					import { escape } from 'lodash';
 | 
				
			||||||
import { mapActions, mapGetters, mapState } from 'vuex';
 | 
					import { mapActions, mapGetters, mapState } from 'vuex';
 | 
				
			||||||
import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
					import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
				
			||||||
import { __ } from '~/locale';
 | 
					import { __ } from '~/locale';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import Suggestions from '~/vue_shared/components/markdown/suggestions.vue';
 | 
					import Suggestions from '~/vue_shared/components/markdown/suggestions.vue';
 | 
				
			||||||
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
import autosave from '../mixins/autosave';
 | 
					import autosave from '../mixins/autosave';
 | 
				
			||||||
import NoteAttachment from './note_attachment.vue';
 | 
					import NoteAttachment from './note_attachment.vue';
 | 
				
			||||||
import NoteAwardsList from './note_awards_list.vue';
 | 
					import NoteAwardsList from './note_awards_list.vue';
 | 
				
			||||||
| 
						 | 
					@ -121,7 +120,7 @@ export default {
 | 
				
			||||||
      'removeSuggestionInfoFromBatch',
 | 
					      'removeSuggestionInfoFromBatch',
 | 
				
			||||||
    ]),
 | 
					    ]),
 | 
				
			||||||
    renderGFM() {
 | 
					    renderGFM() {
 | 
				
			||||||
      $(this.$refs['note-body']).renderGFM();
 | 
					      renderGFM(this.$refs['note-body']);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    handleFormUpdate(noteText, parentElement, callback, resolveDiscussion) {
 | 
					    handleFormUpdate(noteText, parentElement, callback, resolveDiscussion) {
 | 
				
			||||||
      this.$emit('handleFormUpdate', { noteText, parentElement, callback, resolveDiscussion });
 | 
					      this.$emit('handleFormUpdate', { noteText, parentElement, callback, resolveDiscussion });
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,7 @@ import { ignoreWhilePending } from '~/lib/utils/ignore_while_pending';
 | 
				
			||||||
import { truncateSha } from '~/lib/utils/text_utility';
 | 
					import { truncateSha } from '~/lib/utils/text_utility';
 | 
				
			||||||
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
 | 
					import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
 | 
				
			||||||
import { __, s__, sprintf } from '~/locale';
 | 
					import { __, s__, sprintf } from '~/locale';
 | 
				
			||||||
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
import eventHub from '../event_hub';
 | 
					import eventHub from '../event_hub';
 | 
				
			||||||
import noteable from '../mixins/noteable';
 | 
					import noteable from '../mixins/noteable';
 | 
				
			||||||
import resolvable from '../mixins/resolvable';
 | 
					import resolvable from '../mixins/resolvable';
 | 
				
			||||||
| 
						 | 
					@ -287,7 +288,7 @@ export default {
 | 
				
			||||||
      this.isEditing = false;
 | 
					      this.isEditing = false;
 | 
				
			||||||
      this.isRequesting = false;
 | 
					      this.isRequesting = false;
 | 
				
			||||||
      this.oldContent = null;
 | 
					      this.oldContent = null;
 | 
				
			||||||
      $(this.$refs.noteBody.$el).renderGFM();
 | 
					      renderGFM(this.$refs.noteBody.$el);
 | 
				
			||||||
      this.$refs.noteBody.resetAutoSave();
 | 
					      this.$refs.noteBody.resetAutoSave();
 | 
				
			||||||
      this.$emit('updateSuccess');
 | 
					      this.$emit('updateSuccess');
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@ import { createAlert } from '~/flash';
 | 
				
			||||||
import { __ } from '~/locale';
 | 
					import { __ } from '~/locale';
 | 
				
			||||||
import axios from '~/lib/utils/axios_utils';
 | 
					import axios from '~/lib/utils/axios_utils';
 | 
				
			||||||
import { handleLocationHash } from '~/lib/utils/common_utils';
 | 
					import { handleLocationHash } from '~/lib/utils/common_utils';
 | 
				
			||||||
import { renderGFM } from '../render_gfm_facade';
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  components: {
 | 
					  components: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +0,0 @@
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const renderGFM = (el) => {
 | 
					 | 
				
			||||||
  return $(el).renderGFM();
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
| 
						 | 
					@ -35,11 +35,6 @@ export default {
 | 
				
			||||||
      required: true,
 | 
					      required: true,
 | 
				
			||||||
      default: () => [],
 | 
					      default: () => [],
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    stagesClass: {
 | 
					 | 
				
			||||||
      type: [Array, Object, String],
 | 
					 | 
				
			||||||
      required: false,
 | 
					 | 
				
			||||||
      default: '',
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    updateDropdown: {
 | 
					    updateDropdown: {
 | 
				
			||||||
      type: Boolean,
 | 
					      type: Boolean,
 | 
				
			||||||
      required: false,
 | 
					      required: false,
 | 
				
			||||||
| 
						 | 
					@ -59,7 +54,7 @@ export default {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div class="stage-cell" data-testid="pipeline-mini-graph">
 | 
					  <div data-testid="pipeline-mini-graph">
 | 
				
			||||||
    <linked-pipelines-mini-list
 | 
					    <linked-pipelines-mini-list
 | 
				
			||||||
      v-if="upstreamPipeline"
 | 
					      v-if="upstreamPipeline"
 | 
				
			||||||
      :triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
 | 
					      :triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
 | 
				
			||||||
| 
						 | 
					@ -77,7 +72,6 @@ export default {
 | 
				
			||||||
      :is-merge-train="isMergeTrain"
 | 
					      :is-merge-train="isMergeTrain"
 | 
				
			||||||
      :stages="stages"
 | 
					      :stages="stages"
 | 
				
			||||||
      :update-dropdown="updateDropdown"
 | 
					      :update-dropdown="updateDropdown"
 | 
				
			||||||
      :stages-class="stagesClass"
 | 
					 | 
				
			||||||
      data-testid="pipeline-stages"
 | 
					      data-testid="pipeline-stages"
 | 
				
			||||||
      @miniGraphStageClick="$emit('miniGraphStageClick')"
 | 
					      @miniGraphStageClick="$emit('miniGraphStageClick')"
 | 
				
			||||||
    />
 | 
					    />
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,11 +17,6 @@ export default {
 | 
				
			||||||
      required: false,
 | 
					      required: false,
 | 
				
			||||||
      default: false,
 | 
					      default: false,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    stagesClass: {
 | 
					 | 
				
			||||||
      type: [Array, Object, String],
 | 
					 | 
				
			||||||
      required: false,
 | 
					 | 
				
			||||||
      default: '',
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    isMergeTrain: {
 | 
					    isMergeTrain: {
 | 
				
			||||||
      type: Boolean,
 | 
					      type: Boolean,
 | 
				
			||||||
      required: false,
 | 
					      required: false,
 | 
				
			||||||
| 
						 | 
					@ -35,8 +30,7 @@ export default {
 | 
				
			||||||
    <div
 | 
					    <div
 | 
				
			||||||
      v-for="stage in stages"
 | 
					      v-for="stage in stages"
 | 
				
			||||||
      :key="stage.name"
 | 
					      :key="stage.name"
 | 
				
			||||||
      :class="stagesClass"
 | 
					      class="pipeline-mini-graph-stage-container dropdown gl-display-inline-block gl-mr-2 gl-my-2 gl-vertical-align-middle"
 | 
				
			||||||
      class="dropdown gl-display-inline-block gl-mr-2 gl-my-2 gl-vertical-align-middle stage-container"
 | 
					 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <pipeline-stage
 | 
					      <pipeline-stage
 | 
				
			||||||
        :stage="stage"
 | 
					        :stage="stage"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,4 @@
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
import { isEmpty } from 'lodash';
 | 
					import { isEmpty } from 'lodash';
 | 
				
			||||||
import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
					import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
				
			||||||
import { scrollToElement } from '~/lib/utils/common_utils';
 | 
					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 { getLocationHash } from '~/lib/utils/url_utility';
 | 
				
			||||||
import { CREATED_ASC } from '~/releases/constants';
 | 
					import { CREATED_ASC } from '~/releases/constants';
 | 
				
			||||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
 | 
					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 EvidenceBlock from './evidence_block.vue';
 | 
				
			||||||
import ReleaseBlockAssets from './release_block_assets.vue';
 | 
					import ReleaseBlockAssets from './release_block_assets.vue';
 | 
				
			||||||
import ReleaseBlockFooter from './release_block_footer.vue';
 | 
					import ReleaseBlockFooter from './release_block_footer.vue';
 | 
				
			||||||
| 
						 | 
					@ -86,7 +85,7 @@ export default {
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    renderGFM() {
 | 
					    renderGFM() {
 | 
				
			||||||
      $(this.$refs['gfm-content']).renderGFM();
 | 
					      renderGFM(this.$refs['gfm-content']);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
 | 
					  safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,8 @@
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
import { GlIcon, GlLink, GlLoadingIcon } from '@gitlab/ui';
 | 
					import { GlIcon, GlLink, GlLoadingIcon } from '@gitlab/ui';
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
					import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import { handleLocationHash } from '~/lib/utils/common_utils';
 | 
					import { handleLocationHash } from '~/lib/utils/common_utils';
 | 
				
			||||||
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
import readmeQuery from '../../queries/readme.query.graphql';
 | 
					import readmeQuery from '../../queries/readme.query.graphql';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
| 
						 | 
					@ -43,7 +42,7 @@ export default {
 | 
				
			||||||
      if (newVal) {
 | 
					      if (newVal) {
 | 
				
			||||||
        this.$nextTick(() => {
 | 
					        this.$nextTick(() => {
 | 
				
			||||||
          handleLocationHash();
 | 
					          handleLocationHash();
 | 
				
			||||||
          $(this.$refs.readme).renderGFM();
 | 
					          renderGFM(this.$refs.readme);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,4 @@
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
import { GlButton, GlIntersectionObserver } from '@gitlab/ui';
 | 
					import { GlButton, GlIntersectionObserver } from '@gitlab/ui';
 | 
				
			||||||
import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
					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 { isLoggedIn } from '~/lib/utils/common_utils';
 | 
				
			||||||
import { __ } from '~/locale';
 | 
					import { __ } from '~/locale';
 | 
				
			||||||
import csrf from '~/lib/utils/csrf';
 | 
					import csrf from '~/lib/utils/csrf';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import { trackTrialAcceptTerms } from '~/google_tag_manager';
 | 
					import { trackTrialAcceptTerms } from '~/google_tag_manager';
 | 
				
			||||||
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  name: 'TermsApp',
 | 
					  name: 'TermsApp',
 | 
				
			||||||
| 
						 | 
					@ -55,7 +54,7 @@ export default {
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    renderGFM() {
 | 
					    renderGFM() {
 | 
				
			||||||
      $(this.$refs.gfmContainer).renderGFM();
 | 
					      renderGFM(this.$refs.gfmContainer);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    handleBottomReached() {
 | 
					    handleBottomReached() {
 | 
				
			||||||
      this.acceptDisabled = false;
 | 
					      this.acceptDisabled = false;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -190,7 +190,7 @@ export default {
 | 
				
			||||||
    </template>
 | 
					    </template>
 | 
				
			||||||
    <template v-else-if="hasPipeline">
 | 
					    <template v-else-if="hasPipeline">
 | 
				
			||||||
      <a :href="status.details_path" class="gl-align-self-center gl-mr-3">
 | 
					      <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>
 | 
					      </a>
 | 
				
			||||||
      <div class="ci-widget-container d-flex">
 | 
					      <div class="ci-widget-container d-flex">
 | 
				
			||||||
        <div class="ci-widget-content">
 | 
					        <div class="ci-widget-content">
 | 
				
			||||||
| 
						 | 
					@ -280,7 +280,6 @@ export default {
 | 
				
			||||||
              :pipeline-path="pipeline.path"
 | 
					              :pipeline-path="pipeline.path"
 | 
				
			||||||
              :stages="pipeline.details.stages"
 | 
					              :stages="pipeline.details.stages"
 | 
				
			||||||
              :upstream-pipeline="pipeline.triggered_by"
 | 
					              :upstream-pipeline="pipeline.triggered_by"
 | 
				
			||||||
              stages-class="mr-widget-pipeline-stages"
 | 
					 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
            <pipeline-artifacts :pipeline-id="pipeline.id" :artifacts="artifacts" class="gl-ml-3" />
 | 
					            <pipeline-artifacts :pipeline-id="pipeline.id" :artifacts="artifacts" class="gl-ml-3" />
 | 
				
			||||||
          </span>
 | 
					          </span>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,10 @@
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
import { GlSkeletonLoader } from '@gitlab/ui';
 | 
					import { GlSkeletonLoader } from '@gitlab/ui';
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import { forEach, escape } from 'lodash';
 | 
					import { forEach, escape } from 'lodash';
 | 
				
			||||||
import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
					import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
				
			||||||
import axios from '~/lib/utils/axios_utils';
 | 
					import axios from '~/lib/utils/axios_utils';
 | 
				
			||||||
import { __ } from '~/locale';
 | 
					import { __ } from '~/locale';
 | 
				
			||||||
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const { CancelToken } = axios;
 | 
					const { CancelToken } = axios;
 | 
				
			||||||
let axiosSource;
 | 
					let axiosSource;
 | 
				
			||||||
| 
						 | 
					@ -97,7 +96,7 @@ export default {
 | 
				
			||||||
            this.isLoading = false;
 | 
					            this.isLoading = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            this.$nextTick(() => {
 | 
					            this.$nextTick(() => {
 | 
				
			||||||
              $(this.$refs.markdownPreview).renderGFM();
 | 
					              renderGFM(this.$refs.markdownPreview);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
          })
 | 
					          })
 | 
				
			||||||
          .catch(() => {
 | 
					          .catch(() => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
import { GlIcon } from '@gitlab/ui';
 | 
					import { GlIcon } from '@gitlab/ui';
 | 
				
			||||||
import $ from 'jquery';
 | 
					import $ from 'jquery';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import { debounce, unescape } from 'lodash';
 | 
					import { debounce, unescape } from 'lodash';
 | 
				
			||||||
import { createAlert } from '~/flash';
 | 
					import { createAlert } from '~/flash';
 | 
				
			||||||
import GLForm from '~/gl_form';
 | 
					import GLForm from '~/gl_form';
 | 
				
			||||||
| 
						 | 
					@ -11,6 +10,7 @@ import { stripHtml } from '~/lib/utils/text_utility';
 | 
				
			||||||
import { __, sprintf } from '~/locale';
 | 
					import { __, sprintf } from '~/locale';
 | 
				
			||||||
import Suggestions from '~/vue_shared/components/markdown/suggestions.vue';
 | 
					import Suggestions from '~/vue_shared/components/markdown/suggestions.vue';
 | 
				
			||||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
 | 
					import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
 | 
				
			||||||
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
import MarkdownHeader from './header.vue';
 | 
					import MarkdownHeader from './header.vue';
 | 
				
			||||||
import MarkdownToolbar from './toolbar.vue';
 | 
					import MarkdownToolbar from './toolbar.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -314,7 +314,9 @@ export default {
 | 
				
			||||||
      this.markdownPreview = data.body || __('Nothing to preview.');
 | 
					      this.markdownPreview = data.body || __('Nothing to preview.');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      this.$nextTick()
 | 
					      this.$nextTick()
 | 
				
			||||||
        .then(() => $(this.$refs['markdown-preview']).renderGFM())
 | 
					        .then(() => {
 | 
				
			||||||
 | 
					          renderGFM(this.$refs['markdown-preview']);
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
        .catch(() =>
 | 
					        .catch(() =>
 | 
				
			||||||
          createAlert({
 | 
					          createAlert({
 | 
				
			||||||
            message: __('Error rendering Markdown preview'),
 | 
					            message: __('Error rendering Markdown preview'),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,9 @@
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
import $ from 'jquery';
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  mounted() {
 | 
					  mounted() {
 | 
				
			||||||
    this.renderGFM();
 | 
					    renderGFM(this.$el);
 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  methods: {
 | 
					 | 
				
			||||||
    renderGFM() {
 | 
					 | 
				
			||||||
      $(this.$el).renderGFM();
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,9 @@
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
import { GlDrawer, GlAlert, GlSkeletonLoader } from '@gitlab/ui';
 | 
					import { GlDrawer, GlAlert, GlSkeletonLoader } from '@gitlab/ui';
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
					import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import { s__ } from '~/locale';
 | 
					import { s__ } from '~/locale';
 | 
				
			||||||
import { contentTop } from '~/lib/utils/common_utils';
 | 
					import { contentTop } from '~/lib/utils/common_utils';
 | 
				
			||||||
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
import { getRenderedMarkdown } from './utils/fetch';
 | 
					import { getRenderedMarkdown } from './utils/fetch';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const cache = {};
 | 
					export const cache = {};
 | 
				
			||||||
| 
						 | 
					@ -78,7 +77,7 @@ export default {
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    renderGLFM() {
 | 
					    renderGLFM() {
 | 
				
			||||||
      this.$nextTick(() => {
 | 
					      this.$nextTick(() => {
 | 
				
			||||||
        $(this.$refs['content-element']).renderGFM();
 | 
					        renderGFM(this.$refs['content-element']);
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    closeDrawer() {
 | 
					    closeDrawer() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,12 +21,12 @@ import $ from 'jquery';
 | 
				
			||||||
import { mapGetters, mapActions, mapState } from 'vuex';
 | 
					import { mapGetters, mapActions, mapState } from 'vuex';
 | 
				
			||||||
import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
					import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
				
			||||||
import descriptionVersionHistoryMixin from 'ee_else_ce/notes/mixins/description_version_history';
 | 
					import descriptionVersionHistoryMixin from 'ee_else_ce/notes/mixins/description_version_history';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import axios from '~/lib/utils/axios_utils';
 | 
					import axios from '~/lib/utils/axios_utils';
 | 
				
			||||||
import { __ } from '~/locale';
 | 
					import { __ } from '~/locale';
 | 
				
			||||||
import NoteHeader from '~/notes/components/note_header.vue';
 | 
					import NoteHeader from '~/notes/components/note_header.vue';
 | 
				
			||||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
 | 
					import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
 | 
				
			||||||
import { spriteIcon } from '~/lib/utils/common_utils';
 | 
					import { spriteIcon } from '~/lib/utils/common_utils';
 | 
				
			||||||
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
import TimelineEntryItem from './timeline_entry_item.vue';
 | 
					import TimelineEntryItem from './timeline_entry_item.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
 | 
					const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
 | 
				
			||||||
| 
						 | 
					@ -89,7 +89,7 @@ export default {
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  mounted() {
 | 
					  mounted() {
 | 
				
			||||||
    $(this.$refs['gfm-content']).renderGFM();
 | 
					    renderGFM(this.$refs['gfm-content']);
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    ...mapActions(['fetchDescriptionVersion', 'softDeleteDescriptionVersion']),
 | 
					    ...mapActions(['fetchDescriptionVersion', 'softDeleteDescriptionVersion']),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
					import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  directives: {
 | 
					  directives: {
 | 
				
			||||||
| 
						 | 
					@ -26,12 +25,7 @@ export default {
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  mounted() {
 | 
					  mounted() {
 | 
				
			||||||
    this.renderGFM();
 | 
					    renderGFM(this.$refs.gfmContainer);
 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  methods: {
 | 
					 | 
				
			||||||
    renderGFM() {
 | 
					 | 
				
			||||||
      $(this.$refs.gfmContainer).renderGFM();
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,9 +17,9 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
import { GlButton, GlSkeletonLoader, GlTooltipDirective, GlIcon } from '@gitlab/ui';
 | 
					import { GlButton, GlSkeletonLoader, GlTooltipDirective, GlIcon } from '@gitlab/ui';
 | 
				
			||||||
import $ from 'jquery';
 | 
					import $ from 'jquery';
 | 
				
			||||||
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
					import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
				
			||||||
import descriptionVersionHistoryMixin from 'ee_else_ce/notes/mixins/description_version_history';
 | 
					import descriptionVersionHistoryMixin from 'ee_else_ce/notes/mixins/description_version_history';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import axios from '~/lib/utils/axios_utils';
 | 
					import axios from '~/lib/utils/axios_utils';
 | 
				
			||||||
import { getLocationHash } from '~/lib/utils/url_utility';
 | 
					import { getLocationHash } from '~/lib/utils/url_utility';
 | 
				
			||||||
import { __ } from '~/locale';
 | 
					import { __ } from '~/locale';
 | 
				
			||||||
| 
						 | 
					@ -89,7 +89,7 @@ export default {
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  mounted() {
 | 
					  mounted() {
 | 
				
			||||||
    $(this.$refs['gfm-content']).renderGFM();
 | 
					    renderGFM(this.$refs['gfm-content']);
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    fetchDescriptionVersion() {},
 | 
					    fetchDescriptionVersion() {},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,7 @@
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
 | 
					import { GlButton, GlTooltipDirective } from '@gitlab/ui';
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import SafeHtml from '~/vue_shared/directives/safe_html';
 | 
					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');
 | 
					const isCheckbox = (target) => target?.classList.contains('task-list-item-checkbox');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,7 +46,7 @@ export default {
 | 
				
			||||||
    async renderGFM() {
 | 
					    async renderGFM() {
 | 
				
			||||||
      await this.$nextTick();
 | 
					      await this.$nextTick();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      $(this.$refs['gfm-content']).renderGFM();
 | 
					      renderGFM(this.$refs['gfm-content']);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (this.canEdit) {
 | 
					      if (this.canEdit) {
 | 
				
			||||||
        this.checkboxes = this.$el.querySelectorAll('.task-list-item-checkbox');
 | 
					        this.checkboxes = this.$el.querySelectorAll('.task-list-item-checkbox');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,33 +70,20 @@
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Mini Pipelines
 | 
					// Pipeline mini graph
 | 
				
			||||||
 | 
					.pipeline-mini-graph-stage-container {
 | 
				
			||||||
 | 
					  &:last-child {
 | 
				
			||||||
 | 
					    margin-right: 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.stage-cell {
 | 
					  &:not(:last-child) {
 | 
				
			||||||
  .stage-container {
 | 
					    &::after {
 | 
				
			||||||
    &:last-child {
 | 
					      content: '';
 | 
				
			||||||
      margin-right: 0;
 | 
					      border-bottom: 2px solid $gray-200;
 | 
				
			||||||
    }
 | 
					      position: absolute;
 | 
				
			||||||
 | 
					      right: -4px;
 | 
				
			||||||
    // Hack to show a button tooltip inline
 | 
					      top: 11px;
 | 
				
			||||||
    button.has-tooltip + .tooltip {
 | 
					      width: 4px;
 | 
				
			||||||
      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;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -135,6 +135,7 @@ class RegistrationsController < Devise::RegistrationsController
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return identity_verification_redirect_path if custom_confirmation_enabled?
 | 
					    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)
 | 
					    users_almost_there_path(email: resource.email)
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
					 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,5 @@
 | 
				
			||||||
class DependencyProxy::GroupSetting < ApplicationRecord
 | 
					class DependencyProxy::GroupSetting < ApplicationRecord
 | 
				
			||||||
  belongs_to :group
 | 
					  belongs_to :group
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  attribute :enabled, default: true
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  validates :group, presence: true
 | 
					  validates :group, presence: true
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,9 +53,7 @@
 | 
				
			||||||
      = ci_label_for_status(@last_pipeline.status)
 | 
					      = ci_label_for_status(@last_pipeline.status)
 | 
				
			||||||
      - if @last_pipeline.stages_count.nonzero?
 | 
					      - if @last_pipeline.stages_count.nonzero?
 | 
				
			||||||
        #{ n_(s_('Pipeline|with stage'), s_('Pipeline|with stages'), @last_pipeline.stages_count) }
 | 
					        #{ n_(s_('Pipeline|with stage'), s_('Pipeline|with stages'), @last_pipeline.stages_count) }
 | 
				
			||||||
        .mr-widget-pipeline-graph
 | 
					        .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) } }
 | 
				
			||||||
          .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) } }
 | 
					 | 
				
			||||||
      - if @last_pipeline.duration
 | 
					      - if @last_pipeline.duration
 | 
				
			||||||
        in
 | 
					        in
 | 
				
			||||||
        = time_interval_in_words @last_pipeline.duration
 | 
					        = time_interval_in_words @last_pipeline.duration
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
					 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					b14a060e05fc73c9d76d7c8bec3f9e1fa99b33eae6ec0057b4a398b28414a02a
 | 
				
			||||||
| 
						 | 
					@ -14733,7 +14733,7 @@ CREATE TABLE dependency_proxy_group_settings (
 | 
				
			||||||
    group_id integer NOT NULL,
 | 
					    group_id integer NOT NULL,
 | 
				
			||||||
    created_at timestamp with time zone NOT NULL,
 | 
					    created_at timestamp with time zone NOT NULL,
 | 
				
			||||||
    updated_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
 | 
					CREATE SEQUENCE dependency_proxy_group_settings_id_seq
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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.
 | 
					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 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.
 | 
					> 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.
 | 
					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.
 | 
					   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.
 | 
					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"
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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/).
 | 
				
			||||||
| 
						 | 
					@ -199,10 +199,16 @@ You can configure GitLab to use multiple SAML 2.0 identity providers if:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Example multiple providers configuration for Omnibus GitLab:
 | 
					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
 | 
					```ruby
 | 
				
			||||||
gitlab_rails['omniauth_providers'] = [
 | 
					gitlab_rails['omniauth_providers'] = [
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    name: 'saml',
 | 
					    name: 'saml', # This must match the following name configuration parameter
 | 
				
			||||||
    args: {
 | 
					    args: {
 | 
				
			||||||
            name: 'saml', # This is mandatory and must match the provider name
 | 
					            name: 'saml', # This is mandatory and must match the provider name
 | 
				
			||||||
            strategy_class: 'OmniAuth::Strategies::SAML',
 | 
					            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
 | 
					    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: {
 | 
					    args: {
 | 
				
			||||||
            name: 'saml1', # This is mandatory and must match the provider name
 | 
					            name: 'saml1', # This is mandatory and must match the provider name
 | 
				
			||||||
            strategy_class: 'OmniAuth::Strategies::SAML',
 | 
					            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
 | 
					## 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.
 | 
					You can:
 | 
				
			||||||
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).
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
Support for these groups depends on your [subscription](https://about.gitlab.com/pricing/)
 | 
					- Require users to be members of a certain group.
 | 
				
			||||||
and whether you've installed [GitLab Enterprise Edition (EE)](https://about.gitlab.com/install/).
 | 
					- 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? |
 | 
					| Group                        | Tier               | GitLab Enterprise Edition (EE) Only? |
 | 
				
			||||||
|------------------------------|--------------------|--------------------------------------|
 | 
					|------------------------------|--------------------|--------------------------------------|
 | 
				
			||||||
| 
						 | 
					@ -368,9 +380,9 @@ and whether you've installed [GitLab Enterprise Edition (EE)](https://about.gitl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Prerequisites
 | 
					### Prerequisites
 | 
				
			||||||
 | 
					
 | 
				
			||||||
First tell GitLab where to look for group information. For this, you
 | 
					You must tell GitLab where to look for group information. To do this, make sure
 | 
				
			||||||
must make sure that your IdP server sends a specific `AttributeStatement` along
 | 
					that your IdP server sends a specific `AttributeStatement` along with the regular
 | 
				
			||||||
with the regular SAML response. Here is an example:
 | 
					SAML response. For example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```xml
 | 
					```xml
 | 
				
			||||||
<saml:AttributeStatement>
 | 
					<saml:AttributeStatement>
 | 
				
			||||||
| 
						 | 
					@ -383,9 +395,9 @@ with the regular SAML response. Here is an example:
 | 
				
			||||||
</saml:AttributeStatement>
 | 
					</saml:AttributeStatement>
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The name of the attribute can be anything you like, but it must contain the groups
 | 
					The name of the attribute must contain the groups that a user belongs to.
 | 
				
			||||||
to which a user belongs. To tell GitLab where to find these groups, you need
 | 
					To tell GitLab where to find these groups, add a `groups_attribute:`
 | 
				
			||||||
to add a `groups_attribute:` element to your SAML settings.
 | 
					element to your SAML settings.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Required groups
 | 
					### Required groups
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -585,17 +597,13 @@ list.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Validate response signatures
 | 
					## Validate response signatures
 | 
				
			||||||
 | 
					
 | 
				
			||||||
We require Identity Providers to sign SAML responses to ensure that the assertions are
 | 
					IdPs must sign SAML responses to ensure that the assertions are not tampered with.
 | 
				
			||||||
not tampered with.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
This prevents user impersonation and prevents privilege escalation when specific group
 | 
					This prevents user impersonation and privilege escalation when specific group
 | 
				
			||||||
membership is required. Typically this:
 | 
					membership is required.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Is configured using `idp_cert_fingerprint`.
 | 
					You configure the response signature validation using `idp_cert_fingerprint`.
 | 
				
			||||||
- Includes the full certificate in the response, although if your Identity Provider
 | 
					An example configuration:
 | 
				
			||||||
  doesn't support this, you can directly configure GitLab using the `idp_cert` option.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Example configuration with `idp_cert_fingerprint`:
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
```yaml
 | 
					```yaml
 | 
				
			||||||
args: {
 | 
					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
 | 
					```yaml
 | 
				
			||||||
args: {
 | 
					args: {
 | 
				
			||||||
| 
						 | 
					@ -621,15 +630,14 @@ args: {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
If the response signature validation is configured incorrectly, you can see error messages
 | 
					If you have configured the response signature validation incorrectly, you might see
 | 
				
			||||||
such as:
 | 
					error messages such as:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- A key validation error.
 | 
					- A key validation error.
 | 
				
			||||||
- Digest mismatch.
 | 
					- Digest mismatch.
 | 
				
			||||||
- Fingerprint mismatch.
 | 
					- Fingerprint mismatch.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Refer to the [troubleshooting section](#troubleshooting) for more information on
 | 
					For more information on solving these errors, see the [troubleshooting SAML guide](../user/group/saml_sso/troubleshooting.md).
 | 
				
			||||||
solving these errors.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Customize SAML settings
 | 
					## Customize SAML settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -76,7 +76,9 @@ The following AWS regions are not available:
 | 
				
			||||||
- Cape Town (`af-south-1`)
 | 
					- Cape Town (`af-south-1`)
 | 
				
			||||||
- Milan (`eu-south-1`)
 | 
					- Milan (`eu-south-1`)
 | 
				
			||||||
- Paris (`eu-west-3`)
 | 
					- 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
 | 
					## Planned features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,6 +18,8 @@ You can migrate groups in two ways:
 | 
				
			||||||
- By direct transfer (recommended).
 | 
					- By direct transfer (recommended).
 | 
				
			||||||
- By uploading an export file.
 | 
					- 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)
 | 
					## 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.
 | 
					> - [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
 | 
					You might need to reconfigure your firewall to prevent blocking the connection on the self-managed
 | 
				
			||||||
instance.
 | 
					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
 | 
					### Connect to the source GitLab instance
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Create the group you want to import to and connect the source:
 | 
					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.
 | 
					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
 | 
					### Provide feedback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Please leave your feedback about migrating groups by direct transfer in
 | 
					Please leave your feedback about migrating groups by direct transfer in
 | 
				
			||||||
| 
						 | 
					@ -286,7 +310,7 @@ Professional Services team.
 | 
				
			||||||
Note the following:
 | 
					Note the following:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Exports are stored in a temporary directory and are deleted every 24 hours by a specific worker.
 | 
					- 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.
 | 
					  be imported into the desired group structure.
 | 
				
			||||||
- Imported groups are given a `private` visibility level, unless imported into a parent group.
 | 
					- 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.
 | 
					- 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**.
 | 
					   1. In the **Advanced** section, select **Download export**.
 | 
				
			||||||
      You can also generate a new file by selecting **Regenerate 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
 | 
					### Import the group
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 28 KiB  | 
| 
						 | 
					@ -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
 | 
					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)
 | 
					- Import projects from external systems using one of the [available importers](#available-project-importers).
 | 
				
			||||||
- [From Bitbucket Server (also known as Stash)](bitbucket_server.md)
 | 
					- Migrate GitLab projects:
 | 
				
			||||||
- [From ClearCase](clearcase.md)
 | 
					  - Between two GitLab self-managed instances.
 | 
				
			||||||
- [From CVS](cvs.md)
 | 
					  - Between a self-managed instance and GitLab.com in both directions.
 | 
				
			||||||
- [From FogBugz](fogbugz.md)
 | 
					  - In the same GitLab instance.
 | 
				
			||||||
- [From GitHub.com or GitHub Enterprise](github.md)
 | 
					
 | 
				
			||||||
- [From GitLab.com](gitlab_com.md)
 | 
					For any type of source and target, you can migrate projects:
 | 
				
			||||||
- [From Gitea](gitea.md)
 | 
					
 | 
				
			||||||
- [From Perforce](perforce.md)
 | 
					- 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 SVN](https://git-scm.com/book/en/v2/Git-and-Other-Systems-Git-as-a-Client)
 | 
				
			||||||
- [From TFVC](tfvc.md)
 | 
					- [TFVC](tfvc.md)
 | 
				
			||||||
- [From repository by URL](repo_by_url.md)
 | 
					- [Repository by URL](repo_by_url.md)
 | 
				
			||||||
- [By uploading a manifest file (AOSP)](manifest.md)
 | 
					- [Uloading a manifest file (AOSP)](manifest.md)
 | 
				
			||||||
- [From Phabricator](phabricator.md)
 | 
					- [Phabricator](phabricator.md)
 | 
				
			||||||
- [From Jira (issues only)](jira.md)
 | 
					- [Jira (issues only)](jira.md)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
You can also import any Git repository through HTTP from the **New Project** page. Note that if the
 | 
					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.
 | 
					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
 | 
					## Migrate using the API
 | 
				
			||||||
 | 
					 | 
				
			||||||
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**.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||

 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
To migrate all data from self-managed to GitLab.com, you can leverage the [API](../../../api/index.md).
 | 
					To migrate all data from self-managed to GitLab.com, you can leverage the [API](../../../api/index.md).
 | 
				
			||||||
Migrate the assets in this order:
 | 
					Migrate the assets in this order:
 | 
				
			||||||
| 
						 | 
					@ -106,17 +66,9 @@ Migrate the assets in this order:
 | 
				
			||||||
1. [Projects](../../../api/projects.md)
 | 
					1. [Projects](../../../api/projects.md)
 | 
				
			||||||
1. [Project variables](../../../api/project_level_variables.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)
 | 
					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.
 | 
					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
 | 
					## 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
 | 
					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
 | 
					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).
 | 
					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
 | 
					Also note that administrators can use the [Users API](../../../api/users.md) to migrate users.
 | 
				
			||||||
[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)
 | 
					## View project import history
 | 
				
			||||||
to migrate users.
 | 
					
 | 
				
			||||||
 | 
					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)**
 | 
					## Project aliases **(PREMIUM SELF)**
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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](../../group/import/index.md) using the preferred method.
 | 
				
			||||||
- [Migrate groups using file exports](../../group/settings/import_export.md).
 | 
					- [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.
 | 
					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
 | 
					## Between CE and EE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
You can export projects from the [Community Edition to the Enterprise Edition](https://about.gitlab.com/install/ce-or-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)
 | 
					and vice versa. This assumes [version history](#version-history) requirements are met.
 | 
				
			||||||
requirements are met.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
If you're exporting a project from the Enterprise Edition to the Community Edition, you may lose
 | 
					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
 | 
					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:
 | 
					Prerequisites:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Review the list of [items that are exported](#items-that-are-exported).
 | 
					- Review the list of [items that are exported](#items-that-are-exported). Not all items are exported.
 | 
				
			||||||
  Not all items are exported.
 | 
					 | 
				
			||||||
- You must have at least the Maintainer role for the project.
 | 
					- You must have at least the Maintainer role for the project.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
To export a project and its data, follow these steps:
 | 
					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. 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.
 | 
					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.
 | 
					As described in the API documentation, the query may return an import error or exceptions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Changes to imported items
 | 
					### Changes to imported items
 | 
				
			||||||
| 
						 | 
					@ -218,36 +231,9 @@ For example:
 | 
				
			||||||
| 13.0            | 13.0, 12.10, 12.9                |
 | 
					| 13.0            | 13.0, 12.10, 12.9                |
 | 
				
			||||||
| 13.1            | 13.1, 13.0, 12.10                |
 | 
					| 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
 | 
					## Related topics
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- [Project import/export API](../../../api/project_import_export.md)
 | 
					- [Project import and export API](../../../api/project_import_export.md)
 | 
				
			||||||
- [Project import/export administration Rake tasks](../../../administration/raketasks/project_import_export.md)
 | 
					- [Project import and export administration Rake tasks](../../../administration/raketasks/project_import_export.md)
 | 
				
			||||||
- [Group import/export](../../group/settings/import_export.md)
 | 
					- [Migrating GitLab groups](../../group/import/index.md)
 | 
				
			||||||
- [Group import/export API](../../../api/group_import_export.md)
 | 
					- [Group import and export API](../../../api/group_import_export.md)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,7 @@ module API
 | 
				
			||||||
      get ":id/freeze_periods" do
 | 
					      get ":id/freeze_periods" do
 | 
				
			||||||
        authorize! :read_freeze_period, user_project
 | 
					        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
 | 
					        present paginate(freeze_periods), with: Entities::FreezePeriod, current_user: current_user
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -750,6 +750,9 @@ msgstr ""
 | 
				
			||||||
msgid "%{itemsCount} issues with a limit of %{maxIssueCount}"
 | 
					msgid "%{itemsCount} issues with a limit of %{maxIssueCount}"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					msgid "%{key} is not a valid URL."
 | 
				
			||||||
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
 | 
					msgid "%{labelStart}Actual response:%{labelEnd} %{headers}"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49329,9 +49332,6 @@ msgstr ""
 | 
				
			||||||
msgid "is not a descendant of the Group owning the template"
 | 
					msgid "is not a descendant of the Group owning the template"
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
msgid "is not a valid URL."
 | 
					 | 
				
			||||||
msgstr ""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
msgid "is not a valid X509 certificate."
 | 
					msgid "is not a valid X509 certificate."
 | 
				
			||||||
msgstr ""
 | 
					msgstr ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,6 +43,7 @@ class MetricsServer # rubocop:disable Gitlab/NamespacedClass
 | 
				
			||||||
      path = options[:path]&.then { |p| Pathname.new(p) } || Pathname.new('')
 | 
					      path = options[:path]&.then { |p| Pathname.new(p) } || Pathname.new('')
 | 
				
			||||||
      cmd = path.join('gitlab-metrics-exporter').to_path
 | 
					      cmd = path.join('gitlab-metrics-exporter').to_path
 | 
				
			||||||
      env = {
 | 
					      env = {
 | 
				
			||||||
 | 
					        'GOGC' => '10', # Set Go GC heap goal to 10% to curb memory growth.
 | 
				
			||||||
        'GME_MMAP_METRICS_DIR' => metrics_dir.to_s,
 | 
					        'GME_MMAP_METRICS_DIR' => metrics_dir.to_s,
 | 
				
			||||||
        'GME_PROBES' => 'self,mmap',
 | 
					        'GME_PROBES' => 'self,mmap',
 | 
				
			||||||
        'GME_SERVER_HOST' => settings['address'],
 | 
					        'GME_SERVER_HOST' => settings['address'],
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -167,6 +167,16 @@ RSpec.describe RegistrationsController do
 | 
				
			||||||
            expect(controller.current_user).to be_nil
 | 
					            expect(controller.current_user).to be_nil
 | 
				
			||||||
          end
 | 
					          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 registration is triggered from an accepted invite' do
 | 
				
			||||||
            context 'when it is part from the initial invite email', :snowplow 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)) }
 | 
					              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)
 | 
					            expect(response).to redirect_to(users_sign_up_welcome_path)
 | 
				
			||||||
          end
 | 
					          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
 | 
					          context 'when invite email matches email used on registration' do
 | 
				
			||||||
            let(:session_params) { { invite_email: user_params.dig(:user, :email) } }
 | 
					            let(:session_params) { { invite_email: user_params.dig(:user, :email) } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,7 +35,7 @@ RSpec.describe 'Merge request > User sees pipelines', :js, feature_category: :co
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
        wait_for_requests
 | 
					        wait_for_requests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        expect(page).to have_selector('.stage-cell')
 | 
					        expect(page).to have_css('[data-testid="pipeline-mini-graph"]')
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      context 'with a detached merge request pipeline' do
 | 
					      context 'with a detached merge request pipeline' do
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require 'spec_helper'
 | 
					require 'spec_helper'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RSpec.describe FreezePeriodsFinder do
 | 
					RSpec.describe Ci::FreezePeriodsFinder, feature_category: :release_orchestration do
 | 
				
			||||||
  subject(:finder) { described_class.new(project, user).execute }
 | 
					  subject(:finder) { described_class.new(project, user).execute }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let(:project) { create(:project, :private) }
 | 
					  let(:project) { create(:project, :private) }
 | 
				
			||||||
| 
						 | 
					@ -6,9 +6,10 @@ import DraftNote from '~/batch_comments/components/draft_note.vue';
 | 
				
			||||||
import PublishButton from '~/batch_comments/components/publish_button.vue';
 | 
					import PublishButton from '~/batch_comments/components/publish_button.vue';
 | 
				
			||||||
import { createStore } from '~/batch_comments/stores';
 | 
					import { createStore } from '~/batch_comments/stores';
 | 
				
			||||||
import NoteableNote from '~/notes/components/noteable_note.vue';
 | 
					import NoteableNote from '~/notes/components/noteable_note.vue';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import { createDraft } from '../mock_data';
 | 
					import { createDraft } from '../mock_data';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const NoteableNoteStub = stubComponent(NoteableNote, {
 | 
					const NoteableNoteStub = stubComponent(NoteableNote, {
 | 
				
			||||||
  template: `
 | 
					  template: `
 | 
				
			||||||
    <div>
 | 
					    <div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,9 +3,10 @@ import PreviewItem from '~/batch_comments/components/preview_item.vue';
 | 
				
			||||||
import { createStore } from '~/batch_comments/stores';
 | 
					import { createStore } from '~/batch_comments/stores';
 | 
				
			||||||
import diffsModule from '~/diffs/store/modules';
 | 
					import diffsModule from '~/diffs/store/modules';
 | 
				
			||||||
import notesModule from '~/notes/stores/modules';
 | 
					import notesModule from '~/notes/stores/modules';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import { createDraft } from '../mock_data';
 | 
					import { createDraft } from '../mock_data';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('Batch comments draft preview item component', () => {
 | 
					describe('Batch comments draft preview item component', () => {
 | 
				
			||||||
  let wrapper;
 | 
					  let wrapper;
 | 
				
			||||||
  let draft;
 | 
					  let draft;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,9 +4,10 @@ import Vue from 'vue';
 | 
				
			||||||
import Vuex from 'vuex';
 | 
					import Vuex from 'vuex';
 | 
				
			||||||
import PreviewDropdown from '~/batch_comments/components/preview_dropdown.vue';
 | 
					import PreviewDropdown from '~/batch_comments/components/preview_dropdown.vue';
 | 
				
			||||||
import { createStore } from '~/mr_notes/stores';
 | 
					import { createStore } from '~/mr_notes/stores';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import { createDraft } from '../mock_data';
 | 
					import { createDraft } from '../mock_data';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Vue.use(Vuex);
 | 
					Vue.use(Vuex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('Batch comments publish dropdown component', () => {
 | 
					describe('Batch comments publish dropdown component', () => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,9 +5,10 @@ import { createStore } from '~/mr_notes/stores';
 | 
				
			||||||
import DiscussionNotes from '~/notes/components/discussion_notes.vue';
 | 
					import DiscussionNotes from '~/notes/components/discussion_notes.vue';
 | 
				
			||||||
import NoteableDiscussion from '~/notes/components/noteable_discussion.vue';
 | 
					import NoteableDiscussion from '~/notes/components/noteable_discussion.vue';
 | 
				
			||||||
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
 | 
					import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import discussionsMockData from '../mock_data/diff_discussions';
 | 
					import discussionsMockData from '../mock_data/diff_discussions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('DiffDiscussions', () => {
 | 
					describe('DiffDiscussions', () => {
 | 
				
			||||||
  let store;
 | 
					  let store;
 | 
				
			||||||
  let wrapper;
 | 
					  let wrapper;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,6 @@ import { editor as monacoEditor, Range } from 'monaco-editor';
 | 
				
			||||||
import Vue, { nextTick } from 'vue';
 | 
					import Vue, { nextTick } from 'vue';
 | 
				
			||||||
import Vuex from 'vuex';
 | 
					import Vuex from 'vuex';
 | 
				
			||||||
import { shallowMount } from '@vue/test-utils';
 | 
					import { shallowMount } from '@vue/test-utils';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import waitForPromises from 'helpers/wait_for_promises';
 | 
					import waitForPromises from 'helpers/wait_for_promises';
 | 
				
			||||||
import { stubPerformanceWebAPI } from 'helpers/performance';
 | 
					import { stubPerformanceWebAPI } from 'helpers/performance';
 | 
				
			||||||
import { exampleConfigs, exampleFiles } from 'jest/ide/lib/editorconfig/mock_data';
 | 
					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 SourceEditorInstance from '~/editor/source_editor_instance';
 | 
				
			||||||
import { file } from '../helpers';
 | 
					import { file } from '../helpers';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
jest.mock('~/editor/extensions/source_editor_ci_schema_ext');
 | 
					jest.mock('~/editor/extensions/source_editor_ci_schema_ext');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const PREVIEW_MARKDOWN_PATH = '/foo/bar/preview_markdown';
 | 
					const PREVIEW_MARKDOWN_PATH = '/foo/bar/preview_markdown';
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,6 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
 | 
				
			||||||
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
 | 
					import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
 | 
				
			||||||
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
 | 
					import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
 | 
				
			||||||
import waitForPromises from 'helpers/wait_for_promises';
 | 
					import waitForPromises from 'helpers/wait_for_promises';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import { createAlert } from '~/flash';
 | 
					import { createAlert } from '~/flash';
 | 
				
			||||||
import { IssuableStatus, IssuableStatusText, IssuableType } from '~/issues/constants';
 | 
					import { IssuableStatus, IssuableStatusText, IssuableType } from '~/issues/constants';
 | 
				
			||||||
import IssuableApp from '~/issues/show/components/app.vue';
 | 
					import IssuableApp from '~/issues/show/components/app.vue';
 | 
				
			||||||
| 
						 | 
					@ -30,6 +29,7 @@ import {
 | 
				
			||||||
jest.mock('~/flash');
 | 
					jest.mock('~/flash');
 | 
				
			||||||
jest.mock('~/issues/show/event_hub');
 | 
					jest.mock('~/issues/show/event_hub');
 | 
				
			||||||
jest.mock('~/lib/utils/url_utility');
 | 
					jest.mock('~/lib/utils/url_utility');
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const REALTIME_REQUEST_STACK = [initialRequest, secondRequest];
 | 
					const REALTIME_REQUEST_STACK = [initialRequest, secondRequest];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
import $ from 'jquery';
 | 
					import $ from 'jquery';
 | 
				
			||||||
import Vue, { nextTick } from 'vue';
 | 
					import Vue, { nextTick } from 'vue';
 | 
				
			||||||
import VueApollo from 'vue-apollo';
 | 
					import VueApollo from 'vue-apollo';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import { GlTooltip, GlModal } from '@gitlab/ui';
 | 
					import { GlTooltip, GlModal } from '@gitlab/ui';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import setWindowLocation from 'helpers/set_window_location_helper';
 | 
					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 TaskList from '~/task_list';
 | 
				
			||||||
import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
 | 
					import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
 | 
				
			||||||
import { TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
 | 
					import { TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
 | 
				
			||||||
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  projectWorkItemTypesQueryResponse,
 | 
					  projectWorkItemTypesQueryResponse,
 | 
				
			||||||
  createWorkItemFromTaskMutationResponse,
 | 
					  createWorkItemFromTaskMutationResponse,
 | 
				
			||||||
| 
						 | 
					@ -37,6 +37,7 @@ jest.mock('~/lib/utils/url_utility', () => ({
 | 
				
			||||||
  updateHistory: jest.fn(),
 | 
					  updateHistory: jest.fn(),
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
jest.mock('~/task_list');
 | 
					jest.mock('~/task_list');
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const showModal = jest.fn();
 | 
					const showModal = jest.fn();
 | 
				
			||||||
const hideModal = jest.fn();
 | 
					const hideModal = jest.fn();
 | 
				
			||||||
| 
						 | 
					@ -161,7 +162,6 @@ describe('Description component', () => {
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('applies syntax highlighting and math when description changed', async () => {
 | 
					  it('applies syntax highlighting and math when description changed', async () => {
 | 
				
			||||||
    const prototypeSpy = jest.spyOn($.prototype, 'renderGFM');
 | 
					 | 
				
			||||||
    createComponent();
 | 
					    createComponent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await wrapper.setProps({
 | 
					    await wrapper.setProps({
 | 
				
			||||||
| 
						 | 
					@ -169,7 +169,7 @@ describe('Description component', () => {
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    expect(findGfmContent().exists()).toBe(true);
 | 
					    expect(findGfmContent().exists()).toBe(true);
 | 
				
			||||||
    expect(prototypeSpy).toHaveBeenCalled();
 | 
					    expect(renderGFM).toHaveBeenCalled();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('sets data-update-url', () => {
 | 
					  it('sets data-update-url', () => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,5 @@
 | 
				
			||||||
import { getByRole } from '@testing-library/dom';
 | 
					import { getByRole } from '@testing-library/dom';
 | 
				
			||||||
import { shallowMount, mount } from '@vue/test-utils';
 | 
					import { shallowMount, mount } from '@vue/test-utils';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import { nextTick } from 'vue';
 | 
					import { nextTick } from 'vue';
 | 
				
			||||||
import DiscussionNotes from '~/notes/components/discussion_notes.vue';
 | 
					import DiscussionNotes from '~/notes/components/discussion_notes.vue';
 | 
				
			||||||
import NoteableNote from '~/notes/components/noteable_note.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 SystemNote from '~/vue_shared/components/notes/system_note.vue';
 | 
				
			||||||
import { noteableDataMock, discussionMock, notesDataMock } from '../mock_data';
 | 
					import { noteableDataMock, discussionMock, notesDataMock } from '../mock_data';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const LINE_RANGE = {};
 | 
					const LINE_RANGE = {};
 | 
				
			||||||
const DISCUSSION_WITH_LINE_RANGE = {
 | 
					const DISCUSSION_WITH_LINE_RANGE = {
 | 
				
			||||||
  ...discussionMock,
 | 
					  ...discussionMock,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,6 @@ import ResolveWithIssueButton from '~/notes/components/discussion_resolve_with_i
 | 
				
			||||||
import NoteForm from '~/notes/components/note_form.vue';
 | 
					import NoteForm from '~/notes/components/note_form.vue';
 | 
				
			||||||
import NoteableDiscussion from '~/notes/components/noteable_discussion.vue';
 | 
					import NoteableDiscussion from '~/notes/components/noteable_discussion.vue';
 | 
				
			||||||
import createStore from '~/notes/stores';
 | 
					import createStore from '~/notes/stores';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  noteableDataMock,
 | 
					  noteableDataMock,
 | 
				
			||||||
  discussionMock,
 | 
					  discussionMock,
 | 
				
			||||||
| 
						 | 
					@ -18,6 +17,8 @@ import {
 | 
				
			||||||
  userDataMock,
 | 
					  userDataMock,
 | 
				
			||||||
} from '../mock_data';
 | 
					} from '../mock_data';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('noteable_discussion component', () => {
 | 
					describe('noteable_discussion component', () => {
 | 
				
			||||||
  let store;
 | 
					  let store;
 | 
				
			||||||
  let wrapper;
 | 
					  let wrapper;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,11 +14,12 @@ import NotesApp from '~/notes/components/notes_app.vue';
 | 
				
			||||||
import NotesActivityHeader from '~/notes/components/notes_activity_header.vue';
 | 
					import NotesActivityHeader from '~/notes/components/notes_activity_header.vue';
 | 
				
			||||||
import * as constants from '~/notes/constants';
 | 
					import * as constants from '~/notes/constants';
 | 
				
			||||||
import createStore from '~/notes/stores';
 | 
					import createStore from '~/notes/stores';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					 | 
				
			||||||
// TODO: use generated fixture (https://gitlab.com/gitlab-org/gitlab-foss/issues/62491)
 | 
					// TODO: use generated fixture (https://gitlab.com/gitlab-org/gitlab-foss/issues/62491)
 | 
				
			||||||
import OrderedLayout from '~/vue_shared/components/ordered_layout.vue';
 | 
					import OrderedLayout from '~/vue_shared/components/ordered_layout.vue';
 | 
				
			||||||
import * as mockData from '../mock_data';
 | 
					import * as mockData from '../mock_data';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const TYPE_COMMENT_FORM = 'comment-form';
 | 
					const TYPE_COMMENT_FORM = 'comment-form';
 | 
				
			||||||
const TYPE_NOTES_LIST = 'notes-list';
 | 
					const TYPE_NOTES_LIST = 'notes-list';
 | 
				
			||||||
const TEST_NOTES_FILTER_VALUE = 1;
 | 
					const TEST_NOTES_FILTER_VALUE = 1;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,13 +3,13 @@ import { nextTick } from 'vue';
 | 
				
			||||||
import { shallowMount } from '@vue/test-utils';
 | 
					import { shallowMount } from '@vue/test-utils';
 | 
				
			||||||
import MockAdapter from 'axios-mock-adapter';
 | 
					import MockAdapter from 'axios-mock-adapter';
 | 
				
			||||||
import WikiContent from '~/pages/shared/wikis/components/wiki_content.vue';
 | 
					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 axios from '~/lib/utils/axios_utils';
 | 
				
			||||||
import httpStatus from '~/lib/utils/http_status';
 | 
					import httpStatus from '~/lib/utils/http_status';
 | 
				
			||||||
import waitForPromises from 'helpers/wait_for_promises';
 | 
					import waitForPromises from 'helpers/wait_for_promises';
 | 
				
			||||||
import { handleLocationHash } from '~/lib/utils/common_utils';
 | 
					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');
 | 
					jest.mock('~/lib/utils/common_utils');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('pages/shared/wikis/components/wiki_content', () => {
 | 
					describe('pages/shared/wikis/components/wiki_content', () => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,7 +48,6 @@ describe('Pipeline Mini Graph', () => {
 | 
				
			||||||
        isMergeTrain: false,
 | 
					        isMergeTrain: false,
 | 
				
			||||||
        pipelinePath: '',
 | 
					        pipelinePath: '',
 | 
				
			||||||
        stages: expect.any(Array),
 | 
					        stages: expect.any(Array),
 | 
				
			||||||
        stagesClass: '',
 | 
					 | 
				
			||||||
        updateDropdown: false,
 | 
					        updateDropdown: false,
 | 
				
			||||||
        upstreamPipeline: undefined,
 | 
					        upstreamPipeline: undefined,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
| 
						 | 
					@ -83,7 +82,6 @@ describe('Pipeline Mini Graph', () => {
 | 
				
			||||||
        isMergeTrain: false,
 | 
					        isMergeTrain: false,
 | 
				
			||||||
        pipelinePath: '',
 | 
					        pipelinePath: '',
 | 
				
			||||||
        stages: expect.any(Array),
 | 
					        stages: expect.any(Array),
 | 
				
			||||||
        stagesClass: '',
 | 
					 | 
				
			||||||
        updateDropdown: false,
 | 
					        updateDropdown: false,
 | 
				
			||||||
        upstreamPipeline: expect.any(Object),
 | 
					        upstreamPipeline: expect.any(Object),
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
| 
						 | 
					@ -115,7 +113,6 @@ describe('Pipeline Mini Graph', () => {
 | 
				
			||||||
        isMergeTrain: false,
 | 
					        isMergeTrain: false,
 | 
				
			||||||
        pipelinePath: 'my/pipeline/path',
 | 
					        pipelinePath: 'my/pipeline/path',
 | 
				
			||||||
        stages: expect.any(Array),
 | 
					        stages: expect.any(Array),
 | 
				
			||||||
        stagesClass: '',
 | 
					 | 
				
			||||||
        updateDropdown: false,
 | 
					        updateDropdown: false,
 | 
				
			||||||
        upstreamPipeline: undefined,
 | 
					        upstreamPipeline: undefined,
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,12 +26,6 @@ describe('Pipeline Stages', () => {
 | 
				
			||||||
    expect(findPipelineStages()).toHaveLength(mockStages.length);
 | 
					    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', () => {
 | 
					  it('does not fail when stages are empty', () => {
 | 
				
			||||||
    createComponent({ stages: [] });
 | 
					    createComponent({ stages: [] });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,4 @@
 | 
				
			||||||
import { mount } from '@vue/test-utils';
 | 
					import { mount } from '@vue/test-utils';
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
import { nextTick } from 'vue';
 | 
					import { nextTick } from 'vue';
 | 
				
			||||||
import originalOneReleaseQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release.query.graphql.json';
 | 
					import originalOneReleaseQueryResponse from 'test_fixtures/graphql/releases/graphql/queries/one_release.query.graphql.json';
 | 
				
			||||||
import { convertOneReleaseGraphQLResponse } from '~/releases/util';
 | 
					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 ReleaseBlockFooter from '~/releases/components/release_block_footer.vue';
 | 
				
			||||||
import { BACK_URL_PARAM } from '~/releases/constants';
 | 
					import { BACK_URL_PARAM } from '~/releases/constants';
 | 
				
			||||||
import timeagoMixin from '~/vue_shared/mixins/timeago';
 | 
					import timeagoMixin from '~/vue_shared/mixins/timeago';
 | 
				
			||||||
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('Release block', () => {
 | 
					describe('Release block', () => {
 | 
				
			||||||
  let wrapper;
 | 
					  let wrapper;
 | 
				
			||||||
| 
						 | 
					@ -34,7 +36,6 @@ describe('Release block', () => {
 | 
				
			||||||
  const editButton = () => wrapper.find('.js-edit-button');
 | 
					  const editButton = () => wrapper.find('.js-edit-button');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    jest.spyOn($.fn, 'renderGFM');
 | 
					 | 
				
			||||||
    release = convertOneReleaseGraphQLResponse(originalOneReleaseQueryResponse).data;
 | 
					    release = convertOneReleaseGraphQLResponse(originalOneReleaseQueryResponse).data;
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -62,7 +63,7 @@ describe('Release block', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('renders release description', () => {
 | 
					    it('renders release description', () => {
 | 
				
			||||||
      expect(wrapper.vm.$refs['gfm-content']).toBeDefined();
 | 
					      expect(wrapper.vm.$refs['gfm-content']).toBeDefined();
 | 
				
			||||||
      expect($.fn.renderGFM).toHaveBeenCalledTimes(1);
 | 
					      expect(renderGFM).toHaveBeenCalledTimes(1);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    it('renders release date', () => {
 | 
					    it('renders release date', () => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,3 @@
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
import { merge } from 'lodash';
 | 
					import { merge } from 'lodash';
 | 
				
			||||||
import { GlIntersectionObserver } from '@gitlab/ui';
 | 
					import { GlIntersectionObserver } from '@gitlab/ui';
 | 
				
			||||||
import { nextTick } from 'vue';
 | 
					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 { FLASH_TYPES, FLASH_CLOSED_EVENT } from '~/flash';
 | 
				
			||||||
import { isLoggedIn } from '~/lib/utils/common_utils';
 | 
					import { isLoggedIn } from '~/lib/utils/common_utils';
 | 
				
			||||||
import TermsApp from '~/terms/components/app.vue';
 | 
					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/csrf', () => ({ token: 'mock-csrf-token' }));
 | 
				
			||||||
jest.mock('~/lib/utils/common_utils');
 | 
					jest.mock('~/lib/utils/common_utils');
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('TermsApp', () => {
 | 
					describe('TermsApp', () => {
 | 
				
			||||||
  let wrapper;
 | 
					  let wrapper;
 | 
				
			||||||
  let renderGFMSpy;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const defaultProvide = {
 | 
					  const defaultProvide = {
 | 
				
			||||||
    terms: 'foo bar',
 | 
					    terms: 'foo bar',
 | 
				
			||||||
| 
						 | 
					@ -35,7 +35,6 @@ describe('TermsApp', () => {
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
 | 
					 | 
				
			||||||
    isLoggedIn.mockReturnValue(true);
 | 
					    isLoggedIn.mockReturnValue(true);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -65,7 +64,7 @@ describe('TermsApp', () => {
 | 
				
			||||||
    createComponent();
 | 
					    createComponent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    expect(wrapper.findByText(defaultProvide.terms).exists()).toBe(true);
 | 
					    expect(wrapper.findByText(defaultProvide.terms).exists()).toBe(true);
 | 
				
			||||||
    expect(renderGFMSpy).toHaveBeenCalled();
 | 
					    expect(renderGFM).toHaveBeenCalled();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('accept button', () => {
 | 
					  describe('accept button', () => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,8 @@
 | 
				
			||||||
import { mount } from '@vue/test-utils';
 | 
					import { mount } from '@vue/test-utils';
 | 
				
			||||||
import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants';
 | 
					import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants';
 | 
				
			||||||
import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
 | 
					import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
 | 
				
			||||||
import '~/behaviors/markdown/render_gfm';
 | 
					
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('ContentViewer', () => {
 | 
					describe('ContentViewer', () => {
 | 
				
			||||||
  let wrapper;
 | 
					  let wrapper;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,12 @@
 | 
				
			||||||
import { GlSkeletonLoader } from '@gitlab/ui';
 | 
					import { GlSkeletonLoader } from '@gitlab/ui';
 | 
				
			||||||
import { mount } from '@vue/test-utils';
 | 
					import { mount } from '@vue/test-utils';
 | 
				
			||||||
import MockAdapter from 'axios-mock-adapter';
 | 
					import MockAdapter from 'axios-mock-adapter';
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
import waitForPromises from 'helpers/wait_for_promises';
 | 
					import waitForPromises from 'helpers/wait_for_promises';
 | 
				
			||||||
import axios from '~/lib/utils/axios_utils';
 | 
					import axios from '~/lib/utils/axios_utils';
 | 
				
			||||||
import MarkdownViewer from '~/vue_shared/components/content_viewer/viewers/markdown_viewer.vue';
 | 
					import MarkdownViewer from '~/vue_shared/components/content_viewer/viewers/markdown_viewer.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('MarkdownViewer', () => {
 | 
					describe('MarkdownViewer', () => {
 | 
				
			||||||
  let wrapper;
 | 
					  let wrapper;
 | 
				
			||||||
  let mock;
 | 
					  let mock;
 | 
				
			||||||
| 
						 | 
					@ -26,7 +27,6 @@ describe('MarkdownViewer', () => {
 | 
				
			||||||
    mock = new MockAdapter(axios);
 | 
					    mock = new MockAdapter(axios);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    jest.spyOn(axios, 'post');
 | 
					    jest.spyOn(axios, 'post');
 | 
				
			||||||
    jest.spyOn($.fn, 'renderGFM');
 | 
					 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  afterEach(() => {
 | 
					  afterEach(() => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,14 @@
 | 
				
			||||||
import { nextTick } from 'vue';
 | 
					import { nextTick } from 'vue';
 | 
				
			||||||
import AxiosMockAdapter from 'axios-mock-adapter';
 | 
					import AxiosMockAdapter from 'axios-mock-adapter';
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
import { TEST_HOST, FIXTURES_PATH } from 'spec/test_constants';
 | 
					import { TEST_HOST, FIXTURES_PATH } from 'spec/test_constants';
 | 
				
			||||||
import axios from '~/lib/utils/axios_utils';
 | 
					import axios from '~/lib/utils/axios_utils';
 | 
				
			||||||
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
 | 
					import MarkdownField from '~/vue_shared/components/markdown/field.vue';
 | 
				
			||||||
import MarkdownFieldHeader from '~/vue_shared/components/markdown/header.vue';
 | 
					import MarkdownFieldHeader from '~/vue_shared/components/markdown/header.vue';
 | 
				
			||||||
import MarkdownToolbar from '~/vue_shared/components/markdown/toolbar.vue';
 | 
					import MarkdownToolbar from '~/vue_shared/components/markdown/toolbar.vue';
 | 
				
			||||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
 | 
					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 markdownPreviewPath = `${TEST_HOST}/preview`;
 | 
				
			||||||
const markdownDocsPath = `${TEST_HOST}/docs`;
 | 
					const markdownDocsPath = `${TEST_HOST}/docs`;
 | 
				
			||||||
| 
						 | 
					@ -138,15 +140,13 @@ describe('Markdown field component', () => {
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      it('renders markdown preview and GFM', async () => {
 | 
					      it('renders markdown preview and GFM', async () => {
 | 
				
			||||||
        const renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        previewLink = getPreviewLink();
 | 
					        previewLink = getPreviewLink();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        previewLink.vm.$emit('click', { target: {} });
 | 
					        previewLink.vm.$emit('click', { target: {} });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await axios.waitFor(markdownPreviewPath);
 | 
					        await axios.waitFor(markdownPreviewPath);
 | 
				
			||||||
        expect(subject.find('.md-preview-holder').element.innerHTML).toContain(previewHTML);
 | 
					        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 () => {
 | 
					      it('calls video.pause() on comment input when isSubmitting is changed to true', async () => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,11 @@
 | 
				
			||||||
import { shallowMount } from '@vue/test-utils';
 | 
					import { shallowMount } from '@vue/test-utils';
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import MarkdownFieldView from '~/vue_shared/components/markdown/field_view.vue';
 | 
					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', () => {
 | 
					describe('Markdown Field View component', () => {
 | 
				
			||||||
  let renderGFMSpy;
 | 
					 | 
				
			||||||
  let wrapper;
 | 
					  let wrapper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function createComponent() {
 | 
					  function createComponent() {
 | 
				
			||||||
| 
						 | 
					@ -12,7 +13,6 @@ describe('Markdown Field View component', () => {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
 | 
					 | 
				
			||||||
    createComponent();
 | 
					    createComponent();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,6 @@ describe('Markdown Field View component', () => {
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('processes rendering with GFM', () => {
 | 
					  it('processes rendering with GFM', () => {
 | 
				
			||||||
    expect(renderGFMSpy).toHaveBeenCalledTimes(1);
 | 
					    expect(renderGFM).toHaveBeenCalledTimes(1);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,12 @@
 | 
				
			||||||
import MockAdapter from 'axios-mock-adapter';
 | 
					import MockAdapter from 'axios-mock-adapter';
 | 
				
			||||||
import { mount } from '@vue/test-utils';
 | 
					import { mount } from '@vue/test-utils';
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
import waitForPromises from 'helpers/wait_for_promises';
 | 
					import waitForPromises from 'helpers/wait_for_promises';
 | 
				
			||||||
import createStore from '~/notes/stores';
 | 
					import createStore from '~/notes/stores';
 | 
				
			||||||
import IssueSystemNote from '~/vue_shared/components/notes/system_note.vue';
 | 
					import IssueSystemNote from '~/vue_shared/components/notes/system_note.vue';
 | 
				
			||||||
import axios from '~/lib/utils/axios_utils';
 | 
					import axios from '~/lib/utils/axios_utils';
 | 
				
			||||||
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('system note component', () => {
 | 
					describe('system note component', () => {
 | 
				
			||||||
  let vm;
 | 
					  let vm;
 | 
				
			||||||
| 
						 | 
					@ -75,11 +77,9 @@ describe('system note component', () => {
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should renderGFM onMount', () => {
 | 
					  it('should renderGFM onMount', () => {
 | 
				
			||||||
    const renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    createComponent(props);
 | 
					    createComponent(props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    expect(renderGFMSpy).toHaveBeenCalled();
 | 
					    expect(renderGFM).toHaveBeenCalled();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('renders outdated code lines', async () => {
 | 
					  it('renders outdated code lines', async () => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,10 +1,12 @@
 | 
				
			||||||
import { shallowMount } from '@vue/test-utils';
 | 
					import { shallowMount } from '@vue/test-utils';
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import IssuableDescription from '~/vue_shared/issuable/show/components/issuable_description.vue';
 | 
					import IssuableDescription from '~/vue_shared/issuable/show/components/issuable_description.vue';
 | 
				
			||||||
 | 
					import { renderGFM } from '~/behaviors/markdown/render_gfm';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { mockIssuable } from '../mock_data';
 | 
					import { mockIssuable } from '../mock_data';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const createComponent = ({
 | 
					const createComponent = ({
 | 
				
			||||||
  issuable = mockIssuable,
 | 
					  issuable = mockIssuable,
 | 
				
			||||||
  enableTaskList = true,
 | 
					  enableTaskList = true,
 | 
				
			||||||
| 
						 | 
					@ -16,11 +18,9 @@ const createComponent = ({
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('IssuableDescription', () => {
 | 
					describe('IssuableDescription', () => {
 | 
				
			||||||
  let renderGFMSpy;
 | 
					 | 
				
			||||||
  let wrapper;
 | 
					  let wrapper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(() => {
 | 
					  beforeEach(() => {
 | 
				
			||||||
    renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
 | 
					 | 
				
			||||||
    wrapper = createComponent();
 | 
					    wrapper = createComponent();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,17 +30,7 @@ describe('IssuableDescription', () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('mounted', () => {
 | 
					  describe('mounted', () => {
 | 
				
			||||||
    it('calls `renderGFM`', () => {
 | 
					    it('calls `renderGFM`', () => {
 | 
				
			||||||
      expect(renderGFMSpy).toHaveBeenCalledTimes(1);
 | 
					      expect(renderGFM).toHaveBeenCalledTimes(1);
 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  describe('methods', () => {
 | 
					 | 
				
			||||||
    describe('renderGFM', () => {
 | 
					 | 
				
			||||||
      it('calls `renderGFM` on container element', () => {
 | 
					 | 
				
			||||||
        wrapper.vm.renderGFM();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        expect(renderGFMSpy).toHaveBeenCalled();
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,14 @@
 | 
				
			||||||
import { GlIcon } from '@gitlab/ui';
 | 
					import { GlIcon } from '@gitlab/ui';
 | 
				
			||||||
import MockAdapter from 'axios-mock-adapter';
 | 
					import MockAdapter from 'axios-mock-adapter';
 | 
				
			||||||
import { shallowMount } from '@vue/test-utils';
 | 
					import { shallowMount } from '@vue/test-utils';
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
import waitForPromises from 'helpers/wait_for_promises';
 | 
					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 WorkItemSystemNote from '~/work_items/components/notes/system_note.vue';
 | 
				
			||||||
import NoteHeader from '~/notes/components/note_header.vue';
 | 
					import NoteHeader from '~/notes/components/note_header.vue';
 | 
				
			||||||
import axios from '~/lib/utils/axios_utils';
 | 
					import axios from '~/lib/utils/axios_utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('system note component', () => {
 | 
					describe('system note component', () => {
 | 
				
			||||||
  let wrapper;
 | 
					  let wrapper;
 | 
				
			||||||
  let props;
 | 
					  let props;
 | 
				
			||||||
| 
						 | 
					@ -84,11 +86,9 @@ describe('system note component', () => {
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should renderGFM onMount', () => {
 | 
					  it('should renderGFM onMount', () => {
 | 
				
			||||||
    const renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    createComponent(props);
 | 
					    createComponent(props);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    expect(renderGFMSpy).toHaveBeenCalled();
 | 
					    expect(renderGFM).toHaveBeenCalled();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // eslint-disable-next-line jest/no-disabled-tests
 | 
					  // eslint-disable-next-line jest/no-disabled-tests
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,11 @@
 | 
				
			||||||
import { shallowMount } from '@vue/test-utils';
 | 
					import { shallowMount } from '@vue/test-utils';
 | 
				
			||||||
import $ from 'jquery';
 | 
					 | 
				
			||||||
import { nextTick } from 'vue';
 | 
					import { nextTick } from 'vue';
 | 
				
			||||||
import WorkItemDescriptionRendered from '~/work_items/components/work_item_description_rendered.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';
 | 
					import { descriptionTextWithCheckboxes, descriptionHtmlWithCheckboxes } from '../mock_data';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('WorkItemDescription', () => {
 | 
					describe('WorkItemDescription', () => {
 | 
				
			||||||
  let wrapper;
 | 
					  let wrapper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,13 +34,11 @@ describe('WorkItemDescription', () => {
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('renders gfm', async () => {
 | 
					  it('renders gfm', async () => {
 | 
				
			||||||
    const renderGFMSpy = jest.spyOn($.fn, 'renderGFM');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    createComponent();
 | 
					    createComponent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await nextTick();
 | 
					    await nextTick();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    expect(renderGFMSpy).toHaveBeenCalled();
 | 
					    expect(renderGFM).toHaveBeenCalled();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  describe('with checkboxes', () => {
 | 
					  describe('with checkboxes', () => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 WorkItemsRoot from '~/work_items/pages/work_item_root.vue';
 | 
				
			||||||
import { createRouter } from '~/work_items/router';
 | 
					import { createRouter } from '~/work_items/router';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jest.mock('~/behaviors/markdown/render_gfm');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe('Work items router', () => {
 | 
					describe('Work items router', () => {
 | 
				
			||||||
  let wrapper;
 | 
					  let wrapper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ require 'spec_helper'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require_relative '../../metrics_server/metrics_server'
 | 
					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(:prometheus_config) { ::Prometheus::Client.configuration }
 | 
				
			||||||
  let(:metrics_dir) { Dir.mktmpdir }
 | 
					  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_port) { target == 'puma' ? '8083' : '8082' }
 | 
				
			||||||
          let(:expected_env) do
 | 
					          let(:expected_env) do
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					              'GOGC' => '10',
 | 
				
			||||||
              'GME_MMAP_METRICS_DIR' => metrics_dir,
 | 
					              'GME_MMAP_METRICS_DIR' => metrics_dir,
 | 
				
			||||||
              'GME_PROBES' => 'self,mmap',
 | 
					              'GME_PROBES' => 'self,mmap',
 | 
				
			||||||
              'GME_SERVER_HOST' => 'localhost',
 | 
					              'GME_SERVER_HOST' => 'localhost',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -183,25 +183,25 @@ RSpec.describe PipelineSerializer do
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      context 'with triggered pipelines' do
 | 
					      context 'with triggered pipelines' do
 | 
				
			||||||
        before do
 | 
					        before do
 | 
				
			||||||
          pipeline_1 = create(:ci_pipeline)
 | 
					          pipeline_1 = create(:ci_pipeline, project: project)
 | 
				
			||||||
          build_1 = create(:ci_build, pipeline: pipeline_1)
 | 
					          build_1 = create(:ci_build, pipeline: pipeline_1)
 | 
				
			||||||
          create(:ci_sources_pipeline, source_job: build_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
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        it 'verifies number of queries', :request_store do
 | 
					        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
 | 
					          pipeline_2 = create(:ci_pipeline, project: project)
 | 
				
			||||||
          # Ongoing issue:
 | 
					          build_2 = create(:ci_build, pipeline: pipeline_2)
 | 
				
			||||||
          # https://gitlab.com/gitlab-org/gitlab/-/issues/225156
 | 
					          create(:ci_sources_pipeline, source_job: build_2)
 | 
				
			||||||
          expected_queries = Gitlab.ee? ? 78 : 74
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
          expect(recorded.count).to be_within(2).of(expected_queries)
 | 
					          recorded = ActiveRecord::QueryRecorder.new do
 | 
				
			||||||
          expect(recorded.cached_count).to eq(0)
 | 
					            serializer.represent(Ci::Pipeline.all, preload: true)
 | 
				
			||||||
 | 
					          end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          expect(recorded).not_to exceed_query_limit(control)
 | 
				
			||||||
        end
 | 
					        end
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue