diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb index 855965ca6e186c28edf81bb724c3ee850de6bd58..5eddedec7097e3df361376485fb285bfbe4825a9 100644 --- a/app/controllers/projects/badges_controller.rb +++ b/app/controllers/projects/badges_controller.rb @@ -30,6 +30,13 @@ def coverage render_badge coverage_report end + def release + latest_release = Gitlab::Badge::Release::Latest_Release + .new(project, params[:ref]) + + render_badge latest_release + end + private def badge_layout diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index f76278a12a4100023f3741663492d86173a912c4..fe06f899ea8a81345baab7b96fcc13132aef9da9 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -144,7 +144,8 @@ def define_badges_variables @ref = params[:ref] || @project.default_branch || 'master' @badges = [Gitlab::Badge::Pipeline::Status, - Gitlab::Badge::Coverage::Report] + Gitlab::Badge::Coverage::Report, + Gitlab::Badge::Release::Latest_Release] @badges.map! do |badge| badge.new(@project, @ref).metadata diff --git a/changelogs/unreleased/18098-add-release-badge.yml b/changelogs/unreleased/18098-add-release-badge.yml new file mode 100644 index 0000000000000000000000000000000000000000..033a21972b9023a54adf9b01e7cbb1d732b01bcf --- /dev/null +++ b/changelogs/unreleased/18098-add-release-badge.yml @@ -0,0 +1,5 @@ +--- +title: Added "Latest Release" badge, which shows the tag and links to release +merge_request: 18098 +author: Jason D'Amour @jason.damour +type: added diff --git a/config/routes/project.rb b/config/routes/project.rb index 2e61bc8175e4a837b65eb2e9197956b41167171d..58e4de4dd26d8e0b69033fdc0e7aa089cc9688d9 100644 --- a/config/routes/project.rb +++ b/config/routes/project.rb @@ -518,8 +518,9 @@ collection do scope '*ref', constraints: { ref: Gitlab::PathRegex.git_reference_regex } do constraints format: /svg/ do - get :pipeline # rubocop:todo Cop/PutProjectRoutesUnderScope - get :coverage # rubocop:todo Cop/PutProjectRoutesUnderScope + get :pipeline + get :coverage + get :release end end end diff --git a/lib/gitlab/badge/release/latest_release.rb b/lib/gitlab/badge/release/latest_release.rb new file mode 100644 index 0000000000000000000000000000000000000000..066bfb26b82db773120f74d974aaeb2bd2fc7d26 --- /dev/null +++ b/lib/gitlab/badge/release/latest_release.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Gitlab + module Badge + module Release + class Latest_Release < Badge::Base + attr_reader :project, :ref + + def initialize(project, ref) + @project = project + @ref = ref + end + + def entity + 'latest release' + end + + def release + @release = @project.releases.last.tag || 'none' + end + + def metadata + @metadata ||= Release::Metadata.new(self) + end + + def template + @template ||= Release::Template.new(self) + end + end + end + end +end diff --git a/lib/gitlab/badge/release/metadata.rb b/lib/gitlab/badge/release/metadata.rb new file mode 100644 index 0000000000000000000000000000000000000000..5cba7272e304636e77a42616081afa003bf1fff8 --- /dev/null +++ b/lib/gitlab/badge/release/metadata.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Gitlab + module Badge + module Release + class Metadata < Badge::Metadata + def initialize(badge) + @project = badge.project + @ref = badge.ref + end + + def title + 'latest release' + end + + def image_url + release_project_badges_url(@project, @ref, format: :svg) + end + + def link_url + project_release_url(@project, id: @ref) + end + end + end + end +end diff --git a/lib/gitlab/badge/release/template.rb b/lib/gitlab/badge/release/template.rb new file mode 100644 index 0000000000000000000000000000000000000000..d69affc36b25b951cb8912eb416b7d092c9653df --- /dev/null +++ b/lib/gitlab/badge/release/template.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Gitlab + module Badge + module Release + ## + # Class that represents a release badge template. + # + # Template object will be passed to badge.svg.erb template. + # + class Template < Badge::Template + STATUS_COLOR = { + latest: '#3076af', + none: '#e05d44' + }.freeze + + def initialize(badge) + @entity = badge.entity + @release = badge.release + end + + def key_text + @entity.to_s + end + + def value_text + @status.to_s + end + + def key_width + 62 + end + + def value_width + 54 + end + + def value_color + STATUS_COLOR[@status.to_sym] || STATUS_COLOR[:latest] + end + end + end + end +end diff --git a/spec/lib/gitlab/badge/release/latest_release_spec.rb b/spec/lib/gitlab/badge/release/latest_release_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..50795e0fdc93c5ac3d28bcda11c4ac10beeebcd4 --- /dev/null +++ b/spec/lib/gitlab/badge/release/latest_release_spec.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Badge::Release::Latest_Release do + let(:project) { create(:project, :repository) } + let(:job_name) { nil } + + let(:badge) do + described_class.new(project, 'master', job_name) + end + + describe '#entity' do + it 'describes a release' do + expect(badge.entity).to eq 'release' + end + end + + describe '#metadata' do + it 'returns correct metadata' do + expect(badge.metadata.image_url).to include 'release.svg' + end + end + + describe '#template' do + it 'returns correct template' do + expect(badge.template.key_text).to eq 'release' + end + end + +# shared_examples 'unknown latest release' do +# context 'particular job specified' do +# let(:job_name) { '' } +# +# it 'returns nil' do +# expect(badge.status).to be_nil +# end +# end +# +# context 'particular job not specified' do +# let(:job_name) { nil } +# +# it 'returns nil' do +# expect(badge.status).to be_nil +# end +# end +# end +# +# context 'when latest successful pipeline exists' do +# before do +# create_pipeline do |pipeline| +# create(:ci_build, :success, pipeline: pipeline, name: 'first', release: 40) +# create(:ci_build, :success, pipeline: pipeline, release: 60) +# end +# +# create_pipeline do |pipeline| +# create(:ci_build, :failed, pipeline: pipeline, release: 10) +# end +# end +# +# context 'when particular job specified' do +# let(:job_name) { 'first' } +# +# it 'returns release for the particular job' do +# expect(badge.status).to eq 40 +# end +# end +# +# context 'when particular job not specified' do +# let(:job_name) { '' } +# +# it 'returns arithemetic mean for the pipeline' do +# expect(badge.status).to eq 50 +# end +# end +# end +# +# context 'when only failed pipeline exists' do +# before do +# create_pipeline do |pipeline| +# create(:ci_build, :failed, pipeline: pipeline, release: 10) +# end +# end +# +# it_behaves_like 'unknown release report' +# +# context 'particular job specified' do +# let(:job_name) { 'nonexistent' } +# +# it 'retruns nil' do +# expect(badge.status).to be_nil +# end +# end +# end +# +# context 'pipeline does not exist' do +# it_behaves_like 'unknown release report' +# end +# +# def create_pipeline +# opts = { project: project, sha: project.commit.id, ref: 'master' } +# +# create(:ci_pipeline, opts).tap do |pipeline| +# yield pipeline +# pipeline.update_status +# end +# end +# end diff --git a/spec/lib/gitlab/badge/release/metadata_spec.rb b/spec/lib/gitlab/badge/release/metadata_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..1554c22982b78fa03d46237ae14fa57e22419f26 --- /dev/null +++ b/spec/lib/gitlab/badge/release/metadata_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'lib/gitlab/badge/shared/metadata' + +RSpec.describe Gitlab::Badge::Release::Metadata do + let(:badge) do + double(project: create(:project), ref: 'feature', job: 'test') + end + + let(:metadata) { described_class.new(badge) } + + it_behaves_like 'badge metadata' + + describe '#title' do + it 'returns latest release title' do + expect(metadata.title).to eq 'latest release' + end + end + + describe '#image_url' do + it 'returns valid url' do + expect(metadata.image_url).to include 'badges/feature/release.svg' + end + end + + describe '#link_url' do + it 'returns valid link' do + expect(metadata.link_url).to include 'releases' + end + end +end diff --git a/spec/lib/gitlab/badge/release/template_spec.rb b/spec/lib/gitlab/badge/release/template_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..1e8f4302ce2a8c7aa291116d831f0408456b0de1 --- /dev/null +++ b/spec/lib/gitlab/badge/release/template_spec.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Badge::Release::Template do + let(:badge) { double(entity: 'latest release', status: "v1.2.3") } + let(:template) { described_class.new(badge) } + + describe '#key_text' do + it 'is always says latest release' do + expect(template.key_text).to eq 'latest release' + end + end + + describe '#value_text' do + context 'when a release exists' do + it 'returns the tag of the release' do + expect(template.value_text).to eq 'v1.2.3' + end + end + + context 'no releases exist' do + before do + allow(badge).to receive(:release).and_return("none") + end + + it 'returns string that latest release is none' do + expect(template.value_text).to eq 'none' + end + end + end + + describe '#key_width' do + it 'has a fixed key width' do + expect(template.key_width).to eq 62 + end + end + + describe '#value_width' do + it 'has a fixed value width' do + expect(template.value_width).to eq 54 + end + end + + describe '#key_color' do + it 'always has the same color' do + expect(template.key_color).to eq '#555' + end + end + + describe '#value_color' do + context 'when release exists' do + before do + allow(badge).to receive(:release).and_return("v1.2.3") + end + + it 'is blue' do + expect(template.value_color).to eq '#3076af' + end + end + + context 'when release does not exist' do + before do + allow(badge).to receive(:release).and_return("none") + end + + it 'is grey' do + expect(template.value_color).to eq '#e05d44' + end + end + + end +end