Commit d2a516f8 authored by Maxime Orefice's avatar Maxime Orefice Committed by Michael Kozono

Add codequality report worker

This commit generates a codequality report as a pipeline_artifact
after a pipeline completes. It will be used to display codequality
degradations on a MR diff.
parent c72c0acc
...@@ -250,6 +250,7 @@ module Ci ...@@ -250,6 +250,7 @@ module Ci
after_transition any => ::Ci::Pipeline.completed_statuses do |pipeline| after_transition any => ::Ci::Pipeline.completed_statuses do |pipeline|
pipeline.run_after_commit do pipeline.run_after_commit do
::Ci::PipelineArtifacts::CoverageReportWorker.perform_async(pipeline.id) ::Ci::PipelineArtifacts::CoverageReportWorker.perform_async(pipeline.id)
::Ci::PipelineArtifacts::CreateQualityReportWorker.perform_async(pipeline.id)
end end
end end
...@@ -1007,6 +1008,8 @@ module Ci ...@@ -1007,6 +1008,8 @@ module Ci
end end
def can_generate_codequality_reports? def can_generate_codequality_reports?
return false unless Feature.enabled?(:codequality_mr_diff, project)
has_reports?(Ci::JobArtifact.codequality_reports) has_reports?(Ci::JobArtifact.codequality_reports)
end end
......
...@@ -14,7 +14,8 @@ module Ci ...@@ -14,7 +14,8 @@ module Ci
EXPIRATION_DATE = 1.week.freeze EXPIRATION_DATE = 1.week.freeze
DEFAULT_FILE_NAMES = { DEFAULT_FILE_NAMES = {
code_coverage: 'code_coverage.json' code_coverage: 'code_coverage.json',
code_quality: 'code_quality.json'
}.freeze }.freeze
belongs_to :project, class_name: "Project", inverse_of: :pipeline_artifacts belongs_to :project, class_name: "Project", inverse_of: :pipeline_artifacts
......
# frozen_string_literal: true
module Ci
module PipelineArtifacts
class CreateQualityReportService
def execute(pipeline)
return unless pipeline.can_generate_codequality_reports?
return if pipeline.has_codequality_reports?
file = build_carrierwave_file(pipeline)
pipeline.pipeline_artifacts.create!(
project_id: pipeline.project_id,
file_type: :code_quality,
file_format: :raw,
size: file["tempfile"].size,
file: file,
expire_at: Ci::PipelineArtifact::EXPIRATION_DATE.from_now
)
end
private
def build_carrierwave_file(pipeline)
CarrierWaveStringFile.new_file(
file_content: pipeline.codequality_reports.to_json,
filename: Ci::PipelineArtifact::DEFAULT_FILE_NAMES.fetch(:code_quality),
content_type: 'application/json'
)
end
end
end
end
...@@ -1109,6 +1109,14 @@ ...@@ -1109,6 +1109,14 @@
:weight: 1 :weight: 1
:idempotent: true :idempotent: true
:tags: [] :tags: []
- :name: pipeline_background:ci_pipeline_artifacts_create_quality_report
:feature_category: :code_testing
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent: true
:tags: []
- :name: pipeline_background:ci_pipeline_success_unlock_artifacts - :name: pipeline_background:ci_pipeline_success_unlock_artifacts
:feature_category: :continuous_integration :feature_category: :continuous_integration
:has_external_dependencies: :has_external_dependencies:
......
# frozen_string_literal: true
module Ci
module PipelineArtifacts
class CreateQualityReportWorker
include ApplicationWorker
queue_namespace :pipeline_background
feature_category :code_testing
idempotent!
def perform(pipeline_id)
Ci::Pipeline.find_by_id(pipeline_id).try do |pipeline|
Ci::PipelineArtifacts::CreateQualityReportService.new.execute(pipeline)
end
end
end
end
end
...@@ -3522,7 +3522,19 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do ...@@ -3522,7 +3522,19 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do
context 'when pipeline status is success' do context 'when pipeline status is success' do
let(:pipeline) { create(:ci_pipeline, :success, project: project) } let(:pipeline) { create(:ci_pipeline, :success, project: project) }
it { expect(subject).to be_truthy } it 'can generate a codequality report' do
expect(subject).to be_truthy
end
context 'when feature is disabled' do
before do
stub_feature_flags(codequality_mr_diff: false)
end
it 'can not generate a codequality report' do
expect(subject).to be_falsey
end
end
end end
end end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ::Ci::PipelineArtifacts::CreateQualityReportService do
describe '#execute' do
subject(:pipeline_artifact) { described_class.new.execute(pipeline) }
context 'when pipeline has codequality reports' do
let(:project) { create(:project, :repository) }
describe 'pipeline completed status' do
using RSpec::Parameterized::TableSyntax
where(:status, :result) do
:success | 1
:failed | 1
:canceled | 1
:skipped | 1
end
with_them do
let(:pipeline) { create(:ci_pipeline, :with_codequality_reports, status: status, project: project) }
it 'creates a pipeline artifact' do
expect { pipeline_artifact }.to change(Ci::PipelineArtifact, :count).by(result)
end
it 'persists the default file name' do
expect(pipeline_artifact.file.filename).to eq('code_quality.json')
end
it 'sets expire_at to 1 week' do
freeze_time do
expect(pipeline_artifact.expire_at).to eq(1.week.from_now)
end
end
end
end
context 'when pipeline artifact has already been created' do
let(:pipeline) { create(:ci_pipeline, :with_codequality_reports, project: project) }
it 'does not persist the same artifact twice' do
2.times { described_class.new.execute(pipeline) }
expect(Ci::PipelineArtifact.count).to eq(1)
end
end
end
context 'when pipeline is not completed and codequality report does not exist' do
let(:pipeline) { create(:ci_pipeline, :running) }
it 'does not persist data' do
pipeline_artifact
expect(Ci::PipelineArtifact.count).to eq(0)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe ::Ci::PipelineArtifacts::CreateQualityReportWorker do
describe '#perform' do
subject { described_class.new.perform(pipeline_id) }
context 'when pipeline exists' do
let(:pipeline) { create(:ci_pipeline, :with_codequality_reports) }
let(:pipeline_id) { pipeline.id }
it 'calls pipeline codequality report service' do
expect_next_instance_of(::Ci::PipelineArtifacts::CreateQualityReportService) do |quality_report_service|
expect(quality_report_service).to receive(:execute)
end
subject
end
it_behaves_like 'an idempotent worker' do
let(:job_args) { pipeline_id }
it 'creates a pipeline artifact' do
expect { subject }.to change { pipeline.pipeline_artifacts.count }.by(1)
end
end
end
context 'when pipeline does not exist' do
let(:pipeline_id) { non_existing_record_id }
it 'does not call pipeline codequality report service' do
expect(Ci::PipelineArtifacts::CreateQualityReportService).not_to receive(:execute)
subject
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment