diff --git a/app/assets/javascripts/badges/constants.js b/app/assets/javascripts/badges/constants.js index 56b45abbe6bf07cecdba3283f57eb10ef43b86e8..e98df2e5971057c523a3e08f9ce40ae7081fa583 100644 --- a/app/assets/javascripts/badges/constants.js +++ b/app/assets/javascripts/badges/constants.js @@ -7,6 +7,7 @@ export const PLACEHOLDERS = [ 'project_id', 'default_branch', 'commit_sha', + 'latest_tag', ]; export const INITIAL_PAGE = 1; export const PAGE_SIZE = 10; diff --git a/app/models/badge.rb b/app/models/badge.rb index f4e719887ba058ecab54e4693000b032e3709212..2c07751a3ef1507015b030062e01a31e57e71ee6 100644 --- a/app/models/badge.rb +++ b/app/models/badge.rb @@ -12,7 +12,10 @@ class Badge < ApplicationRecord 'project_name' => :path, 'project_id' => :id, 'default_branch' => :default_branch, - 'commit_sha' => ->(project) { project.commit&.sha } + 'commit_sha' => ->(project) { project.commit&.sha }, + 'latest_tag' => ->(project) do + TagsFinder.new(project.repository, per_page: 1, sort: 'updated_desc').execute.first&.name if project.repository + end }.freeze # This regex is built dynamically using the keys from the PLACEHOLDER struct. diff --git a/doc/api/group_badges.md b/doc/api/group_badges.md index 52ed529682d9efaa44e67f0f62ef184956bfd002..53f6936665524c8071280e460cc289aa522f8cd9 100644 --- a/doc/api/group_badges.md +++ b/doc/api/group_badges.md @@ -21,7 +21,8 @@ DETAILS: - **%{project_name}**: replaced by the project name. - **%{project_id}**: replaced by the project ID. - **%{default_branch}**: replaced by the project default branch. -- **%{commit_sha}**: replaced by the last project's commit SHA. +- **%{commit_sha}**: replaced by the project's last commit SHA. +- **%{latest_tag}**: replaced by the project's last tag. diff --git a/doc/api/project_badges.md b/doc/api/project_badges.md index 9787decbda019e7569da370f400af420d8150d50..93f916d4fde5c0afbe248ac8e38fe6bfa9aca6af 100644 --- a/doc/api/project_badges.md +++ b/doc/api/project_badges.md @@ -21,7 +21,8 @@ DETAILS: - **%{project_name}**: Replaced by the project name. - **%{project_id}**: Replaced by the project ID. - **%{default_branch}**: Replaced by the project default branch. -- **%{commit_sha}**: Replaced by the last project's commit SHA. +- **%{commit_sha}**: Replaced by the project's last commit SHA. +- **%{latest_tag}**: Replaced by the project's last tag. diff --git a/doc/user/project/badges.md b/doc/user/project/badges.md index dd257732fe8e59d23f6f1e887ddb8d8ada01d8fc..f1646c9911ed7b1654a2973159482a9bb7c5ebce 100644 --- a/doc/user/project/badges.md +++ b/doc/user/project/badges.md @@ -301,6 +301,7 @@ The following placeholders are available: - `%{default_branch}`: Default branch name configured for a project's repository - `%{commit_sha}`: ID of the most recent commit to the default branch of a project's repository +- `%{latest_tag}`: Latest tag added to the project's repository NOTE: Placeholders allow badges to expose otherwise-private information, such as the diff --git a/spec/models/badge_spec.rb b/spec/models/badge_spec.rb index 79a716c2087ceb7e5eff4416386975c09c6fa614..188c5a78e09b0b57379799e47b16ec716ef321ca 100644 --- a/spec/models/badge_spec.rb +++ b/spec/models/badge_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Badge do - let(:placeholder_url) { 'http://www.example.com/%{project_path}/%{project_id}/%{project_name}/%{default_branch}/%{commit_sha}/%{project_title}' } + let(:placeholder_url) { 'http://www.example.com/%{project_path}/%{project_id}/%{project_name}/%{default_branch}/%{commit_sha}/%{project_title}/%{latest_tag}' } describe 'validations' do # Requires the let variable url_sym @@ -61,25 +61,48 @@ end shared_examples 'rendered_links' do - it 'uses the project information to populate the url placeholders' do - stub_project_commit_info(project) - - expect(badge.public_send("rendered_#{method}", project)).to eq "http://www.example.com/#{project.full_path}/#{project.id}/#{project.path}/master/whatever/#{project.title}" - end + context 'when the repository is not nil' do + let_it_be(:full_path) { project.full_path } + let_it_be(:id) { project.id } + let_it_be(:path) { project.path } + let_it_be(:title) { project.title } + let_it_be(:default_branch) { 'master' } + let_it_be(:tag) { 'v1.1.1' } + let_it_be(:commit_sha) { project.commit.sha } + + it 'uses the project information to populate the url placeholders' do + url = "http://www.example.com/#{full_path}/#{id}/#{path}/#{default_branch}/#{commit_sha}/#{title}/#{tag}" + expect(badge.public_send("rendered_#{method}", project)).to eq(url) + end - it 'returns the url if the project used is nil' do - expect(badge.public_send("rendered_#{method}", nil)).to eq placeholder_url + it 'returns the url if the project used is nil' do + expect(badge.public_send("rendered_#{method}", nil)).to eq placeholder_url + end end - def stub_project_commit_info(project) - allow(project).to receive(:commit).and_return(double('Commit', sha: 'whatever')) - allow(project).to receive(:default_branch).and_return('master') + context 'when the repository is nil' do + let_it_be(:full_path_empty_repo) { project_empty_repository.full_path } + let_it_be(:id_empty_repo) { project_empty_repository.id } + let_it_be(:path_empty_repo) { project_empty_repository.path } + let_it_be(:title_empty_repo) { project_empty_repository.title } + # Using constant values for the placeholders which won't be populated in the placeholder_url as there is no repo. + let_it_be(:default_branch_empty_repo) { '%{default_branch}' } + let_it_be(:tag_empty_repo) { '%{latest_tag}' } + let_it_be(:commit_sha_empty_repo) { '%{commit_sha}' } + + it 'populate the placeholders' do + url = "http://www.example.com/#{full_path_empty_repo}/#{id_empty_repo}/#{path_empty_repo}/" \ + "#{default_branch_empty_repo}/#{commit_sha_empty_repo}/#{title_empty_repo}/#{tag_empty_repo}" + + expect(badge.public_send("rendered_#{method}", project_empty_repository)).to eq(url) + end end end context 'methods' do let(:badge) { build(:badge, link_url: placeholder_url, image_url: placeholder_url) } - let!(:project) { create(:project) } + let_it_be(:project) { create(:project, :repository) } + let_it_be(:project_empty_repository) { create(:project, :empty_repo) } describe '#rendered_link_url' do let(:method) { :link_url }