Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
42883e12f1
commit
3b1798cdfa
12 changed files with 415 additions and 23 deletions
|
@ -1 +1 @@
|
|||
6aabab39e06f8a4f786a99449a49d5f0cb332310
|
||||
a193b76fa6cbf33108c6a5ed36a99cc5fdc215c3
|
||||
|
|
|
@ -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
|
||||
|
|
40
app/finders/ci/commit_statuses_finder.rb
Normal file
40
app/finders/ci/commit_statuses_finder.rb
Normal file
|
@ -0,0 +1,40 @@
|
|||
# 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
|
|
@ -1,5 +1,6 @@
|
|||
- 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)
|
||||
|
|
5
changelogs/unreleased/show_build_status_in_tag_list.yml
Normal file
5
changelogs/unreleased/show_build_status_in_tag_list.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Show build status in tag list
|
||||
merge_request: 34307
|
||||
author: Lee Tickett
|
||||
type: added
|
|
@ -22,7 +22,7 @@ of `state` are:
|
|||
- `finished`
|
||||
- `failed`
|
||||
- `replicated`
|
||||
- `cleanup_failed`
|
||||
- `cleanup failed`
|
||||
|
||||
To ensure data integrity, projects are put in a temporary read-only state for the
|
||||
duration of the move. During this time, users receive a `The repository is temporarily
|
||||
|
|
|
@ -285,7 +285,7 @@ you can use the `MAVEN_CLI_OPTS` environment variable.
|
|||
|
||||
Read more on [how to use private Maven repositories](../index.md#using-private-maven-repos).
|
||||
|
||||
#### Enabling Kubesec analyzer
|
||||
### Enabling Kubesec analyzer
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/12752) in GitLab Ultimate 12.6.
|
||||
|
||||
|
@ -300,7 +300,7 @@ variables:
|
|||
SCAN_KUBERNETES_MANIFESTS: "true"
|
||||
```
|
||||
|
||||
#### Pre-compilation
|
||||
### Pre-compilation
|
||||
|
||||
If your project requires custom build configurations, it can be preferable to avoid
|
||||
compilation during your SAST execution and instead pass all job artifacts from an
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module QA
|
||||
RSpec.describe 'Package', :orchestrated, :packages do
|
||||
describe 'Conan Repository' do
|
||||
include Runtime::Fixtures
|
||||
|
||||
let(:package_name) { 'conantest' }
|
||||
|
||||
let(:project) do
|
||||
Resource::Project.fabricate_via_api! do |project|
|
||||
project.name = 'conan-package-project'
|
||||
end
|
||||
end
|
||||
|
||||
let!(:runner) do
|
||||
Resource::Runner.fabricate! do |runner|
|
||||
runner.name = "qa-runner-#{Time.now.to_i}"
|
||||
runner.tags = ["runner-for-#{project.name}"]
|
||||
runner.executor = :docker
|
||||
runner.project = project
|
||||
end
|
||||
end
|
||||
|
||||
let(:gitlab_address_with_port) do
|
||||
uri = URI.parse(Runtime::Scenario.gitlab_address)
|
||||
"#{uri.scheme}://#{uri.host}:#{uri.port}"
|
||||
end
|
||||
|
||||
after do
|
||||
runner.remove_via_api!
|
||||
end
|
||||
|
||||
it 'publishes a conan package and deletes it', testcase: 'https://gitlab.com/gitlab-org/quality/testcases/-/issues/1077' do
|
||||
Flow::Login.sign_in
|
||||
|
||||
Resource::Repository::Commit.fabricate_via_api! do |commit|
|
||||
commit.project = project
|
||||
commit.commit_message = 'Add .gitlab-ci.yml'
|
||||
commit.add_files([{
|
||||
file_path: '.gitlab-ci.yml',
|
||||
content:
|
||||
<<~YAML
|
||||
image: conanio/gcc7
|
||||
|
||||
create_package:
|
||||
stage: deploy
|
||||
script:
|
||||
- "conan remote add gitlab #{gitlab_address_with_port}/api/v4/projects/#{project.id}/packages/conan"
|
||||
- "conan new #{package_name}/0.1 -t"
|
||||
- "conan create . mycompany/stable"
|
||||
- "CONAN_LOGIN_USERNAME=ci_user CONAN_PASSWORD=${CI_JOB_TOKEN} conan upload #{package_name}/0.1@mycompany/stable --all --remote=gitlab"
|
||||
tags:
|
||||
- "runner-for-#{project.name}"
|
||||
YAML
|
||||
}])
|
||||
end
|
||||
|
||||
project.visit!
|
||||
Page::Project::Menu.perform(&:click_ci_cd_pipelines)
|
||||
Page::Project::Pipeline::Index.perform(&:click_on_latest_pipeline)
|
||||
|
||||
Page::Project::Pipeline::Show.perform do |pipeline|
|
||||
pipeline.click_job('create_package')
|
||||
end
|
||||
|
||||
Page::Project::Job::Show.perform do |job|
|
||||
expect(job).to be_successful(timeout: 800)
|
||||
end
|
||||
|
||||
Page::Project::Menu.perform(&:click_packages_link)
|
||||
|
||||
Page::Project::Packages::Index.perform do |index|
|
||||
expect(index).to have_package(package_name)
|
||||
index.click_package(package_name)
|
||||
end
|
||||
|
||||
Page::Project::Packages::Show.perform do |package|
|
||||
package.click_delete
|
||||
end
|
||||
|
||||
Page::Project::Packages::Index.perform do |index|
|
||||
expect(index).to have_content("Package deleted successfully")
|
||||
expect(index).to have_no_package(package_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -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)
|
||||
|
|
178
spec/finders/ci/commit_statuses_finder_spec.rb
Normal file
178
spec/finders/ci/commit_statuses_finder_spec.rb
Normal file
|
@ -0,0 +1,178 @@
|
|||
# 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
|
||||
|
|
Loading…
Reference in a new issue