diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue index 69a732a8e6e..94c37219260 100644 --- a/app/assets/javascripts/work_items/components/work_item_detail.vue +++ b/app/assets/javascripts/work_items/components/work_item_detail.vue @@ -320,18 +320,18 @@ export default { :can-update="canUpdate" @error="error = $event" /> + - Introduced in GitLab 15.4 [with a flag](../administration/feature_flags.md) named `markdown_dollar_math`. Disabled by default. + +[View this topic in GitLab](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/user/markdown.md#latex-compatible-fencing). + +FLAG: +On self-managed GitLab, by default this feature is not available. To make it available, +ask an administrator to [enable the feature flag](../administration/feature_flags.md) named `markdown_dollar_math`. +On GitLab.com, this feature is available. +The feature is not ready for production use. + +Math written between dollar signs (`$...$`) is rendered +inline with the text. Math written between double dollar signs (`$$...$$`) is rendered +on a separate line: + +````markdown +This math is inline: $a^2+b^2=c^2$. + +This math is on a separate line: $$a^2+b^2=c^2$$ + +This math is on a separate line: + +$$ +a^2+b^2=c^2 +$$ +```` + + + ### Task lists diff --git a/doc/user/tasks.md b/doc/user/tasks.md index f1518482c9d..c33cc7e3aa3 100644 --- a/doc/user/tasks.md +++ b/doc/user/tasks.md @@ -99,6 +99,24 @@ To delete a task: 1. In the task window, in the options menu (**{ellipsis_v}**), select **Delete task**. 1. Select **OK**. +## Assign users to a task + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/334810) in GitLab 15.4. + +To show who is responsible for a task, you can assign users to it. + +Prerequisites: + +- You must have at least the Reporter role for the project. + +To change the assignee on a task: + +1. In the issue description, in the **Tasks** section, select the title of the task you want to edit. + The task window opens. +1. Next to **Assignees**, select **Add assignees**. +1. From the dropdown list, select the user(s) to add as an assignee. +1. Select any area outside the dropdown list. + ## Set task weight **(PREMIUM)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/362550) in GitLab 15.3. diff --git a/lib/api/ci/jobs.rb b/lib/api/ci/jobs.rb index 943cd370e44..6049993bf6f 100644 --- a/lib/api/ci/jobs.rb +++ b/lib/api/ci/jobs.rb @@ -142,7 +142,8 @@ module API reject_if_build_artifacts_size_refreshing!(build.project) - build.erase(erased_by: current_user) + ::Ci::BuildEraseService.new(build, current_user).execute + present build, with: Entities::Ci::Job end diff --git a/lib/banzai/filter/math_filter.rb b/lib/banzai/filter/math_filter.rb index 0ac506776be..1ca4b2c89db 100644 --- a/lib/banzai/filter/math_filter.rb +++ b/lib/banzai/filter/math_filter.rb @@ -7,7 +7,7 @@ require 'uri' # - app/assets/javascripts/behaviors/markdown/nodes/code_block.js module Banzai module Filter - # HTML filter that adds class="code math" and removes the dollar sign in $`2+2`$. + # HTML filter that implements our math syntax, adding class="code math" # class MathFilter < HTML::Pipeline::Filter CSS_MATH = 'pre.code.language-math' @@ -15,14 +15,42 @@ module Banzai CSS_CODE = 'code' XPATH_CODE = Gitlab::Utils::Nokogiri.css_to_xpath(CSS_CODE).freeze + # These are based on the Pandoc heuristics, + # https://pandoc.org/MANUAL.html#extension-tex_math_dollars + # Note: at this time, using a dollar sign literal, `\$` inside + # a math statement does not work correctly. + # Corresponds to the "$...$" syntax + DOLLAR_INLINE_PATTERN = %r{ + (?\$(?(?:\S[^$\n]*?\S|[^$\s]))\$)(?:[^\d]|$) + }x.freeze + + # Corresponds to the "$$...$$" syntax + DOLLAR_DISPLAY_INLINE_PATTERN = %r{ + (?\$\$\ *(?[^$\n]+?)\ *\$\$) + }x.freeze + + # Corresponds to the $$\n...\n$$ syntax + DOLLAR_DISPLAY_BLOCK_PATTERN = %r{ + ^(?\$\$\ *\n(?.*)\n\$\$\ *)$ + }x.freeze + + # Order dependent. Handle the `$$` syntax before the `$` syntax + DOLLAR_MATH_PIPELINE = [ + { pattern: DOLLAR_DISPLAY_INLINE_PATTERN, tag: :code, style: :display }, + { pattern: DOLLAR_DISPLAY_BLOCK_PATTERN, tag: :pre, style: :display }, + { pattern: DOLLAR_INLINE_PATTERN, tag: :code, style: :inline } + ].freeze + + # Do not recognize math inside these tags + IGNORED_ANCESTOR_TAGS = %w[pre code tt].to_set + # Attribute indicating inline or display math. STYLE_ATTRIBUTE = 'data-math-style' # Class used for tagging elements that should be rendered TAG_CLASS = 'js-render-math' - INLINE_CLASSES = "code math #{TAG_CLASS}" - + MATH_CLASSES = "code math #{TAG_CLASS}" DOLLAR_SIGN = '$' # Limit to how many nodes can be marked as math elements. @@ -31,8 +59,48 @@ module Banzai RENDER_NODES_LIMIT = 50 def call - nodes_count = 0 + @nodes_count = 0 + process_dollar_pipeline if Feature.enabled?(:markdown_dollar_math, group) + + process_dollar_backtick_inline + process_math_codeblock + + doc + end + + def process_dollar_pipeline + doc.xpath('descendant-or-self::text()').each do |node| + next if has_ancestor?(node, IGNORED_ANCESTOR_TAGS) + + node_html = node.to_html + next unless node_html.match?(DOLLAR_INLINE_PATTERN) || + node_html.match?(DOLLAR_DISPLAY_INLINE_PATTERN) || + node_html.match?(DOLLAR_DISPLAY_BLOCK_PATTERN) + + temp_doc = Nokogiri::HTML.fragment(node_html) + DOLLAR_MATH_PIPELINE.each do |pipeline| + temp_doc.xpath('child::text()').each do |temp_node| + html = temp_node.to_html + temp_node.content.scan(pipeline[:pattern]).each do |matched, math| + html.sub!(matched, math_html(tag: pipeline[:tag], style: pipeline[:style], math: math)) + + @nodes_count += 1 + break if @nodes_count >= RENDER_NODES_LIMIT + end + + temp_node.replace(html) + + break if @nodes_count >= RENDER_NODES_LIMIT + end + end + + node.replace(temp_doc) + end + end + + # Corresponds to the "$`...`$" syntax + def process_dollar_backtick_inline doc.xpath(XPATH_CODE).each do |code| closing = code.next opening = code.previous @@ -44,22 +112,38 @@ module Banzai closing.content.first == DOLLAR_SIGN && opening.content.last == DOLLAR_SIGN - code[:class] = INLINE_CLASSES + code[:class] = MATH_CLASSES code[STYLE_ATTRIBUTE] = 'inline' closing.content = closing.content[1..] opening.content = opening.content[0..-2] - nodes_count += 1 - break if nodes_count >= RENDER_NODES_LIMIT + @nodes_count += 1 + break if @nodes_count >= RENDER_NODES_LIMIT end end + end + # corresponds to the "```math...```" syntax + def process_math_codeblock doc.xpath(XPATH_MATH).each do |el| el[STYLE_ATTRIBUTE] = 'display' el[:class] += " #{TAG_CLASS}" end + end - doc + private + + def math_html(tag:, math:, style:) + case tag + when :code + "#{math}" + when :pre + "#{math}" + end + end + + def group + context[:group] || context[:project]&.group end end end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index fc075f34de2..f448e253595 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -6910,6 +6910,9 @@ msgstr "" msgid "Browse templates" msgstr "" +msgid "Build cannot be erased" +msgstr "" + msgid "BuildArtifacts|An error occurred while fetching the artifacts" msgstr "" @@ -34544,9 +34547,15 @@ msgstr "" msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run" msgstr "" +msgid "ScanExecutionPolicy|%{thenLabelStart}Then%{thenLabelEnd} Require a %{scan} scan to run with site profile %{siteProfile} and scanner profile %{scannerProfile}" +msgstr "" + msgid "ScanExecutionPolicy|A pipeline is run" msgstr "" +msgid "ScanExecutionPolicy|Scanner profile" +msgstr "" + msgid "ScanExecutionPolicy|Schedule" msgstr "" @@ -34556,6 +34565,15 @@ msgstr "" msgid "ScanExecutionPolicy|Select branches" msgstr "" +msgid "ScanExecutionPolicy|Select scanner profile" +msgstr "" + +msgid "ScanExecutionPolicy|Select site profile" +msgstr "" + +msgid "ScanExecutionPolicy|Site profile" +msgstr "" + msgid "ScanExecutionPolicy|branch" msgstr "" diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb index 9eff02a8c1b..08f9b8eda13 100644 --- a/spec/features/markdown/markdown_spec.rb +++ b/spec/features/markdown/markdown_spec.rb @@ -263,6 +263,10 @@ RSpec.describe 'GitLab Markdown', :aggregate_failures do expect(doc).to parse_task_lists end + aggregate_failures 'MathFilter' do + expect(doc).to parse_math + end + aggregate_failures 'InlineDiffFilter' do expect(doc).to parse_inline_diffs end diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb index 18cd63b7bcb..14885813d93 100644 --- a/spec/fixtures/markdown.md.erb +++ b/spec/fixtures/markdown.md.erb @@ -283,7 +283,23 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e - [x] Complete sub-task 1 - [X] Complete task 2 -#### Gollum Tags +### Math + +- Dollar math: $a^2 + b^2 = c^2$ +- Dollar math and snippet reference: $d^2 + e^2 = f^2$ and <%= snippet.to_reference %> +- Dollar math and snippet in another project: <%= xsnippet.to_reference(project) %> and $g^2 + h^2 = i^2$ +- Not dollar math: $20,000 and $30,000 +- Dollar-backtick math: $`j^2 + k^2 = l^2`$ +- Dollar display math: $$m^2 + n^2 = o^2$$ +- Dollar display math and snippet reference: $$p^2 + q^2 = r^2$$ and <%= snippet.to_reference %> +- Dollar math and snippet in another project: <%= xsnippet.to_reference(project) %> and $$s^2 + t^2 = u^2$$ +- Display math using a block + + ```math + v^2 + w^2 = x^2 + ``` + +### Gollum Tags - [[linked-resource]] - [[link-text|linked-resource]] @@ -326,15 +342,15 @@ However the wrapping tags cannot be mixed as such: ### Colors -`#F00` -`#F00A` -`#FF0000` -`#FF0000AA` -`RGB(0,255,0)` -`RGB(0%,100%,0%)` -`RGBA(0,255,0,0.7)` -`HSL(540,70%,50%)` -`HSLA(540,70%,50%,0.7)` +`#F00` +`#F00A` +`#FF0000` +`#FF0000AA` +`RGB(0,255,0)` +`RGB(0%,100%,0%)` +`RGBA(0,255,0,0.7)` +`HSL(540,70%,50%)` +`HSLA(540,70%,50%,0.7)` ### Mermaid diff --git a/spec/frontend/work_items/components/work_item_detail_spec.js b/spec/frontend/work_items/components/work_item_detail_spec.js index 51467656908..49f7957440d 100644 --- a/spec/frontend/work_items/components/work_item_detail_spec.js +++ b/spec/frontend/work_items/components/work_item_detail_spec.js @@ -403,8 +403,8 @@ describe('WorkItemDetail component', () => { }); }); - describe('when work_items_mvc_2 feature flag is enabled', () => { - it('renders assignees component when assignees widget is returned from the API', async () => { + describe('assignees widget', () => { + it('renders assignees component when widget is returned from the API', async () => { createComponent({ workItemsMvc2Enabled: true, }); @@ -413,7 +413,7 @@ describe('WorkItemDetail component', () => { expect(findWorkItemAssignees().exists()).toBe(true); }); - it('does not render assignees component when assignees widget is not returned from the API', async () => { + it('does not render assignees component when widget is not returned from the API', async () => { createComponent({ workItemsMvc2Enabled: true, handler: jest @@ -426,13 +426,6 @@ describe('WorkItemDetail component', () => { }); }); - it('does not render assignees component when assignees feature flag is disabled', async () => { - createComponent(); - await waitForPromises(); - - expect(findWorkItemAssignees().exists()).toBe(false); - }); - describe('labels widget', () => { it.each` description | includeWidgets | exists diff --git a/spec/lib/banzai/filter/math_filter_spec.rb b/spec/lib/banzai/filter/math_filter_spec.rb index 128f8532d39..dd116eb1109 100644 --- a/spec/lib/banzai/filter/math_filter_spec.rb +++ b/spec/lib/banzai/filter/math_filter_spec.rb @@ -3,128 +3,179 @@ require 'spec_helper' RSpec.describe Banzai::Filter::MathFilter do + using RSpec::Parameterized::TableSyntax include FilterSpecHelper - it 'leaves regular inline code unchanged' do - input = "2+2" - doc = filter(input) + shared_examples 'inline math' do + it 'removes surrounding dollar signs and adds class code, math and js-render-math' do + doc = filter(text) + expected = result_template.gsub('', '') + expected.gsub!('', '') - expect(doc.to_s).to eq input + expect(doc.to_s).to eq expected + end end - it 'removes surrounding dollar signs and adds class code, math and js-render-math' do - doc = filter("$2+2$") + shared_examples 'display math' do + let_it_be(:template_prefix_with_pre) { '' } + let_it_be(:template_prefix_with_code) { '' } + let(:use_pre_tags) { false } - expect(doc.to_s).to eq '2+2' + it 'removes surrounding dollar signs and adds class code, math and js-render-math' do + doc = filter(text) + + template_prefix = use_pre_tags ? template_prefix_with_pre : template_prefix_with_code + template_suffix = "#{'' if use_pre_tags}" + expected = result_template.gsub('', template_prefix) + expected.gsub!('', template_suffix) + + expect(doc.to_s).to eq expected + end end - it 'only removes surrounding dollar signs' do - doc = filter("test $2+2$ test") - before = doc.xpath('descendant-or-self::text()[1]').first - after = doc.xpath('descendant-or-self::text()[3]').first + describe 'inline math using $...$ syntax' do + context 'with valid syntax' do + where(:text, :result_template) do + '$2+2$' | '2+2' + '$22+1$ and $22 + a^2$' | '22+1 and 22 + a^2' + '$22 and $2+2$' | '$22 and 2+2' + '$2+2$ $22 and flightjs/Flight$22 $2+2$' | '2+2 $22 and flightjs/Flight$22 2+2' + '$1/2$ <b>test</b>' | '1/2 <b>test</b>' + '$a!$' | 'a!' + '$x$' | 'x' + end - expect(before.to_s).to eq 'test ' - expect(after.to_s).to eq ' test' + with_them do + it_behaves_like 'inline math' + end + end + + it 'does not handle dollar literals properly' do + doc = filter('$20+30\$$') + expected = '20+30\\$' + + expect(doc.to_s).to eq expected + end end - it 'only removes surrounding single dollar sign' do - doc = filter("test $$2+2$$ test") - before = doc.xpath('descendant-or-self::text()[1]').first - after = doc.xpath('descendant-or-self::text()[3]').first + describe 'inline math using $`...`$ syntax' do + context 'with valid syntax' do + where(:text, :result_template) do + '$2+2$' | '2+2' + '$22+1$ and $22 + a^2$' | '22+1 and 22 + a^2' + '$22 and $2+2$' | '$22 and 2+2' + '$2+2$ $22 and flightjs/Flight$22 $2+2$' | '2+2 $22 and flightjs/Flight$22 2+2' + 'test $$2+2$$ test' | 'test $2+2$ test' + end - expect(before.to_s).to eq 'test $' - expect(after.to_s).to eq '$ test' + with_them do + it_behaves_like 'inline math' + end + end end - it 'adds data-math-style inline attribute to inline math' do - doc = filter('$2+2$') - code = doc.xpath('descendant-or-self::code').first + describe 'inline display math using $$...$$ syntax' do + context 'with valid syntax' do + where(:text, :result_template) do + '$$2+2$$' | '2+2' + '$$ 2+2 $$' | '2+2' + '$$22+1$$ and $$22 + a^2$$' | '22+1 and 22 + a^2' + '$22 and $$2+2$$' | '$22 and 2+2' + '$$2+2$$ $22 and flightjs/Flight$22 $$2+2$$' | '2+2 $22 and flightjs/Flight$22 2+2' + 'flightjs/Flight$22 and $$a^2 + b^2 = c^2$$' | 'flightjs/Flight$22 and a^2 + b^2 = c^2' + '$$a!$$' | 'a!' + '$$x$$' | 'x' + '$$20,000 and $$30,000' | '20,000 and30,000' + end - expect(code['data-math-style']).to eq 'inline' + with_them do + it_behaves_like 'display math' + end + end end - it 'adds class code and math to inline math' do - doc = filter('$2+2$') - code = doc.xpath('descendant-or-self::code').first + describe 'block display math using $$\n...\n$$ syntax' do + context 'with valid syntax' do + where(:text, :result_template) do + "$$\n2+2\n$$" | "2+2" + end - expect(code[:class]).to include("code") - expect(code[:class]).to include("math") + with_them do + it_behaves_like 'display math' do + let(:use_pre_tags) { true } + end + end + end end - it 'adds js-render-math class to inline math' do - doc = filter('$2+2$') - code = doc.xpath('descendant-or-self::code').first + describe 'display math using ```math...``` syntax' do + it 'adds data-math-style display attribute to display math' do + doc = filter('2+2') + pre = doc.xpath('descendant-or-self::pre').first - expect(code[:class]).to include("js-render-math") + expect(pre['data-math-style']).to eq 'display' + end + + it 'adds js-render-math class to display math' do + doc = filter('2+2') + pre = doc.xpath('descendant-or-self::pre').first + + expect(pre[:class]).to include("js-render-math") + end + + it 'ignores code blocks that are not math' do + input = '2+2' + doc = filter(input) + + expect(doc.to_s).to eq input + end + + it 'requires the pre to contain both code and math' do + input = '2+2' + doc = filter(input) + + expect(doc.to_s).to eq input + end + + it 'dollar signs around to display math' do + doc = filter('$2+2$') + before = doc.xpath('descendant-or-self::text()[1]').first + after = doc.xpath('descendant-or-self::text()[3]').first + + expect(before.to_s).to eq '$' + expect(after.to_s).to eq '$' + end end - # Cases with faulty syntax. Should be a no-op + describe 'unrecognized syntax' do + where(:text) do + [ + '2+2', + 'test $2+2 test', + 'test 2+2$ test', + '$2+2$', + '$20,000 and $30,000', + '$20,000 in $USD', + '$ a^2 $', + "test $$\n2+2\n$$", + "$\n$", + '$$$' + ] + end - it 'ignores cases with missing dolar sign at the end' do - input = "test $2+2 test" - doc = filter(input) - - expect(doc.to_s).to eq input + with_them do + it 'is ignored' do + expect(filter(text).to_s).to eq text + end + end end - it 'ignores cases with missing dolar sign at the beginning' do - input = "test 2+2$ test" - doc = filter(input) + it 'handles multiple styles in one text block' do + doc = filter('$2+2$ + $3+3$ + $$4+4$$') - expect(doc.to_s).to eq input - end - - it 'ignores dollar signs if it is not adjacent' do - input = 'We check strictly $2+2 and 2+2$ ' - doc = filter(input) - - expect(doc.to_s).to eq input - end - - it 'ignores dollar signs if they are inside another element' do - input = 'We check strictly $2+2$' - doc = filter(input) - - expect(doc.to_s).to eq input - end - - # Display math - - it 'adds data-math-style display attribute to display math' do - doc = filter('2+2') - pre = doc.xpath('descendant-or-self::pre').first - - expect(pre['data-math-style']).to eq 'display' - end - - it 'adds js-render-math class to display math' do - doc = filter('2+2') - pre = doc.xpath('descendant-or-self::pre').first - - expect(pre[:class]).to include("js-render-math") - end - - it 'ignores code blocks that are not math' do - input = '2+2' - doc = filter(input) - - expect(doc.to_s).to eq input - end - - it 'requires the pre to contain both code and math' do - input = '2+2' - doc = filter(input) - - expect(doc.to_s).to eq input - end - - it 'dollar signs around to display math' do - doc = filter('$2+2$') - before = doc.xpath('descendant-or-self::text()[1]').first - after = doc.xpath('descendant-or-self::text()[3]').first - - expect(before.to_s).to eq '$' - expect(after.to_s).to eq '$' + expect(doc.search('.js-render-math').count).to eq(3) + expect(doc.search('[data-math-style="inline"]').count).to eq(2) + expect(doc.search('[data-math-style="display"]').count).to eq(1) end it 'limits how many elements can be marked as math' do @@ -134,4 +185,11 @@ RSpec.describe Banzai::Filter::MathFilter do expect(doc.search('.js-render-math').count).to eq(2) end + + it 'does not recognize new syntax when feature flag is off' do + stub_feature_flags(markdown_dollar_math: false) + doc = filter('$1+2$') + + expect(doc.to_s).to eq '$1+2$' + end end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 3a656c4a42d..0c181854fce 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -1782,12 +1782,6 @@ RSpec.describe Ci::Build do context 'build is not erasable' do let!(:build) { create(:ci_build) } - describe '#erase' do - subject { build.erase } - - it { is_expected.to be false } - end - describe '#erasable?' do subject { build.erasable? } @@ -1796,71 +1790,9 @@ RSpec.describe Ci::Build do end context 'build is erasable' do - context 'logging erase' do - let!(:build) { create(:ci_build, :test_reports, :trace_artifact, :success, :artifacts) } - - it 'logs erased artifacts' do - expect(Gitlab::Ci::Artifacts::Logger) - .to receive(:log_deleted) - .with( - match_array(build.job_artifacts.to_a), - 'Ci::Build#erase' - ) - - build.erase - end - end - - context 'when project is undergoing stats refresh' do - let!(:build) { create(:ci_build, :test_reports, :trace_artifact, :success, :artifacts) } - - describe '#erase' do - before do - allow(build.project).to receive(:refreshing_build_artifacts_size?).and_return(true) - end - - it 'logs and continues with deleting the artifacts' do - expect(Gitlab::ProjectStatsRefreshConflictsLogger).to receive(:warn_artifact_deletion_during_stats_refresh).with( - method: 'Ci::Build#erase', - project_id: build.project.id - ) - - build.erase - - expect(build.job_artifacts.count).to eq(0) - end - end - end - context 'new artifacts' do let!(:build) { create(:ci_build, :test_reports, :trace_artifact, :success, :artifacts) } - describe '#erase' do - before do - build.erase(erased_by: erased_by) - end - - context 'erased by user' do - let!(:erased_by) { create(:user, username: 'eraser') } - - include_examples 'erasable' - - it 'records user who erased a build' do - expect(build.erased_by).to eq erased_by - end - end - - context 'erased by system' do - let(:erased_by) { nil } - - include_examples 'erasable' - - it 'does not set user who erased a build' do - expect(build.erased_by).to be_nil - end - end - end - describe '#erasable?' do subject { build.erasable? } @@ -1878,26 +1810,12 @@ RSpec.describe Ci::Build do context 'job has been erased' do before do - build.erase + build.update!(erased_at: 1.minute.ago) end it { is_expected.to be_truthy } end end - - context 'metadata and build trace are not available' do - let!(:build) { create(:ci_build, :success, :artifacts) } - - before do - build.erase_erasable_artifacts! - end - - describe '#erase' do - it 'does not raise error' do - expect { build.erase }.not_to raise_error - end - end - end end end end @@ -1913,14 +1831,7 @@ RSpec.describe Ci::Build do end end - it "erases erasable artifacts and logs them" do - expect(Gitlab::Ci::Artifacts::Logger) - .to receive(:log_deleted) - .with( - match_array(build.job_artifacts.erasable.to_a), - 'Ci::Build#erase_erasable_artifacts!' - ) - + it "erases erasable artifacts" do subject expect(build.job_artifacts.erasable).to be_empty @@ -3934,10 +3845,6 @@ RSpec.describe Ci::Build do context 'when artifacts of depended job has been erased' do let!(:pre_stage_job) { create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago) } - before do - pre_stage_job.erase - end - it { expect(job).not_to have_valid_build_dependencies } end end @@ -3958,10 +3865,6 @@ RSpec.describe Ci::Build do context 'when artifacts of depended job has been erased' do let!(:pre_stage_job) { create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago) } - before do - pre_stage_job.erase - end - it { expect(job).to have_valid_build_dependencies } end end diff --git a/spec/services/ci/build_erase_service_spec.rb b/spec/services/ci/build_erase_service_spec.rb new file mode 100644 index 00000000000..e750a163621 --- /dev/null +++ b/spec/services/ci/build_erase_service_spec.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::BuildEraseService do + let_it_be(:user) { user } + + let(:build) { create(:ci_build, :artifacts, :trace_artifact, artifacts_expire_at: 100.days.from_now) } + + subject(:service) { described_class.new(build, user) } + + describe '#execute' do + context 'when build is erasable' do + before do + allow(build).to receive(:erasable?).and_return(true) + end + + it 'is successful' do + result = service.execute + + expect(result).to be_success + end + + it 'erases artifacts' do + service.execute + + expect(build.artifacts_file).not_to be_present + expect(build.artifacts_metadata).not_to be_present + end + + it 'erases trace' do + service.execute + + expect(build.trace).not_to exist + end + + it 'records erasure detail' do + freeze_time do + service.execute + + expect(build.erased_by).to eq(user) + expect(build.erased_at).to eq(Time.current) + expect(build.artifacts_expire_at).to be_nil + end + end + + context 'when project is undergoing statistics refresh' do + before do + allow(build.project).to receive(:refreshing_build_artifacts_size?).and_return(true) + end + + it 'logs a warning' do + expect(Gitlab::ProjectStatsRefreshConflictsLogger) + .to receive(:warn_artifact_deletion_during_stats_refresh) + .with(method: 'Ci::BuildEraseService#execute', project_id: build.project_id) + + service.execute + end + end + end + + context 'when build is not erasable' do + before do + allow(build).to receive(:erasable?).and_return(false) + end + + it 'is not successful' do + result = service.execute + + expect(result).to be_error + expect(result.http_status).to eq(:unprocessable_entity) + end + + it 'does not erase artifacts' do + service.execute + + expect(build.artifacts_file).to be_present + expect(build.artifacts_metadata).to be_present + end + + it 'does not erase trace' do + service.execute + + expect(build.trace).to exist + end + end + end +end diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb index cabd60a22d1..e2e760b9812 100644 --- a/spec/services/ci/register_job_service_spec.rb +++ b/spec/services/ci/register_job_service_spec.rb @@ -571,10 +571,6 @@ module Ci context 'when artifacts of depended job has been erased' do let!(:pre_stage_job) { create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago) } - before do - pre_stage_job.erase - end - it_behaves_like 'not pick' end @@ -612,10 +608,6 @@ module Ci context 'when artifacts of depended job has been erased' do let!(:pre_stage_job) { create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0, erased_at: 1.minute.ago) } - before do - pre_stage_job.erase - end - it { expect(subject).to eq(pending_job) } end end diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb index 8bec3be2535..a80c269f915 100644 --- a/spec/support/matchers/markdown_matchers.rb +++ b/spec/support/matchers/markdown_matchers.rb @@ -134,7 +134,7 @@ module MarkdownMatchers set_default_markdown_messages match do |actual| - expect(actual).to have_selector('a.gfm.gfm-snippet', count: 5) + expect(actual).to have_selector('a.gfm.gfm-snippet', count: 9) end end @@ -196,6 +196,16 @@ module MarkdownMatchers end end + # MathFilter + matcher :parse_math do + set_default_markdown_messages + + match do |actual| + expect(actual).to have_selector('[data-math-style="inline"]', count: 4) + expect(actual).to have_selector('[data-math-style="display"]', count: 4) + end + end + # InlineDiffFilter matcher :parse_inline_diffs do set_default_markdown_messages diff --git a/workhorse/go.mod b/workhorse/go.mod index 2959cebdf0a..8fe31573dea 100644 --- a/workhorse/go.mod +++ b/workhorse/go.mod @@ -26,7 +26,7 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/smartystreets/goconvey v1.7.2 github.com/stretchr/testify v1.8.0 - gitlab.com/gitlab-org/gitaly/v15 v15.3.2 + gitlab.com/gitlab-org/gitaly/v15 v15.3.3 gitlab.com/gitlab-org/golang-archive-zip v0.1.1 gitlab.com/gitlab-org/labkit v1.16.0 gocloud.dev v0.25.0 diff --git a/workhorse/go.sum b/workhorse/go.sum index b61566a39c0..8034726f018 100644 --- a/workhorse/go.sum +++ b/workhorse/go.sum @@ -1126,8 +1126,8 @@ gitlab.com/gitlab-org/gitaly v1.68.0 h1:VlcJs1+PrhW7lqJUU7Fh1q8FMJujmbbivdfde/cw gitlab.com/gitlab-org/gitaly v1.68.0/go.mod h1:/pCsB918Zu5wFchZ9hLYin9WkJ2yQqdVNz0zlv5HbXg= gitlab.com/gitlab-org/gitaly/v14 v14.0.0-rc1/go.mod h1:4Cz8tOAyueSZX5o6gYum1F/unupaOclxqETPcg4ODvQ= gitlab.com/gitlab-org/gitaly/v14 v14.9.0-rc5.0.20220329111719-51da8bc17059/go.mod h1:uX1qhFKBDuPqATlpMcFL2dKDiX8D/tbUg7CYWx7OXt4= -gitlab.com/gitlab-org/gitaly/v15 v15.3.2 h1:H8NoBZil23mZ8Y31XdPWCNp27m2nfFGV+z/pQd5Rsq4= -gitlab.com/gitlab-org/gitaly/v15 v15.3.2/go.mod h1:DPO/d7DnhJ0TTcL6UZNJ6mcRHhdBPg+f/iBA+LEKEq0= +gitlab.com/gitlab-org/gitaly/v15 v15.3.3 h1:BAlskWIiPSBtFrsIZXABaBR6mRCEWyvXoCvnQzKH7xk= +gitlab.com/gitlab-org/gitaly/v15 v15.3.3/go.mod h1:DPO/d7DnhJ0TTcL6UZNJ6mcRHhdBPg+f/iBA+LEKEq0= gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20201117050822-3f9890ef73dc/go.mod h1:5QSTbpAHY2v0iIH5uHh2KA9w7sPUqPmnLjDApI/sv1U= gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20210720163109-50da611814d2/go.mod h1:QWDYBwuy24qGMandtCngLRPzFgnGPg6LSNoJWPKmJMc= gitlab.com/gitlab-org/gitlab-shell/v14 v14.10.0/go.mod h1:ynuwa2oLLQSOs6Ss6RpLDkAQuFUNreI3NdEOJxfh1ac=
#{math}
2+2
') + expected.gsub!('
' } + let_it_be(:template_prefix_with_code) { '' } + let(:use_pre_tags) { false } - expect(doc.to_s).to eq '2+2' + it 'removes surrounding dollar signs and adds class code, math and js-render-math' do + doc = filter(text) + + template_prefix = use_pre_tags ? template_prefix_with_pre : template_prefix_with_code + template_suffix = "#{'
' } + let(:use_pre_tags) { false } - expect(doc.to_s).to eq '2+2' + it 'removes surrounding dollar signs and adds class code, math and js-render-math' do + doc = filter(text) + + template_prefix = use_pre_tags ? template_prefix_with_pre : template_prefix_with_code + template_suffix = "
20+30\\
22+1
22 + a^2
We check strictly $2+2 and 2+2$
We check strictly $2+2$