Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-11-01 18:10:46 +00:00
parent eac94e5cd6
commit 28d82e1650
60 changed files with 396 additions and 531 deletions

View file

@ -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

View file

@ -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'

View file

@ -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'

View file

@ -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),
);

View file

@ -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">&times;</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();
}

View file

@ -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>

View file

@ -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) => {

View file

@ -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;

View file

@ -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"
/>

View file

@ -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

View file

@ -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;" }
&nbsp;
@ -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: "" }/

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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**.

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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!

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 |

View file

@ -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:

View 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)

View file

@ -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

View file

@ -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).

View file

@ -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

View file

@ -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).

View file

@ -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` |

View file

@ -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

View file

@ -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

View file

@ -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'),

View file

@ -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 ""

View file

@ -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)

View file

@ -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!

View file

@ -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!

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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!

View file

@ -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!

View file

@ -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)

View file

@ -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

View file

@ -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--&gt;B</span>
<span id="LC3" class="line" lang="mermaid">A--&gt;C</span>
<span id="LC4" class="line" lang="mermaid">B--&gt;D</span>
<span id="LC5" class="line" lang="mermaid">C--&gt;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);
});
});
});

View file

@ -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>
`;

View file

@ -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]]);
});
});
});

View file

@ -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);

View file

@ -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);
});

View file

@ -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', () => {

View file

@ -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] }

View file

@ -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