Commit 538fff82 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 3692e9f8
Please view this file on the master branch, on stable branches it's out of date. 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 ## 12.3.1
- No changes. - No changes.
...@@ -187,6 +195,15 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -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. - 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 ## 12.2.5
### Security (1 change) ### Security (1 change)
...@@ -439,6 +456,16 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -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. - 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 ## 12.1.10
- No changes. - No changes.
......
...@@ -4,16 +4,18 @@ entry. ...@@ -4,16 +4,18 @@ entry.
## 12.3.2 ## 12.3.2
### Security (10 changes) ### Security (12 changes)
- Fix Gitaly SearchBlobs flag RPC injection. - 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. - 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. - 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. - 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. - Fix new project path being disclosed through unsubscribe link of issue/merge requests.
- Prevent bypassing email verification using Salesforce. - Prevent bypassing email verification using Salesforce.
- Do not show resource label events referencing not accessible labels. - Do not show resource label events referencing not accessible labels.
- Cancel all running CI jobs triggered by the user who is just blocked. - 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. - Only render fixed number of mermaid blocks.
- Prevent GitLab accounts takeover if SAML is configured. - Prevent GitLab accounts takeover if SAML is configured.
...@@ -299,11 +301,12 @@ entry. ...@@ -299,11 +301,12 @@ entry.
## 12.2.6 ## 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. - 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. - 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. - 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. - Fix new project path being disclosed through unsubscribe link of issue/merge requests.
- Prevent bypassing email verification using Salesforce. - Prevent bypassing email verification using Salesforce.
- Do not show resource label events referencing not accessible labels. - Do not show resource label events referencing not accessible labels.
...@@ -633,11 +636,12 @@ entry. ...@@ -633,11 +636,12 @@ entry.
## 12.1.12 ## 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. - 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. - 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. - 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. - Fix new project path being disclosed through unsubscribe link of issue/merge requests.
- Prevent bypassing email verification using Salesforce. - Prevent bypassing email verification using Salesforce.
- Do not show resource label events referencing not accessible labels. - Do not show resource label events referencing not accessible labels.
......
...@@ -104,6 +104,13 @@ export default { ...@@ -104,6 +104,13 @@ export default {
helpLink() { helpLink() {
return boardsStore.scopedLabels.helpLink; return boardsStore.scopedLabels.helpLink;
}, },
validIssueWeight() {
if (_.isNumber(this.issue.weight)) {
return this.issue.weight >= 0;
}
return false;
},
}, },
methods: { methods: {
isIndexLessThanlimit(index) { isIndexLessThanlimit(index) {
...@@ -212,7 +219,7 @@ export default { ...@@ -212,7 +219,7 @@ export default {
<issue-due-date v-if="issue.dueDate" :date="issue.dueDate" /> <issue-due-date v-if="issue.dueDate" :date="issue.dueDate" />
<issue-time-estimate v-if="issue.timeEstimate" :estimate="issue.timeEstimate" /> <issue-time-estimate v-if="issue.timeEstimate" :estimate="issue.timeEstimate" />
<issue-card-weight <issue-card-weight
v-if="issue.weight" v-if="validIssueWeight"
:weight="issue.weight" :weight="issue.weight"
@click="filterByWeight(issue.weight)" @click="filterByWeight(issue.weight)"
/> />
......
...@@ -72,7 +72,7 @@ class Projects::CommitsController < Projects::ApplicationController ...@@ -72,7 +72,7 @@ class Projects::CommitsController < Projects::ApplicationController
@repository.commits(@ref, path: @path, limit: @limit, offset: @offset) @repository.commits(@ref, path: @path, limit: @limit, offset: @offset)
end end
@commits = @commits.with_pipeline_status @commits = @commits.with_latest_pipeline(@ref)
@commits = set_commits_for_rendering(@commits) @commits = set_commits_for_rendering(@commits)
end end
......
...@@ -82,7 +82,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -82,7 +82,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
# Get commits from repository # Get commits from repository
# or from cache if already merged # or from cache if already merged
@commits = @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') } render json: { html: view_to_html_string('projects/merge_requests/_commits') }
end end
......
...@@ -64,7 +64,7 @@ module CiStatusHelper ...@@ -64,7 +64,7 @@ module CiStatusHelper
def ci_icon_for_status(status, size: 16) def ci_icon_for_status(status, size: 16)
if detailed_status?(status) if detailed_status?(status)
return sprite_icon(status.icon) return sprite_icon(status.icon, size: size)
end end
icon_name = icon_name =
...@@ -96,23 +96,29 @@ module CiStatusHelper ...@@ -96,23 +96,29 @@ module CiStatusHelper
sprite_icon(icon_name, size: size) sprite_icon(icon_name, size: size)
end 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) def pipeline_status_cache_key(pipeline_status)
"pipeline-status/#{pipeline_status.sha}-#{pipeline_status.status}" "pipeline-status/#{pipeline_status.sha}-#{pipeline_status.status}"
end 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 project = commit.project
path = pipelines_project_commit_path(project, commit, ref: ref) path = pipelines_project_commit_path(project, commit, ref: ref)
render_status_with_link( render_status_with_link(
commit.status(ref), status,
path, path,
tooltip_placement: tooltip_placement, tooltip_placement: tooltip_placement,
icon_size: 24) icon_size: 24)
end end
def render_status_with_link(status, path = nil, type: _('pipeline'), tooltip_placement: 'left', cssclass: '', container: 'body', icon_size: 16) 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)}" title = "#{type.titleize}: #{ci_label_for_status(status)}"
data = { toggle: 'tooltip', placement: tooltip_placement, container: container } data = { toggle: 'tooltip', placement: tooltip_placement, container: container }
...@@ -127,6 +133,7 @@ module CiStatusHelper ...@@ -127,6 +133,7 @@ module CiStatusHelper
def detailed_status?(status) def detailed_status?(status)
status.respond_to?(:text) && status.respond_to?(:text) &&
status.respond_to?(:group) &&
status.respond_to?(:label) && status.respond_to?(:label) &&
status.respond_to?(:icon) status.respond_to?(:icon)
end end
......
...@@ -281,16 +281,16 @@ module Ci ...@@ -281,16 +281,16 @@ module Ci
end end
end end
# Returns a Hash containing the latest pipeline status for every given # Returns a Hash containing the latest pipeline for every given
# commit. # 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 # 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 # given we simply get the latest pipelines for the commits, regardless
# of what refs their pipelines belong to. # of what refs the pipelines belong to.
def self.latest_status_per_commit(commits, ref = nil) def self.latest_pipeline_per_commit(commits, ref = nil)
p1 = arel_table p1 = arel_table
p2 = arel_table.alias p2 = arel_table.alias
...@@ -304,15 +304,14 @@ module Ci ...@@ -304,15 +304,14 @@ module Ci
cond = cond.and(p1[:ref].eq(p2[:ref])) if ref cond = cond.and(p1[:ref].eq(p2[:ref])) if ref
join = p1.join(p2, Arel::Nodes::OuterJoin).on(cond) join = p1.join(p2, Arel::Nodes::OuterJoin).on(cond)
relation = select(:sha, :status) relation = where(sha: commits)
.where(sha: commits)
.where(p2[:id].eq(nil)) .where(p2[:id].eq(nil))
.joins(join.join_sources) .joins(join.join_sources)
relation = relation.where(ref: ref) if ref relation = relation.where(ref: ref) if ref
relation.each_with_object({}) do |row, hash| relation.each_with_object({}) do |pipeline, hash|
hash[row[:sha]] = row[:status] hash[pipeline.sha] = pipeline
end end
end end
......
...@@ -119,10 +119,22 @@ class Commit ...@@ -119,10 +119,22 @@ class Commit
@raw = raw_commit @raw = raw_commit
@project = project @project = project
@statuses = {}
@gpg_commit = Gitlab::Gpg::Commit.new(self) if project @gpg_commit = Gitlab::Gpg::Commit.new(self) if project
end 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 def id
raw.id raw.id
end end
...@@ -301,30 +313,6 @@ class Commit ...@@ -301,30 +313,6 @@ class Commit
) )
end 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 def signature
return @signature if defined?(@signature) return @signature if defined?(@signature)
......
...@@ -34,6 +34,20 @@ class CommitCollection ...@@ -34,6 +34,20 @@ class CommitCollection
end end
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 def unenriched
commits.reject(&:gitaly_commit?) commits.reject(&:gitaly_commit?)
end end
...@@ -65,20 +79,6 @@ class CommitCollection ...@@ -65,20 +79,6 @@ class CommitCollection
self self
end 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) def respond_to_missing?(message, inc_private = false)
commits.respond_to?(message, inc_private) commits.respond_to?(message, inc_private)
end 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 ...@@ -6,11 +6,15 @@ class CommitPresenter < Gitlab::View::Presenter::Delegated
presents :commit presents :commit
def status_for(ref) 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 end
def any_pipelines? 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 end
def web_url def web_url
......
...@@ -35,8 +35,8 @@ class CommitEntity < API::Entities::Commit ...@@ -35,8 +35,8 @@ class CommitEntity < API::Entities::Commit
pipeline_project = options[:pipeline_project] || commit.project pipeline_project = options[:pipeline_project] || commit.project
next unless pipeline_ref && pipeline_project next unless pipeline_ref && pipeline_project
status = commit.status_for_project(pipeline_ref, pipeline_project) pipeline = commit.latest_pipeline_for_project(pipeline_ref, pipeline_project)
next unless status next unless pipeline&.status
pipelines_project_commit_path(pipeline_project, commit.id, ref: pipeline_ref) pipelines_project_commit_path(pipeline_project, commit.id, ref: pipeline_ref)
end end
......
...@@ -9,9 +9,7 @@ module Search ...@@ -9,9 +9,7 @@ module Search
end end
def execute def execute
snippets = SnippetsFinder.new(current_user).execute Gitlab::SnippetSearchResults.new(current_user, params[:search])
Gitlab::SnippetSearchResults.new(snippets, params[:search])
end end
def scope def scope
......
...@@ -6,7 +6,8 @@ ...@@ -6,7 +6,8 @@
- merge_request = local_assigns.fetch(:merge_request, nil) - merge_request = local_assigns.fetch(:merge_request, nil)
- project = local_assigns.fetch(:project) { merge_request&.project } - project = local_assigns.fetch(:project) { merge_request&.project }
- ref = local_assigns.fetch(:ref) { merge_request&.source_branch } - 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) - link = commit_path(project, commit, merge_request: merge_request)
...@@ -48,7 +49,7 @@ ...@@ -48,7 +49,7 @@
= render partial: 'projects/commit/ajax_signature', locals: { commit: commit } = render partial: 'projects/commit/ajax_signature', locals: { commit: commit }
- if commit_status - 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) } } .js-commit-pipeline-status{ data: { endpoint: pipelines_project_commit_path(project, commit.id, ref: ref) } }
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
- if pipeline_status && can?(current_user, :read_cross_project) && project.pipeline_status.has_status? && can?(current_user, :read_build, project) - 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) - pipeline_path = pipelines_project_commit_path(project.pipeline_status.project, project.pipeline_status.sha, ref: project.pipeline_status.ref)
%span.icon-wrapper.pipeline-status %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 - if project.archived
%span.d-flex.icon-wrapper.badge.badge-warning 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 ...@@ -3137,8 +3137,8 @@ ActiveRecord::Schema.define(version: 2019_09_27_074328) do
t.string "tag" t.string "tag"
t.text "description" t.text "description"
t.integer "project_id" t.integer "project_id"
t.datetime "created_at" t.datetime "created_at", null: false
t.datetime "updated_at" t.datetime "updated_at", null: false
t.text "description_html" t.text "description_html"
t.integer "cached_markdown_version" t.integer "cached_markdown_version"
t.integer "author_id" t.integer "author_id"
......
...@@ -74,7 +74,7 @@ The following options are available. ...@@ -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 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. | | 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. | | 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:** 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). 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 ...@@ -4,19 +4,19 @@ module Gitlab
class SnippetSearchResults < SearchResults class SnippetSearchResults < SearchResults
include SnippetsHelper include SnippetsHelper
attr_reader :limit_snippets attr_reader :current_user
def initialize(limit_snippets, query) def initialize(current_user, query)
@limit_snippets = limit_snippets @current_user = current_user
@query = query @query = query
end end
def objects(scope, page = nil) def objects(scope, page = nil)
case scope case scope
when 'snippet_titles' when 'snippet_titles'
snippet_titles.page(page).per(per_page) paginated_objects(snippet_titles, page)
when 'snippet_blobs' when 'snippet_blobs'
snippet_blobs.page(page).per(per_page) paginated_objects(snippet_blobs, page)
else else
super(scope, nil, false) super(scope, nil, false)
end end
...@@ -25,38 +25,47 @@ module Gitlab ...@@ -25,38 +25,47 @@ module Gitlab
def formatted_count(scope) def formatted_count(scope)
case scope case scope
when 'snippet_titles' when 'snippet_titles'
snippet_titles_count.to_s formatted_limited_count(limited_snippet_titles_count)
when 'snippet_blobs' when 'snippet_blobs'
snippet_blobs_count.to_s formatted_limited_count(limited_snippet_blobs_count)
else else
super super
end end
end end
def snippet_titles_count def limited_snippet_titles_count
@snippet_titles_count ||= snippet_titles.count @limited_snippet_titles_count ||= limited_count(snippet_titles)
end end
def snippet_blobs_count def limited_snippet_blobs_count
@snippet_blobs_count ||= snippet_blobs.count @limited_snippet_blobs_count ||= limited_count(snippet_blobs)
end end
private private
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def snippet_titles def snippets
limit_snippets.search(query).order('updated_at DESC').includes(:author) SnippetsFinder.new(current_user)
.execute
.includes(:author)
.reorder(updated_at: :desc)
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord def snippet_titles
snippets.search(query)
end
def snippet_blobs def snippet_blobs
limit_snippets.search_code(query).order('updated_at DESC').includes(:author) snippets.search_code(query)
end end
# rubocop: enable CodeReuse/ActiveRecord
def default_scope def default_scope
'snippet_blobs' 'snippet_blobs'
end end
def paginated_objects(relation, page)
relation.page(page).per(per_page)
end
end end
end end
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
require 'spec_helper' require 'spec_helper'
describe 'Import/Export - project import integration test', :js do describe 'Import/Export - project import integration test', :js do
include Select2Helper
include GitHelpers include GitHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
...@@ -31,7 +30,6 @@ describe 'Import/Export - project import integration test', :js do ...@@ -31,7 +30,6 @@ describe 'Import/Export - project import integration test', :js do
it 'user imports an exported project successfully' do it 'user imports an exported project successfully' do
visit new_project_path visit new_project_path
select2(namespace.id, from: '#project_namespace_id')
fill_in :project_name, with: project_name, visible: true fill_in :project_name, with: project_name, visible: true
click_import_project_tab click_import_project_tab
click_link 'GitLab export' click_link 'GitLab export'
...@@ -78,7 +76,6 @@ describe 'Import/Export - project import integration test', :js do ...@@ -78,7 +76,6 @@ describe 'Import/Export - project import integration test', :js do
visit new_project_path visit new_project_path
select2(user.namespace.id, from: '#project_namespace_id')
fill_in :project_name, with: project.name, visible: true fill_in :project_name, with: project.name, visible: true
click_import_project_tab click_import_project_tab
click_link 'GitLab export' click_link 'GitLab export'
......
...@@ -42,6 +42,7 @@ describe('Issue card component', () => { ...@@ -42,6 +42,7 @@ describe('Issue card component', () => {
assignees: [], assignees: [],
reference_path: '#1', reference_path: '#1',
real_path: '/test/1', real_path: '/test/1',
weight: 1,
}); });
component = new Vue({ component = new Vue({
...@@ -287,8 +288,17 @@ describe('Issue card component', () => { ...@@ -287,8 +288,17 @@ describe('Issue card component', () => {
}); });
describe('weights', () => { describe('weights', () => {
it('not shows weight component', () => { it('shows weight component is greater than 0', () => {
expect(component.$el.querySelector('.board-card-weight')).toBeNull(); 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 ...@@ -6,18 +6,17 @@ describe Gitlab::SnippetSearchResults do
include SearchHelpers include SearchHelpers
let!(:snippet) { create(:snippet, content: 'foo', file_name: 'foo') } let!(:snippet) { create(:snippet, content: 'foo', file_name: 'foo') }
let(:results) { described_class.new(snippet.author, 'foo') }
let(:results) { described_class.new(Snippet.all, 'foo') }
describe '#snippet_titles_count' do describe '#snippet_titles_count' do
it 'returns the amount of matched snippet titles' 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
end end
describe '#snippet_blobs_count' do describe '#snippet_blobs_count' do
it 'returns the amount of matched snippet blobs' 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
end end
...@@ -25,8 +24,8 @@ describe Gitlab::SnippetSearchResults do ...@@ -25,8 +24,8 @@ describe Gitlab::SnippetSearchResults do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
where(:scope, :count_method, :expected) do where(:scope, :count_method, :expected) do
'snippet_titles' | :snippet_titles_count | '1234' 'snippet_titles' | :limited_snippet_titles_count | max_limited_count
'snippet_blobs' | :snippet_blobs_count | '1234' 'snippet_blobs' | :limited_snippet_blobs_count | max_limited_count
'projects' | :limited_projects_count | max_limited_count 'projects' | :limited_projects_count | max_limited_count
'unknown' | nil | nil 'unknown' | nil | nil
end end
......
# 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 ...@@ -1966,40 +1966,57 @@ describe Ci::Pipeline, :mailer do
end end
end end
describe '.latest_status_per_commit' do describe '.latest_pipeline_per_commit' do
let(:project) { create(:project) } let(:project) { create(:project) }
before do let!(:commit_123_ref_master) do
pairs = [
%w[success ref1 123],
%w[manual master 123],
%w[failed ref 456]
]
pairs.each do |(status, ref, sha)|
create( create(
:ci_empty_pipeline, :ci_empty_pipeline,
status: status, status: 'success',
ref: ref, ref: 'master',
sha: sha, sha: '123',
project: project project: project
) )
end 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 end
context 'without a ref' do context 'without a ref' do
it 'returns a Hash containing the latest status per commit for all refs' do it 'returns a Hash containing the latest pipeline per commit for all refs' do
expect(described_class.latest_status_per_commit(%w[123 456])) result = described_class.latest_pipeline_per_commit(%w[123 456])
.to eq({ '123' => 'manual', '456' => 'failed' })
expect(result).to match(
'123' => commit_123_ref_develop,
'456' => commit_456_ref_test
)
end end
it 'only includes the status of the given commit SHAs' do it 'only includes the latest pipeline of the given commit SHAs' do
expect(described_class.latest_status_per_commit(%w[123])) result = described_class.latest_pipeline_per_commit(%w[123])
.to eq({ '123' => 'manual' })
expect(result).to match(
'123' => commit_123_ref_develop
)
end end
context 'when there are two pipelines for a ref and SHA' do 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( create(
:ci_empty_pipeline, :ci_empty_pipeline,
status: 'failed', status: 'failed',
...@@ -2007,17 +2024,25 @@ describe Ci::Pipeline, :mailer do ...@@ -2007,17 +2024,25 @@ describe Ci::Pipeline, :mailer do
sha: '123', sha: '123',
project: project 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])) expect(result).to match(
.to eq({ '123' => 'failed' }) '123' => commit_123_ref_master_latest
)
end end
end end
end end
context 'with a ref' do context 'with a ref' do
it 'only includes the pipelines for the given ref' do it 'only includes the pipelines for the given ref' do
expect(described_class.latest_status_per_commit(%w[123 456], 'master')) result = described_class.latest_pipeline_per_commit(%w[123 456], 'master')
.to eq({ '123' => 'manual' })
expect(result).to match(
'123' => commit_123_ref_master
)
end end
end end
end end
......
...@@ -51,6 +51,30 @@ describe CommitCollection do ...@@ -51,6 +51,30 @@ describe CommitCollection do
end end
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 describe 'enrichment methods' do
let(:gitaly_commit) { commit } let(:gitaly_commit) { commit }
let(:hash_commit) { Commit.from_hash(gitaly_commit.to_hash, project) } let(:hash_commit) { Commit.from_hash(gitaly_commit.to_hash, project) }
...@@ -128,27 +152,6 @@ describe CommitCollection do ...@@ -128,27 +152,6 @@ describe CommitCollection do
end end
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 describe '#respond_to_missing?' do
it 'returns true when the underlying Array responds to the message' do it 'returns true when the underlying Array responds to the message' do
collection = described_class.new(project, []) collection = described_class.new(project, [])
......
...@@ -462,78 +462,6 @@ eos ...@@ -462,78 +462,6 @@ eos
end end
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 describe '#participants' do
let(:user1) { build(:user) } let(:user1) { build(:user) }
let(:user2) { 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 ...@@ -17,15 +17,19 @@ describe CommitPresenter do
end end
it 'returns commit status for ref' do 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
end end
context 'when user can not read_commit_status' do context 'when user can not read_commit_status' do
it 'is false' do it 'is nil' do
is_expected.to eq(false) is_expected.to eq(nil)
end end
end end
end end
......
...@@ -24,7 +24,7 @@ module Select2Helper ...@@ -24,7 +24,7 @@ module Select2Helper
selector = options.fetch(:from) selector = options.fetch(:from)
first(selector, visible: false) ensure_select2_loaded(selector)
if options[:multiple] if options[:multiple]
execute_script("$('#{selector}').select2('val', ['#{value}']).trigger('change');") execute_script("$('#{selector}').select2('val', ['#{value}']).trigger('change');")
...@@ -34,14 +34,24 @@ module Select2Helper ...@@ -34,14 +34,24 @@ module Select2Helper
end end
def open_select2(selector) def open_select2(selector)
ensure_select2_loaded(selector)
execute_script("$('#{selector}').select2('open');") execute_script("$('#{selector}').select2('open');")
end end
def close_select2(selector) def close_select2(selector)
ensure_select2_loaded(selector)
execute_script("$('#{selector}').select2('close');") execute_script("$('#{selector}').select2('close');")
end end
def scroll_select2_to_bottom(selector) def scroll_select2_to_bottom(selector)
evaluate_script "$('#{selector}').scrollTop($('#{selector}')[0].scrollHeight); $('#{selector}');" evaluate_script "$('#{selector}').scrollTop($('#{selector}')[0].scrollHeight); $('#{selector}');"
end end
private
def ensure_select2_loaded(selector)
first(selector, visible: :all).sibling('.select2-container')
end
end end
# frozen_string_literal: true
module StrategyHelpers module StrategyHelpers
include Rack::Test::Methods include Rack::Test::Methods
include ActionDispatch::Assertions::ResponseAssertions include ActionDispatch::Assertions::ResponseAssertions
......
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
...@@ -995,10 +995,10 @@ ...@@ -995,10 +995,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.75.0.tgz#93f9e6bdef78dd84ac88d8273711dc1f25e4e5ac" resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.75.0.tgz#93f9e6bdef78dd84ac88d8273711dc1f25e4e5ac"
integrity sha512-hOCfF73++yG+KTYxaQNMkbDUg0XKije41g6XR2dgj7466rzZmebG/nt6pUXonmlqy/NLGaRUPBKs0zuM7tcLhA== integrity sha512-hOCfF73++yG+KTYxaQNMkbDUg0XKije41g6XR2dgj7466rzZmebG/nt6pUXonmlqy/NLGaRUPBKs0zuM7tcLhA==
"@gitlab/ui@5.26.1": "@gitlab/ui@5.26.2":
version "5.26.1" version "5.26.2"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.26.1.tgz#1662af9be444239bcf9570ea9a4c31aef4639a00" resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-5.26.2.tgz#b1474152e91a7f208f4c454a83b4f2f492afce57"
integrity sha512-PtOuAQWkShA5O78Gy16BtBsic1O9jl7dc07jusgYL5rmIYw/cRZiI92/RZwkelJahdbJpaD9YulsjgsejDIp8g== integrity sha512-atRTd7C2rby1vWQNAT2aokGHTAFcNtQsIhKmIC0Q1phnsnyWHVqT/xURr9cAiBpGznPooNVlQDldLOBqSoKcHA==
dependencies: dependencies:
"@babel/standalone" "^7.0.0" "@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.2.1" "@gitlab/vue-toasted" "^1.2.1"
......
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