Commit f5e0d0d8 authored by Lee Tickett's avatar Lee Tickett Committed by Thong Kuah

Show build status in tag list

parent 26087e16
......@@ -27,7 +27,7 @@ class Projects::BranchesController < Projects::ApplicationController
@refs_pipelines = @project.ci_pipelines.latest_successful_for_refs(@branches.map(&:name))
@merged_branch_names = repository.merged_branch_names(@branches.map(&:name))
@branch_pipeline_statuses = branch_pipeline_statuses
@branch_pipeline_statuses = Ci::CommitStatusesFinder.new(@project, repository, current_user, @branches).execute
# https://gitlab.com/gitlab-org/gitlab/-/issues/22851
Gitlab::GitalyClient.allow_n_plus_1_calls do
......@@ -197,15 +197,4 @@ class Projects::BranchesController < Projects::ApplicationController
confidential_issue_project
end
def branch_pipeline_statuses
latest_commits = @branches.map do |branch|
[branch.name, repository.commit(branch.dereferenced_target).sha]
end.to_h
latest_pipelines = project.ci_pipelines.latest_pipeline_per_commit(latest_commits.values)
latest_commits.transform_values do |commit_sha|
latest_pipelines[commit_sha]&.detailed_status(current_user)
end.compact
end
end
......@@ -24,6 +24,7 @@ class Projects::TagsController < Projects::ApplicationController
tag_names = @tags.map(&:name)
@tags_pipelines = @project.ci_pipelines.latest_successful_for_refs(tag_names)
@releases = project.releases.where(tag: tag_names)
@tag_pipeline_statuses = Ci::CommitStatusesFinder.new(@project, @repository, current_user, @tags).execute
respond_to do |format|
format.html
......
# frozen_string_literal: true
module Ci
class CommitStatusesFinder
include ::Gitlab::Utils::StrongMemoize
def initialize(project, repository, current_user, refs)
@project = project
@repository = repository
@current_user = current_user
@refs = refs
end
def execute
return [] unless Ability.allowed?(@current_user, :read_pipeline, @project)
commit_statuses
end
private
def latest_commits
strong_memoize(:latest_commits) do
refs.map do |ref|
[ref.name, @repository.commit(ref.dereferenced_target).sha]
end.to_h
end
end
def commit_statuses
latest_pipelines = project.ci_pipelines.latest_pipeline_per_commit(latest_commits.values)
latest_commits.transform_values do |commit_sha|
latest_pipelines[commit_sha]&.detailed_status(current_user)
end.compact
end
attr_reader :project, :repository, :current_user, :refs
end
end
- commit = @repository.commit(tag.dereferenced_target)
- release = @releases.find { |release| release.tag == tag.name }
- commit_status = @tag_pipeline_statuses[tag.name] unless @tag_pipeline_statuses.nil?
%li.flex-row.allow-wrap.js-tag-list
.row-main-content
......@@ -34,6 +35,12 @@
- if tag.has_signature?
= render partial: 'projects/commit/signature', object: tag.signature
- if commit_status
= render 'ci/status/icon', size: 24, status: commit_status, option_css_classes: 'gl-display-inline-flex gl-vertical-align-middle gl-mr-5'
- elsif @tag_pipeline_statuses && @tag_pipeline_statuses.any?
.gl-display-inline-flex.gl-vertical-align-middle.gl-mr-5
%svg.s24
= render 'projects/buttons/download', project: @project, ref: tag.name, pipeline: @tags_pipelines[tag.name]
- if can?(current_user, :admin_tag, @project)
......
---
title: Show build status in tag list
merge_request: 34307
author: Lee Tickett
type: added
......@@ -9,18 +9,75 @@ RSpec.describe Projects::TagsController do
let(:user) { create(:user) }
describe 'GET index' do
before do
get :index, params: { namespace_id: project.namespace.to_param, project_id: project }
end
subject { get :index, params: { namespace_id: project.namespace.to_param, project_id: project } }
it 'returns the tags for the page' do
subject
expect(assigns(:tags).map(&:name)).to include('v1.1.0', 'v1.0.0')
end
it 'returns releases matching those tags' do
subject
expect(assigns(:releases)).to include(release)
expect(assigns(:releases)).not_to include(invalid_release)
end
context '@tag_pipeline_status' do
context 'when no pipelines exist' do
it 'is empty' do
subject
expect(assigns(:tag_pipeline_statuses)).to be_empty
end
end
context 'when multiple tags exist' do
before do
create(:ci_pipeline,
project: project,
ref: 'v1.1.0',
sha: project.commit('v1.1.0').sha,
status: :running)
create(:ci_pipeline,
project: project,
ref: 'v1.0.0',
sha: project.commit('v1.0.0').sha,
status: :success)
end
it 'all relevant commit statuses are received' do
subject
expect(assigns(:tag_pipeline_statuses)['v1.1.0'].group).to eq("running")
expect(assigns(:tag_pipeline_statuses)['v1.0.0'].group).to eq("success")
end
end
context 'when a tag has multiple pipelines' do
before do
create(:ci_pipeline,
project: project,
ref: 'v1.0.0',
sha: project.commit('v1.0.0').sha,
status: :running,
created_at: 6.months.ago)
create(:ci_pipeline,
project: project,
ref: 'v1.0.0',
sha: project.commit('v1.0.0').sha,
status: :success,
created_at: 2.months.ago)
end
it 'chooses the latest to determine status' do
subject
expect(assigns(:tag_pipeline_statuses)['v1.0.0'].group).to eq("success")
end
end
end
end
describe 'GET show' do
......@@ -70,7 +127,8 @@ RSpec.describe Projects::TagsController do
end
let(:release_description) { nil }
let(:request) do
subject(:request) do
post(:create, params: {
namespace_id: project.namespace.to_param,
project_id: project,
......@@ -81,7 +139,7 @@ RSpec.describe Projects::TagsController do
end
it 'creates tag' do
request
subject
expect(response).to have_gitlab_http_status(:found)
expect(project.repository.find_tag('1.0')).to be_present
......@@ -92,7 +150,7 @@ RSpec.describe Projects::TagsController do
let(:release_description) { 'some release description' }
it 'creates tag and release' do
request
subject
expect(response).to have_gitlab_http_status(:found)
expect(project.repository.find_tag('1.0')).to be_present
......@@ -118,7 +176,7 @@ RSpec.describe Projects::TagsController do
expect(service).to receive(:execute).and_call_original
end
request
subject
aggregate_failures do
expect(response).to have_gitlab_http_status(:found)
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::CommitStatusesFinder, '#execute' do
let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:release) { create(:release, project: project) }
let_it_be(:user) { create(:user) }
context 'tag refs' do
let_it_be(:tags) { TagsFinder.new(project.repository, {}).execute }
let(:subject) { described_class.new(project, project.repository, user, tags).execute }
context 'no pipelines' do
it 'returns nil' do
expect(subject).to be_blank
end
end
context 'when multiple tags exist' do
before do
create(:ci_pipeline,
project: project,
ref: 'v1.1.0',
sha: project.commit('v1.1.0').sha,
status: :running)
create(:ci_pipeline,
project: project,
ref: 'v1.0.0',
sha: project.commit('v1.0.0').sha,
status: :success)
end
it 'all relevant commit statuses are received' do
expect(subject['v1.1.0'].group).to eq("running")
expect(subject['v1.0.0'].group).to eq("success")
end
end
context 'when a tag has multiple pipelines' do
before do
create(:ci_pipeline,
project: project,
ref: 'v1.0.0',
sha: project.commit('v1.0.0').sha,
status: :running,
created_at: 6.months.ago)
create(:ci_pipeline,
project: project,
ref: 'v1.0.0',
sha: project.commit('v1.0.0').sha,
status: :success,
created_at: 2.months.ago)
end
it 'chooses the latest to determine status' do
expect(subject['v1.0.0'].group).to eq("success")
end
end
end
context 'branch refs' do
let(:subject) { described_class.new(project, project.repository, user, branches).execute }
before do
project.add_developer(user)
end
context 'no pipelines' do
let(:branches) { BranchesFinder.new(project.repository, {}).execute }
it 'returns nil' do
expect(subject).to be_blank
end
end
context 'when a branch has multiple pipelines' do
let(:branches) { BranchesFinder.new(project.repository, {}).execute }
before do
sha = project.repository.create_file(user, generate(:branch), 'content', message: 'message', branch_name: 'master')
create(:ci_pipeline,
project: project,
user: user,
ref: "master",
sha: sha,
status: :running,
created_at: 6.months.ago)
create(:ci_pipeline,
project: project,
user: user,
ref: "master",
sha: sha,
status: :success,
created_at: 2.months.ago)
end
it 'chooses the latest to determine status' do
expect(subject["master"].group).to eq("success")
end
end
context 'when multiple branches exist' do
let(:branches) { BranchesFinder.new(project.repository, {}).execute }
before do
master_sha = project.repository.create_file(user, generate(:branch), 'content', message: 'message', branch_name: 'master')
create(:ci_pipeline,
project: project,
user: user,
ref: "master",
sha: master_sha,
status: :running,
created_at: 6.months.ago)
test_sha = project.repository.create_file(user, generate(:branch), 'content', message: 'message', branch_name: 'test')
create(:ci_pipeline,
project: project,
user: user,
ref: "test",
sha: test_sha,
status: :success,
created_at: 2.months.ago)
end
it 'all relevant commit statuses are received' do
expect(subject["master"].group).to eq("running")
expect(subject["test"].group).to eq("success")
end
end
end
context 'CI pipelines visible to' do
let_it_be(:tags) { TagsFinder.new(project.repository, {}).execute }
let(:subject) { described_class.new(project, project.repository, user, tags).execute }
before do
create(:ci_pipeline,
project: project,
ref: 'v1.1.0',
sha: project.commit('v1.1.0').sha,
status: :running)
end
context 'everyone' do
it 'returns something' do
expect(subject).not_to be_blank
end
end
context 'project members only' do
before do
project.project_feature.update!(builds_access_level: ProjectFeature::PRIVATE)
end
it 'returns nil' do
expect(subject).to be_empty
end
end
context 'when not a member of a private project' do
let(:private_project) { create(:project, :private, :repository) }
let(:private_tags) { TagsFinder.new(private_tags.repository, {}).execute }
let(:private_subject) { described_class.new(private_project, private_project.repository, user, tags).execute }
before do
create(:ci_pipeline,
project: private_project,
ref: 'v1.1.0',
sha: private_project.commit('v1.1.0').sha,
status: :running)
end
it 'returns nil' do
expect(private_subject).to be_empty
end
end
end
end
......@@ -57,4 +57,29 @@ RSpec.describe 'projects/tags/index.html.haml' do
expect(rendered).not_to have_link(href: latest_succeeded_project_artifacts_path(project, "#{pipeline.ref}/download", job: 'test'))
end
end
context 'build stats' do
let(:tag) { 'v1.0.0' }
let(:page) { Capybara::Node::Simple.new(rendered) }
it 'shows build status or placeholder when pipelines present' do
create(:ci_pipeline,
project: project,
ref: tag,
sha: project.commit(tag).sha,
status: :success)
assign(:tag_pipeline_statuses, Ci::CommitStatusesFinder.new(project, project.repository, project.namespace.owner, tags).execute)
render
expect(page.find('.tags .content-list li', text: tag)).to have_css 'a.ci-status-icon-success'
expect(page.all('.tags .content-list li')).to all(have_css('svg.s24'))
end
it 'shows no build status or placeholder when no pipelines present' do
render
expect(page.all('.tags .content-list li')).not_to have_css 'svg.s24'
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