提交 538fff82 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 3692e9f8
Please view this file on the master branch, on stable branches it's out of date.
## 12.3.2
### Security (2 changes)
- Hide approvers if a rule has any hidden groups.
- Prevent IDOR when adding groups to protected environments.
## 12.3.1
- No changes.
......@@ -187,6 +195,15 @@ Please view this file on the master branch, on stable branches it's out of date.
- Fixes style-lint errors and warnings for EE builds.scss file.
## 12.2.6
### Security (3 changes)
- Hide approvers if a rule has any hidden groups.
- Fix Gitaly SearchBlobs flag RPC injection [Gitaly v1.59.3].
- Prevent IDOR when adding groups to protected environments.
## 12.2.5
### Security (1 change)
......@@ -439,6 +456,16 @@ Please view this file on the master branch, on stable branches it's out of date.
- Fix alignment of activity dropdown in epic tabs; add counter to discussion tab.
## 12.1.12
### Security (4 changes)
- Hide approvers if a rule has any hidden groups.
- Fix Gitaly SearchBlobs flag RPC injection [Gitaly v1.53.4].
- Prevent IDOR when adding groups to protected environments.
- Upgrade mermaid to prevent XSS.
## 12.1.10
- No changes.
......
......@@ -4,16 +4,18 @@ entry.
## 12.3.2
### Security (10 changes)
### Security (12 changes)
- Fix Gitaly SearchBlobs flag RPC injection.
- Add a policy check for system notes that may not be visible due to cross references to private items.
- Display only participants that user has permission to see on milestone page.
- Do not disclose project milestones on group milestones page when project milestones access is disabled in project settings.
- Check permissions before showing head pipeline blocking merge requests.
- Fix new project path being disclosed through unsubscribe link of issue/merge requests.
- Prevent bypassing email verification using Salesforce.
- Do not show resource label events referencing not accessible labels.
- Cancel all running CI jobs triggered by the user who is just blocked.
- Fix Gitaly SearchBlobs flag RPC injection.
- Only render fixed number of mermaid blocks.
- Prevent GitLab accounts takeover if SAML is configured.
......@@ -299,11 +301,12 @@ entry.
## 12.2.6
### Security (10 changes)
### Security (11 changes)
- Add a policy check for system notes that may not be visible due to cross references to private items.
- Display only participants that user has permission to see on milestone page.
- Do not disclose project milestones on group milestones page when project milestones access is disabled in project settings.
- Check permissions before showing head pipeline blocking merge requests.
- Fix new project path being disclosed through unsubscribe link of issue/merge requests.
- Prevent bypassing email verification using Salesforce.
- Do not show resource label events referencing not accessible labels.
......@@ -633,11 +636,12 @@ entry.
## 12.1.12
### Security (11 changes)
### Security (12 changes)
- Add a policy check for system notes that may not be visible due to cross references to private items.
- Display only participants that user has permission to see on milestone page.
- Do not disclose project milestones on group milestones page when project milestones access is disabled in project settings.
- Check permissions before showing head pipeline blocking merge requests.
- Fix new project path being disclosed through unsubscribe link of issue/merge requests.
- Prevent bypassing email verification using Salesforce.
- Do not show resource label events referencing not accessible labels.
......
......@@ -104,6 +104,13 @@ export default {
helpLink() {
return boardsStore.scopedLabels.helpLink;
},
validIssueWeight() {
if (_.isNumber(this.issue.weight)) {
return this.issue.weight >= 0;
}
return false;
},
},
methods: {
isIndexLessThanlimit(index) {
......@@ -212,7 +219,7 @@ export default {
<issue-due-date v-if="issue.dueDate" :date="issue.dueDate" />
<issue-time-estimate v-if="issue.timeEstimate" :estimate="issue.timeEstimate" />
<issue-card-weight
v-if="issue.weight"
v-if="validIssueWeight"
:weight="issue.weight"
@click="filterByWeight(issue.weight)"
/>
......
......@@ -72,7 +72,7 @@ class Projects::CommitsController < Projects::ApplicationController
@repository.commits(@ref, path: @path, limit: @limit, offset: @offset)
end
@commits = @commits.with_pipeline_status
@commits = @commits.with_latest_pipeline(@ref)
@commits = set_commits_for_rendering(@commits)
end
......
......@@ -82,7 +82,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
# Get commits from repository
# or from cache if already merged
@commits =
set_commits_for_rendering(@merge_request.commits.with_pipeline_status)
set_commits_for_rendering(@merge_request.commits.with_latest_pipeline)
render json: { html: view_to_html_string('projects/merge_requests/_commits') }
end
......
......@@ -64,7 +64,7 @@ module CiStatusHelper
def ci_icon_for_status(status, size: 16)
if detailed_status?(status)
return sprite_icon(status.icon)
return sprite_icon(status.icon, size: size)
end
icon_name =
......@@ -96,23 +96,29 @@ module CiStatusHelper
sprite_icon(icon_name, size: size)
end
def ci_icon_class_for_status(status)
group = detailed_status?(status) ? status.group : status.dasherize
"ci-status-icon-#{group}"
end
def pipeline_status_cache_key(pipeline_status)
"pipeline-status/#{pipeline_status.sha}-#{pipeline_status.status}"
end
def render_commit_status(commit, ref: nil, tooltip_placement: 'left')
def render_commit_status(commit, status, ref: nil, tooltip_placement: 'left')
project = commit.project
path = pipelines_project_commit_path(project, commit, ref: ref)
render_status_with_link(
commit.status(ref),
status,
path,
tooltip_placement: tooltip_placement,
icon_size: 24)
end
def render_status_with_link(status, path = nil, type: _('pipeline'), tooltip_placement: 'left', cssclass: '', container: 'body', icon_size: 16)
klass = "ci-status-link ci-status-icon-#{status.dasherize} d-inline-flex #{cssclass}"
klass = "ci-status-link #{ci_icon_class_for_status(status)} d-inline-flex #{cssclass}"
title = "#{type.titleize}: #{ci_label_for_status(status)}"
data = { toggle: 'tooltip', placement: tooltip_placement, container: container }
......@@ -127,6 +133,7 @@ module CiStatusHelper
def detailed_status?(status)
status.respond_to?(:text) &&
status.respond_to?(:group) &&
status.respond_to?(:label) &&
status.respond_to?(:icon)
end
......
......@@ -281,16 +281,16 @@ module Ci
end
end
# Returns a Hash containing the latest pipeline status for every given
# Returns a Hash containing the latest pipeline for every given
# commit.
#
# The keys of this Hash are the commit SHAs, the values the statuses.
# The keys of this Hash are the commit SHAs, the values the pipelines.
#
# commits - The list of commit SHAs to get the status for.
# commits - The list of commit SHAs to get the pipelines for.
# ref - The ref to scope the data to (e.g. "master"). If the ref is not
# given we simply get the latest status for the commits, regardless
# of what refs their pipelines belong to.
def self.latest_status_per_commit(commits, ref = nil)
# given we simply get the latest pipelines for the commits, regardless
# of what refs the pipelines belong to.
def self.latest_pipeline_per_commit(commits, ref = nil)
p1 = arel_table
p2 = arel_table.alias
......@@ -304,15 +304,14 @@ module Ci
cond = cond.and(p1[:ref].eq(p2[:ref])) if ref
join = p1.join(p2, Arel::Nodes::OuterJoin).on(cond)
relation = select(:sha, :status)
.where(sha: commits)
relation = where(sha: commits)
.where(p2[:id].eq(nil))
.joins(join.join_sources)
relation = relation.where(ref: ref) if ref
relation.each_with_object({}) do |row, hash|
hash[row[:sha]] = row[:status]
relation.each_with_object({}) do |pipeline, hash|
hash[pipeline.sha] = pipeline
end
end
......
......@@ -119,10 +119,22 @@ class Commit
@raw = raw_commit
@project = project
@statuses = {}
@gpg_commit = Gitlab::Gpg::Commit.new(self) if project
end
delegate \
:pipelines,
:last_pipeline,
:latest_pipeline,
:latest_pipeline_for_project,
:set_latest_pipeline_for_ref,
:status,
to: :with_pipeline
def with_pipeline
@with_pipeline ||= CommitWithPipeline.new(self)
end
def id
raw.id
end
......@@ -301,30 +313,6 @@ class Commit
)
end
def pipelines
project.ci_pipelines.where(sha: sha)
end
def last_pipeline
strong_memoize(:last_pipeline) do
pipelines.last
end
end
def status(ref = nil)
return @statuses[ref] if @statuses.key?(ref)
@statuses[ref] = status_for_project(ref, project)
end
def status_for_project(ref, pipeline_project)
pipeline_project.ci_pipelines.latest_status_per_commit(id, ref)[id]
end
def set_status_for_ref(ref, status)
@statuses[ref] = status
end
def signature
return @signature if defined?(@signature)
......
......@@ -34,6 +34,20 @@ class CommitCollection
end
end
# Returns the collection with the latest pipeline for every commit pre-set.
#
# Setting the pipeline for each commit ahead of time removes the need for running
# a query for every commit we're displaying.
def with_latest_pipeline(ref = nil)
pipelines = project.ci_pipelines.latest_pipeline_per_commit(map(&:id), ref)
each do |commit|
commit.set_latest_pipeline_for_ref(ref, pipelines[commit.id])
end
self
end
def unenriched
commits.reject(&:gitaly_commit?)
end
......@@ -65,20 +79,6 @@ class CommitCollection
self
end
# Sets the pipeline status for every commit.
#
# Setting this status ahead of time removes the need for running a query for
# every commit we're displaying.
def with_pipeline_status
statuses = project.ci_pipelines.latest_status_per_commit(map(&:id), ref)
each do |commit|
commit.set_status_for_ref(ref, statuses[commit.id])
end
self
end
def respond_to_missing?(message, inc_private = false)
commits.respond_to?(message, inc_private)
end
......
# frozen_string_literal: true
class CommitWithPipeline < SimpleDelegator
include Presentable
def initialize(commit)
@latest_pipelines = {}
super(commit)
end
def pipelines
project.ci_pipelines.where(sha: sha)
end
def last_pipeline
strong_memoize(:last_pipeline) do
pipelines.last
end
end
def latest_pipeline(ref = nil)
@latest_pipelines.fetch(ref) do |ref|
@latest_pipelines[ref] = latest_pipeline_for_project(ref, project)
end
end
def latest_pipeline_for_project(ref, pipeline_project)
pipeline_project.ci_pipelines.latest_pipeline_per_commit(id, ref)[id]
end
def set_latest_pipeline_for_ref(ref, pipeline)
@latest_pipelines[ref] = pipeline
end
def status(ref = nil)
latest_pipeline(ref)&.status
end
end
......@@ -6,11 +6,15 @@ class CommitPresenter < Gitlab::View::Presenter::Delegated
presents :commit
def status_for(ref)
can?(current_user, :read_commit_status, commit.project) && commit.status(ref)
return unless can?(current_user, :read_commit_status, commit.project)
commit.latest_pipeline(ref)&.detailed_status(current_user)
end
def any_pipelines?
can?(current_user, :read_pipeline, commit.project) && commit.pipelines.any?
return false unless can?(current_user, :read_pipeline, commit.project)
commit.pipelines.any?
end
def web_url
......
......@@ -35,8 +35,8 @@ class CommitEntity < API::Entities::Commit
pipeline_project = options[:pipeline_project] || commit.project
next unless pipeline_ref && pipeline_project
status = commit.status_for_project(pipeline_ref, pipeline_project)
next unless status
pipeline = commit.latest_pipeline_for_project(pipeline_ref, pipeline_project)
next unless pipeline&.status
pipelines_project_commit_path(pipeline_project, commit.id, ref: pipeline_ref)
end
......
......@@ -9,9 +9,7 @@ module Search
end
def execute
snippets = SnippetsFinder.new(current_user).execute
Gitlab::SnippetSearchResults.new(snippets, params[:search])
Gitlab::SnippetSearchResults.new(current_user, params[:search])
end
def scope
......
......@@ -6,7 +6,8 @@
- merge_request = local_assigns.fetch(:merge_request, nil)
- project = local_assigns.fetch(:project) { merge_request&.project }
- ref = local_assigns.fetch(:ref) { merge_request&.source_branch }
- commit_status = commit.present(current_user: current_user).status_for(ref)
- commit = commit.present(current_user: current_user)
- commit_status = commit.status_for(ref)
- link = commit_path(project, commit, merge_request: merge_request)
......@@ -48,7 +49,7 @@
= render partial: 'projects/commit/ajax_signature', locals: { commit: commit }
- if commit_status
= render_commit_status(commit, ref: ref)
= render_commit_status(commit, commit_status, ref: ref)
.js-commit-pipeline-status{ data: { endpoint: pipelines_project_commit_path(project, commit.id, ref: ref) } }
......
......@@ -63,7 +63,7 @@
- if pipeline_status && can?(current_user, :read_cross_project) && project.pipeline_status.has_status? && can?(current_user, :read_build, project)
- pipeline_path = pipelines_project_commit_path(project.pipeline_status.project, project.pipeline_status.sha, ref: project.pipeline_status.ref)
%span.icon-wrapper.pipeline-status
= render 'ci/status/icon', status: project.commit.last_pipeline.detailed_status(current_user), tooltip_placement: 'top', path: pipeline_path
= render 'ci/status/icon', status: project.last_pipeline.detailed_status(current_user), tooltip_placement: 'top', path: pipeline_path
- if project.archived
%span.d-flex.icon-wrapper.badge.badge-warning archived
......
---
title: Show issue weight when weight is 0
merge_request: 17329
author: briankabiro
type: fixed
---
title: Backfill releases table updated_at column and add not null constraints to created_at and updated_at
merge_request: 17400
author:
type: fixed
---
title: Show correct CI indicator when build succeeded with warnings.
merge_request: 17034
author:
type: fixed
---
title: Limit snippets search count
merge_request: 17585
author:
type: performance
---
title: Document Git LFS and max file size interaction
merge_request: 17609
author:
type: other
---
title: Check permissions before showing head pipeline blocking merge requests
merge_request:
author:
type: security
# frozen_string_literal: true
class BackfillReleasesTableUpdatedAtAndAddNotNullConstraintsToTimestamps < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
change_column_null(:releases, :created_at, false, Time.zone.now)
update_column_in_batches(:releases, :updated_at, Arel.sql('created_at')) do |table, query|
query.where(table[:updated_at].eq(nil))
end
change_column_null(:releases, :updated_at, false, Time.zone.now)
end
def down
change_column_null(:releases, :updated_at, true)
change_column_null(:releases, :created_at, true)
end
end
......@@ -3137,8 +3137,8 @@ ActiveRecord::Schema.define(version: 2019_09_27_074328) do
t.string "tag"
t.text "description"
t.integer "project_id"
t.datetime "created_at"
t.datetime "updated_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "description_html"
t.integer "cached_markdown_version"
t.integer "author_id"
......
......@@ -74,7 +74,7 @@ The following options are available.
| Restrict by branch name | **Starter** 9.3 | Only branch names that match this regular expression are allowed to be pushed. Leave empty to allow any branch name. |
| Restrict by commit author's email | **Starter** 7.10 | Only commit author's email that match this regular expression are allowed to be pushed. Leave empty to allow any email. |
| Prohibited file names | **Starter** 7.10 | Any committed filenames that match this regular expression are not allowed to be pushed. Leave empty to allow any filenames. |
| Maximum file size | **Starter** 7.12 | Pushes that contain added or updated files that exceed this file size (in MB) are rejected. Set to 0 to allow files of any size. |
| Maximum file size | **Starter** 7.12 | Pushes that contain added or updated files that exceed this file size (in MB) are rejected. Set to 0 to allow files of any size. Files tracked by Git LFS are exempted. |
TIP: **Tip:**
GitLab uses [RE2 syntax](https://github.com/google/re2/wiki/Syntax) for regular expressions in push rules, and you can test them at the [GoLang regex tester](https://regex-golang.appspot.com).
......
......@@ -4,19 +4,19 @@ module Gitlab
class SnippetSearchResults < SearchResults
include SnippetsHelper
attr_reader :limit_snippets
attr_reader :current_user
def initialize(limit_snippets, query)
@limit_snippets = limit_snippets
def initialize(current_user, query)
@current_user = current_user
@query = query
end
def objects(scope, page = nil)
case scope
when 'snippet_titles'
snippet_titles.page(page).per(per_page)
paginated_objects(snippet_titles, page)
when 'snippet_blobs'
snippet_blobs.page(page).per(per_page)
paginated_objects(snippet_blobs, page)
else
super(scope, nil, false)
end
......@@ -25,38 +25,47 @@ module Gitlab
def formatted_count(scope)
case scope
when 'snippet_titles'
snippet_titles_count.to_s
formatted_limited_count(limited_snippet_titles_count)
when 'snippet_blobs'
snippet_blobs_count.to_s
formatted_limited_count(limited_snippet_blobs_count)
else
super
end
end
def snippet_titles_count
@snippet_titles_count ||= snippet_titles.count
def limited_snippet_titles_count
@limited_snippet_titles_count ||= limited_count(snippet_titles)
end
def snippet_blobs_count
@snippet_blobs_count ||= snippet_blobs.count
def limited_snippet_blobs_count
@limited_snippet_blobs_count ||= limited_count(snippet_blobs)
end
private
# rubocop: disable CodeReuse/ActiveRecord
def snippet_titles
limit_snippets.search(query).order('updated_at DESC').includes(:author)
def snippets
SnippetsFinder.new(current_user)
.execute
.includes(:author)
.reorder(updated_at: :desc)
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def snippet_titles
snippets.search(query)
end
def snippet_blobs
limit_snippets.search_code(query).order('updated_at DESC').includes(:author)
snippets.search_code(query)
end
# rubocop: enable CodeReuse/ActiveRecord
def default_scope
'snippet_blobs'
end
def paginated_objects(relation, page)
relation.page(page).per(per_page)
end
end
end
......@@ -3,7 +3,6 @@
require 'spec_helper'
describe 'Import/Export - project import integration test', :js do
include Select2Helper
include GitHelpers
let(:user) { create(:user) }
......@@ -31,7 +30,6 @@ describe 'Import/Export - project import integration test', :js do
it 'user imports an exported project successfully' do
visit new_project_path
select2(namespace.id, from: '#project_namespace_id')
fill_in :project_name, with: project_name, visible: true
click_import_project_tab
click_link 'GitLab export'
......@@ -78,7 +76,6 @@ describe 'Import/Export - project import integration test', :js do
visit new_project_path
select2(user.namespace.id, from: '#project_namespace_id')
fill_in :project_name, with: project.name, visible: true
click_import_project_tab
click_link 'GitLab export'
......
......@@ -42,6 +42,7 @@ describe('Issue card component', () => {
assignees: [],
reference_path: '#1',
real_path: '/test/1',
weight: 1,
});
component = new Vue({
......@@ -287,8 +288,17 @@ describe('Issue card component', () => {
});
describe('weights', () => {
it('not shows weight component', () => {
expect(component.$el.querySelector('.board-card-weight')).toBeNull();
it('shows weight component is greater than 0', () => {
expect(component.$el.querySelector('.board-card-weight')).not.toBeNull();
});
it('shows weight component when weight is 0', done => {
component.issue.weight = 0;
Vue.nextTick(() => {
expect(component.$el.querySelector('.board-card-weight')).not.toBeNull();
done();
});
});
});
});
......@@ -6,18 +6,17 @@ describe Gitlab::SnippetSearchResults do
include SearchHelpers
let!(:snippet) { create(:snippet, content: 'foo', file_name: 'foo') }
let(:results) { described_class.new(Snippet.all, 'foo') }
let(:results) { described_class.new(snippet.author, 'foo') }
describe '#snippet_titles_count' do
it 'returns the amount of matched snippet titles' do
expect(results.snippet_titles_count).to eq(1)
expect(results.limited_snippet_titles_count).to eq(1)
end
end
describe '#snippet_blobs_count' do
it 'returns the amount of matched snippet blobs' do
expect(results.snippet_blobs_count).to eq(1)
expect(results.limited_snippet_blobs_count).to eq(1)
end
end
......@@ -25,10 +24,10 @@ describe Gitlab::SnippetSearchResults do
using RSpec::Parameterized::TableSyntax
where(:scope, :count_method, :expected) do
'snippet_titles' | :snippet_titles_count | '1234'
'snippet_blobs' | :snippet_blobs_count | '1234'
'projects' | :limited_projects_count | max_limited_count
'unknown' | nil | nil
'snippet_titles' | :limited_snippet_titles_count | max_limited_count
'snippet_blobs' | :limited_snippet_blobs_count | max_limited_count
'projects' | :limited_projects_count | max_limited_count
'unknown' | nil | nil
end
with_them do
......
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'migrate', '20190920194925_backfill_releases_table_updated_at_and_add_not_null_constraints_to_timestamps.rb')
describe BackfillReleasesTableUpdatedAtAndAddNotNullConstraintsToTimestamps, :migration do
let(:releases) { table(:releases) }
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
subject(:migration) { described_class.new }
it 'fills null updated_at rows with the value of created_at' do
created_at_a = Time.zone.parse('2014-03-11T04:30:00Z')
created_at_b = Time.zone.parse('2019-09-10T12:00:00Z')
namespace = namespaces.create(name: 'foo', path: 'foo')
project = projects.create!(namespace_id: namespace.id)
release_a = releases.create!(project_id: project.id,
released_at: Time.zone.parse('2014-12-10T06:00:00Z'),
created_at: created_at_a)
release_b = releases.create!(project_id: project.id,
released_at: Time.zone.parse('2019-09-11T06:00:00Z'),
created_at: created_at_b)
release_a.update!(updated_at: nil)
release_b.update!(updated_at: nil)
disable_migrations_output { migrate! }
release_a.reload
release_b.reload
expect(release_a.updated_at).to eq(created_at_a)
expect(release_b.updated_at).to eq(created_at_b)
end
it 'does not change updated_at columns with a value' do
created_at_a = Time.zone.parse('2014-03-11T04:30:00Z')
updated_at_a = Time.zone.parse('2015-01-16T10:00:00Z')
created_at_b = Time.zone.parse('2019-09-10T12:00:00Z')
namespace = namespaces.create(name: 'foo', path: 'foo')
project = projects.create!(namespace_id: namespace.id)
release_a = releases.create!(project_id: project.id,
released_at: Time.zone.parse('2014-12-10T06:00:00Z'),
created_at: created_at_a,
updated_at: updated_at_a)
release_b = releases.create!(project_id: project.id,
released_at: Time.zone.parse('2019-09-11T06:00:00Z'),
created_at: created_at_b)
release_b.update!(updated_at: nil)
disable_migrations_output { migrate! }
release_a.reload
release_b.reload
expect(release_a.updated_at).to eq(updated_at_a)
expect(release_b.updated_at).to eq(created_at_b)
end
end
......@@ -1966,40 +1966,57 @@ describe Ci::Pipeline, :mailer do
end
end
describe '.latest_status_per_commit' do
describe '.latest_pipeline_per_commit' do
let(:project) { create(:project) }
before do
pairs = [
%w[success ref1 123],
%w[manual master 123],
%w[failed ref 456]
]
pairs.each do |(status, ref, sha)|
create(
:ci_empty_pipeline,
status: status,
ref: ref,
sha: sha,
project: project
)
end
let!(:commit_123_ref_master) do
create(
:ci_empty_pipeline,
status: 'success',
ref: 'master',
sha: '123',
project: project
)
end
let!(:commit_123_ref_develop) do
create(
:ci_empty_pipeline,
status: 'success',
ref: 'develop',
sha: '123',
project: project
)
end
let!(:commit_456_ref_test) do
create(
:ci_empty_pipeline,
status: 'success',
ref: 'test',
sha: '456',
project: project
)
end
context 'without a ref' do
it 'returns a Hash containing the latest status per commit for all refs' do
expect(described_class.latest_status_per_commit(%w[123 456]))
.to eq({ '123' => 'manual', '456' => 'failed' })
it 'returns a Hash containing the latest pipeline per commit for all refs' do
result = described_class.latest_pipeline_per_commit(%w[123 456])
expect(result).to match(
'123' => commit_123_ref_develop,
'456' => commit_456_ref_test
)
end
it 'only includes the status of the given commit SHAs' do
expect(described_class.latest_status_per_commit(%w[123]))
.to eq({ '123' => 'manual' })
it 'only includes the latest pipeline of the given commit SHAs' do
result = described_class.latest_pipeline_per_commit(%w[123])
expect(result).to match(
'123' => commit_123_ref_develop
)
end
context 'when there are two pipelines for a ref and SHA' do
it 'returns the status of the latest pipeline' do
let!(:commit_123_ref_master_latest) do
create(
:ci_empty_pipeline,
status: 'failed',
......@@ -2007,17 +2024,25 @@ describe Ci::Pipeline, :mailer do
sha: '123',
project: project
)
end
it 'returns the latest pipeline' do
result = described_class.latest_pipeline_per_commit(%w[123])
expect(described_class.latest_status_per_commit(%w[123]))
.to eq({ '123' => 'failed' })
expect(result).to match(
'123' => commit_123_ref_master_latest
)
end
end
end
context 'with a ref' do
it 'only includes the pipelines for the given ref' do
expect(described_class.latest_status_per_commit(%w[123 456], 'master'))
.to eq({ '123' => 'manual' })
result = described_class.latest_pipeline_per_commit(%w[123 456], 'master')
expect(result).to match(
'123' => commit_123_ref_master
)
end
end
end
......
......@@ -51,6 +51,30 @@ describe CommitCollection do
end
end
describe '#with_latest_pipeline' do
let!(:pipeline) do
create(
:ci_empty_pipeline,
ref: 'master',
sha: commit.id,
status: 'success',
project: project
)
end
let(:collection) { described_class.new(project, [commit]) }
it 'sets the latest pipeline for every commit so no additional queries are necessary' do
commits = collection.with_latest_pipeline('master')
recorder = ActiveRecord::QueryRecorder.new do
expect(commits.map { |c| c.latest_pipeline('master') })
.to eq([pipeline])
end
expect(recorder.count).to be_zero
end
end
describe 'enrichment methods' do
let(:gitaly_commit) { commit }
let(:hash_commit) { Commit.from_hash(gitaly_commit.to_hash, project) }
......@@ -128,27 +152,6 @@ describe CommitCollection do
end
end
describe '#with_pipeline_status' do
it 'sets the pipeline status for every commit so no additional queries are necessary' do
create(
:ci_empty_pipeline,
ref: 'master',
sha: commit.id,
status: 'success',
project: project
)
collection = described_class.new(project, [commit])
collection.with_pipeline_status
recorder = ActiveRecord::QueryRecorder.new do
expect(commit.status).to eq('success')
end
expect(recorder.count).to be_zero
end
end
describe '#respond_to_missing?' do
it 'returns true when the underlying Array responds to the message' do
collection = described_class.new(project, [])
......
......@@ -462,78 +462,6 @@ eos
end
end
describe '#last_pipeline' do
let!(:first_pipeline) do
create(:ci_empty_pipeline,
project: project,
sha: commit.sha,
status: 'success')
end
let!(:second_pipeline) do
create(:ci_empty_pipeline,
project: project,
sha: commit.sha,
status: 'success')
end
it 'returns last pipeline' do
expect(commit.last_pipeline).to eq second_pipeline
end
end
describe '#status' do
context 'without ref argument' do
before do
%w[success failed created pending].each do |status|
create(:ci_empty_pipeline,
project: project,
sha: commit.sha,
status: status)
end
end
it 'gives compound status from latest pipelines' do
expect(commit.status).to eq(Ci::Pipeline.latest_status)
expect(commit.status).to eq('pending')
end
end
context 'when a particular ref is specified' do
let!(:pipeline_from_master) do
create(:ci_empty_pipeline,
project: project,
sha: commit.sha,
ref: 'master',
status: 'failed')
end
let!(:pipeline_from_fix) do
create(:ci_empty_pipeline,
project: project,
sha: commit.sha,
ref: 'fix',
status: 'success')
end
it 'gives pipelines from a particular branch' do
expect(commit.status('master')).to eq(pipeline_from_master.status)
expect(commit.status('fix')).to eq(pipeline_from_fix.status)
end
it 'gives compound status from latest pipelines if ref is nil' do
expect(commit.status(nil)).to eq(pipeline_from_fix.status)
end
end
end
describe '#set_status_for_ref' do
it 'sets the status for a given reference' do
commit.set_status_for_ref('master', 'failed')
expect(commit.status('master')).to eq('failed')
end
end
describe '#participants' do
let(:user1) { build(:user) }
let(:user2) { build(:user) }
......
# frozen_string_literal: true
require 'spec_helper'
describe CommitWithPipeline do
let(:project) { create(:project, :public, :repository) }
let(:commit) { described_class.new(project.commit) }
describe '#last_pipeline' do
let!(:first_pipeline) do
create(:ci_empty_pipeline,
project: project,
sha: commit.sha,
status: 'success')
end
let!(:second_pipeline) do
create(:ci_empty_pipeline,
project: project,
sha: commit.sha,
status: 'success')
end
it 'returns last pipeline' do
expect(commit.last_pipeline).to eq second_pipeline
end
end
describe '#latest_pipeline' do
let(:pipeline) { double }
shared_examples_for 'fetching latest pipeline' do |ref|
it 'returns the latest pipeline for the project' do
expect(commit)
.to receive(:latest_pipeline_for_project)
.with(ref, project)
.and_return(pipeline)
expect(result).to eq(pipeline)
end
it "returns the memoized pipeline for the key of #{ref}" do
commit.set_latest_pipeline_for_ref(ref, pipeline)
expect(commit)
.not_to receive(:latest_pipeline_for_project)
expect(result).to eq(pipeline)
end
end
context 'without ref argument' do
let(:result) { commit.latest_pipeline }
it_behaves_like 'fetching latest pipeline', nil
end
context 'when a particular ref is specified' do
let(:result) { commit.latest_pipeline('master') }
it_behaves_like 'fetching latest pipeline', 'master'
end
end
describe '#latest_pipeline_for_project' do
let(:project_pipelines) { double }
let(:pipeline_project) { double }
let(:pipeline) { double }
let(:ref) { 'master' }
let(:result) { commit.latest_pipeline_for_project(ref, pipeline_project) }
before do
allow(pipeline_project).to receive(:ci_pipelines).and_return(project_pipelines)
end
it 'returns the latest pipeline of the commit for the given ref and project' do
expect(project_pipelines)
.to receive(:latest_pipeline_per_commit)
.with(commit.id, ref)
.and_return(commit.id => pipeline)
expect(result).to eq(pipeline)
end
end
describe '#set_latest_pipeline_for_ref' do
let(:pipeline) { double }
it 'sets the latest pipeline for a given reference' do
commit.set_latest_pipeline_for_ref('master', pipeline)
expect(commit.latest_pipeline('master')).to eq(pipeline)
end
end
describe "#status" do
it 'returns the status of the latest pipeline for the given ref' do
expect(commit)
.to receive(:latest_pipeline)
.with('master')
.and_return(double(status: 'success'))
expect(commit.status('master')).to eq('success')
end
it 'returns nil when latest pipeline is not present for the given ref' do
expect(commit)
.to receive(:latest_pipeline)
.with('master')
.and_return(nil)
expect(commit.status('master')).to eq(nil)
end
it 'returns the status of the latest pipeline when no ref is given' do
expect(commit)
.to receive(:latest_pipeline)
.with(nil)
.and_return(double(status: 'success'))
expect(commit.status).to eq('success')
end
end
end
......@@ -17,15 +17,19 @@ describe CommitPresenter do
end
it 'returns commit status for ref' do
expect(commit).to receive(:status).with('ref').and_return('test')
pipeline = double
status = double
expect(subject).to eq('test')
expect(commit).to receive(:latest_pipeline).with('ref').and_return(pipeline)
expect(pipeline).to receive(:detailed_status).with(user).and_return(status)
expect(subject).to eq(status)
end
end
context 'when user can not read_commit_status' do
it 'is false' do
is_expected.to eq(false)
it 'is nil' do
is_expected.to eq(nil)
end
end
end
......
......@@ -24,7 +24,7 @@ module Select2Helper
selector = options.fetch(:from)
first(selector, visible: false)
ensure_select2_loaded(selector)
if options[:multiple]
execute_script("$('#{selector}').select2('val', ['#{value}']).trigger('change');")
......@@ -34,14 +34,24 @@ module Select2Helper
end
def open_select2(selector)
ensure_select2_loaded(selector)
execute_script("$('#{selector}').select2('open');")
end
def close_select2(selector)
ensure_select2_loaded(selector)
execute_script("$('#{selector}').select2('close');")
end
def scroll_select2_to_bottom(selector)
evaluate_script "$('#{selector}').scrollTop($('#{selector}')[0].scrollHeight); $('#{selector}');"
end
private
def ensure_select2_loaded(selector)
first(selector, visible: :all).sibling('.select2-container')
end
end
# frozen_string_literal: true
module StrategyHelpers
include Rack::Test::Methods
include ActionDispatch::Assertions::ResponseAssertions
......
文件模式从 100755 更改为 100644
文件模式从 100755 更改为 100644
......@@ -995,10 +995,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.75.0.tgz#93f9e6bdef78dd84ac88d8273711dc1f25e4e5ac"
integrity sha512-hOCfF73++yG+KTYxaQNMkbDUg0XKije41g6XR2dgj7466rzZmebG/nt6pUXonmlqy/NLGaRUPBKs0zuM7tcLhA==
"@gitlab/ui@5.26.1":
version "5.26.1"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.26.1.tgz#1662af9be444239bcf9570ea9a4c31aef4639a00"
integrity sha512-PtOuAQWkShA5O78Gy16BtBsic1O9jl7dc07jusgYL5rmIYw/cRZiI92/RZwkelJahdbJpaD9YulsjgsejDIp8g==
"@gitlab/ui@5.26.2":
version "5.26.2"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.26.2.tgz#b1474152e91a7f208f4c454a83b4f2f492afce57"
integrity sha512-atRTd7C2rby1vWQNAT2aokGHTAFcNtQsIhKmIC0Q1phnsnyWHVqT/xURr9cAiBpGznPooNVlQDldLOBqSoKcHA==
dependencies:
"@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.2.1"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册