diff options
60 files changed, 395 insertions, 530 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index 13389dd508b..85ad4adbede 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -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 diff --git a/.rubocop_todo/gitlab/namespaced_class.yml b/.rubocop_todo/gitlab/namespaced_class.yml index 230ef959153..3fb2df623e0 100644 --- a/.rubocop_todo/gitlab/namespaced_class.yml +++ b/.rubocop_todo/gitlab/namespaced_class.yml @@ -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' diff --git a/.rubocop_todo/style/guard_clause.yml b/.rubocop_todo/style/guard_clause.yml index 4c2f924ecb2..f46f8c8f4f6 100644 --- a/.rubocop_todo/style/guard_clause.yml +++ b/.rubocop_todo/style/guard_clause.yml @@ -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' diff --git a/app/assets/javascripts/behaviors/markdown/render_gfm.js b/app/assets/javascripts/behaviors/markdown/render_gfm.js index ee5c0fe5ef3..a08cf48c327 100644 --- a/app/assets/javascripts/behaviors/markdown/render_gfm.js +++ b/app/assets/javascripts/behaviors/markdown/render_gfm.js @@ -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), ); diff --git a/app/assets/javascripts/behaviors/markdown/render_sandboxed_mermaid.js b/app/assets/javascripts/behaviors/markdown/render_sandboxed_mermaid.js index 077e96b2fee..66007aa9e3d 100644 --- a/app/assets/javascripts/behaviors/markdown/render_sandboxed_mermaid.js +++ b/app/assets/javascripts/behaviors/markdown/render_sandboxed_mermaid.js @@ -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).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'); + const parent = el.parentNode; + + 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(); - - const el = pre.find('.js-render-mermaid'); - - parent.remove(); - - // sandbox update - const element = el.get(0); - const { source } = fixElementSource(element); +export default function renderMermaid(els) { + if (!els.length) return; - renderMermaidEl(element, source); - }); -}); - -export default function renderMermaid($els) { - if (!$els.length) return; + const visibleMermaids = []; + const hiddenMermaids = []; - 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(); } diff --git a/app/assets/javascripts/blob/components/blob_edit_content.vue b/app/assets/javascripts/blob/components/blob_edit_content.vue deleted file mode 100644 index 0e670bbd80a..00000000000 --- a/app/assets/javascripts/blob/components/blob_edit_content.vue +++ /dev/null @@ -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> diff --git a/app/assets/javascripts/blob/utils.js b/app/assets/javascripts/blob/utils.js index 92c6277d4f1..b2400a4b224 100644 --- a/app/assets/javascripts/blob/utils.js +++ b/app/assets/javascripts/blob/utils.js @@ -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) => { diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue index f4292183f7b..a2d8b7cbd15 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue +++ b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue @@ -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; diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue index 27752d4576a..f621a23734a 100644 --- a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue +++ b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue @@ -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" /> diff --git a/app/views/award_emoji/_awards_block.html.haml b/app/views/award_emoji/_awards_block.html.haml index 6bdf1f7aae1..5062599c261 100644 --- a/app/views/award_emoji/_awards_block.html.haml +++ b/app/views/award_emoji/_awards_block.html.haml @@ -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 diff --git a/app/views/notify/pipeline_failed_email.html.haml b/app/views/notify/pipeline_failed_email.html.haml index 4e8d8a20ef1..5d4d2c0fcd8 100644 --- a/app/views/notify/pipeline_failed_email.html.haml +++ b/app/views/notify/pipeline_failed_email.html.haml @@ -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: "" }/ diff --git a/db/fixtures/development/98_gitlab_instance_administration_project.rb b/db/fixtures/development/98_gitlab_instance_administration_project.rb index ccd6907c8cc..35bc3edbd70 100644 --- a/db/fixtures/development/98_gitlab_instance_administration_project.rb +++ b/db/fixtures/development/98_gitlab_instance_administration_project.rb @@ -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 diff --git a/doc/administration/auth/oidc.md b/doc/administration/auth/oidc.md index bb263512e06..3134afc1041 100644 --- a/doc/administration/auth/oidc.md +++ b/doc/administration/auth/oidc.md @@ -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 diff --git a/doc/administration/integration/kroki.md b/doc/administration/integration/kroki.md index aa029be90e7..491eeb002e4 100644 --- a/doc/administration/integration/kroki.md +++ b/doc/administration/integration/kroki.md @@ -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) diff --git a/doc/administration/integration/mailgun.md b/doc/administration/integration/mailgun.md index baf9e8c8a3b..d78cd9e1796 100644 --- a/doc/administration/integration/mailgun.md +++ b/doc/administration/integration/mailgun.md @@ -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**. diff --git a/doc/administration/object_storage.md b/doc/administration/object_storage.md index f4741068584..d2c9c35148c 100644 --- a/doc/administration/object_storage.md +++ b/doc/administration/object_storage.md @@ -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. diff --git a/doc/api/integrations.md b/doc/api/integrations.md index 176ed931d38..d64c67e1402 100644 --- a/doc/api/integrations.md +++ b/doc/api/integrations.md @@ -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 diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md index 43e537ee683..ea62133cb7f 100644 --- a/doc/ci/docker/using_docker_build.md +++ b/doc/ci/docker/using_docker_build.md @@ -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 diff --git a/doc/ci/migration/circleci.md b/doc/ci/migration/circleci.md index 5354e406e34..034c8ba5ad6 100644 --- a/doc/ci/migration/circleci.md +++ b/doc/ci/migration/circleci.md @@ -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 diff --git a/doc/ci/services/index.md b/doc/ci/services/index.md index 0f82f2301c7..18932512a75 100644 --- a/doc/ci/services/index.md +++ b/doc/ci/services/index.md @@ -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. diff --git a/doc/development/contributing/index.md b/doc/development/contributing/index.md index cbccd832d78..8c0d18f877b 100644 --- a/doc/development/contributing/index.md +++ b/doc/development/contributing/index.md @@ -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! diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md index e0d7ab59429..d52db71b633 100644 --- a/doc/development/documentation/index.md +++ b/doc/development/documentation/index.md @@ -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. diff --git a/doc/development/documentation/styleguide/index.md b/doc/development/documentation/styleguide/index.md index adc999d2a18..f6ed4cdcaa9 100644 --- a/doc/development/documentation/styleguide/index.md +++ b/doc/development/documentation/styleguide/index.md @@ -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. diff --git a/doc/development/fe_guide/view_component.md b/doc/development/fe_guide/view_component.md index d9d2b6707f7..b61c23cadef 100644 --- a/doc/development/fe_guide/view_component.md +++ b/doc/development/fe_guide/view_component.md @@ -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. diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md index 505f480c410..3de74e2e048 100644 --- a/doc/development/rake_tasks.md +++ b/doc/development/rake_tasks.md @@ -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 diff --git a/doc/development/routing.md b/doc/development/routing.md index 16ed15fdcc6..8f475674c39 100644 --- a/doc/development/routing.md +++ b/doc/development/routing.md @@ -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 diff --git a/doc/development/sec/analyzer_development_guide.md b/doc/development/sec/analyzer_development_guide.md index 002c2fa3581..dda738eb563 100644 --- a/doc/development/sec/analyzer_development_guide.md +++ b/doc/development/sec/analyzer_development_guide.md @@ -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 diff --git a/doc/development/spam_protection_and_captcha/exploratory_testing.md b/doc/development/spam_protection_and_captcha/exploratory_testing.md index e17d25acb9d..f6e3e6814a8 100644 --- a/doc/development/spam_protection_and_captcha/exploratory_testing.md +++ b/doc/development/spam_protection_and_captcha/exploratory_testing.md @@ -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 diff --git a/doc/development/stage_group_observability/dashboards/stage_group_dashboard.md b/doc/development/stage_group_observability/dashboards/stage_group_dashboard.md index fb5d5bbe379..abc46c52407 100644 --- a/doc/development/stage_group_observability/dashboards/stage_group_dashboard.md +++ b/doc/development/stage_group_observability/dashboards/stage_group_dashboard.md @@ -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 diff --git a/doc/install/aws/gitlab_hybrid_on_aws.md b/doc/install/aws/gitlab_hybrid_on_aws.md index ad3d28d15e3..7ae4391dde0 100644 --- a/doc/install/aws/gitlab_hybrid_on_aws.md +++ b/doc/install/aws/gitlab_hybrid_on_aws.md @@ -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 | diff --git a/doc/install/docker.md b/doc/install/docker.md index 22a6a2d8086..7709d71f90a 100644 --- a/doc/install/docker.md +++ b/doc/install/docker.md @@ -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: diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 43d31212f03..cfa7ead3601 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -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) diff --git a/doc/topics/autodevops/customize.md b/doc/topics/autodevops/customize.md index 3206e9e7df3..264ac790453 100644 --- a/doc/topics/autodevops/customize.md +++ b/doc/topics/autodevops/customize.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 diff --git a/doc/tutorials/index.md b/doc/tutorials/index.md index 96e815942ad..e60ca2878a5 100644 --- a/doc/tutorials/index.md +++ b/doc/tutorials/index.md @@ -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). diff --git a/doc/user/application_security/policies/scan-result-policies.md b/doc/user/application_security/policies/scan-result-policies.md index 45b715a52b8..215604ff18d 100644 --- a/doc/user/application_security/policies/scan-result-policies.md +++ b/doc/user/application_security/policies/scan-result-policies.md @@ -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 diff --git a/doc/user/infrastructure/clusters/connect/new_gke_cluster.md b/doc/user/infrastructure/clusters/connect/new_gke_cluster.md index 80bd5a05c72..6b8e2003b8d 100644 --- a/doc/user/infrastructure/clusters/connect/new_gke_cluster.md +++ b/doc/user/infrastructure/clusters/connect/new_gke_cluster.md @@ -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). diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md index bcdb626d0f2..83389c15eba 100644 --- a/doc/user/project/repository/index.md +++ b/doc/user/project/repository/index.md @@ -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` | diff --git a/lib/api/unleash.rb b/lib/api/unleash.rb index 1fbd7cf5afc..a8304419827 100644 --- a/lib/api/unleash.rb +++ b/lib/api/unleash.rb @@ -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 diff --git a/lib/tasks/gitlab/seed.rake b/lib/tasks/gitlab/seed.rake index 7b9c57b1876..8437ae0a31e 100644 --- a/lib/tasks/gitlab/seed.rake +++ b/lib/tasks/gitlab/seed.rake @@ -2,31 +2,37 @@ namespace :gitlab do namespace :seed do - 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) + def projects_from_args(args) + full_path = args.project_full_path - projects = - if args.project_full_path - project = Project.find_by_full_path(args.project_full_path) + if full_path + project = Project.find_by_full_path(full_path) - unless project - error_message = "Project '#{args.project_full_path}' does not exist!" - potential_projects = Project.search(args.project_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 + if potential_projects.present? + error_message += " Did you mean '#{potential_projects.first.full_path}'?" end - [project] - else - Project.not_mass_generated.find_each + 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 = projects_from_args(args) + projects.each do |project| puts "\nSeeding issues for the '#{project.full_path}' project" seeder = Quality::Seeders::Issues.new(project: 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 diff --git a/lib/tasks/gitlab/tw/codeowners.rake b/lib/tasks/gitlab/tw/codeowners.rake index 36441b35182..61acb479b49 100644 --- a/lib/tasks/gitlab/tw/codeowners.rake +++ b/lib/tasks/gitlab/tw/codeowners.rake @@ -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'), diff --git a/locale/gitlab.pot b/locale/gitlab.pot index ef67a5be10c..7f4fe6795e8 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -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 "" diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/add_comment_to_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/add_comment_to_snippet_spec.rb index 65d7e8becb6..db180b729c8 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/add_comment_to_snippet_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/add_comment_to_snippet_spec.rb @@ -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) diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb index 715f6360528..20a4866baee 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/add_file_to_snippet_spec.rb @@ -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! diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb index ee249b6271e..1a614e538ea 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/copy_snippet_file_contents_spec.rb @@ -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! diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb index 8a9937563b4..9693e7148ad 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb @@ -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 diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb index 12563543ac0..66376769419 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_with_multiple_files_spec.rb @@ -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 diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb index 9e39558b21d..0e10a9908f4 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_spec.rb @@ -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 diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb index 27dc261c0ac..3c94107950c 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_project_snippet_with_multiple_files_spec.rb @@ -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 diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb index a24dccf3817..906ac6e09b7 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/delete_file_from_snippet_spec.rb @@ -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! diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb index 3bcf019ef55..b58b487531e 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/share_snippet_spec.rb @@ -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! diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb index f066d6349f5..63e9fdbb881 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/snippet_index_page_spec.rb @@ -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) diff --git a/spec/features/markdown/sandboxed_mermaid_spec.rb b/spec/features/markdown/sandboxed_mermaid_spec.rb index 056a6b86f46..2bf88d7882d 100644 --- a/spec/features/markdown/sandboxed_mermaid_spec.rb +++ b/spec/features/markdown/sandboxed_mermaid_spec.rb @@ -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 + + let_it_be(:expected) do + %(<iframe src="/-/sandbox/mermaid" sandbox="allow-scripts allow-popups" frameborder="0" scrolling="no") + end + + context 'in an issue' do + let(:issue) { create(:issue, project: project, description: description) } + + it 'includes mermaid frame correctly' do + visit project_issue_path(project, issue) + + 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 - issue = create(:issue, project: project, description: description) + page.within('.tab-content') do + expect(page).to have_selector('.diffs') + end - visit project_issue_path(project, issue) + visit(project_merge_request_path(project, merge_request)) - wait_for_requests + wait_for_requests - expected = %(<iframe src="/-/sandbox/mermaid" sandbox="allow-scripts allow-popups" frameborder="0" scrolling="no") - expect(page.html).to include(expected) + page.within('.merge-request') do + expect(page.html).to include(expected) + end + end end end diff --git a/spec/frontend/behaviors/markdown/render_sandboxed_mermaid_spec.js b/spec/frontend/behaviors/markdown/render_sandboxed_mermaid_spec.js index 2b9442162aa..de0e5063e49 100644 --- a/spec/frontend/behaviors/markdown/render_sandboxed_mermaid_spec.js +++ b/spec/frontend/behaviors/markdown/render_sandboxed_mermaid_spec.js @@ -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); + }); + }); }); diff --git a/spec/frontend/blob/components/__snapshots__/blob_edit_content_spec.js.snap b/spec/frontend/blob/components/__snapshots__/blob_edit_content_spec.js.snap deleted file mode 100644 index 72761c18b3d..00000000000 --- a/spec/frontend/blob/components/__snapshots__/blob_edit_content_spec.js.snap +++ /dev/null @@ -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> -`; diff --git a/spec/frontend/blob/components/blob_edit_content_spec.js b/spec/frontend/blob/components/blob_edit_content_spec.js deleted file mode 100644 index 5017b624292..00000000000 --- a/spec/frontend/blob/components/blob_edit_content_spec.js +++ /dev/null @@ -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]]); - }); - }); -}); diff --git a/spec/frontend/blob/utils_spec.js b/spec/frontend/blob/utils_spec.js index 9f597aa016e..24f70acb093 100644 --- a/spec/frontend/blob/utils_spec.js +++ b/spec/frontend/blob/utils_spec.js @@ -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); diff --git a/spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js b/spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js index 43f64e9386e..d720574ce6d 100644 --- a/spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js +++ b/spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js @@ -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); }); diff --git a/spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js b/spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js index 14ebc24fb54..33f370efdfa 100644 --- a/spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js +++ b/spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js @@ -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', () => { diff --git a/spec/lib/gitlab/checks/lfs_integrity_spec.rb b/spec/lib/gitlab/checks/lfs_integrity_spec.rb index 3468094ffa5..abad2bfa905 100644 --- a/spec/lib/gitlab/checks/lfs_integrity_spec.rb +++ b/spec/lib/gitlab/checks/lfs_integrity_spec.rb @@ -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] } diff --git a/spec/support/helpers/bare_repo_operations.rb b/spec/support/helpers/bare_repo_operations.rb deleted file mode 100644 index e29e12a15f6..00000000000 --- a/spec/support/helpers/bare_repo_operations.rb +++ /dev/null @@ -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 |