Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
eac94e5cd6
commit
28d82e1650
60 changed files with 396 additions and 531 deletions
|
@ -486,7 +486,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/administration/geo/setup/ @axil
|
||||
/doc/administration/git_protocol.md @aqualls
|
||||
/doc/administration/gitaly/ @eread
|
||||
/doc/administration/housekeeping.md @axil
|
||||
/doc/administration/housekeeping.md @eread
|
||||
/doc/administration/inactive_project_deletion.md @eread
|
||||
/doc/administration/incoming_email.md @msedlakjakubowski
|
||||
/doc/administration/index.md @axil
|
||||
|
@ -499,7 +499,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/administration/invalidate_markdown_cache.md @msedlakjakubowski
|
||||
/doc/administration/issue_closing_pattern.md @aqualls
|
||||
/doc/administration/job_artifacts.md @marcel.amirault
|
||||
/doc/administration/job_logs.md @sselhorn
|
||||
/doc/administration/job_logs.md @fneill
|
||||
/doc/administration/lfs/ @aqualls
|
||||
/doc/administration/libravatar.md @axil
|
||||
/doc/administration/load_balancer.md @axil
|
||||
|
@ -682,7 +682,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/resource_milestone_events.md @msedlakjakubowski
|
||||
/doc/api/resource_state_events.md @msedlakjakubowski
|
||||
/doc/api/resource_weight_events.md @msedlakjakubowski
|
||||
/doc/api/runners.md @sselhorn
|
||||
/doc/api/runners.md @fneill
|
||||
/doc/api/saml.md @jglassman1
|
||||
/doc/api/scim.md @jglassman1
|
||||
/doc/api/search.md @ashrafkhamis
|
||||
|
@ -710,12 +710,9 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/api/vulnerability_exports.md @claytoncornell
|
||||
/doc/api/vulnerability_findings.md @claytoncornell
|
||||
/doc/api/wikis.md @ashrafkhamis
|
||||
/doc/architecture/blueprints/ci_pipeline_components/ @marcel.amirault
|
||||
/doc/architecture/blueprints/container_registry_metadata_database/ @claytoncornell
|
||||
/doc/architecture/blueprints/database/scalability/patterns/ @aqualls
|
||||
/doc/architecture/blueprints/database_scaling/ @aqualls
|
||||
/doc/architecture/blueprints/gitlab_to_kubernetes_communication/ @phillipwells
|
||||
/doc/architecture/blueprints/work_items/ @msedlakjakubowski
|
||||
/doc/architecture/blueprints/runner_tokens/ @fneill
|
||||
/doc/ci/ @marcel.amirault
|
||||
/doc/ci/caching/ @marcel.amirault
|
||||
/doc/ci/chatops/ @phillipwells
|
||||
|
@ -728,7 +725,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/ci/cloud_services/google_cloud/ @marcel.amirault
|
||||
/doc/ci/directed_acyclic_graph/ @marcel.amirault
|
||||
/doc/ci/docker/ @marcel.amirault
|
||||
/doc/ci/docker/using_docker_images.md @sselhorn
|
||||
/doc/ci/docker/using_docker_images.md @fneill
|
||||
/doc/ci/environments/ @rdickenson
|
||||
/doc/ci/examples/ @marcel.amirault
|
||||
/doc/ci/examples/authenticating-with-hashicorp-vault/ @marcel.amirault
|
||||
|
@ -736,22 +733,22 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/ci/examples/end_to_end_testing_webdriverio/ @marcel.amirault
|
||||
/doc/ci/examples/laravel_with_gitlab_and_envoy/ @marcel.amirault
|
||||
/doc/ci/examples/semantic-release.md @claytoncornell
|
||||
/doc/ci/interactive_web_terminal/ @sselhorn
|
||||
/doc/ci/interactive_web_terminal/ @fneill
|
||||
/doc/ci/introduction/ @marcel.amirault
|
||||
/doc/ci/jobs/ @marcel.amirault
|
||||
/doc/ci/large_repositories/ @sselhorn
|
||||
/doc/ci/large_repositories/ @fneill
|
||||
/doc/ci/migration/ @marcel.amirault
|
||||
/doc/ci/pipeline_editor/ @marcel.amirault
|
||||
/doc/ci/pipelines/ @marcel.amirault
|
||||
/doc/ci/quick_start/ @marcel.amirault
|
||||
/doc/ci/resource_groups/ @rdickenson
|
||||
/doc/ci/review_apps/ @marcel.amirault
|
||||
/doc/ci/runners/ @sselhorn
|
||||
/doc/ci/runners/saas/ @sselhorn
|
||||
/doc/ci/runners/saas/macos/ @sselhorn
|
||||
/doc/ci/runners/ @fneill
|
||||
/doc/ci/runners/saas/ @fneill
|
||||
/doc/ci/runners/saas/macos/ @fneill
|
||||
/doc/ci/secrets/ @marcel.amirault
|
||||
/doc/ci/secure_files/ @marcel.amirault
|
||||
/doc/ci/services/ @sselhorn
|
||||
/doc/ci/services/ @fneill
|
||||
/doc/ci/ssh_keys/ @marcel.amirault
|
||||
/doc/ci/test_cases/ @msedlakjakubowski
|
||||
/doc/ci/testing/ @marcel.amirault
|
||||
|
@ -835,6 +832,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/development/prometheus_metrics.md @msedlakjakubowski
|
||||
/doc/development/real_time.md @msedlakjakubowski
|
||||
/doc/development/sec/ @rdickenson
|
||||
/doc/development/sec/security_report_ingestion_overview.md @claytoncornell
|
||||
/doc/development/secure_coding_guidelines.md @sselhorn
|
||||
/doc/development/service_ping/ @claytoncornell
|
||||
/doc/development/snowplow/ @claytoncornell
|
||||
|
@ -876,13 +874,11 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/integration/sourcegraph.md @aqualls
|
||||
/doc/integration/trello_power_up.md @ashrafkhamis
|
||||
/doc/integration/vault.md @phillipwells
|
||||
/doc/operations/error_tracking.md msedlakjakubowski
|
||||
/doc/operations/ @msedlakjakubowski
|
||||
/doc/operations/feature_flags.md @rdickenson
|
||||
/doc/operations/incident_management/ @msedlakjakubowski
|
||||
/doc/operations/index.md @msedlakjakubowski
|
||||
/doc/operations/metrics/ @msedlakjakubowski
|
||||
/doc/operations/metrics/dashboards/ @msedlakjakubowski
|
||||
/doc/operations/tracing.md @msedlakjakubowski
|
||||
/doc/policy/ @axil
|
||||
/doc/raketasks/ @axil
|
||||
/doc/raketasks/generate_sample_prometheus_data.md @msedlakjakubowski
|
||||
|
@ -1009,7 +1005,7 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/user/infrastructure/clusters/deploy/ @phillipwells
|
||||
/doc/user/infrastructure/clusters/manage/ @phillipwells
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/ @phillipwells
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/runner.md @sselhorn
|
||||
/doc/user/infrastructure/clusters/manage/management_project_applications/runner.md @fneill
|
||||
/doc/user/infrastructure/iac/ @phillipwells
|
||||
/doc/user/markdown.md @aqualls
|
||||
/doc/user/namespace/ @lciutacu
|
||||
|
@ -1023,18 +1019,19 @@ lib/gitlab/checks/** @proglottis @toon @zj-gitlab
|
|||
/doc/user/packages/go_proxy/ @claytoncornell
|
||||
/doc/user/packages/harbor_container_registry/ @claytoncornell
|
||||
/doc/user/packages/helm_repository/ @claytoncornell
|
||||
/doc/user/packages/infrastructure_registry/ @phillipwells
|
||||
/doc/user/packages/infrastructure_registry/ @claytoncornell
|
||||
/doc/user/packages/maven_repository/ @claytoncornell
|
||||
/doc/user/packages/npm_registry/ @claytoncornell
|
||||
/doc/user/packages/nuget_repository/ @claytoncornell
|
||||
/doc/user/packages/package_registry/ @claytoncornell
|
||||
/doc/user/packages/pypi_repository/ @claytoncornell
|
||||
/doc/user/packages/rubygems_registry/ @claytoncornell
|
||||
/doc/user/packages/terraform_module_registry/ @phillipwells
|
||||
/doc/user/packages/terraform_module_registry/ @claytoncornell
|
||||
/doc/user/packages/workflows/ @claytoncornell
|
||||
/doc/user/permissions.md @jglassman1
|
||||
/doc/user/profile/ @jglassman1
|
||||
/doc/user/profile/account/ @jglassman1
|
||||
/doc/user/profile/contributions_calendar.md @lciutacu
|
||||
/doc/user/profile/notifications.md @msedlakjakubowski
|
||||
/doc/user/project/ @aqualls
|
||||
/doc/user/project/clusters/ @phillipwells
|
||||
|
|
|
@ -1283,7 +1283,6 @@ Gitlab/NamespacedClass:
|
|||
- 'spec/models/concerns/batch_destroy_dependent_associations_spec.rb'
|
||||
- 'spec/models/concerns/bulk_insertable_associations_spec.rb'
|
||||
- 'spec/models/concerns/triggerable_hooks_spec.rb'
|
||||
- 'spec/support/helpers/bare_repo_operations.rb'
|
||||
- 'spec/support/helpers/ci_artifact_metadata_generator.rb'
|
||||
- 'spec/support/helpers/fake_migration_classes.rb'
|
||||
- 'spec/support/helpers/fake_u2f_device.rb'
|
||||
|
|
|
@ -716,7 +716,6 @@ Style/GuardClause:
|
|||
- 'spec/support/capybara.rb'
|
||||
- 'spec/support/database/prevent_cross_joins.rb'
|
||||
- 'spec/support/helpers/access_matchers_helpers.rb'
|
||||
- 'spec/support/helpers/bare_repo_operations.rb'
|
||||
- 'spec/support/helpers/capybara_helpers.rb'
|
||||
- 'spec/support/helpers/dropzone_helper.rb'
|
||||
- 'spec/support/helpers/fake_blob_helpers.rb'
|
||||
|
|
|
@ -15,7 +15,7 @@ $.fn.renderGFM = function renderGFM() {
|
|||
syntaxHighlight(this.find('.js-syntax-highlight').get());
|
||||
renderKroki(this.find('.js-render-kroki[hidden]').get());
|
||||
renderMath(this.find('.js-render-math'));
|
||||
renderSandboxedMermaid(this.find('.js-render-mermaid'));
|
||||
renderSandboxedMermaid(this.find('.js-render-mermaid').get());
|
||||
renderJSONTable(
|
||||
Array.from(this.find('[lang="json"][data-lang-params="table"]').get()).map((e) => e.parentNode),
|
||||
);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import $ from 'jquery';
|
||||
import { once, countBy } from 'lodash';
|
||||
import { countBy } from 'lodash';
|
||||
import { __ } from '~/locale';
|
||||
import {
|
||||
getBaseURL,
|
||||
|
@ -8,7 +7,8 @@ import {
|
|||
joinPaths,
|
||||
} from '~/lib/utils/url_utility';
|
||||
import { darkModeEnabled } from '~/lib/utils/color_utils';
|
||||
import { setAttributes } from '~/lib/utils/dom_utils';
|
||||
import { setAttributes, isElementVisible } from '~/lib/utils/dom_utils';
|
||||
import { createAlert, VARIANT_WARNING } from '~/flash';
|
||||
import { unrestrictedPages } from './constants';
|
||||
|
||||
// Renders diagrams and flowcharts from text using Mermaid in any element with the
|
||||
|
@ -27,17 +27,30 @@ import { unrestrictedPages } from './constants';
|
|||
|
||||
const SANDBOX_FRAME_PATH = '/-/sandbox/mermaid';
|
||||
// This is an arbitrary number; Can be iterated upon when suitable.
|
||||
const MAX_CHAR_LIMIT = 2000;
|
||||
export const MAX_CHAR_LIMIT = 2000;
|
||||
// Max # of mermaid blocks that can be rendered in a page.
|
||||
const MAX_MERMAID_BLOCK_LIMIT = 50;
|
||||
export const MAX_MERMAID_BLOCK_LIMIT = 50;
|
||||
// Max # of `&` allowed in Chaining of links syntax
|
||||
const MAX_CHAINING_OF_LINKS_LIMIT = 30;
|
||||
|
||||
export const BUFFER_IFRAME_HEIGHT = 10;
|
||||
export const SANDBOX_ATTRIBUTES = 'allow-scripts allow-popups';
|
||||
|
||||
const ALERT_CONTAINER_CLASS = 'mermaid-alert-container';
|
||||
export const LAZY_ALERT_SHOWN_CLASS = 'lazy-alert-shown';
|
||||
|
||||
// Keep a map of mermaid blocks we've already rendered.
|
||||
const elsProcessingMap = new WeakMap();
|
||||
let renderedMermaidBlocks = 0;
|
||||
|
||||
/**
|
||||
* Determines whether a given Mermaid diagram is visible.
|
||||
*
|
||||
* @param {Element} el The Mermaid DOM node
|
||||
* @returns
|
||||
*/
|
||||
const isVisibleMermaid = (el) => el.closest('details') === null && isElementVisible(el);
|
||||
|
||||
function shouldLazyLoadMermaidBlock(source) {
|
||||
/**
|
||||
* If source contains `&`, which means that it might
|
||||
|
@ -104,8 +117,8 @@ function renderMermaidEl(el, source) {
|
|||
);
|
||||
}
|
||||
|
||||
function renderMermaids($els) {
|
||||
if (!$els.length) return;
|
||||
function renderMermaids(els) {
|
||||
if (!els.length) return;
|
||||
|
||||
const pageName = document.querySelector('body').dataset.page;
|
||||
|
||||
|
@ -114,7 +127,7 @@ function renderMermaids($els) {
|
|||
|
||||
let renderedChars = 0;
|
||||
|
||||
$els.each((i, el) => {
|
||||
els.forEach((el) => {
|
||||
// Skipping all the elements which we've already queued in requestIdleCallback
|
||||
if (elsProcessingMap.has(el)) {
|
||||
return;
|
||||
|
@ -133,33 +146,29 @@ function renderMermaids($els) {
|
|||
renderedMermaidBlocks >= MAX_MERMAID_BLOCK_LIMIT ||
|
||||
shouldLazyLoadMermaidBlock(source))
|
||||
) {
|
||||
const html = `
|
||||
<div class="alert gl-alert gl-alert-warning alert-dismissible lazy-render-mermaid-container js-lazy-render-mermaid-container fade show" role="alert">
|
||||
<div>
|
||||
<div>
|
||||
<div class="js-warning-text"></div>
|
||||
<div class="gl-alert-actions">
|
||||
<button type="button" class="js-lazy-render-mermaid btn gl-alert-action btn-confirm btn-md gl-button">Display</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
const parent = el.parentNode;
|
||||
|
||||
const $parent = $(el).parent();
|
||||
|
||||
if (!$parent.hasClass('lazy-alert-shown')) {
|
||||
$parent.after(html);
|
||||
$parent
|
||||
.siblings()
|
||||
.find('.js-warning-text')
|
||||
.text(
|
||||
__('Warning: Displaying this diagram might cause performance issues on this page.'),
|
||||
);
|
||||
$parent.addClass('lazy-alert-shown');
|
||||
if (!parent.classList.contains(LAZY_ALERT_SHOWN_CLASS)) {
|
||||
const alertContainer = document.createElement('div');
|
||||
alertContainer.classList.add(ALERT_CONTAINER_CLASS);
|
||||
alertContainer.classList.add('gl-mb-5');
|
||||
parent.after(alertContainer);
|
||||
createAlert({
|
||||
message: __(
|
||||
'Warning: Displaying this diagram might cause performance issues on this page.',
|
||||
),
|
||||
variant: VARIANT_WARNING,
|
||||
parent: parent.parentNode,
|
||||
containerSelector: `.${ALERT_CONTAINER_CLASS}`,
|
||||
primaryButton: {
|
||||
text: __('Display'),
|
||||
clickHandler: () => {
|
||||
alertContainer.remove();
|
||||
renderMermaidEl(el, source);
|
||||
},
|
||||
},
|
||||
});
|
||||
parent.classList.add(LAZY_ALERT_SHOWN_CLASS);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -176,37 +185,33 @@ function renderMermaids($els) {
|
|||
});
|
||||
}
|
||||
|
||||
const hookLazyRenderMermaidEvent = once(() => {
|
||||
$(document.body).on('click', '.js-lazy-render-mermaid', function eventHandler() {
|
||||
const parent = $(this).closest('.js-lazy-render-mermaid-container');
|
||||
const pre = parent.prev();
|
||||
export default function renderMermaid(els) {
|
||||
if (!els.length) return;
|
||||
|
||||
const el = pre.find('.js-render-mermaid');
|
||||
const visibleMermaids = [];
|
||||
const hiddenMermaids = [];
|
||||
|
||||
parent.remove();
|
||||
|
||||
// sandbox update
|
||||
const element = el.get(0);
|
||||
const { source } = fixElementSource(element);
|
||||
|
||||
renderMermaidEl(element, source);
|
||||
});
|
||||
});
|
||||
|
||||
export default function renderMermaid($els) {
|
||||
if (!$els.length) return;
|
||||
|
||||
const visibleMermaids = $els.filter(function filter() {
|
||||
return $(this).closest('details').length === 0 && $(this).is(':visible');
|
||||
});
|
||||
for (const el of els) {
|
||||
if (isVisibleMermaid(el)) {
|
||||
visibleMermaids.push(el);
|
||||
} else {
|
||||
hiddenMermaids.push(el);
|
||||
}
|
||||
}
|
||||
|
||||
renderMermaids(visibleMermaids);
|
||||
|
||||
$els.closest('details').one('toggle', function toggle() {
|
||||
if (this.open) {
|
||||
renderMermaids($(this).find('.js-render-mermaid'));
|
||||
}
|
||||
hiddenMermaids.forEach((el) => {
|
||||
el.closest('details')?.addEventListener(
|
||||
'toggle',
|
||||
({ target: details }) => {
|
||||
if (details.open) {
|
||||
renderMermaids([...details.querySelectorAll('.js-render-mermaid')]);
|
||||
}
|
||||
},
|
||||
{
|
||||
once: true,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
hookLazyRenderMermaidEvent();
|
||||
}
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
<script>
|
||||
import { debounce } from 'lodash';
|
||||
import { initSourceEditor } from '~/blob/utils';
|
||||
import { SNIPPET_MEASURE_BLOBS_CONTENT } from '~/performance/constants';
|
||||
|
||||
import eventHub from './eventhub';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
fileName: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
// This is used to help uniquely create a monaco model
|
||||
// even if two blob's share a file path.
|
||||
fileGlobalId: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
fileName(newVal) {
|
||||
this.editor.updateModelLanguage(newVal);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.editor = initSourceEditor({
|
||||
el: this.$refs.editor,
|
||||
blobPath: this.fileName,
|
||||
blobContent: this.value,
|
||||
blobGlobalId: this.fileGlobalId,
|
||||
});
|
||||
|
||||
this.editor.onDidChangeModelContent(debounce(this.onFileChange.bind(this), 250));
|
||||
|
||||
eventHub.$emit(SNIPPET_MEASURE_BLOBS_CONTENT);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.editor.dispose();
|
||||
},
|
||||
methods: {
|
||||
onFileChange() {
|
||||
this.$emit('input', this.editor.getValue());
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="file-content code">
|
||||
<div id="editor" ref="editor" data-editor-loading>
|
||||
<pre class="editor-loading-content">{{ value }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -1,19 +1,5 @@
|
|||
import Editor from '~/editor/source_editor';
|
||||
import { getBaseURL } from '~/lib/utils/url_utility';
|
||||
|
||||
export function initSourceEditor({ el, ...args }) {
|
||||
const editor = new Editor({
|
||||
scrollbar: {
|
||||
alwaysConsumeMouseWheel: false,
|
||||
},
|
||||
});
|
||||
|
||||
return editor.createInstance({
|
||||
el,
|
||||
...args,
|
||||
});
|
||||
}
|
||||
|
||||
const blameLinesPerPage = document.querySelector('.js-per-page')?.dataset?.blamePerPage;
|
||||
|
||||
export const getPageParamValue = (lineNum, blamePerPage = blameLinesPerPage) => {
|
||||
|
|
|
@ -24,6 +24,11 @@ export default {
|
|||
SafeHtml: GlSafeHtmlDirective,
|
||||
},
|
||||
props: {
|
||||
isFirstChunk: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
chunkIndex: {
|
||||
type: Number,
|
||||
required: false,
|
||||
|
@ -74,6 +79,11 @@ export default {
|
|||
},
|
||||
|
||||
created() {
|
||||
if (this.isFirstChunk) {
|
||||
this.isLoading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
window.requestIdleCallback(() => {
|
||||
this.isLoading = false;
|
||||
const { hash } = this.$route;
|
||||
|
|
|
@ -203,6 +203,7 @@ export default {
|
|||
:content="firstChunk.content"
|
||||
:starting-from="firstChunk.startingFrom"
|
||||
:is-highlighted="firstChunk.isHighlighted"
|
||||
is-first-chunk
|
||||
:language="firstChunk.language"
|
||||
:blame-path="blob.blamePath"
|
||||
/>
|
||||
|
|
|
@ -8,15 +8,19 @@
|
|||
- grouped_emojis = awardable.grouped_awards(with_thumbs: inline)
|
||||
.awards.js-awards-block{ class: ("hidden" if !inline && grouped_emojis.empty?), data: { award_url: toggle_award_url(awardable) } }
|
||||
- awards_sort(grouped_emojis).each do |emoji, awards|
|
||||
= render Pajamas::ButtonComponent.new(button_options: { class: "#{award_state_class(awardable, awards, current_user)} award-control js-emoji-btn", title: award_user_list(awards, current_user), data: { toggle: 'tooltip', container: 'body' } }) do
|
||||
%button.gl-button.btn.btn-default.award-control.js-emoji-btn.has-tooltip{ type: "button",
|
||||
class: [award_state_class(awardable, awards, current_user)],
|
||||
data: { title: award_user_list(awards, current_user) } }
|
||||
= emoji_icon(emoji)
|
||||
%span.award-control-text.js-counter
|
||||
= awards.count
|
||||
|
||||
- if can?(current_user, :award_emoji, awardable)
|
||||
.award-menu-holder.js-award-holder
|
||||
= render Pajamas::ButtonComponent.new(button_text_classes: 'gl-display-flex', button_options: { title: _('Add reaction'), class: 'js-add-award award-control has-tooltip', 'aria-label': _('Add reaction') }) do
|
||||
= sprite_icon('slight-smile', css_class: 'award-control-icon-neutral gl-icon gl-mr-0!')
|
||||
= sprite_icon('smiley', css_class: 'award-control-icon-positive gl-icon')
|
||||
= sprite_icon('smile', css_class: 'award-control-icon-super-positive gl-icon')
|
||||
%button.gl-button.btn.btn-default.award-control.has-tooltip.js-add-award{ type: 'button',
|
||||
'aria-label': _('Add reaction'),
|
||||
data: { title: _('Add reaction') } }
|
||||
%span{ class: "award-control-icon award-control-icon-neutral gl-icon" }= sprite_icon('slight-smile')
|
||||
%span{ class: "award-control-icon award-control-icon-positive gl-icon" }= sprite_icon('smiley')
|
||||
%span{ class: "award-control-icon award-control-icon-super-positive gl-icon" }= sprite_icon('smile')
|
||||
= yield
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;padding-right:5px;line-height:1;" }
|
||||
%img{ alt: "✖", height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-x-red-inverted.gif'), style: "display:block;", width: "13" }/
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;vertical-align:middle;color:#ffffff;text-align:center;" }
|
||||
Pipeline ##{@pipeline.id} has failed!
|
||||
= s_('Notify|Pipeline #%{pipeline_id} has failed!') % { pipeline_id: @pipeline.id }
|
||||
%tr.spacer
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;height:18px;font-size:18px;line-height:18px;" }
|
||||
|
||||
|
@ -15,7 +15,8 @@
|
|||
%table.table-info{ border: "0", cellpadding: "0", cellspacing: "0", style: "width:100%;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" } Project
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;" }
|
||||
= _('Project')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;" }
|
||||
- namespace_name = @project.group ? @project.group.name : @project.namespace.owner.name
|
||||
- namespace_url = @project.group ? group_url(@project.group) : user_url(@project.namespace.owner)
|
||||
|
@ -25,7 +26,8 @@
|
|||
%a.muted{ href: project_url(@project), style: "color:#333333;text-decoration:none;" }
|
||||
= @project.name
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Branch
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
= _('Branch')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
|
@ -36,7 +38,8 @@
|
|||
%a.muted{ href: commits_url(@pipeline), style: "color:#333333;text-decoration:none;" }
|
||||
= @pipeline.source_ref
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
= _('Commit')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:400;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
|
@ -54,7 +57,8 @@
|
|||
= @pipeline.git_commit_message.truncate(50)
|
||||
- commit = @pipeline.commit
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" } Commit Author
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:300;padding:14px 0;margin:0;border-top:1px solid #ededed;" }
|
||||
= s_('Notify|Commit Author')
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#8c8c8c;font-weight:500;padding:14px 0;margin:0;color:#333333;width:75%;padding-left:5px;border-top:1px solid #ededed;" }
|
||||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;" }
|
||||
%tbody
|
||||
|
@ -93,11 +97,10 @@
|
|||
%table.img{ border: "0", cellpadding: "0", cellspacing: "0", style: "border-collapse:collapse;margin:0 auto;" }
|
||||
%tbody
|
||||
%tr
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" }
|
||||
Pipeline
|
||||
%a{ href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;" }
|
||||
= "\##{@pipeline.id}"
|
||||
triggered by
|
||||
- common_style = "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;"
|
||||
- pipeline_link = link_to "##{@pipeline.id}", pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;"
|
||||
%td{ style: "#{common_style}" }
|
||||
= s_('Notify|Pipeline %{pipeline_link} triggered by').html_safe % { pipeline_link: pipeline_link }
|
||||
- if @pipeline.user
|
||||
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;padding-left:5px", width: "24" }
|
||||
%img.avatar{ height: "24", src: avatar_icon_for_user(@pipeline.user, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
response = Sidekiq::Worker.skipping_transaction_check do
|
||||
result = ::Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService.new.execute
|
||||
|
||||
next result unless result[:status] == :success
|
||||
|
||||
AuthorizedProjectUpdate::ProjectRecalculateService.new(result[:project]).execute
|
||||
|
||||
result
|
||||
|
|
|
@ -154,7 +154,7 @@ different providers with Omnibus GitLab.
|
|||
|
||||
### Configure Google
|
||||
|
||||
See the [Google documentation](https://developers.google.com/identity/protocols/oauth2/openid-connect)
|
||||
See the [Google documentation](https://developers.google.com/identity/openid-connect/openid-connect)
|
||||
for more details:
|
||||
|
||||
```ruby
|
||||
|
|
|
@ -34,7 +34,7 @@ The [`yuzutech/kroki`](https://hub.docker.com/r/yuzutech/kroki) image contains t
|
|||
<!-- vale gitlab.Spelling = NO -->
|
||||
|
||||
- [Bytefield](https://bytefield-svg.deepsymmetry.org/)
|
||||
- [Ditaa](http://ditaa.sourceforge.net)
|
||||
- [Ditaa](https://ditaa.sourceforge.net)
|
||||
- [Erd](https://github.com/BurntSushi/erd)
|
||||
- [GraphViz](https://www.graphviz.org/)
|
||||
- [Nomnoml](https://github.com/skanaar/nomnoml)
|
||||
|
|
|
@ -48,5 +48,5 @@ you're ready to enable the Mailgun integration:
|
|||
1. Select the **Enable Mailgun** checkbox.
|
||||
1. Enter the Mailgun HTTP webhook signing key as described in
|
||||
[the Mailgun documentation](https://documentation.mailgun.com/en/latest/user_manual.html#webhooks-1) and
|
||||
shown in the [API security](https://app.mailgun.com/app/account/security/api_keys) section for your Mailgun account.
|
||||
shown in the API security (`https://app.mailgun.com/app/account/security/api_keys`) section for your Mailgun account.
|
||||
1. Select **Save changes**.
|
||||
|
|
|
@ -279,7 +279,7 @@ Here are the valid connection parameters for GCS:
|
|||
| `google_project` | GCP project name. | `gcp-project-12345` |
|
||||
| `google_json_key_location` | JSON key path. | `/path/to/gcp-project-12345-abcde.json` |
|
||||
| `google_json_key_string` | JSON key string. | `{ "type": "service_account", "project_id": "example-project-382839", ... }` |
|
||||
| `google_application_default` | Set to `true` to use [Google Cloud Application Default Credentials](https://cloud.google.com/docs/authentication/production#automatically) to locate service account credentials. | |
|
||||
| `google_application_default` | Set to `true` to use [Google Cloud Application Default Credentials](https://cloud.google.com/docs/authentication#adc) to locate service account credentials. | |
|
||||
|
||||
GitLab reads the value of `google_json_key_location`, then `google_json_key_string`, and finally, `google_application_default`.
|
||||
It uses the first of these settings that has a value.
|
||||
|
|
|
@ -262,7 +262,7 @@ GET /projects/:id/integrations/buildkite
|
|||
## Campfire
|
||||
|
||||
Send notifications about push events to Campfire chat rooms.
|
||||
[New users can no longer sign up for Campfire](https://basecamp.com/retired/campfire).
|
||||
[New users can no longer sign up for Campfire](https://basecamp.com/handbook/05-product-histories#campfire).
|
||||
|
||||
### Create/Edit Campfire integration
|
||||
|
||||
|
|
|
@ -829,7 +829,7 @@ environment = ["DOCKER_DRIVER=overlay2"]
|
|||
If you're running multiple runners, you have to modify all configuration files.
|
||||
|
||||
Read more about the [runner configuration](https://docs.gitlab.com/runner/configuration/)
|
||||
and [using the OverlayFS storage driver](https://docs.docker.com/engine/userguide/storagedriver/overlayfs-driver/).
|
||||
and [using the OverlayFS storage driver](https://docs.docker.com/storage/storagedriver/overlayfs-driver/).
|
||||
|
||||
## Docker alternatives
|
||||
|
||||
|
|
|
@ -268,7 +268,7 @@ test_async:
|
|||
|
||||
## Contexts and variables
|
||||
|
||||
CircleCI provides [Contexts](https://circleci.com/docs/contexts) to securely pass environment variables across project pipelines. In GitLab, a [Group](../../user/group/index.md) can be created to assemble related projects together. At the group level, [CI/CD variables](../variables/index.md#add-a-cicd-variable-to-a-group) can be stored outside the individual projects, and securely passed into pipelines across multiple projects.
|
||||
CircleCI provides [Contexts](https://circleci.com/docs/contexts/) to securely pass environment variables across project pipelines. In GitLab, a [Group](../../user/group/index.md) can be created to assemble related projects together. At the group level, [CI/CD variables](../variables/index.md#add-a-cicd-variable-to-a-group) can be stored outside the individual projects, and securely passed into pipelines across multiple projects.
|
||||
|
||||
## Orbs
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ the CI container itself.
|
|||
## How services are linked to the job
|
||||
|
||||
To better understand how container linking works, read
|
||||
[Linking containers together](https://docs.docker.com/engine/userguide/networking/default_network/dockerlinks/).
|
||||
[Linking containers together](https://docs.docker.com/network/links/).
|
||||
|
||||
If you add `mysql` as service to your application, the image is
|
||||
used to create a container that's linked to the job container.
|
||||
|
|
|
@ -100,7 +100,7 @@ If you have any questions or need help, visit [Getting Help](https://about.gitla
|
|||
communicate with the GitLab community. GitLab prefers [asynchronous communication](https://about.gitlab.com/handbook/communication/#internal-communication) over real-time communication.
|
||||
|
||||
We do encourage you to connect and hang out with us. GitLab has a Gitter room dedicated for [contributors](https://gitter.im/gitlab/contributors), which is bridged with our
|
||||
internal Slack. We actively monitor this channel. There is also a community-run [Discord server](https://discord.gg/S4cwz9sR8u) where you can
|
||||
internal Slack. We actively monitor this channel. There is also a community-run [Discord server](http://discord.gg/gitlab) where you can
|
||||
find other contributors in the `#contributors` channel.
|
||||
|
||||
Thanks for your contribution!
|
||||
|
|
|
@ -170,8 +170,8 @@ To update the `CODEOWNERS` file:
|
|||
bundle exec rake tw:codeowners > ~/Desktop/updates.md
|
||||
```
|
||||
|
||||
1. Open the file (for example, `~/Desktop/updates.md`) and copy
|
||||
the lines in the `^[Documentation Pages]` section.
|
||||
1. Open the file (for example, `~/Desktop/updates.md`) and copy everything
|
||||
except the errors at the bottom of the file.
|
||||
1. Open the [`CODEOWNERS`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/.gitlab/CODEOWNERS)
|
||||
file and paste the lines into the `^[Documentation Pages]` section.
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ In addition to this page, the following resources can help you craft and contrib
|
|||
## The GitLab voice
|
||||
|
||||
The GitLab brand guidelines define the
|
||||
[voice used by the larger organization](https://design.gitlab.com/brand/overview#tone-of-voice).
|
||||
[voice used by the larger organization](https://design.gitlab.com/brand/overview/#tone-of-voice).
|
||||
|
||||
Building on that guidance, the voice in the GitLab documentation strives to be concise,
|
||||
direct, and precise. The goal is to provide information that's easy to search and scan.
|
||||
|
|
|
@ -15,7 +15,7 @@ watch this [introduction video](https://youtu.be/akRhUbvtnmo).
|
|||
|
||||
## Browse components with Lookbook
|
||||
|
||||
We have a [Lookbook](https://github.com/allmarkedup/lookbook) in [http://gdk.test:3000/rails/lookbook](http://gdk.test:3000/rails/lookbook) (only available in development mode) to browse and interact with ViewComponent previews.
|
||||
We have a [Lookbook](https://github.com/allmarkedup/lookbook) in `http://gdk.test:3000/rails/lookbook` (only available in development mode) to browse and interact with ViewComponent previews.
|
||||
|
||||
## Pajamas components
|
||||
|
||||
|
@ -29,7 +29,7 @@ if the component you are looking for is not yet available.
|
|||
|
||||
### Available components
|
||||
|
||||
Consider this list a best effort. The full list can be found in [`app/components/pajamas`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/app/components/pajamas). Also see [our Lookbook](http://gdk.test:3000/rails/lookbook) for a more interactive way to browse our components.
|
||||
Consider this list a best effort. The full list can be found in [`app/components/pajamas`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/app/components/pajamas). Also see our Lookbook (`http://gdk.test:3000/rails/lookbook`) for a more interactive way to browse our components.
|
||||
|
||||
#### Alert
|
||||
|
||||
|
@ -155,7 +155,7 @@ For the full list of options, see its
|
|||
|
||||
#### Checkbox tag
|
||||
|
||||
The `Pajamas::CheckboxTagComponent` follows the [Pajamas Checkbox](https://design.gitlab.com/components/checkbox) specification.
|
||||
The `Pajamas::CheckboxTagComponent` follows the [Pajamas Checkbox](https://design.gitlab.com/components/checkbox/) specification.
|
||||
|
||||
The `name` argument and `label` slot are required.
|
||||
|
||||
|
@ -176,7 +176,7 @@ For the full list of options, see its
|
|||
|
||||
#### Checkbox
|
||||
|
||||
The `Pajamas::CheckboxComponent` follows the [Pajamas Checkbox](https://design.gitlab.com/components/checkbox) specification.
|
||||
The `Pajamas::CheckboxComponent` follows the [Pajamas Checkbox](https://design.gitlab.com/components/checkbox/) specification.
|
||||
|
||||
NOTE:
|
||||
`Pajamas::CheckboxComponent` is used internally by the [GitLab UI form builder](haml.md#use-the-gitlab-ui-form-builder) and requires an instance of [ActionView::Helpers::FormBuilder](https://api.rubyonrails.org/v6.1.0/classes/ActionView/Helpers/FormBuilder.html) to be passed as the `form` argument.
|
||||
|
|
|
@ -85,6 +85,18 @@ To import these metrics, you can run:
|
|||
bundle exec rake 'gitlab:seed:development_metrics[your_project_id]'
|
||||
```
|
||||
|
||||
#### Seed a project with vulnerabilities
|
||||
|
||||
You can seed a project with [security vulnerabilities](../user/application_security/vulnerabilities/index.md).
|
||||
|
||||
```shell
|
||||
# Seed all projects
|
||||
bin/rake 'gitlab:seed:vulnerabilities'
|
||||
|
||||
# Seed a specific project
|
||||
bin/rake 'gitlab:seed:vulnerabilities[group-path/project-path]'
|
||||
```
|
||||
|
||||
### Automation
|
||||
|
||||
If you're very sure that you want to **wipe the current database** and refill
|
||||
|
|
|
@ -39,7 +39,7 @@ You can view and find routes from the console by running:
|
|||
rails routes | grep crm
|
||||
```
|
||||
|
||||
You can also view routes in your browser by going to [http://gdk.test:3000/rails/info/routes](http://gdk.test:3000/rails/info/routes).
|
||||
You can also view routes in your browser by going to `http://gdk.test:3000/rails/info/routes`.
|
||||
|
||||
## Global routes
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ go build -o analyzer
|
|||
|
||||
Video walkthrough of how Dependency Scanning analyzers are using [downstream pipeline](../../ci/pipelines/downstream_pipelines.md) feature to test analyzers using test projects:
|
||||
|
||||
[![How Sec leverages the downstream pipeline feature of GitLab to test analyzers end to end](http://img.youtube.com/vi/KauRBlfUbDE/0.jpg)](http://www.youtube.com/watch?v=KauRBlfUbDE)
|
||||
[![How Sec leverages the downstream pipeline feature of GitLab to test analyzers end to end](https://img.youtube.com/vi/KauRBlfUbDE/0.jpg)](https://www.youtube.com/watch?v=KauRBlfUbDE)
|
||||
|
||||
### Testing local changes
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ CAPTCHA response string does not matter. It can be any string. If you use a
|
|||
real, valid key pair, you must solve the CAPTCHA to obtain a
|
||||
valid CAPTCHA response to use. You can do this once only, and only before it expires.
|
||||
|
||||
To directly test the GraphQL API via [GraphQL Explorer](http://gdk.test:3000/-/graphql-explorer),
|
||||
To directly test the GraphQL API via GraphQL Explorer (`http://gdk.test:3000/-/graphql-explorer`),
|
||||
get a reCAPTCHA response string via this form: `public/recaptcha.html` (`http://gdk.test:3000/recaptcha.html`):
|
||||
|
||||
```html
|
||||
|
|
|
@ -56,7 +56,7 @@ description, note the following:
|
|||
|
||||
To inspect the raw data of the panel for further calculation, select **Inspect** from the dropdown list of a panel.
|
||||
Queries, raw data, and panel JSON structure are available.
|
||||
Read more at [Grafana panel inspection](http://grafana.com/docs/grafana/next/panels/query-a-data-source/).
|
||||
Read more at [Grafana panel inspection](https://grafana.com/docs/grafana/latest/panels-visualizations/query-transform-data/).
|
||||
|
||||
All the dashboards are powered by [Grafana](https://grafana.com/), a frontend for displaying metrics.
|
||||
Grafana consumes the data returned from queries to backend Prometheus data source, then presents it
|
||||
|
|
|
@ -46,7 +46,7 @@ The Beta version deploys Aurora PostgreSQL, but the release version will deploy
|
|||
|
||||
| | [AWS Quick Start for GitLab Cloud Native Hybrid on EKS](https://aws-quickstart.github.io/quickstart-eks-gitlab/) | [GitLab Environment Toolkit (GET)](https://gitlab.com/gitlab-org/gitlab-environment-toolkit) |
|
||||
| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
|
||||
| Overview and Vision | [AWS Quick Start](https://aws.amazon.com/quickstart/) | [GitLab Environment Toolkit](https://gitlab.com/gitlab-org/gitlab-environment-toolkit/-/blob/main/README.md) |
|
||||
| Overview and Vision | [AWS Quick Start](https://aws.amazon.com/quickstart/architecture/amazon-eks/) | [GitLab Environment Toolkit](https://gitlab.com/gitlab-org/gitlab-environment-toolkit/-/blob/main/README.md) |
|
||||
| Licensing | [Open Source (Apache 2.0)](https://github.com/aws-quickstart/quickstart-eks-gitlab/blob/main/LICENSE.txt) | [GitLab Enterprise Edition license](https://gitlab.com/gitlab-org/gitlab-environment-toolkit/-/blob/main/LICENSE) ([GitLab Premium tier](https://gitlab.com/gitlab-org/gitlab-environment-toolkit/-/blob/main/README.md)) |
|
||||
| GitLab Support | [GitLab Beta Support](../../policy/alpha-beta-support.md#beta-features) | [GitLab GA Support](../../policy/alpha-beta-support.md#generally-available-ga) |
|
||||
| GitLab Reference Architecture Compliant | Yes | Yes |
|
||||
|
|
|
@ -211,7 +211,7 @@ and [Docker configurations](https://docs.docker.com/engine/swarm/configs/) to ef
|
|||
Secrets can be used to securely pass your initial root password without exposing it as an environment variable.
|
||||
Configurations can help you to keep your GitLab image as generic as possible.
|
||||
|
||||
Here's an example that deploys GitLab with four runners as a [stack](https://docs.docker.com/get-started/part5/), using secrets and configurations:
|
||||
Here's an example that deploys GitLab with four runners as a [stack](https://docs.docker.com/get-started/swarm-deploy/#describe-apps-using-stack-files), using secrets and configurations:
|
||||
|
||||
1. [Set up a Docker swarm](https://docs.docker.com/engine/swarm/swarm-tutorial/).
|
||||
1. Create a `docker-compose.yml` file:
|
||||
|
|
|
@ -55,7 +55,7 @@ Memory requirements are dependent on the number of users and expected workload.
|
|||
The following is the recommended minimum Memory hardware guidance for a handful of example GitLab user base sizes.
|
||||
|
||||
- **4GB RAM** is the **required** minimum memory size and supports up to 500 users
|
||||
- Our [Memory Team](https://about.gitlab.com/handbook/engineering/development/enablement/data_stores/memory/) is working to reduce the memory requirement.
|
||||
- Our [Memory Team](https://about.gitlab.com/handbook/engineering/development/enablement/data_stores/application_performance/) is working to reduce the memory requirement.
|
||||
- 8GB RAM supports up to 1000 users
|
||||
- More users? Consult the [reference architectures page](../administration/reference_architectures/index.md)
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ to `CI_COMMIT_SHA,CI_ENVIRONMENT_NAME`.
|
|||
```
|
||||
|
||||
When `AUTO_DEVOPS_BUILD_IMAGE_FORWARDED_CI_VARIABLES` is set, Auto DevOps
|
||||
enables the experimental [Docker BuildKit](https://docs.docker.com/develop/develop-images/build_enhancements/)
|
||||
enables the experimental [Docker BuildKit](https://docs.docker.com/build/buildkit/)
|
||||
feature to use the `--secret` flag.
|
||||
|
||||
## Custom Helm Chart
|
||||
|
|
|
@ -114,7 +114,6 @@ content:
|
|||
|
||||
- Find learning tracks and certification options at [GitLab Learn](https://about.gitlab.com/learn/).
|
||||
GitLab learning platform login required (email and password for non-GitLab team members).
|
||||
For more information, see [First time login details](https://about.gitlab.com/handbook/people-group/learning-and-development/gitlab-learn/user/#first-time-login-to-gitlab-learn).
|
||||
|
||||
- Find recent tutorials on the GitLab blog by [searching by the `tutorial` tag](https://about.gitlab.com/blog/tags.html#tutorial).
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Scan result policies **(ULTIMATE)**
|
||||
|
||||
> Group-level scan result policies [introduced](https://gitlab.com/groups/gitlab-org/-/epics/7622) in GitLab 15.6.
|
||||
|
||||
You can use scan result policies to take action based on scan results. For example, one type of scan
|
||||
result policy is a security approval policy that allows approval to be required based on the
|
||||
findings of one or more security scan jobs. Scan result policies are evaluated after a CI scanning
|
||||
|
|
|
@ -20,7 +20,7 @@ by using the GitLab agent for Kubernetes.
|
|||
|
||||
**Prerequisites:**
|
||||
|
||||
- A [Google Cloud Platform (GCP) service account](https://cloud.google.com/docs/authentication/getting-started).
|
||||
- A [Google Cloud Platform (GCP) service account](https://cloud.google.com/docs/authentication#service-accounts).
|
||||
- [A runner](https://docs.gitlab.com/runner/install/) you can use to run the GitLab CI/CD pipeline.
|
||||
|
||||
**Steps:**
|
||||
|
@ -71,7 +71,7 @@ To create a GitLab agent for Kubernetes:
|
|||
|
||||
To set up your project to communicate to GCP and the GitLab API:
|
||||
|
||||
1. To authenticate GCP with GitLab, create a [GCP service account](https://cloud.google.com/docs/authentication/getting-started)
|
||||
1. To authenticate GCP with GitLab, create a [GCP service account](https://cloud.google.com/docs/authentication#service-accounts)
|
||||
with following roles: `Compute Network Viewer`, `Kubernetes Engine Admin`, `Service Account User`, and `Service Account Admin`. Both User and Admin
|
||||
service accounts are necessary. The User role impersonates the [default service account](https://cloud.google.com/compute/docs/access/service-accounts#default_service_account)
|
||||
when [creating the node pool](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/using_gke_with_terraform#node-pool-management).
|
||||
|
|
|
@ -153,7 +153,7 @@ contents of the file's [markup language](https://en.wikipedia.org/wiki/Lightweig
|
|||
| [reStructuredText](https://docutils.sourceforge.io/rst.html) | `rst` |
|
||||
| [AsciiDoc](../../asciidoc.md) | `adoc`, `ad`, `asciidoc` |
|
||||
| [Textile](https://textile-lang.com/) | `textile` |
|
||||
| [Rdoc](http://rdoc.sourceforge.net/doc/index.html) | `rdoc` |
|
||||
| [Rdoc](https://rdoc.sourceforge.net/doc/index.html) | `rdoc` |
|
||||
| [Org mode](https://orgmode.org/) | `org` |
|
||||
| [creole](http://www.wikicreole.org/) | `creole` |
|
||||
| [MediaWiki](https://www.mediawiki.org/wiki/MediaWiki) | `wiki`, `mediawiki` |
|
||||
|
|
|
@ -24,7 +24,7 @@ module API
|
|||
end
|
||||
|
||||
desc 'Get a list of features (deprecated, v2 client support)'
|
||||
get 'features' do
|
||||
get 'features', urgency: :low do
|
||||
if ::Feature.enabled?(:cache_unleash_client_api, project)
|
||||
present_feature_flags
|
||||
else
|
||||
|
@ -50,7 +50,7 @@ module API
|
|||
status :ok
|
||||
end
|
||||
|
||||
post 'client/metrics' do
|
||||
post 'client/metrics', urgency: :low do
|
||||
# not supported yet
|
||||
status :ok
|
||||
end
|
||||
|
|
|
@ -2,30 +2,36 @@
|
|||
|
||||
namespace :gitlab do
|
||||
namespace :seed do
|
||||
def projects_from_args(args)
|
||||
full_path = args.project_full_path
|
||||
|
||||
if full_path
|
||||
project = Project.find_by_full_path(full_path)
|
||||
|
||||
unless project
|
||||
error_message = "Project '#{full_path}' does not exist!"
|
||||
potential_projects = Project.search(full_path)
|
||||
|
||||
if potential_projects.present?
|
||||
error_message += " Did you mean '#{potential_projects.first.full_path}'?"
|
||||
end
|
||||
|
||||
puts error_message.color(:red)
|
||||
exit 1
|
||||
end
|
||||
|
||||
[project]
|
||||
else
|
||||
scope = Project.respond_to?(:not_mass_generated) ? Project.not_mass_generated : Project
|
||||
scope.find_each
|
||||
end
|
||||
end
|
||||
|
||||
desc "GitLab | Seed | Seeds issues"
|
||||
task :issues, [:project_full_path, :backfill_weeks, :average_issues_per_week] => :environment do |t, args|
|
||||
args.with_defaults(backfill_weeks: 5, average_issues_per_week: 2)
|
||||
|
||||
projects =
|
||||
if args.project_full_path
|
||||
project = Project.find_by_full_path(args.project_full_path)
|
||||
|
||||
unless project
|
||||
error_message = "Project '#{args.project_full_path}' does not exist!"
|
||||
potential_projects = Project.search(args.project_full_path)
|
||||
|
||||
if potential_projects.present?
|
||||
error_message += " Did you mean '#{potential_projects.first.full_path}'?"
|
||||
end
|
||||
|
||||
puts error_message.color(:red)
|
||||
exit 1
|
||||
end
|
||||
|
||||
[project]
|
||||
else
|
||||
Project.not_mass_generated.find_each
|
||||
end
|
||||
projects = projects_from_args(args)
|
||||
|
||||
projects.each do |project|
|
||||
puts "\nSeeding issues for the '#{project.full_path}' project"
|
||||
|
@ -70,5 +76,17 @@ namespace :gitlab do
|
|||
puts "\n#{epics} epics created!"
|
||||
end
|
||||
end
|
||||
|
||||
desc "GitLab | Seed | Seed a project with vulnerabilities"
|
||||
task :vulnerabilities, [:project_full_path] => :environment do |t, args|
|
||||
projects = projects_from_args(args)
|
||||
|
||||
projects.each do |project|
|
||||
puts "\nSeeding vulnerabilities for the '#{project.full_path}' project"
|
||||
seeder = Quality::Seeders::Vulnerabilities.new(project)
|
||||
seeder.seed!
|
||||
puts "\nDone."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,6 +28,7 @@ namespace :tw do
|
|||
CodeOwnerRule.new('Compliance', '@eread'),
|
||||
CodeOwnerRule.new('Composition Analysis', '@rdickenson'),
|
||||
CodeOwnerRule.new('Configure', '@phillipwells'),
|
||||
CodeOwnerRule.new('Container Registry', '@claytoncornell'),
|
||||
CodeOwnerRule.new('Contributor Experience', '@eread'),
|
||||
CodeOwnerRule.new('Conversion', '@kpaizee'),
|
||||
CodeOwnerRule.new('Database', '@aqualls'),
|
||||
|
@ -49,9 +50,9 @@ namespace :tw do
|
|||
CodeOwnerRule.new('Knowledge', '@aqualls'),
|
||||
CodeOwnerRule.new('Application Performance', '@jglassman1'),
|
||||
CodeOwnerRule.new('Monitor', '@msedlakjakubowski'),
|
||||
CodeOwnerRule.new('Observability', 'msedlakjakubowski'),
|
||||
CodeOwnerRule.new('Observability', '@msedlakjakubowski'),
|
||||
CodeOwnerRule.new('Optimize', '@lciutacu'),
|
||||
CodeOwnerRule.new('Package', '@claytoncornell'),
|
||||
CodeOwnerRule.new('Package Registry', '@claytoncornell'),
|
||||
CodeOwnerRule.new('Pipeline Authoring', '@marcel.amirault'),
|
||||
CodeOwnerRule.new('Pipeline Execution', '@marcel.amirault'),
|
||||
CodeOwnerRule.new('Pipeline Insights', '@marcel.amirault'),
|
||||
|
|
|
@ -27724,6 +27724,9 @@ msgstr ""
|
|||
msgid "Notify|No preview for this file type"
|
||||
msgstr ""
|
||||
|
||||
msgid "Notify|Pipeline #%{pipeline_id} has failed!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Notify|Pipeline %{pipeline_link} triggered by"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -23,11 +23,6 @@ module QA
|
|||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
after do
|
||||
personal_snippet&.remove_via_api!
|
||||
project_snippet&.remove_via_api!
|
||||
end
|
||||
|
||||
shared_examples 'comments on snippets' do |snippet_type, testcase|
|
||||
it "adds, edits, and deletes a comment on a #{snippet_type}", testcase: testcase do
|
||||
send(snippet_type)
|
||||
|
|
|
@ -23,11 +23,6 @@ module QA
|
|||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
after do
|
||||
personal_snippet&.remove_via_api!
|
||||
project_snippet&.remove_via_api!
|
||||
end
|
||||
|
||||
shared_examples 'adding file to snippet' do |snippet_type, testcase|
|
||||
it "adds second file to an existing #{snippet_type} to make it multi-file", testcase: testcase do
|
||||
send(snippet_type).visit!
|
||||
|
|
|
@ -58,11 +58,6 @@ module QA
|
|||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
after do
|
||||
personal_snippet&.remove_via_api!
|
||||
project_snippet&.remove_via_api!
|
||||
end
|
||||
|
||||
shared_examples 'copying snippet file contents' do |snippet_type, testcase|
|
||||
it "copies file contents of a multi-file #{snippet_type} to a comment and verifies them", testcase: testcase do
|
||||
send(snippet_type).visit!
|
||||
|
|
|
@ -17,10 +17,6 @@ module QA
|
|||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
after do
|
||||
snippet.remove_via_api!
|
||||
end
|
||||
|
||||
it 'user creates a personal snippet', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347799' do
|
||||
snippet
|
||||
|
||||
|
|
|
@ -22,10 +22,6 @@ module QA
|
|||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
after do
|
||||
snippet.remove_via_api!
|
||||
end
|
||||
|
||||
it 'creates a personal snippet with multiple files', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347723' do
|
||||
snippet
|
||||
|
||||
|
|
|
@ -17,10 +17,6 @@ module QA
|
|||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
after do
|
||||
snippet.remove_via_api!
|
||||
end
|
||||
|
||||
it 'user creates a project snippet', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347798' do
|
||||
snippet
|
||||
|
||||
|
|
|
@ -24,10 +24,6 @@ module QA
|
|||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
after do
|
||||
snippet.remove_via_api!
|
||||
end
|
||||
|
||||
it 'creates a project snippet with multiple files', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347725' do
|
||||
snippet
|
||||
|
||||
|
|
|
@ -31,11 +31,6 @@ module QA
|
|||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
after do
|
||||
personal_snippet&.remove_via_api!
|
||||
project_snippet&.remove_via_api!
|
||||
end
|
||||
|
||||
shared_examples 'deleting file from snippet' do |snippet_type, testcase|
|
||||
it "deletes second file from an existing #{snippet_type} to make it single-file", testcase: testcase do
|
||||
send(snippet_type).visit!
|
||||
|
|
|
@ -16,10 +16,6 @@ module QA
|
|||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
after do
|
||||
snippet&.remove_via_api!
|
||||
end
|
||||
|
||||
context 'when the snippet is public' do
|
||||
it 'can be shared with not signed-in users', testcase: 'https://gitlab.com/gitlab-org/gitlab/-/quality/test_cases/347836' do
|
||||
snippet.visit!
|
||||
|
|
|
@ -49,13 +49,6 @@ module QA
|
|||
Flow::Login.sign_in
|
||||
end
|
||||
|
||||
after do
|
||||
personal_snippet_with_single_file.remove_via_api!
|
||||
personal_snippet_with_multiple_files.remove_via_api!
|
||||
project_snippet_with_single_file.remove_via_api!
|
||||
project_snippet_with_multiple_files.remove_via_api!
|
||||
end
|
||||
|
||||
shared_examples 'displaying details on index page' do |snippet_type, testcase|
|
||||
it "shows correct details of #{snippet_type} including file number", testcase: testcase do
|
||||
send(snippet_type)
|
||||
|
|
|
@ -3,26 +3,54 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe 'Sandboxed Mermaid rendering', :js do
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
|
||||
it 'includes mermaid frame correctly' do
|
||||
description = <<~MERMAID
|
||||
```mermaid
|
||||
graph TD;
|
||||
A-->B;
|
||||
A-->C;
|
||||
B-->D;
|
||||
C-->D;
|
||||
```
|
||||
let_it_be(:project) { create(:project, :public, :repository) }
|
||||
let_it_be(:description) do
|
||||
<<~MERMAID
|
||||
```mermaid
|
||||
graph TD;
|
||||
A-->B;
|
||||
A-->C;
|
||||
B-->D;
|
||||
C-->D;
|
||||
```
|
||||
MERMAID
|
||||
end
|
||||
|
||||
issue = create(:issue, project: project, description: description)
|
||||
let_it_be(:expected) do
|
||||
%(<iframe src="/-/sandbox/mermaid" sandbox="allow-scripts allow-popups" frameborder="0" scrolling="no")
|
||||
end
|
||||
|
||||
visit project_issue_path(project, issue)
|
||||
context 'in an issue' do
|
||||
let(:issue) { create(:issue, project: project, description: description) }
|
||||
|
||||
wait_for_requests
|
||||
it 'includes mermaid frame correctly' do
|
||||
visit project_issue_path(project, issue)
|
||||
|
||||
expected = %(<iframe src="/-/sandbox/mermaid" sandbox="allow-scripts allow-popups" frameborder="0" scrolling="no")
|
||||
expect(page.html).to include(expected)
|
||||
wait_for_requests
|
||||
|
||||
expect(page.html).to include(expected)
|
||||
end
|
||||
end
|
||||
|
||||
context 'in a merge request' do
|
||||
let(:merge_request) { create(:merge_request_with_diffs, source_project: project, description: description) }
|
||||
|
||||
it 'renders diffs and includes mermaid frame correctly' do
|
||||
visit(diffs_project_merge_request_path(project, merge_request))
|
||||
|
||||
wait_for_requests
|
||||
|
||||
page.within('.tab-content') do
|
||||
expect(page).to have_selector('.diffs')
|
||||
end
|
||||
|
||||
visit(project_merge_request_path(project, merge_request))
|
||||
|
||||
wait_for_requests
|
||||
|
||||
page.within('.merge-request') do
|
||||
expect(page.html).to include(expected)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,34 +1,127 @@
|
|||
import $ from 'jquery';
|
||||
import { createWrapper } from '@vue/test-utils';
|
||||
import { __ } from '~/locale';
|
||||
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
|
||||
import renderMermaid from '~/behaviors/markdown/render_sandboxed_mermaid';
|
||||
import renderMermaid, {
|
||||
MAX_CHAR_LIMIT,
|
||||
MAX_MERMAID_BLOCK_LIMIT,
|
||||
LAZY_ALERT_SHOWN_CLASS,
|
||||
} from '~/behaviors/markdown/render_sandboxed_mermaid';
|
||||
|
||||
describe('Render mermaid diagrams for Gitlab Flavoured Markdown', () => {
|
||||
it('Does something', () => {
|
||||
document.body.dataset.page = '';
|
||||
setHTMLFixture(`
|
||||
<div class="gl-relative markdown-code-block js-markdown-code">
|
||||
<pre data-sourcepos="1:1-7:3" class="code highlight js-syntax-highlight language-mermaid white" lang="mermaid" id="code-4">
|
||||
<code class="js-render-mermaid">
|
||||
<span id="LC1" class="line" lang="mermaid">graph TD;</span>
|
||||
<span id="LC2" class="line" lang="mermaid">A-->B</span>
|
||||
<span id="LC3" class="line" lang="mermaid">A-->C</span>
|
||||
<span id="LC4" class="line" lang="mermaid">B-->D</span>
|
||||
<span id="LC5" class="line" lang="mermaid">C-->D</span>
|
||||
</code>
|
||||
</pre>
|
||||
<copy-code>
|
||||
<button type="button" class="btn btn-default btn-md gl-button btn-icon has-tooltip" data-title="Copy to clipboard" data-clipboard-target="pre#code-4">
|
||||
<svg><use xlink:href="/assets/icons-7f1680a3670112fe4c8ef57b9dfb93f0f61b43a2a479d7abd6c83bcb724b9201.svg#copy-to-clipboard"></use></svg>
|
||||
</button>
|
||||
</copy-code>
|
||||
</div>`);
|
||||
const els = $('pre.js-syntax-highlight').find('.js-render-mermaid');
|
||||
|
||||
renderMermaid(els);
|
||||
describe('Mermaid diagrams renderer', () => {
|
||||
// Finders
|
||||
const findMermaidIframes = () => document.querySelectorAll('iframe[src="/-/sandbox/mermaid"]');
|
||||
const findDangerousMermaidAlert = () =>
|
||||
createWrapper(document.querySelector('[data-testid="alert-warning"]'));
|
||||
|
||||
// Helpers
|
||||
const renderDiagrams = () => {
|
||||
renderMermaid([...document.querySelectorAll('.js-render-mermaid')]);
|
||||
jest.runAllTimers();
|
||||
expect(document.querySelector('pre.js-syntax-highlight').classList).toContain('gl-sr-only');
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.dataset.page = '';
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resetHTMLFixture();
|
||||
});
|
||||
|
||||
it('renders a mermaid diagram', () => {
|
||||
setHTMLFixture('<pre><code class="js-render-mermaid"></code></pre>');
|
||||
|
||||
expect(findMermaidIframes()).toHaveLength(0);
|
||||
|
||||
renderDiagrams();
|
||||
|
||||
expect(document.querySelector('pre').classList).toContain('gl-sr-only');
|
||||
expect(findMermaidIframes()).toHaveLength(1);
|
||||
});
|
||||
|
||||
describe('within a details element', () => {
|
||||
beforeEach(() => {
|
||||
setHTMLFixture('<details><pre><code class="js-render-mermaid"></code></pre></details>');
|
||||
renderDiagrams();
|
||||
});
|
||||
|
||||
it('does not render the diagram on load', () => {
|
||||
expect(findMermaidIframes()).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('render the diagram when the details element is opened', () => {
|
||||
document.querySelector('details').setAttribute('open', true);
|
||||
document.querySelector('details').dispatchEvent(new Event('toggle'));
|
||||
jest.runAllTimers();
|
||||
|
||||
expect(findMermaidIframes()).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('dangerous diagrams', () => {
|
||||
describe(`when the diagram's source exceeds ${MAX_CHAR_LIMIT} characters`, () => {
|
||||
beforeEach(() => {
|
||||
setHTMLFixture(
|
||||
`<pre>
|
||||
<code class="js-render-mermaid">${Array(MAX_CHAR_LIMIT + 1)
|
||||
.fill('a')
|
||||
.join('')}</code>
|
||||
</pre>`,
|
||||
);
|
||||
renderDiagrams();
|
||||
});
|
||||
it('does not render the diagram on load', () => {
|
||||
expect(findMermaidIframes()).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('shows a warning about performance impact when rendering the diagram', () => {
|
||||
expect(document.querySelector('pre').classList).toContain(LAZY_ALERT_SHOWN_CLASS);
|
||||
expect(findDangerousMermaidAlert().exists()).toBe(true);
|
||||
expect(findDangerousMermaidAlert().text()).toContain(
|
||||
__('Warning: Displaying this diagram might cause performance issues on this page.'),
|
||||
);
|
||||
});
|
||||
|
||||
it("renders the diagram when clicking on the alert's button", () => {
|
||||
findDangerousMermaidAlert().find('button').trigger('click');
|
||||
jest.runAllTimers();
|
||||
|
||||
expect(findMermaidIframes()).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
it(`stops rendering diagrams once the total rendered source exceeds ${MAX_CHAR_LIMIT} characters`, () => {
|
||||
setHTMLFixture(
|
||||
`<pre>
|
||||
<code class="js-render-mermaid">${Array(MAX_CHAR_LIMIT - 1)
|
||||
.fill('a')
|
||||
.join('')}</code>
|
||||
<code class="js-render-mermaid">2</code>
|
||||
<code class="js-render-mermaid">3</code>
|
||||
<code class="js-render-mermaid">4</code>
|
||||
</pre>`,
|
||||
);
|
||||
renderDiagrams();
|
||||
|
||||
expect(findMermaidIframes()).toHaveLength(3);
|
||||
});
|
||||
|
||||
// Note: The test case below is provided for convenience but should remain skipped as the DOM
|
||||
// operations it requires are too expensive and would significantly slow down the test suite.
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip(`stops rendering diagrams when the rendered diagrams count exceeds ${MAX_MERMAID_BLOCK_LIMIT}`, () => {
|
||||
setHTMLFixture(
|
||||
`<pre>
|
||||
${Array(MAX_MERMAID_BLOCK_LIMIT + 1)
|
||||
.fill('<code class="js-render-mermaid"></code>')
|
||||
.join('')}
|
||||
</pre>`,
|
||||
);
|
||||
renderDiagrams();
|
||||
|
||||
expect([...document.querySelectorAll('.js-render-mermaid')]).toHaveLength(
|
||||
MAX_MERMAID_BLOCK_LIMIT + 1,
|
||||
);
|
||||
expect(findMermaidIframes()).toHaveLength(MAX_MERMAID_BLOCK_LIMIT);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Blob Header Editing rendering matches the snapshot 1`] = `
|
||||
<div
|
||||
class="file-content code"
|
||||
>
|
||||
<div
|
||||
data-editor-loading=""
|
||||
id="editor"
|
||||
>
|
||||
<pre
|
||||
class="editor-loading-content"
|
||||
>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -1,105 +0,0 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { nextTick } from 'vue';
|
||||
import BlobEditContent from '~/blob/components/blob_edit_content.vue';
|
||||
import * as utils from '~/blob/utils';
|
||||
|
||||
jest.mock('~/editor/source_editor');
|
||||
|
||||
describe('Blob Header Editing', () => {
|
||||
let wrapper;
|
||||
const onDidChangeModelContent = jest.fn();
|
||||
const updateModelLanguage = jest.fn();
|
||||
const getValue = jest.fn();
|
||||
const value = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.';
|
||||
const fileName = 'lorem.txt';
|
||||
const fileGlobalId = 'snippet_777';
|
||||
|
||||
function createComponent(props = {}) {
|
||||
wrapper = shallowMount(BlobEditContent, {
|
||||
propsData: {
|
||||
value,
|
||||
fileName,
|
||||
fileGlobalId,
|
||||
...props,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(utils, 'initSourceEditor').mockImplementation(() => ({
|
||||
onDidChangeModelContent,
|
||||
updateModelLanguage,
|
||||
getValue,
|
||||
dispose: jest.fn(),
|
||||
}));
|
||||
|
||||
createComponent();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
});
|
||||
|
||||
const triggerChangeContent = (val) => {
|
||||
getValue.mockReturnValue(val);
|
||||
const [cb] = onDidChangeModelContent.mock.calls[0];
|
||||
|
||||
cb();
|
||||
|
||||
jest.runOnlyPendingTimers();
|
||||
};
|
||||
|
||||
describe('rendering', () => {
|
||||
it('matches the snapshot', () => {
|
||||
expect(wrapper.element).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders content', () => {
|
||||
expect(wrapper.text()).toContain(value);
|
||||
});
|
||||
});
|
||||
|
||||
describe('functionality', () => {
|
||||
it('does not fail without content', () => {
|
||||
const spy = jest.spyOn(global.console, 'error');
|
||||
createComponent({ value: undefined });
|
||||
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
expect(wrapper.find('#editor').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('initialises Source Editor', () => {
|
||||
const el = wrapper.findComponent({ ref: 'editor' }).element;
|
||||
expect(utils.initSourceEditor).toHaveBeenCalledWith({
|
||||
el,
|
||||
blobPath: fileName,
|
||||
blobGlobalId: fileGlobalId,
|
||||
blobContent: value,
|
||||
});
|
||||
});
|
||||
|
||||
it('reacts to the changes in fileName', () => {
|
||||
const newFileName = 'ipsum.txt';
|
||||
|
||||
wrapper.setProps({
|
||||
fileName: newFileName,
|
||||
});
|
||||
|
||||
return nextTick().then(() => {
|
||||
expect(updateModelLanguage).toHaveBeenCalledWith(newFileName);
|
||||
});
|
||||
});
|
||||
|
||||
it('registers callback with editor onChangeContent', () => {
|
||||
expect(onDidChangeModelContent).toHaveBeenCalledWith(expect.any(Function));
|
||||
});
|
||||
|
||||
it('emits input event when the blob content is changed', () => {
|
||||
expect(wrapper.emitted().input).toBeUndefined();
|
||||
|
||||
triggerChangeContent(value);
|
||||
|
||||
expect(wrapper.emitted().input).toEqual([[value]]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,46 +1,6 @@
|
|||
import * as utils from '~/blob/utils';
|
||||
import Editor from '~/editor/source_editor';
|
||||
|
||||
jest.mock('~/editor/source_editor');
|
||||
|
||||
describe('Blob utilities', () => {
|
||||
describe('initSourceEditor', () => {
|
||||
let editorEl;
|
||||
const blobPath = 'foo.txt';
|
||||
const blobContent = 'Foo bar';
|
||||
const blobGlobalId = 'snippet_777';
|
||||
|
||||
beforeEach(() => {
|
||||
editorEl = document.createElement('div');
|
||||
});
|
||||
|
||||
describe('Monaco editor', () => {
|
||||
it('initializes the Source Editor', () => {
|
||||
utils.initSourceEditor({ el: editorEl });
|
||||
expect(Editor).toHaveBeenCalledWith({
|
||||
scrollbar: {
|
||||
alwaysConsumeMouseWheel: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it.each([[{}], [{ blobPath, blobContent, blobGlobalId }]])(
|
||||
'creates the instance with the passed parameters %s',
|
||||
(extraParams) => {
|
||||
const params = {
|
||||
el: editorEl,
|
||||
...extraParams,
|
||||
};
|
||||
|
||||
expect(Editor.prototype.createInstance).not.toHaveBeenCalled();
|
||||
|
||||
utils.initSourceEditor(params);
|
||||
|
||||
expect(Editor.prototype.createInstance).toHaveBeenCalledWith(params);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
describe('getPageParamValue', () => {
|
||||
it('returns empty string if no perPage parameter is provided', () => {
|
||||
const pageParamValue = utils.getPageParamValue(5);
|
||||
|
|
|
@ -61,6 +61,14 @@ describe('Chunk component', () => {
|
|||
});
|
||||
|
||||
describe('rendering', () => {
|
||||
it('does not register window.requestIdleCallback if isFirstChunk prop is true, renders lines immediately', () => {
|
||||
jest.clearAllMocks();
|
||||
createComponent({ isFirstChunk: true });
|
||||
|
||||
expect(window.requestIdleCallback).not.toHaveBeenCalled();
|
||||
expect(findContent().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('does not render a Chunk Line component if isHighlighted is false', () => {
|
||||
expect(findChunkLines().length).toBe(0);
|
||||
});
|
||||
|
|
|
@ -122,6 +122,7 @@ describe('Source Viewer component', () => {
|
|||
|
||||
it('highlights the first chunk', () => {
|
||||
expect(hljs.highlight).toHaveBeenCalledWith(chunk1.trim(), { language: mappedLanguage });
|
||||
expect(findChunks().at(0).props('isFirstChunk')).toBe(true);
|
||||
});
|
||||
|
||||
describe('auto-detects if a language cannot be loaded', () => {
|
||||
|
|
|
@ -9,13 +9,26 @@ RSpec.describe Gitlab::Checks::LfsIntegrity do
|
|||
let(:project) { create(:project, :repository) }
|
||||
let(:repository) { project.repository }
|
||||
let(:newrev) do
|
||||
operations = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
|
||||
BareRepoOperations.new(repository.path)
|
||||
end
|
||||
newrev = repository.commit_files(
|
||||
project.creator,
|
||||
branch_name: 'lfs_integrity_spec',
|
||||
message: 'New LFS objects',
|
||||
actions: [{
|
||||
action: :create,
|
||||
file_path: 'files/lfs/some.iso',
|
||||
content: <<~LFS
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897
|
||||
size 1575078
|
||||
LFS
|
||||
}]
|
||||
)
|
||||
|
||||
# Create a commit not pointed at by any ref to emulate being in the
|
||||
# pre-receive hook so that `--not --all` returns some objects
|
||||
operations.commit_tree('8856a329dd38ca86dfb9ce5aa58a16d88cc119bd', "New LFS objects")
|
||||
repository.delete_branch('lfs_integrity_spec')
|
||||
|
||||
newrev
|
||||
end
|
||||
|
||||
let(:newrevs) { [newrev] }
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'zlib'
|
||||
|
||||
class BareRepoOperations
|
||||
include Gitlab::Popen
|
||||
|
||||
def initialize(path_to_repo)
|
||||
@path_to_repo = path_to_repo
|
||||
end
|
||||
|
||||
def commit_tree(tree_id, msg, parent: Gitlab::Git::EMPTY_TREE_ID)
|
||||
commit_tree_args = ['commit-tree', tree_id, '-m', msg]
|
||||
commit_tree_args += ['-p', parent] unless parent == Gitlab::Git::EMPTY_TREE_ID
|
||||
commit_id = execute(commit_tree_args)
|
||||
|
||||
commit_id[0]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def execute(args, allow_failure: false)
|
||||
output, status = popen(base_args + args, nil) do |stdin|
|
||||
yield stdin if block_given?
|
||||
end
|
||||
|
||||
unless status == 0
|
||||
if allow_failure
|
||||
return []
|
||||
else
|
||||
raise "Got a non-zero exit code while calling out `#{args.join(' ')}`: #{output}"
|
||||
end
|
||||
end
|
||||
|
||||
output.split("\n")
|
||||
end
|
||||
|
||||
def base_args
|
||||
[
|
||||
Gitlab.config.git.bin_path,
|
||||
"--git-dir=#{@path_to_repo}"
|
||||
]
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue