Commit 900ef6fc authored by Robert Speicher's avatar Robert Speicher

Merge branch 'mc/feature/find-all-artifacts-for-sha' into 'master'

Find build by sha from ref

Closes #64534 and #45697

See merge request gitlab-org/gitlab-ce!30843
parents 995d8e6c ae58f82e
...@@ -92,7 +92,10 @@ class Projects::ArtifactsController < Projects::ApplicationController ...@@ -92,7 +92,10 @@ class Projects::ArtifactsController < Projects::ApplicationController
def build_from_ref def build_from_ref
return unless @ref_name return unless @ref_name
project.latest_successful_build_for(params[:job], @ref_name) commit = project.commit(@ref_name)
return unless commit
project.latest_successful_build_for_sha(params[:job], commit.id)
end end
def artifacts_file def artifacts_file
......
...@@ -51,6 +51,6 @@ class Projects::BuildArtifactsController < Projects::ApplicationController ...@@ -51,6 +51,6 @@ class Projects::BuildArtifactsController < Projects::ApplicationController
def job_from_ref def job_from_ref
return unless @ref_name return unless @ref_name
project.latest_successful_build_for(params[:job], @ref_name) project.latest_successful_build_for_ref(params[:job], @ref_name)
end end
end end
...@@ -229,10 +229,12 @@ module Ci ...@@ -229,10 +229,12 @@ module Ci
# #
# ref - The name (or names) of the branch(es)/tag(s) to limit the list of # ref - The name (or names) of the branch(es)/tag(s) to limit the list of
# pipelines to. # pipelines to.
# sha - The commit SHA (or mutliple SHAs) to limit the list of pipelines to.
# limit - This limits a backlog search, default to 100. # limit - This limits a backlog search, default to 100.
def self.newest_first(ref: nil, limit: 100) def self.newest_first(ref: nil, sha: nil, limit: 100)
relation = order(id: :desc) relation = order(id: :desc)
relation = relation.where(ref: ref) if ref relation = relation.where(ref: ref) if ref
relation = relation.where(sha: sha) if sha
if limit if limit
ids = relation.limit(limit).select(:id) ids = relation.limit(limit).select(:id)
...@@ -246,10 +248,14 @@ module Ci ...@@ -246,10 +248,14 @@ module Ci
newest_first(ref: ref).pluck(:status).first newest_first(ref: ref).pluck(:status).first
end end
def self.latest_successful_for(ref) def self.latest_successful_for_ref(ref)
newest_first(ref: ref).success.take newest_first(ref: ref).success.take
end end
def self.latest_successful_for_sha(sha)
newest_first(sha: sha).success.take
end
def self.latest_successful_for_refs(refs) def self.latest_successful_for_refs(refs)
relation = newest_first(ref: refs).success relation = newest_first(ref: refs).success
......
...@@ -719,16 +719,27 @@ class Project < ApplicationRecord ...@@ -719,16 +719,27 @@ class Project < ApplicationRecord
repository.commits_by(oids: oids) repository.commits_by(oids: oids)
end end
# ref can't be HEAD, can only be branch/tag name or SHA # ref can't be HEAD, can only be branch/tag name
def latest_successful_build_for(job_name, ref = default_branch) def latest_successful_build_for_ref(job_name, ref = default_branch)
latest_pipeline = ci_pipelines.latest_successful_for(ref) return unless ref
latest_pipeline = ci_pipelines.latest_successful_for_ref(ref)
return unless latest_pipeline
latest_pipeline.builds.latest.with_artifacts_archive.find_by(name: job_name)
end
def latest_successful_build_for_sha(job_name, sha)
return unless sha
latest_pipeline = ci_pipelines.latest_successful_for_sha(sha)
return unless latest_pipeline return unless latest_pipeline
latest_pipeline.builds.latest.with_artifacts_archive.find_by(name: job_name) latest_pipeline.builds.latest.with_artifacts_archive.find_by(name: job_name)
end end
def latest_successful_build_for!(job_name, ref = default_branch) def latest_successful_build_for_ref!(job_name, ref = default_branch)
latest_successful_build_for(job_name, ref) || raise(ActiveRecord::RecordNotFound.new("Couldn't find job #{job_name}")) latest_successful_build_for_ref(job_name, ref) || raise(ActiveRecord::RecordNotFound.new("Couldn't find job #{job_name}"))
end end
def merge_base_commit(first_commit_id, second_commit_id) def merge_base_commit(first_commit_id, second_commit_id)
...@@ -1503,12 +1514,12 @@ class Project < ApplicationRecord ...@@ -1503,12 +1514,12 @@ class Project < ApplicationRecord
end end
@latest_successful_pipeline_for_default_branch = @latest_successful_pipeline_for_default_branch =
ci_pipelines.latest_successful_for(default_branch) ci_pipelines.latest_successful_for_ref(default_branch)
end end
def latest_successful_pipeline_for(ref = nil) def latest_successful_pipeline_for(ref = nil)
if ref && ref != default_branch if ref && ref != default_branch
ci_pipelines.latest_successful_for(ref) ci_pipelines.latest_successful_for_ref(ref)
else else
latest_successful_pipeline_for_default_branch latest_successful_pipeline_for_default_branch
end end
......
...@@ -27,7 +27,7 @@ module API ...@@ -27,7 +27,7 @@ module API
requirements: { ref_name: /.+/ } do requirements: { ref_name: /.+/ } do
authorize_download_artifacts! authorize_download_artifacts!
latest_build = user_project.latest_successful_build_for!(params[:job], params[:ref_name]) latest_build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name])
present_carrierwave_file!(latest_build.artifacts_file) present_carrierwave_file!(latest_build.artifacts_file)
end end
...@@ -45,7 +45,7 @@ module API ...@@ -45,7 +45,7 @@ module API
requirements: { ref_name: /.+/ } do requirements: { ref_name: /.+/ } do
authorize_download_artifacts! authorize_download_artifacts!
build = user_project.latest_successful_build_for!(params[:job], params[:ref_name]) build = user_project.latest_successful_build_for_ref!(params[:job], params[:ref_name])
path = Gitlab::Ci::Build::Artifacts::Path path = Gitlab::Ci::Build::Artifacts::Path
.new(params[:artifact_path]) .new(params[:artifact_path])
......
...@@ -14,7 +14,7 @@ module Gitlab ...@@ -14,7 +14,7 @@ module Gitlab
@ref = ref @ref = ref
@job = job @job = job
@pipeline = @project.ci_pipelines.latest_successful_for(@ref) @pipeline = @project.ci_pipelines.latest_successful_for_ref(@ref)
end end
def entity def entity
......
require "spec_helper" require "spec_helper"
describe "User downloads artifacts" do describe "User downloads artifacts" do
set(:project) { create(:project, :public) } set(:project) { create(:project, :repository, :public) }
set(:pipeline) { create(:ci_empty_pipeline, status: :success, project: project) } set(:pipeline) { create(:ci_empty_pipeline, status: :success, sha: project.commit.id, project: project) }
set(:job) { create(:ci_build, :artifacts, :success, pipeline: pipeline) } set(:job) { create(:ci_build, :artifacts, :success, pipeline: pipeline) }
shared_examples "downloading" do shared_examples "downloading" do
......
...@@ -1799,7 +1799,7 @@ describe Ci::Pipeline, :mailer do ...@@ -1799,7 +1799,7 @@ describe Ci::Pipeline, :mailer do
end end
end end
describe '.latest_successful_for' do describe '.latest_successful_for_ref' do
include_context 'with some outdated pipelines' include_context 'with some outdated pipelines'
let!(:latest_successful_pipeline) do let!(:latest_successful_pipeline) do
...@@ -1807,7 +1807,20 @@ describe Ci::Pipeline, :mailer do ...@@ -1807,7 +1807,20 @@ describe Ci::Pipeline, :mailer do
end end
it 'returns the latest successful pipeline' do it 'returns the latest successful pipeline' do
expect(described_class.latest_successful_for('ref')) expect(described_class.latest_successful_for_ref('ref'))
.to eq(latest_successful_pipeline)
end
end
describe '.latest_successful_for_sha' do
include_context 'with some outdated pipelines'
let!(:latest_successful_pipeline) do
create_pipeline(:success, 'ref', 'awesomesha', project)
end
it 'returns the latest successful pipeline' do
expect(described_class.latest_successful_for_sha('awesomesha'))
.to eq(latest_successful_pipeline) .to eq(latest_successful_pipeline)
end end
end end
......
...@@ -2019,62 +2019,33 @@ describe Project do ...@@ -2019,62 +2019,33 @@ describe Project do
end end
end end
describe '#latest_successful_build_for' do describe '#latest_successful_build_for_ref' do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:pipeline) { create_pipeline(project) } let(:pipeline) { create_pipeline(project) }
context 'with many builds' do it_behaves_like 'latest successful build for sha or ref'
it 'gives the latest builds from latest pipeline' do
pipeline1 = create_pipeline(project)
pipeline2 = create_pipeline(project)
create_build(pipeline1, 'test')
create_build(pipeline1, 'test2')
build1_p2 = create_build(pipeline2, 'test')
create_build(pipeline2, 'test2')
expect(project.latest_successful_build_for(build1_p2.name))
.to eq(build1_p2)
end
end
context 'with succeeded pipeline' do subject { project.latest_successful_build_for_ref(build_name) }
let!(:build) { create_build }
context 'standalone pipeline' do context 'with a specified ref' do
it 'returns builds for ref for default_branch' do let(:build) { create_build }
expect(project.latest_successful_build_for(build.name))
.to eq(build)
end
it 'returns empty relation if the build cannot be found' do subject { project.latest_successful_build_for_ref(build.name, project.default_branch) }
expect(project.latest_successful_build_for('TAIL'))
.to be_nil
end
end
context 'with some pending pipeline' do it { is_expected.to eq(build) }
before do
create_build(create_pipeline(project, 'pending'))
end
it 'gives the latest build from latest pipeline' do
expect(project.latest_successful_build_for(build.name))
.to eq(build)
end
end
end end
end
context 'with pending pipeline' do describe '#latest_successful_build_for_sha' do
it 'returns empty relation' do let(:project) { create(:project, :repository) }
pipeline.update(status: 'pending') let(:pipeline) { create_pipeline(project) }
pending_build = create_build(pipeline)
expect(project.latest_successful_build_for(pending_build.name)).to be_nil it_behaves_like 'latest successful build for sha or ref'
end
end subject { project.latest_successful_build_for_sha(build_name, project.commit.sha) }
end end
describe '#latest_successful_build_for!' do describe '#latest_successful_build_for_ref!' do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:pipeline) { create_pipeline(project) } let(:pipeline) { create_pipeline(project) }
...@@ -2087,7 +2058,7 @@ describe Project do ...@@ -2087,7 +2058,7 @@ describe Project do
build1_p2 = create_build(pipeline2, 'test') build1_p2 = create_build(pipeline2, 'test')
create_build(pipeline2, 'test2') create_build(pipeline2, 'test2')
expect(project.latest_successful_build_for(build1_p2.name)) expect(project.latest_successful_build_for_ref!(build1_p2.name))
.to eq(build1_p2) .to eq(build1_p2)
end end
end end
...@@ -2097,12 +2068,12 @@ describe Project do ...@@ -2097,12 +2068,12 @@ describe Project do
context 'standalone pipeline' do context 'standalone pipeline' do
it 'returns builds for ref for default_branch' do it 'returns builds for ref for default_branch' do
expect(project.latest_successful_build_for!(build.name)) expect(project.latest_successful_build_for_ref!(build.name))
.to eq(build) .to eq(build)
end end
it 'returns exception if the build cannot be found' do it 'returns exception if the build cannot be found' do
expect { project.latest_successful_build_for!(build.name, 'TAIL') } expect { project.latest_successful_build_for_ref!(build.name, 'TAIL') }
.to raise_error(ActiveRecord::RecordNotFound) .to raise_error(ActiveRecord::RecordNotFound)
end end
end end
...@@ -2113,7 +2084,7 @@ describe Project do ...@@ -2113,7 +2084,7 @@ describe Project do
end end
it 'gives the latest build from latest pipeline' do it 'gives the latest build from latest pipeline' do
expect(project.latest_successful_build_for!(build.name)) expect(project.latest_successful_build_for_ref!(build.name))
.to eq(build) .to eq(build)
end end
end end
...@@ -2124,7 +2095,7 @@ describe Project do ...@@ -2124,7 +2095,7 @@ describe Project do
pipeline.update(status: 'pending') pipeline.update(status: 'pending')
pending_build = create_build(pipeline) pending_build = create_build(pipeline)
expect { project.latest_successful_build_for!(pending_build.name) } expect { project.latest_successful_build_for_ref!(pending_build.name) }
.to raise_error(ActiveRecord::RecordNotFound) .to raise_error(ActiveRecord::RecordNotFound)
end end
end end
...@@ -4033,7 +4004,7 @@ describe Project do ...@@ -4033,7 +4004,7 @@ describe Project do
context 'with a ref that is not the default branch' do context 'with a ref that is not the default branch' do
it 'returns the latest successful pipeline for the given ref' do it 'returns the latest successful pipeline for the given ref' do
expect(project.ci_pipelines).to receive(:latest_successful_for).with('foo') expect(project.ci_pipelines).to receive(:latest_successful_for_ref).with('foo')
project.latest_successful_pipeline_for('foo') project.latest_successful_pipeline_for('foo')
end end
...@@ -4061,7 +4032,7 @@ describe Project do ...@@ -4061,7 +4032,7 @@ describe Project do
it 'memoizes and returns the latest successful pipeline for the default branch' do it 'memoizes and returns the latest successful pipeline for the default branch' do
pipeline = double(:pipeline) pipeline = double(:pipeline)
expect(project.ci_pipelines).to receive(:latest_successful_for) expect(project.ci_pipelines).to receive(:latest_successful_for_ref)
.with(project.default_branch) .with(project.default_branch)
.and_return(pipeline) .and_return(pipeline)
.once .once
......
# frozen_string_literal: true
shared_examples 'latest successful build for sha or ref' do
context 'with many builds' do
let(:other_pipeline) { create_pipeline(project) }
let(:other_build) { create_build(other_pipeline, 'test') }
let(:build_name) { other_build.name }
before do
pipeline1 = create_pipeline(project)
pipeline2 = create_pipeline(project)
create_build(pipeline1, 'test')
create_build(pipeline1, 'test2')
create_build(pipeline2, 'test2')
end
it 'gives the latest builds from latest pipeline' do
expect(subject).to eq(other_build)
end
end
context 'with succeeded pipeline' do
let!(:build) { create_build }
let(:build_name) { build.name }
context 'standalone pipeline' do
it 'returns builds for ref for default_branch' do
expect(subject).to eq(build)
end
context 'with nonexistent build' do
let(:build_name) { 'TAIL' }
it 'returns empty relation if the build cannot be found' do
expect(subject).to be_nil
end
end
end
context 'with some pending pipeline' do
before do
create_build(create_pipeline(project, 'pending'))
end
it 'gives the latest build from latest pipeline' do
expect(subject).to eq(build)
end
end
end
context 'with pending pipeline' do
let!(:pending_build) { create_build(pipeline) }
let(:build_name) { pending_build.name }
before do
pipeline.update(status: 'pending')
end
it 'returns empty relation' do
expect(subject).to be_nil
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