diff options
23 files changed, 267 insertions, 158 deletions
diff --git a/Gemfile.lock b/Gemfile.lock index d7d91a92015..3962fe27073 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -81,7 +81,7 @@ GEM faraday_middleware (~> 1.0) faraday_middleware-multi_json (~> 0.0) oauth2 (~> 1.4) - asciidoctor (2.0.10) + asciidoctor (2.0.12) asciidoctor-include-ext (0.3.1) asciidoctor (>= 1.5.6, < 3.0.0) asciidoctor-kroki (0.2.2) diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index 103d59382b4..bda123fa7ea 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -353,23 +353,17 @@ span.idiff { color: $gl-text-color; } - .file-actions .btn:not(.btn-icon) { - padding: 0 10px; - font-size: 13px; - line-height: 28px; - display: inline-block; - float: none; - } - .file-actions .ide-edit-button { z-index: 2; } - @include media-breakpoint-down(sm) { - display: block; - + @include media-breakpoint-down(md) { .file-actions { - margin-top: 5px; + margin-top: $gl-padding-8; + + .btn { + margin-bottom: $gl-padding-8; + } } } } diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index 9b17da80023..226c7867eca 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -108,30 +108,6 @@ } } -.text-expander { - display: inline-flex; - background: $white; - color: $gl-text-color-secondary; - padding: 1px $gl-padding-4; - cursor: pointer; - border: 1px solid $border-white-normal; - border-radius: $border-radius-default; - margin-left: 5px; - font-size: 12px; - line-height: $gl-font-size; - outline: none; - - &.open { - background-color: darken($gray-light, 10%); - box-shadow: inset 0 0 2px rgba($black, 0.2); - } - - &:hover { - background-color: darken($gray-light, 10%); - text-decoration: none; - } -} - .commit.flex-list { display: flex; } @@ -240,11 +216,6 @@ .commit, .generic-commit-status { - a, - button { - vertical-align: baseline; - } - a { color: $gl-text-color; diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 3435dfcf317..28a947a6ca1 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -64,7 +64,7 @@ module BlobHelper def edit_blob_button(project = @project, ref = @ref, path = @path, options = {}) return unless blob = readable_blob(options, path, project, ref) - common_classes = "btn gl-button btn-confirm js-edit-blob gl-mr-3 #{options[:extra_class]}" + common_classes = "btn gl-button btn-confirm js-edit-blob gl-ml-3 #{options[:extra_class]}" data = { track_event: 'click_edit', track_label: 'Edit' } if Feature.enabled?(:web_ide_primary_edit, project.group) @@ -84,7 +84,7 @@ module BlobHelper def ide_edit_button(project = @project, ref = @ref, path = @path, blob:) return unless blob - common_classes = 'btn gl-button btn-confirm ide-edit-button gl-mr-3' + common_classes = 'btn gl-button btn-confirm ide-edit-button gl-ml-3' data = { track_event: 'click_edit_ide', track_label: 'Web IDE' } unless Feature.enabled?(:web_ide_primary_edit, project.group) @@ -229,13 +229,13 @@ module BlobHelper end def copy_file_path_button(file_path) - clipboard_button(text: file_path, gfm: "`#{file_path}`", class: 'btn-clipboard btn-transparent', title: _('Copy file path')) + clipboard_button(text: file_path, gfm: "`#{file_path}`", class: 'gl-button btn btn-default-tertiary btn-icon btn-sm', title: _('Copy file path')) end def copy_blob_source_button(blob) return unless blob.rendered_as_text?(ignore_errors: false) - clipboard_button(target: ".blob-content[data-blob-id='#{blob.id}'] > pre", class: "btn gl-button btn-default btn-sm js-copy-blob-source-btn", title: _("Copy file contents")) + clipboard_button(target: ".blob-content[data-blob-id='#{blob.id}'] > pre", class: "btn gl-button btn-default btn-icon js-copy-blob-source-btn", title: _("Copy file contents")) end def open_raw_blob_button(blob) @@ -245,7 +245,7 @@ module BlobHelper title = _('Open raw') link_to sprite_icon('doc-code'), external_storage_url_or_path(blob_raw_path), - class: 'btn gl-button btn-default btn-sm has-tooltip', + class: 'btn gl-button btn-default btn-icon has-tooltip', target: '_blank', rel: 'noopener noreferrer', aria: { label: title }, @@ -260,7 +260,7 @@ module BlobHelper link_to sprite_icon('download'), external_storage_url_or_path(blob_raw_path(inline: false)), download: @path, - class: 'btn gl-button btn-default btn-sm has-tooltip', + class: 'btn gl-button btn-default btn-icon has-tooltip', target: '_blank', rel: 'noopener noreferrer', aria: { label: title }, @@ -361,7 +361,7 @@ module BlobHelper end def edit_link_tag(link_text, edit_path, common_classes, data) - link_to link_text, edit_path, class: "#{common_classes} btn-sm", data: data + link_to link_text, edit_path, class: "#{common_classes}", data: data end def edit_button_tag(blob, common_classes, text, edit_path, project, ref, data) diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index f4454db0af8..fc4405ef704 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -8,6 +8,8 @@ module MergeRequests # Executed when you do merge via GitLab UI # class MergeService < MergeRequests::MergeBaseService + GENERIC_ERROR_MESSAGE = 'An error occurred while merging' + delegate :merge_jid, :state, to: :@merge_request def execute(merge_request, options = {}) @@ -79,7 +81,7 @@ module MergeRequests if commit_id log_info("Git merge finished on JID #{merge_jid} commit #{commit_id}") else - raise_error('Conflicts detected during merge') + raise_error(GENERIC_ERROR_MESSAGE) end merge_request.update!(merge_commit_sha: commit_id) @@ -96,7 +98,7 @@ module MergeRequests "Something went wrong during merge pre-receive hook. #{e.message}".strip rescue => e handle_merge_error(log_message: e.message) - raise_error('Something went wrong during merge') + raise_error(GENERIC_ERROR_MESSAGE) end def after_merge diff --git a/app/views/projects/blob/_header.html.haml b/app/views/projects/blob/_header.html.haml index 6d01206a128..7adb91f1fe6 100644 --- a/app/views/projects/blob/_header.html.haml +++ b/app/views/projects/blob/_header.html.haml @@ -2,19 +2,19 @@ .js-file-title.file-title-flex-parent = render 'projects/blob/header_content', blob: blob - .file-actions.gl-display-flex.gl-flex-fill-1.gl-align-self-start.gl-md-justify-content-end< + .file-actions.gl-display-flex.gl-align-items-center.gl-flex-wrap.gl-md-justify-content-end< = render 'projects/blob/viewer_switcher', blob: blob unless blame - if Feature.enabled?(:consolidated_edit_button, @project) = render 'shared/web_ide_button', blob: blob - else = edit_blob_button(@project, @ref, @path, blob: blob) = ide_edit_button(@project, @ref, @path, blob: blob) - .btn-group.ml-2{ role: "group" }> + .btn-group{ role: "group", class: ("gl-ml-3" if current_user) }> = render_if_exists 'projects/blob/header_file_locks_link' - if current_user = replace_blob_link(@project, @ref, @path, blob: blob) = delete_blob_link(@project, @ref, @path, blob: blob) - .btn-group.ml-2{ role: "group" } + .btn-group.gl-ml-3{ role: "group" } = copy_blob_source_button(blob) unless blame = open_raw_blob_button(blob) = download_blob_button(blob) diff --git a/app/views/projects/blob/_viewer_switcher.html.haml b/app/views/projects/blob/_viewer_switcher.html.haml index c6b13deaece..043b8629289 100644 --- a/app/views/projects/blob/_viewer_switcher.html.haml +++ b/app/views/projects/blob/_viewer_switcher.html.haml @@ -2,11 +2,11 @@ - simple_viewer = blob.simple_viewer - rich_viewer = blob.rich_viewer - .btn-group.js-blob-viewer-switcher.ml-2{ role: "group" }> + .btn-group.js-blob-viewer-switcher.gl-ml-3{ role: "group" }> - simple_label = "Display #{simple_viewer.switcher_title}" - %button.btn.gl-button.btn-default.btn-sm.js-blob-viewer-switch-btn.has-tooltip{ 'aria-label' => simple_label, title: simple_label, data: { viewer: 'simple', container: 'body' } }> + %button.btn.gl-button.btn-default.btn-icon.js-blob-viewer-switch-btn.has-tooltip{ 'aria-label' => simple_label, title: simple_label, data: { viewer: 'simple', container: 'body' } }> = sprite_icon(simple_viewer.switcher_icon) - rich_label = "Display #{rich_viewer.switcher_title}" - %button.btn.gl-button.btn-default.btn-sm.js-blob-viewer-switch-btn.gl-mr-3.has-tooltip{ 'aria-label' => rich_label, title: rich_label, data: { viewer: 'rich', container: 'body' } }> + %button.btn.gl-button.btn-default.btn-icon.js-blob-viewer-switch-btn.has-tooltip{ 'aria-label' => rich_label, title: rich_label, data: { viewer: 'rich', container: 'body' } }> = sprite_icon(rich_viewer.switcher_icon) diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index 179b0c5efbd..c708efe7c7b 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -32,7 +32,7 @@ · = commit.short_id - if commit.description? && collapsible - %button.text-expander.js-toggle-button + %button.gl-button.btn.btn-default.button-ellipsis-horizontal.btn-sm.gl-ml-2.text-expander.js-toggle-button = sprite_icon('ellipsis_h', size: 12) .committer diff --git a/changelogs/unreleased/273288-update-blob-page-buttons.yml b/changelogs/unreleased/273288-update-blob-page-buttons.yml new file mode 100644 index 00000000000..6e1b7bcc1b0 --- /dev/null +++ b/changelogs/unreleased/273288-update-blob-page-buttons.yml @@ -0,0 +1,5 @@ +--- +title: Update blob page buttons to gitlab-ui pajamas styles +merge_request: 53775 +author: +type: changed diff --git a/changelogs/unreleased/30141-merge-error-msg-update.yml b/changelogs/unreleased/30141-merge-error-msg-update.yml new file mode 100644 index 00000000000..4abe2c6ebf6 --- /dev/null +++ b/changelogs/unreleased/30141-merge-error-msg-update.yml @@ -0,0 +1,5 @@ +--- +title: Show more appropriate error message when merging fails +merge_request: 52671 +author: +type: fixed diff --git a/changelogs/unreleased/asciidoctor-2-0-12.yml b/changelogs/unreleased/asciidoctor-2-0-12.yml new file mode 100644 index 00000000000..0ee92803fc5 --- /dev/null +++ b/changelogs/unreleased/asciidoctor-2-0-12.yml @@ -0,0 +1,5 @@ +--- +title: Upgrade Asciidoctor to 2.0.12 +merge_request: 54121 +author: Guillaume Grossetie +type: changed diff --git a/changelogs/unreleased/sh-prepend-marginalia-comments.yml b/changelogs/unreleased/sh-prepend-marginalia-comments.yml new file mode 100644 index 00000000000..3ebdd0527ec --- /dev/null +++ b/changelogs/unreleased/sh-prepend-marginalia-comments.yml @@ -0,0 +1,5 @@ +--- +title: Prepend the Marginalia comment in SQL query +merge_request: 54015 +author: +type: changed diff --git a/config/initializers/0_marginalia.rb b/config/initializers/0_marginalia.rb index 5c6cf7752c4..952dd75886d 100644 --- a/config/initializers/0_marginalia.rb +++ b/config/initializers/0_marginalia.rb @@ -9,6 +9,11 @@ require 'marginalia' # Refer: https://github.com/basecamp/marginalia/blob/v1.8.0/lib/marginalia/railtie.rb#L67 ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(Gitlab::Marginalia::ActiveRecordInstrumentation) +# By default, PostgreSQL only tracks the first 1024 bytes of a SQL +# query. Prepending the comment allows us to trace the source of the +# query without having to increase the `track_activity_query_size` +# parameter. +Marginalia::Comment.prepend_comment = true unless Rails.env.test? # Some tests do string matching against raw SQL Marginalia::Comment.components = [:application, :controller, :action, :correlation_id, :jid, :job_class] # As mentioned in https://github.com/basecamp/marginalia/pull/93/files, diff --git a/doc/administration/auth/oidc.md b/doc/administration/auth/oidc.md index 1ddf75e7c1b..cde8944fadc 100644 --- a/doc/administration/auth/oidc.md +++ b/doc/administration/auth/oidc.md @@ -130,8 +130,7 @@ different providers with Omnibus GitLab. ### Google -See the [Google -documentation](https://developers.google.com/identity/protocols/oauth2/openid-connect) +See the [Google documentation](https://developers.google.com/identity/protocols/oauth2/openid-connect) for more details: ```ruby @@ -156,6 +155,44 @@ for more details: } ``` +### Microsoft Azure + +The OpenID Connect (OIDC) protocol for Microsoft Azure uses the [Microsoft identity platform (v2) endpoints](https://docs.microsoft.com/en-us/azure/active-directory/azuread-dev/azure-ad-endpoint-comparison). +To get started, sign in to the [Azure Portal](https://portal.azure.com). For your app, you'll need the +following information: + +- A tenant ID. You may already have one. For more information, review the + [Microsoft Azure Tenant](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-create-new-tenant) documentation. +- A client ID and a client secret. Follow the instructions in the + [Microsoft Quickstart Register an Application](https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app) documentation. +to obtain the tenant ID, client ID, and client secret for your app. + +Example Omnibus configuration block: + +```ruby + gitlab_rails['omniauth_providers'] = [ + { + 'name' => 'openid_connect', + 'label' => 'Azure OIDC', + 'args' => { + 'name' => 'openid_connect', + 'scope' => ['openid', 'profile', 'email'], + 'response_type' => 'code', + 'issuer' => 'https://login.microsoftonline.com/<YOUR-TENANT-ID>/v2.0', + 'client_auth_method' => 'query', + 'discovery' => true, + 'uid_field' => 'preferred_username', + 'client_options' => { + 'identifier' => '<YOUR APP CLIENT ID>', + 'secret' => '<YOUR APP CLIENT SECRET>', + 'redirect_uri' => 'https://gitlab.example.com/users/auth/openid_connect/callback' + } + } + } +``` + +Microsoft has documented how its platform works with [the OIDC protocol](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-protocols-oidc). + ## Troubleshooting If you're having trouble, here are some tips: diff --git a/doc/integration/azure.md b/doc/integration/azure.md index f22a94a01c7..c83ef650f54 100644 --- a/doc/integration/azure.md +++ b/doc/integration/azure.md @@ -6,6 +6,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Microsoft Azure OAuth2 OmniAuth Provider +NOTE: +Per Microsoft, this provider uses the [older Azure Active Directory v1.0 endpoint](https://docs.microsoft.com/en-us/azure/active-directory/azuread-dev/v1-protocols-oauth-code). +Microsoft documentation suggests that you should use the [OpenID Connect protocol to use the v2 endpoints](../administration/auth/oidc.md#microsoft-azure) for new projects. + To enable the Microsoft Azure OAuth2 OmniAuth provider, you must register your application with Azure. Azure generates a client ID and secret key for you to use. Sign in to the [Azure Portal](https://portal.azure.com), and follow the instructions in diff --git a/doc/user/admin_area/settings/visibility_and_access_controls.md b/doc/user/admin_area/settings/visibility_and_access_controls.md index f8e9be9b628..0fcdbf3ca90 100644 --- a/doc/user/admin_area/settings/visibility_and_access_controls.md +++ b/doc/user/admin_area/settings/visibility_and_access_controls.md @@ -124,10 +124,10 @@ For more details on group visibility, see [Public access](../../../public_access ## Restricted visibility levels -To set the available visibility levels for projects, snippets, and selected pages: +To set the restricted visibility levels for projects, snippets, and selected pages: -1. Check the desired visibility levels. -1. Click **Save changes**. +1. Select the desired visibility levels to restrict. +1. Select **Save changes**. For more details on project visibility, see [Public access](../../../public_access/public_access.md). diff --git a/lib/banzai/filter/plantuml_filter.rb b/lib/banzai/filter/plantuml_filter.rb index 1a75cd14b11..37d4126c1ba 100644 --- a/lib/banzai/filter/plantuml_filter.rb +++ b/lib/banzai/filter/plantuml_filter.rb @@ -9,7 +9,7 @@ module Banzai # class PlantumlFilter < HTML::Pipeline::Filter def call - return doc unless doc.at('pre > code[lang="plantuml"]') && settings.plantuml_enabled + return doc unless settings.plantuml_enabled? && doc.at('pre > code[lang="plantuml"]') plantuml_setup diff --git a/spec/benchmarks/banzai_benchmark.rb b/spec/benchmarks/banzai_benchmark.rb index e489237a2f2..a87414ba512 100644 --- a/spec/benchmarks/banzai_benchmark.rb +++ b/spec/benchmarks/banzai_benchmark.rb @@ -1,114 +1,124 @@ # frozen_string_literal: true -if ENV.key?('BENCHMARK') - require 'spec_helper' - require 'erb' - require 'benchmark/ips' - - # This benchmarks some of the Banzai pipelines and filters. - # They are not definitive, but can be used by a developer to - # get a rough idea how the changing or addition of a new filter - # will effect performance. - # - # Run by: - # BENCHMARK=1 rspec spec/benchmarks/banzai_benchmark.rb - # or - # rake benchmark:banzai - # - RSpec.describe 'GitLab Markdown Benchmark', :aggregate_failures do - include MarkupHelper - - let_it_be(:feature) { MarkdownFeature.new } - let_it_be(:project) { feature.project } - let_it_be(:group) { feature.group } - let_it_be(:wiki) { feature.wiki } - let_it_be(:wiki_page) { feature.wiki_page } - let_it_be(:markdown_text) { feature.raw_markdown } - - let!(:render_context) { Banzai::RenderContext.new(project, current_user) } - - before do - stub_application_setting(asset_proxy_enabled: true) - stub_application_setting(asset_proxy_secret_key: 'shared-secret') - stub_application_setting(asset_proxy_url: 'https://assets.example.com') - stub_application_setting(asset_proxy_whitelist: %w(gitlab.com *.mydomain.com)) - - Banzai::Filter::AssetProxyFilter.initialize_settings - end +return unless ENV.key?('BENCHMARK') + +require 'spec_helper' +require 'erb' +require 'benchmark/ips' + +# This benchmarks some of the Banzai pipelines and filters. +# They are not definitive, but can be used by a developer to +# get a rough idea how the changing or addition of a new filter +# will effect performance. +# +# Run by: +# BENCHMARK=1 rspec spec/benchmarks/banzai_benchmark.rb +# or +# rake benchmark:banzai +# +# rubocop: disable RSpec/TopLevelDescribePath +RSpec.describe 'GitLab Markdown Benchmark', :aggregate_failures do + include MarkupHelper + + let_it_be(:feature) { MarkdownFeature.new } + let_it_be(:project) { feature.project } + let_it_be(:group) { feature.group } + let_it_be(:wiki) { feature.wiki } + let_it_be(:wiki_page) { feature.wiki_page } + let_it_be(:markdown_text) { feature.raw_markdown } + let_it_be(:grafana_integration) { create(:grafana_integration, project: project) } + let_it_be(:default_context) do + { + project: project, + current_user: current_user, + suggestions_filter_enabled: true + } + end - context 'pipelines' do - it 'benchmarks several pipelines' do - path = 'images/example.jpg' - gitaly_wiki_file = Gitlab::GitalyClient::WikiFile.new(path: path) - allow(wiki).to receive(:find_file).with(path).and_return(Gitlab::Git::WikiFile.new(gitaly_wiki_file)) - allow(wiki).to receive(:wiki_base_path) { '/namespace1/gitlabhq/wikis' } + let(:context) do + Banzai::Filter::AssetProxyFilter.transform_context(default_context) + end - puts "\n--> Benchmarking Full, Wiki, and Plain pipelines\n" + let!(:render_context) { Banzai::RenderContext.new(project, current_user) } - Benchmark.ips do |x| - x.config(time: 10, warmup: 2) + before do + stub_application_setting(asset_proxy_enabled: true) + stub_application_setting(asset_proxy_secret_key: 'shared-secret') + stub_application_setting(asset_proxy_url: 'https://assets.example.com') + stub_application_setting(asset_proxy_whitelist: %w(gitlab.com *.mydomain.com)) + stub_application_setting(plantuml_enabled: true, plantuml_url: 'http://localhost:8080') + stub_application_setting(kroki_enabled: true, kroki_url: 'http://localhost:8000') - x.report('Full pipeline') { markdown(markdown_text, { pipeline: :full }) } - x.report('Wiki pipeline') { markdown(markdown_text, { pipeline: :wiki, wiki: wiki, page_slug: wiki_page.slug }) } - x.report('Plain pipeline') { markdown(markdown_text, { pipeline: :plain_markdown }) } + Banzai::Filter::AssetProxyFilter.initialize_settings + end - x.compare! - end - end - end + context 'pipelines' do + it 'benchmarks several pipelines' do + path = 'images/example.jpg' + gitaly_wiki_file = Gitlab::GitalyClient::WikiFile.new(path: path) + allow(wiki).to receive(:find_file).with(path).and_return(Gitlab::Git::WikiFile.new(gitaly_wiki_file)) + allow(wiki).to receive(:wiki_base_path) { '/namespace1/gitlabhq/wikis' } - context 'filters' do - let(:context) do - tmp = { project: project, current_user: current_user, render_context: render_context } - Banzai::Filter::AssetProxyFilter.transform_context(tmp) - end + puts "\n--> Benchmarking Full, Wiki, and Plain pipelines\n" - it 'benchmarks all filters in the FullPipeline' do - benchmark_pipeline_filters(:full) - end + Benchmark.ips do |x| + x.config(time: 10, warmup: 2) + + x.report('Full pipeline') { Banzai::Pipeline::FullPipeline.call(markdown_text, context) } + x.report('Wiki pipeline') { Banzai::Pipeline::WikiPipeline.call(markdown_text, context.merge(wiki: wiki, page_slug: wiki_page.slug)) } + x.report('Plain pipeline') { Banzai::Pipeline::PlainMarkdownPipeline.call(markdown_text, context) } - it 'benchmarks all filters in the PlainMarkdownPipeline' do - benchmark_pipeline_filters(:plain_markdown) + x.compare! end end + end - # build up the source text for each filter - def build_filter_text(pipeline, initial_text) - filter_source = {} - input_text = initial_text + context 'filters' do + it 'benchmarks all filters in the FullPipeline' do + benchmark_pipeline_filters(:full) + end - pipeline.filters.each do |filter_klass| - filter_source[filter_klass] = input_text + it 'benchmarks all filters in the PlainMarkdownPipeline' do + benchmark_pipeline_filters(:plain_markdown) + end + end - output = filter_klass.call(input_text, context) - input_text = output - end + # build up the source text for each filter + def build_filter_text(pipeline, initial_text) + filter_source = {} + input_text = initial_text - filter_source + pipeline.filters.each do |filter_klass| + filter_source[filter_klass] = input_text + + output = filter_klass.call(input_text, context) + input_text = output end - def benchmark_pipeline_filters(pipeline_type) - pipeline = Banzai::Pipeline[pipeline_type] - filter_source = build_filter_text(pipeline, markdown_text) + filter_source + end - puts "\n--> Benchmarking #{pipeline.name.demodulize} filters\n" + def benchmark_pipeline_filters(pipeline_type) + pipeline = Banzai::Pipeline[pipeline_type] + filter_source = build_filter_text(pipeline, markdown_text) - Benchmark.ips do |x| - x.config(time: 10, warmup: 2) + puts "\n--> Benchmarking #{pipeline.name.demodulize} filters\n" - pipeline.filters.each do |filter_klass| - label = filter_klass.name.demodulize.delete_suffix('Filter').truncate(20) + Benchmark.ips do |x| + x.config(time: 10, warmup: 2) - x.report(label) { filter_klass.call(filter_source[filter_klass], context) } - end + pipeline.filters.each do |filter_klass| + label = filter_klass.name.demodulize.delete_suffix('Filter').truncate(20) - x.compare! + x.report(label) { filter_klass.call(filter_source[filter_klass], context) } end - end - # Fake a `current_user` helper - def current_user - feature.user + x.compare! end end + + # Fake a `current_user` helper + def current_user + feature.user + end end diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb index 151ef76e884..8e28f89f49e 100644 --- a/spec/features/markdown/markdown_spec.rb +++ b/spec/features/markdown/markdown_spec.rb @@ -206,6 +206,9 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do # `markdown` helper expects a `@project` and `@group` variable @project = @feat.project @group = @feat.group + + stub_application_setting(plantuml_enabled: true, plantuml_url: 'http://localhost:8080') + stub_application_setting(kroki_enabled: true, kroki_url: 'http://localhost:8000') end let(:project) { @feat.project } # Shadow this so matchers can use it @@ -265,6 +268,18 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do aggregate_failures 'ColorFilter' do expect(doc).to parse_colors end + + aggregate_failures 'MermaidFilter' do + expect(doc).to parse_mermaid + end + + aggregate_failures 'PlantumlFilter' do + expect(doc).to parse_plantuml + end + + aggregate_failures 'KrokiFilter' do + expect(doc).to parse_kroki + end end end @@ -338,6 +353,18 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do aggregate_failures 'ColorFilter' do expect(doc).to parse_colors end + + aggregate_failures 'MermaidFilter' do + expect(doc).to parse_mermaid + end + + aggregate_failures 'PlantumlFilter' do + expect(doc).to parse_plantuml + end + + aggregate_failures 'KrokiFilter' do + expect(doc).to parse_kroki + end end end diff --git a/spec/features/merge_request/user_sees_merge_widget_spec.rb b/spec/features/merge_request/user_sees_merge_widget_spec.rb index 34689c084f2..0854a8b9fb7 100644 --- a/spec/features/merge_request/user_sees_merge_widget_spec.rb +++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb @@ -388,7 +388,7 @@ RSpec.describe 'Merge request > User sees merge widget', :js do click_button 'Merge' page.within('.mr-widget-body') do - expect(page).to have_content('Conflicts detected during merge') + expect(page).to have_content('An error occurred while merging') end end end diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb index 30ae58dbf9e..100d17cc16e 100644 --- a/spec/fixtures/markdown.md.erb +++ b/spec/fixtures/markdown.md.erb @@ -358,3 +358,17 @@ For details see the [Mermaid official page][mermaid]. [mermaid]: https://mermaidjs.github.io/ "Mermaid website" +### PLantUML + +```plantuml +Bob -> Sara : Hello +``` + +### Kroki + +```nomnoml +[Pirate|eyeCount: Int|raid();pillage()| + [beard]--[parrot] + [beard]-:>[foul mouth] +] +``` diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb index a4c70ac0266..611f12c8146 100644 --- a/spec/services/merge_requests/merge_service_spec.rb +++ b/spec/services/merge_requests/merge_service_spec.rb @@ -310,12 +310,12 @@ RSpec.describe MergeRequests::MergeService do it 'logs and saves error if there is an exception' do error_message = 'error message' - allow(service).to receive(:repository).and_raise('error message') + allow(service).to receive(:repository).and_raise(error_message) allow(service).to receive(:execute_hooks) service.execute(merge_request) - expect(merge_request.merge_error).to include('Something went wrong during merge') + expect(merge_request.merge_error).to eq(described_class::GENERIC_ERROR_MESSAGE) expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(error_message)) end @@ -343,9 +343,7 @@ RSpec.describe MergeRequests::MergeService do expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(error_message)) end - it 'logs and saves error if there is a merge conflict' do - error_message = 'Conflicts detected during merge' - + it 'logs and saves error if commit is not created' do allow_any_instance_of(Repository).to receive(:merge).and_return(false) allow(service).to receive(:execute_hooks) @@ -353,8 +351,8 @@ RSpec.describe MergeRequests::MergeService do expect(merge_request).to be_open expect(merge_request.merge_commit_sha).to be_nil - expect(merge_request.merge_error).to include(error_message) - expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(error_message)) + expect(merge_request.merge_error).to include(described_class::GENERIC_ERROR_MESSAGE) + expect(Gitlab::AppLogger).to have_received(:error).with(a_string_matching(described_class::GENERIC_ERROR_MESSAGE)) end context 'when squashing is required' do diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb index 6e75fa58700..47cffad8c41 100644 --- a/spec/support/matchers/markdown_matchers.rb +++ b/spec/support/matchers/markdown_matchers.rb @@ -246,6 +246,33 @@ module MarkdownMatchers end end end + + # MermaidFilter + matcher :parse_mermaid do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('code.js-render-mermaid') + end + end + + # PLantumlFilter + matcher :parse_plantuml do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_link(href: 'http://localhost:8080/png/U9npoazIqBLJ24uiIbImKl18pSd9vm80EtS5lW00') + end + end + + # KrokiFilter + matcher :parse_kroki do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_link(href: 'http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjligUAMhEd0g==') + end + end end # Monkeypatch the matcher DSL so that we can reduce some noisy duplication for |