Commit fff9f8dc authored by Steve Azzopardi's avatar Steve Azzopardi

Merge branch 'master' into ce-to-ee-2018-12-04

parents d1aa3afd 45f87552
......@@ -1157,9 +1157,10 @@ review-deploy:
review-qa-smoke:
<<: *review-qa-base
retry: 2
# retry: 2
script:
- gitlab-qa Test::Instance::Smoke "${QA_IMAGE}" "${CI_ENVIRONMENT_URL}"
allow_failure: true
review-qa-all:
<<: *review-qa-base
......@@ -1204,27 +1205,3 @@ schedule:review-cleanup:
- gem install gitlab --no-document
script:
- ruby -rrubygems scripts/review_apps/automated_cleanup.rb
merge:master:
image: registry.gitlab.com/gitlab-org/merge-train
stage: merge
# The global before_script/after_script blocks break this job, or aren't
# necessary. These two lines result in them being ignored.
before_script: []
after_script: []
only:
refs:
- master
- schedules
variables:
- $CI_PROJECT_PATH == "gitlab-org/gitlab-ce"
- $MERGE_TRAIN_SSH_PUBLIC_KEY
- $MERGE_TRAIN_SSH_PRIVATE_KEY
- $MERGE_TRAIN_API_TOKEN
- $MERGE_FORCE_ENABLE
script:
- scripts/merge-train
cache:
paths:
- gitlab-ee
key: "merge:master"
......@@ -411,11 +411,10 @@ export default {
<div slot="description">
<p>
{{
s__(`ClusterIntegration|Knative (pronounced kay-nay-tiv) extends
Kubernetes to provide a set of middleware components that are
essential to build modern, source-centric, and container-based
applications that can run anywhere: on premises, in the cloud, or
even in a third-party data center.`)
s__(`ClusterIntegration|Knative extends Kubernetes to provide
a set of middleware components that are essential to build modern,
source-centric, and container-based applications that can run
anywhere: on premises, in the cloud, or even in a third-party data center.`)
}}
</p>
......
......@@ -148,7 +148,7 @@ export const scrollToLineIfNeededParallel = (_, line) => {
};
export const loadCollapsedDiff = ({ commit }, file) =>
axios.get(file.loadCollapsedDiffUrl).then(res => {
axios.get(file.load_collapsed_diff_url).then(res => {
commit(types.ADD_COLLAPSED_DIFFS, {
file,
data: res.data,
......
......@@ -100,18 +100,12 @@ module Boards
.merge(board_id: params[:board_id], list_id: params[:list_id], request: request)
end
def serializer
IssueSerializer.new(current_user: current_user)
end
def serialize_as_json(resource)
resource.as_json(
only: [:id, :iid, :project_id, :title, :confidential, :due_date, :relative_position, :weight],
labels: true,
issue_endpoints: true,
include_full_project_path: board.group_board?,
include: {
project: { only: [:id, :path] },
assignees: { only: [:id, :name, :username], methods: [:avatar_url] },
milestone: { only: [:id, :title] }
}
)
serializer.represent(resource, serializer: 'board', include_full_project_path: board.group_board?)
end
def whitelist_query_limiting
......
......@@ -22,7 +22,7 @@ class Projects::BranchesController < Projects::ApplicationController
# Fetch branches for the specified mode
fetch_branches_by_mode
@refs_pipelines = @project.pipelines.latest_successful_for_refs(@branches.map(&:name))
@refs_pipelines = @project.ci_pipelines.latest_successful_for_refs(@branches.map(&:name))
@merged_branch_names = repository.merged_branch_names(@branches.map(&:name))
# n+1: https://gitlab.com/gitlab-org/gitaly/issues/992
......
......@@ -48,7 +48,7 @@ class Projects::PipelinesController < Projects::ApplicationController
end
def new
@pipeline = project.pipelines.new(ref: @project.default_branch)
@pipeline = project.all_pipelines.new(ref: @project.default_branch)
end
def create
......@@ -144,9 +144,9 @@ class Projects::PipelinesController < Projects::ApplicationController
@charts[:pipeline_times] = Gitlab::Ci::Charts::PipelineTime.new(project)
@counts = {}
@counts[:total] = @project.pipelines.count(:all)
@counts[:success] = @project.pipelines.success.count(:all)
@counts[:failed] = @project.pipelines.failed.count(:all)
@counts[:total] = @project.all_pipelines.count(:all)
@counts[:success] = @project.all_pipelines.success.count(:all)
@counts[:failed] = @project.all_pipelines.failed.count(:all)
end
private
......@@ -166,7 +166,7 @@ class Projects::PipelinesController < Projects::ApplicationController
# rubocop: disable CodeReuse/ActiveRecord
def pipeline
@pipeline ||= project
.pipelines
.all_pipelines
.includes(user: :status)
.find_by!(id: params[:id])
.present(current_user: current_user)
......
......@@ -20,7 +20,7 @@ class Projects::TagsController < Projects::ApplicationController
@tags = Kaminari.paginate_array(@tags).page(params[:page])
tag_names = @tags.map(&:name)
@tags_pipelines = @project.pipelines.latest_successful_for_refs(tag_names)
@tags_pipelines = @project.ci_pipelines.latest_successful_for_refs(tag_names)
@releases = project.releases.where(tag: tag_names)
respond_to do |format|
......
......@@ -8,7 +8,7 @@ class PipelinesFinder
def initialize(project, current_user, params = {})
@project = project
@current_user = current_user
@pipelines = project.pipelines
@pipelines = project.all_pipelines
@params = params
end
......
......@@ -122,6 +122,18 @@ module SortingHelper
}
end
def users_sort_options_hash
{
sort_value_name => sort_title_name,
sort_value_recently_signin => sort_title_recently_signin,
sort_value_oldest_signin => sort_title_oldest_signin,
sort_value_recently_created => sort_title_recently_created,
sort_value_oldest_created => sort_title_oldest_created,
sort_value_recently_updated => sort_title_recently_updated,
sort_value_oldest_updated => sort_title_oldest_updated
}
end
def sortable_item(item, path, sorted_by)
link_to item, path, class: sorted_by == item ? 'is-active' : ''
end
......
......@@ -14,13 +14,14 @@ module Ci
prepend ::EE::Ci::Pipeline
belongs_to :project, inverse_of: :pipelines
belongs_to :project, inverse_of: :all_pipelines
belongs_to :user
belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'
belongs_to :pipeline_schedule, class_name: 'Ci::PipelineSchedule'
belongs_to :merge_request, class_name: 'MergeRequest'
has_internal_id :iid, scope: :project, presence: false, init: ->(s) do
s&.project&.pipelines&.maximum(:iid) || s&.project&.pipelines&.count
s&.project&.all_pipelines&.maximum(:iid) || s&.project&.all_pipelines&.count
end
has_one :source_pipeline, class_name: Ci::Sources::Pipeline
......@@ -61,6 +62,9 @@ module Ci
validates :sha, presence: { unless: :importing? }
validates :ref, presence: { unless: :importing? }
validates :merge_request, presence: { if: :merge_request? }
validates :merge_request, absence: { unless: :merge_request? }
validates :tag, inclusion: { in: [false], if: :merge_request? }
validates :status, presence: { unless: :importing? }
validate :valid_commit_sha, unless: :importing?
......@@ -182,6 +186,14 @@ module Ci
end
scope :internal, -> { where(source: internal_sources) }
scope :ci_sources, -> { where(config_source: ci_sources_values) }
scope :sort_by_merge_request_pipelines, -> do
sql = 'CASE ci_pipelines.source WHEN (?) THEN 0 ELSE 1 END, ci_pipelines.id DESC'
query = ActiveRecord::Base.send(:sanitize_sql_array, [sql, sources[:merge_request]]) # rubocop:disable GitlabSecurity/PublicSend
order(query)
end
scope :for_user, -> (user) { where(user: user) }
......@@ -272,6 +284,10 @@ module Ci
sources.reject { |source| source == "external" }.values
end
def self.ci_sources_values
config_sources.values_at(:repository_source, :auto_devops_source, :unknown_source)
end
def stages_count
statuses.select(:stage).distinct.count
end
......@@ -384,7 +400,7 @@ module Ci
end
def branch?
!tag?
!tag? && !merge_request?
end
def stuck?
......@@ -631,7 +647,12 @@ module Ci
# All the merge requests for which the current pipeline runs/ran against
def all_merge_requests
@all_merge_requests ||= project.merge_requests.where(source_branch: ref)
@all_merge_requests ||=
if merge_request?
project.merge_requests.where(id: merge_request.id)
else
project.merge_requests.where(source_branch: ref)
end
end
def detailed_status(current_user)
......@@ -708,6 +729,8 @@ module Ci
def git_ref
if branch?
Gitlab::Git::BRANCH_REF_PREFIX + ref.to_s
elsif merge_request?
Gitlab::Git::BRANCH_REF_PREFIX + ref.to_s
elsif tag?
Gitlab::Git::TAG_REF_PREFIX + ref.to_s
else
......
......@@ -21,7 +21,8 @@ module Ci
trigger: 3,
schedule: 4,
api: 5,
external: 6
external: 6,
merge_request: 10
}
end
end
......
......@@ -298,7 +298,7 @@ class Commit
end
def pipelines
project.pipelines.where(sha: sha)
project.ci_pipelines.where(sha: sha)
end
def last_pipeline
......@@ -312,7 +312,7 @@ class Commit
end
def status_for_project(ref, pipeline_project)
pipeline_project.pipelines.latest_status_per_commit(id, ref)[id]
pipeline_project.ci_pipelines.latest_status_per_commit(id, ref)[id]
end
def set_status_for_ref(ref, status)
......
......@@ -24,7 +24,7 @@ class CommitCollection
# 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.pipelines.latest_status_per_commit(map(&:id), ref)
statuses = project.ci_pipelines.latest_status_per_commit(map(&:id), ref)
each do |commit|
commit.set_status_for_ref(ref, statuses[commit.id])
......
......@@ -6,12 +6,12 @@ class WebHook < ActiveRecord::Base
attr_encrypted :token,
mode: :per_attribute_iv,
algorithm: 'aes-256-gcm',
key: Settings.attr_encrypted_db_key_base_truncated
key: Settings.attr_encrypted_db_key_base_32
attr_encrypted :url,
mode: :per_attribute_iv,
algorithm: 'aes-256-gcm',
key: Settings.attr_encrypted_db_key_base_truncated
key: Settings.attr_encrypted_db_key_base_32
has_many :web_hook_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
......
......@@ -235,20 +235,6 @@ class Issue < ActiveRecord::Base
def as_json(options = {})
super(options).tap do |json|
if options.key?(:issue_endpoints) && project
url_helper = Gitlab::Routing.url_helpers
issue_reference = options[:include_full_project_path] ? to_reference(full: true) : to_reference
json.merge!(
reference_path: issue_reference,
real_path: url_helper.project_issue_path(project, self),
issue_sidebar_endpoint: url_helper.project_issue_path(project, self, format: :json, serializer: 'sidebar'),
toggle_subscription_endpoint: url_helper.toggle_subscription_project_issue_path(project, self),
assignable_labels_endpoint: url_helper.project_labels_path(project, format: :json, include_ancestor_groups: true)
)
end
if options.key?(:labels)
json[:labels] = labels.as_json(
project: project,
......
......@@ -65,6 +65,7 @@ class MergeRequest < ActiveRecord::Base
dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_many :cached_closes_issues, through: :merge_requests_closing_issues, source: :issue
has_many :merge_request_pipelines, foreign_key: 'merge_request_id', class_name: 'Ci::Pipeline'
belongs_to :assignee, class_name: "User"
......@@ -1054,12 +1055,17 @@ class MergeRequest < ActiveRecord::Base
diverged_commits_count > 0
end
def all_pipelines
def all_pipelines(shas: all_commit_shas)
return Ci::Pipeline.none unless source_project
@all_pipelines ||= source_project.pipelines
.where(sha: all_commit_shas, ref: source_branch)
.order(id: :desc)
@all_pipelines ||= source_project.ci_pipelines
.where(sha: shas, ref: source_branch)
.where(merge_request: [nil, self])
.sort_by_merge_request_pipelines
end
def merge_request_pipeline_exists?
merge_request_pipelines.exists?(sha: diff_head_sha)
end
def has_test_reports?
......@@ -1216,7 +1222,7 @@ class MergeRequest < ActiveRecord::Base
end
def base_pipeline
@base_pipeline ||= project.pipelines
@base_pipeline ||= project.ci_pipelines
.order(id: :desc)
.find_by(sha: diff_base_sha)
end
......
......@@ -248,7 +248,17 @@ class Project < ActiveRecord::Base
has_many :container_repositories, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :commit_statuses
has_many :pipelines, class_name: 'Ci::Pipeline', inverse_of: :project
# The relation :all_pipelines is intented to be used when we want to get the
# whole list of pipelines associated to the project
has_many :all_pipelines, class_name: 'Ci::Pipeline', inverse_of: :project
# The relation :ci_pipelines is intented to be used when we want to get only
# those pipeline which are directly related to CI. There are
# other pipelines, like webide ones, that we won't retrieve
# if we use this relation.
has_many :ci_pipelines,
-> { Feature.enabled?(:pipeline_ci_sources_only, default_enabled: true) ? ci_sources : all },
class_name: 'Ci::Pipeline',
inverse_of: :project
has_many :stages, class_name: 'Ci::Stage', inverse_of: :project
# Ci::Build objects store data on the file system such as artifact files and
......@@ -621,7 +631,7 @@ class Project < ActiveRecord::Base
# ref can't be HEAD, can only be branch/tag name or SHA
def latest_successful_builds_for(ref = default_branch)
latest_pipeline = pipelines.latest_successful_for(ref)
latest_pipeline = ci_pipelines.latest_successful_for(ref)
if latest_pipeline
latest_pipeline.builds.latest.with_artifacts_archive
......@@ -1141,6 +1151,11 @@ class Project < ActiveRecord::Base
"#{web_url}.git"
end
# Is overriden in EE
def lfs_http_url_to_repo(_)
http_url_to_repo
end
def forked?
fork_network && fork_network.root_project != self
end
......@@ -1371,7 +1386,7 @@ class Project < ActiveRecord::Base
return unless sha
pipelines.order(id: :desc).find_by(sha: sha, ref: ref)
ci_pipelines.order(id: :desc).find_by(sha: sha, ref: ref)
end
def latest_successful_pipeline_for_default_branch
......@@ -1380,12 +1395,12 @@ class Project < ActiveRecord::Base
end
@latest_successful_pipeline_for_default_branch =
pipelines.latest_successful_for(default_branch)
ci_pipelines.latest_successful_for(default_branch)
end
def latest_successful_pipeline_for(ref = nil)
if ref && ref != default_branch
pipelines.latest_successful_for(ref)
ci_pipelines.latest_successful_for(ref)
else
latest_successful_pipeline_for_default_branch
end
......
......@@ -38,11 +38,11 @@ class PipelinesEmailService < Service
end
def can_test?
project.pipelines.any?
project.ci_pipelines.any?
end
def test_data(project, user)
data = Gitlab::DataBuilder::Pipeline.build(project.pipelines.last)
data = Gitlab::DataBuilder::Pipeline.build(project.ci_pipelines.last)
data[:user] = user.hook_attrs
data
end
......
......@@ -180,7 +180,7 @@ def index
render json: MyResourceSerializer
.new(current_user: @current_user)
.represent_details(@project.resources)
nd
end
end
```
......@@ -196,7 +196,7 @@ def index
.represent_details(@project.resources),
count: @project.resources.count
}
nd
end
end
```
......
# frozen_string_literal: true
class IssueBoardEntity < Grape::Entity
include RequestAwareEntity
prepend ::EE::IssueBoardEntity
expose :id
expose :iid
expose :title
expose :confidential
expose :due_date
expose :project_id
expose :relative_position
expose :project do |issue|
API::Entities::Project.represent issue.project, only: [:id, :path]
end
expose :milestone, expose_nil: false do |issue|
API::Entities::Project.represent issue.milestone, only: [:id, :title]
end
expose :assignees do |issue|
API::Entities::UserBasic.represent issue.assignees, only: [:id, :name, :username, :avatar_url]
end
expose :labels do |issue|
LabelEntity.represent issue.labels, project: issue.project, only: [:id, :title, :description, :color, :priority, :text_color]
end
expose :reference_path, if: -> (issue) { issue.project } do |issue, options|
options[:include_full_project_path] ? issue.to_reference(full: true) : issue.to_reference
end
expose :real_path, if: -> (issue) { issue.project } do |issue|
project_issue_path(issue.project, issue)
end
expose :issue_sidebar_endpoint, if: -> (issue) { issue.project } do |issue|
project_issue_path(issue.project, issue, format: :json, serializer: 'sidebar')
end
expose :toggle_subscription_endpoint, if: -> (issue) { issue.project } do |issue|
toggle_subscription_project_issue_path(issue.project, issue)
end
expose :assignable_labels_endpoint, if: -> (issue) { issue.project } do |issue|
project_labels_path(issue.project, format: :json, include_ancestor_groups: true)
end
end
......@@ -4,15 +4,17 @@ class IssueSerializer < BaseSerializer
# This overrided method takes care of which entity should be used
# to serialize the `issue` based on `basic` key in `opts` param.
# Hence, `entity` doesn't need to be declared on the class scope.
def represent(merge_request, opts = {})
def represent(issue, opts = {})
entity =
case opts[:serializer]
when 'sidebar'
IssueSidebarEntity
when 'board'
IssueBoardEntity
else
IssueEntity
end
super(merge_request, opts, entity)
super(issue, opts, entity)
end
end
......@@ -12,4 +12,8 @@ class LabelEntity < Grape::Entity
expose :text_color
expose :created_at
expose :updated_at
expose :priority, if: -> (*) { options.key?(:project) } do |label|
label.priority(options[:project])
end
end
......@@ -48,6 +48,7 @@ class PipelineEntity < Grape::Entity
expose :tag?, as: :tag
expose :branch?, as: :branch
expose :merge_request?, as: :merge_request
end
expose :commit, using: CommitEntity
......
......@@ -17,7 +17,7 @@ module Ci
Gitlab::Ci::Pipeline::Chain::Create,
EE::Gitlab::Ci::Pipeline::Chain::Limit::Activity].freeze
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, mirror_update: false, &block)
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, merge_request: nil, mirror_update: false, &block)
@pipeline = Ci::Pipeline.new
command = Gitlab::Ci::Pipeline::Chain::Command.new(
......@@ -28,6 +28,7 @@ module Ci
before_sha: params[:before],
trigger_request: trigger_request,
schedule: schedule,
merge_request: merge_request,
ignore_skip_ci: ignore_skip_ci,
save_incompleted: save_on_errors,
seeds_block: block,
......@@ -85,7 +86,7 @@ module Ci
# rubocop: disable CodeReuse/ActiveRecord
def auto_cancelable_pipelines
project.pipelines
project.ci_pipelines
.where(ref: pipeline.ref)
.where.not(id: pipeline.id)
.where.not(sha: project.commit(pipeline.ref).try(:id))
......
......@@ -12,7 +12,8 @@ module Clusters
create_gitlab_service_account!
configure_kubernetes
cluster.save!
configure_project_service_account
ClusterPlatformConfigureWorker.perform_async(cluster.id)
rescue Google::Apis::ServerError, Google::Apis::ClientError, Google::Apis::AuthorizationError => e
provider.make_errored!("Failed to request to CloudPlatform; #{e.message}")
......@@ -25,7 +26,7 @@ module Clusters
private
def create_gitlab_service_account!
Clusters::Gcp::Kubernetes::CreateServiceAccountService.gitlab_creator(
Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService.gitlab_creator(
kube_client,
rbac: create_rbac_cluster?
).execute
......@@ -55,15 +56,6 @@ module Clusters
).execute
end
def configure_project_service_account
kubernetes_namespace = cluster.find_or_initialize_kubernetes_namespace(cluster.cluster_project)
Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService.new(
cluster: cluster,
kubernetes_namespace: kubernetes_namespace
).execute
end
def authorization_type
create_rbac_cluster? ? 'rbac' : 'abac'
end
......
......@@ -27,7 +27,7 @@ module Clusters
end
def create_project_service_account
Clusters::Gcp::Kubernetes::CreateServiceAccountService.namespace_creator(
Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService.namespace_creator(
platform.kubeclient,
service_account_name: kubernetes_namespace.service_account_name,
service_account_namespace: kubernetes_namespace.namespace,
......
......@@ -3,7 +3,7 @@
module Clusters
module Gcp
module Kubernetes
class CreateServiceAccountService
class CreateOrUpdateServiceAccountService
def initialize(kubeclient, service_account_name:, service_account_namespace:, token_name:, rbac:, namespace_creator: false, role_binding_name: nil)
@kubeclient = kubeclient
@service_account_name = service_account_name
......@@ -38,8 +38,9 @@ module Clusters
def execute
ensure_project_namespace_exists if namespace_creator
kubeclient.create_service_account(service_account_resource)
kubeclient.create_secret(service_account_token_resource)
kubeclient.create_or_update_service_account(service_account_resource)
kubeclient.create_or_update_secret(service_account_token_resource)
create_role_or_cluster_role_binding if rbac
end
......@@ -56,9 +57,9 @@ module Clusters
def create_role_or_cluster_role_binding
if namespace_creator
kubeclient.create_role_binding(role_binding_resource)
kubeclient.create_or_update_role_binding(role_binding_resource)
else
kubeclient.create_cluster_role_binding(cluster_role_binding_resource)
kubeclient.create_or_update_cluster_role_binding(cluster_role_binding_resource)
end
end
......
......@@ -54,6 +54,24 @@ module MergeRequests
merge_request, merge_request.project, current_user, merge_request.assignee)
end
def create_merge_request_pipeline(merge_request, user)
return unless Feature.enabled?(:ci_merge_request_pipeline,
merge_request.source_project,
default_enabled: true)
##
# UpdateMergeRequestsWorker could be retried by an exception.
# MR pipelines should not be recreated in such case.
return if merge_request.merge_request_pipeline_exists?
Ci::CreatePipelineService
.new(merge_request.source_project, user, ref: merge_request.source_branch)
.execute(:merge_request,
ignore_skip_ci: true,
save_on_errors: false,
merge_request: merge_request)
end
# Returns all origin and fork merge requests from `@project` satisfying passed arguments.
# rubocop: disable CodeReuse/ActiveRecord
def merge_requests_for(source_branch, mr_states: [:opened])
......
......@@ -25,6 +25,7 @@ module MergeRequests
def after_create(issuable)
todo_service.new_merge_request(issuable, current_user)
issuable.cache_merge_request_closes_issues!(current_user)
create_merge_request_pipeline(issuable, current_user)
update_merge_requests_head_pipeline(issuable)
super
......@@ -49,18 +50,14 @@ module MergeRequests
merge_request.update(head_pipeline_id: pipeline.id) if pipeline
end
# rubocop: disable CodeReuse/ActiveRecord
def head_pipeline_for(merge_request)
return unless merge_request.source_project
sha = merge_request.source_branch_sha
return unless sha
pipelines = merge_request.source_project.pipelines.where(ref: merge_request.source_branch, sha: sha)
pipelines.order(id: :desc).first
merge_request.all_pipelines(shas: sha).first
end
# rubocop: enable CodeReuse/ActiveRecord
def set_projects!
# @project is used to determine whether the user can set the merge request's
......
......@@ -92,6 +92,7 @@ module MergeRequests
end
merge_request.mark_as_unchecked
create_merge_request_pipeline(merge_request, current_user)
UpdateHeadPipelineForMergeRequestWorker.perform_async(merge_request.id)
end
......
......@@ -34,7 +34,7 @@ module Projects
end
def auto_devops_pipelines
@auto_devops_pipelines ||= project.pipelines.auto_devops_source
@auto_devops_pipelines ||= project.ci_pipelines.auto_devops_source
end
end
end
......
......@@ -49,7 +49,7 @@ module TestHooks
end
def pipeline_events_data
pipeline = project.pipelines.first
pipeline = project.ci_pipelines.first
throw(:validation_error, 'Ensure the project has CI pipelines.') unless pipeline.present?
Gitlab::DataBuilder::Pipeline.build(pipeline)
......
......@@ -9,28 +9,20 @@
.search-holder
.search-field-holder
= search_field_tag :search_query, params[:search_query], placeholder: 'Search by name, email or username', class: 'form-control search-text-input js-search-input', spellcheck: false
- if @sort.present?
= hidden_field_tag :sort, @sort
= icon("search", class: "search-icon")
.dropdown
- toggle_text = if @sort.present? then sort_options_hash[@sort] else sort_title_name end
= button_tag 'Search users' if Rails.env.test?
.dropdown.user-sort-dropdown
- toggle_text = if @sort.present? then users_sort_options_hash[@sort] else sort_title_name end
= dropdown_toggle(toggle_text, { toggle: 'dropdown' })
%ul.dropdown-menu.dropdown-menu-right
%li.dropdown-header
Sort by
%li
= link_to admin_users_path(sort: sort_value_name, filter: params[:filter]) do
= sort_title_name
= link_to admin_users_path(sort: sort_value_recently_signin, filter: params[:filter]) do
= sort_title_recently_signin
= link_to admin_users_path(sort: sort_value_oldest_signin, filter: params[:filter]) do
= sort_title_oldest_signin
= link_to admin_users_path(sort: sort_value_recently_created, filter: params[:filter]) do
= sort_title_recently_created
= link_to admin_users_path(sort: sort_value_oldest_created, filter: params[:filter]) do
= sort_title_oldest_created
= link_to admin_users_path(sort: sort_value_recently_updated, filter: params[:filter]) do
= sort_title_recently_updated
= link_to admin_users_path(sort: sort_value_oldest_updated, filter: params[:filter]) do
= sort_title_oldest_updated
- users_sort_options_hash.each do |value, title|
= link_to admin_users_path(sort: value, filter: params[:filter], search_query: params[:search_query]) do
= title
= link_to 'Send email to users', admin_email_path, class: 'btn'
= link_to 'New user', new_admin_user_path, class: 'btn btn-success btn-search'
......
......@@ -6,10 +6,11 @@ class UpdateHeadPipelineForMergeRequestWorker
queue_namespace :pipeline_processing
# rubocop: disable CodeReuse/ActiveRecord
def perform(merge_request_id)
merge_request = MergeRequest.find(merge_request_id)
pipeline = Ci::Pipeline.where(project: merge_request.source_project, ref: merge_request.source_branch).last
sha = merge_request.diff_head_sha
pipeline = merge_request.all_pipelines(shas: sha).first
return unless pipeline && pipeline.latest?
......@@ -21,7 +22,6 @@ class UpdateHeadPipelineForMergeRequestWorker
merge_request.update_attribute(:head_pipeline_id, pipeline.id)
end
# rubocop: enable CodeReuse/ActiveRecord
def log_error_message_for(merge_request)
Rails.logger.error(
......
---
title: Allow search and sort users at same time on admin users page
merge_request: 23439
author:
type: fixed
---
title: Fix web hook functionality when the database encryption key is too short
merge_request: 23573
author:
type: fixed
---
title: Define the default value for only/except policies
merge_request: 23531
author:
type: changed
---
title: Merge request pipelines
merge_request: 23217
author:
type: added
---
title: Updates service to update Kubernetes project namespaces and restricted service
account if present
merge_request: 23525
author:
type: changed
......@@ -111,7 +111,7 @@ class Gitlab::Seeder::Pipelines
end
def create_pipeline!(project, ref, commit)
project.pipelines.create!(sha: commit.id, ref: ref, source: :push)
project.ci_pipelines.create!(sha: commit.id, ref: ref, source: :push)
end
def build_create!(pipeline, opts = {})
......
......@@ -115,7 +115,7 @@ class Gitlab::Seeder::Vulnerabilities
end
def pipeline
@pipeline ||= project.pipelines.where(ref: project.default_branch).last
@pipeline ||= project.ci_pipelines.where(ref: project.default_branch).last
end
def author
......@@ -124,7 +124,7 @@ class Gitlab::Seeder::Vulnerabilities
end
Gitlab::Seeder.quiet do
Project.joins(:pipelines).uniq.all.sample(5).each do |project|
Project.joins(:ci_pipelines).uniq.all.sample(5).each do |project|
seeder = Gitlab::Seeder::Vulnerabilities.new(project)
seeder.seed!
end
......
# frozen_string_literal: true
class AddMergeRequestIdToCiPipelines < ActiveRecord::Migration
DOWNTIME = false
def up
add_column :ci_pipelines, :merge_request_id, :integer
end
def down
remove_column :ci_pipelines, :merge_request_id, :integer
end
end
# frozen_string_literal: true
class AddForeignKeyToCiPipelinesMergeRequests < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :ci_pipelines, :merge_request_id
add_concurrent_foreign_key :ci_pipelines, :merge_requests, column: :merge_request_id, on_delete: :cascade
end
def down
if foreign_key_exists?(:ci_pipelines, :merge_requests, column: :merge_request_id)
remove_foreign_key :ci_pipelines, :merge_requests
end
remove_concurrent_index :ci_pipelines, :merge_request_id
end
end
......@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20181126153547) do
ActiveRecord::Schema.define(version: 20181203154104) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -577,7 +577,9 @@ ActiveRecord::Schema.define(version: 20181126153547) do
t.boolean "protected"
t.integer "failure_reason"
t.integer "iid"
t.integer "merge_request_id"
t.index ["auto_canceled_by_id"], name: "index_ci_pipelines_on_auto_canceled_by_id", using: :btree
t.index ["merge_request_id"], name: "index_ci_pipelines_on_merge_request_id", using: :btree
t.index ["pipeline_schedule_id"], name: "index_ci_pipelines_on_pipeline_schedule_id", using: :btree
t.index ["project_id", "iid"], name: "index_ci_pipelines_on_project_id_and_iid", unique: true, where: "(iid IS NOT NULL)", using: :btree
t.index ["project_id", "ref", "status", "id"], name: "index_ci_pipelines_on_project_id_and_ref_and_status_and_id", using: :btree
......@@ -3124,6 +3126,7 @@ ActiveRecord::Schema.define(version: 20181126153547) do
add_foreign_key "ci_pipeline_variables", "ci_pipelines", column: "pipeline_id", name: "fk_f29c5f4380", on_delete: :cascade
add_foreign_key "ci_pipelines", "ci_pipeline_schedules", column: "pipeline_schedule_id", name: "fk_3d34ab2e06", on_delete: :nullify
add_foreign_key "ci_pipelines", "ci_pipelines", column: "auto_canceled_by_id", name: "fk_262d4c2d19", on_delete: :nullify
add_foreign_key "ci_pipelines", "merge_requests", name: "fk_a23be95014", on_delete: :cascade
add_foreign_key "ci_pipelines", "projects", name: "fk_86635dbd80", on_delete: :cascade
add_foreign_key "ci_runner_namespaces", "ci_runners", column: "runner_id", on_delete: :cascade
add_foreign_key "ci_runner_namespaces", "namespaces", on_delete: :cascade
......
......@@ -22,7 +22,7 @@ PUT /projects/:id/repository/submodules/:submodule
| `commit_message` | string | no | Commit message. If no message is provided, a default one will be set |
```sh
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repositories/submodules/lib%2Fmodules%2Fexample"
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repository/submodules/lib%2Fmodules%2Fexample"
--data "branch=master&commit_sha=3ddec28ea23acc5caa5d8331a6ecb2a65fc03e88&commit_message=Update submodule reference"
```
......
......@@ -58,7 +58,7 @@ installed before running `make`.
To install on Debian or Ubutu, run:
```
```sh
sudo apt install libicu-dev
```
......@@ -66,7 +66,7 @@ sudo apt install libicu-dev
To install on macOS, run:
```
```sh
brew install icu4c
export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig:$PKG_CONFIG_PATH"
```
......@@ -75,7 +75,7 @@ export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig:$PKG_CONFIG_PATH"
To build and install the indexer, run:
```
```sh
make
sudo make install
```
......@@ -87,7 +87,7 @@ Please remember to pass the `-E` flag to `sudo` if you do so.
Example:
```
```sh
PREFIX=/usr sudo -E make install
```
......@@ -138,7 +138,7 @@ To disable the Elasticsearch integration:
Configure Elasticsearch's host and port in **Admin > Settings**. Then create empty indexes using one of the following commands:
```
```sh
# Omnibus installations
sudo gitlab-rake gitlab:elastic:create_empty_index
......@@ -148,7 +148,7 @@ bundle exec rake gitlab:elastic:create_empty_index RAILS_ENV=production
Then enable Elasticsearch indexing and run repository indexing tasks:
```
```sh
# Omnibus installations
sudo gitlab-rake gitlab:elastic:index
......@@ -162,7 +162,7 @@ Enable Elasticsearch search.
Configure Elasticsearch's host and port in **Admin > Settings**. Then create empty indexes using one of the following commands:
```
```sh
# Omnibus installations
sudo gitlab-rake gitlab:elastic:create_empty_index
......@@ -184,7 +184,7 @@ curl --request PUT localhost:9200/gitlab-production/_settings --data '{
Then enable Elasticsearch indexing and run repository indexing tasks:
```
```sh
# Omnibus installations
sudo gitlab-rake gitlab:elastic:index_repositories_async
......@@ -196,8 +196,7 @@ This enqueues a number of Sidekiq jobs to index your existing repositories.
You can view the jobs in the admin panel (they are placed in the `elastic_batch_project_indexer`)
queue), or you can query indexing status using a rake task:
```
```sh
# Omnibus installations
sudo gitlab-rake gitlab:elastic:index_repositories_status
......@@ -215,7 +214,7 @@ You can also run the initial indexing synchronously - this is most useful if
you have a small number of projects, or need finer-grained control over indexing
than Sidekiq permits:
```
```sh
# Omnibus installations
sudo gitlab-rake gitlab:elastic:index_repositories
......@@ -228,7 +227,7 @@ It might take a while depending on how big your Git repositories are.
If you want to run several tasks in parallel (probably in separate terminal
windows) you can provide the `ID_FROM` and `ID_TO` parameters:
```
```sh
# Omnibus installations
sudo gitlab-rake gitlab:elastic:index_repositories ID_FROM=1001 ID_TO=2000
......@@ -239,7 +238,7 @@ bundle exec rake gitlab:elastic:index_repositories ID_FROM=1001 ID_TO=2000 RAILS
Where `ID_FROM` and `ID_TO` are project IDs. Both parameters are optional.
As an example, if you have 3,000 repositories and you want to run three separate indexing tasks, you might run:
```
```sh
# Omnibus installations
sudo gitlab-rake gitlab:elastic:index_repositories ID_TO=1000
sudo gitlab-rake gitlab:elastic:index_repositories ID_FROM=1001 ID_TO=2000
......@@ -261,7 +260,7 @@ database, you can run the indexer with the special parameter `UPDATE_INDEX` and
it will check every project repository again to make sure that every commit in
that repository is indexed, it can be useful in case if your index is outdated:
```
```sh
# Omnibus installations
sudo gitlab-rake gitlab:elastic:index_repositories UPDATE_INDEX=true ID_TO=1000
......@@ -275,7 +274,7 @@ start.
To index all wikis:
```
```sh
# Omnibus installations
sudo gitlab-rake gitlab:elastic:index_wikis
......@@ -288,7 +287,7 @@ to limit a project set.
Index all database entities (Keep in mind it can take a while so consider using `screen` or `tmux`):
```
```sh
# Omnibus installations
sudo gitlab-rake gitlab:elastic:index_database
......@@ -318,6 +317,12 @@ Enable Elasticsearch search in **Admin > Settings**. That's it. Enjoy it!
Here are some common pitfalls and how to overcome them:
- **I updated GitLab and now I can't find anything**
We continuously make updates to our indexing strategies and aim to support
newer versions of Elasticsearch. When indexing changes are made, it may
be necessary for you to [reindex](#adding-gitlabs-data-to-the-elasticsearch-index) after updating GitLab.
- **I indexed all the repositories but I can't find anything**
Make sure you indexed all the database data [as stated above](#adding-gitlab-data-to-the-elasticsearch-index).
......@@ -331,7 +336,7 @@ Here are some common pitfalls and how to overcome them:
If you enabled Elasticsearch before GitLab 8.12 and have not rebuilt indexes you will get
exception in lots of different cases:
```
```text
Elasticsearch::Transport::Transport::Errors::BadRequest([400] {
"error": {
"root_cause": [{
......@@ -355,7 +360,7 @@ Here are some common pitfalls and how to overcome them:
- Exception `Elasticsearch::Transport::Transport::Errors::RequestEntityTooLarge`
```
```text
[413] {"Message":"Request size exceeded 10485760 bytes"}
```
......
# From Community Edition 11.6 to Enterprise Edition 11.6
This guide assumes you have a correctly configured and tested installation of
GitLab Community Edition 11.6. If you run into any trouble or if you have any
questions please contact us at [support@gitlab.com].
### 0. Backup
Make a backup just in case something goes wrong:
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
For installations using MySQL, this may require granting "LOCK TABLES"
privileges to the GitLab user on the database version.
### 1. Stop server
```bash
sudo service gitlab stop
```
### 2. Get the EE code
```bash
cd /home/git/gitlab
sudo -u git -H git remote add -f ee https://gitlab.com/gitlab-org/gitlab-ee.git
sudo -u git -H git checkout 11-6-stable-ee
```
### 3. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
# MySQL installations (note: the line below states '--without postgres')
sudo -u git -H bundle install --without postgres development test --deployment
# PostgreSQL installations (note: the line below states '--without mysql')
sudo -u git -H bundle install --without mysql development test --deployment
# Run database migrations
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Clean up assets and cache
sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
```
### 4. Install `gitlab-elasticsearch-indexer` (optional) **[STARTER ONLY]**
If you're interested in using GitLab's new [elasticsearch repository indexer](../integration/elasticsearch.md#elasticsearch-repository-indexer-beta) (currently in beta)
please follow the instructions on the document linked above and enable the
indexer usage in the GitLab admin settings.
### 5. Start application
```bash
sudo service gitlab start
sudo service nginx restart
```
### 6. Check application status
Check if GitLab and its environment are configured correctly:
```bash
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
```
To make sure you didn't miss anything run a more thorough check with:
```bash
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
```
If all items are green, then congratulations upgrade complete!
## Things went south? Revert to previous version (Community Edition 11.6)
### 1. Revert the code to the previous version
```bash
cd /home/git/gitlab
sudo -u git -H git checkout 11-6-stable-ee
```
### 2. Restore from the backup
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
[support@gitlab.com]: mailto:support@gitlab.com
......@@ -27,7 +27,7 @@ request.
1. Bitbucket Server allows multiple levels of threading. GitLab
import will collapse this into one discussion and quote part of the original
comment.
1. Declined pull requests have unrecahable commits, which prevents the GitLab
1. Declined pull requests have unreachable commits, which prevents the GitLab
importer from generating a proper diff. These pull requests will show up as
empty changes.
1. Attachments in Markdown are currently not imported.
......
# Enabling emails on push
By enabling this service, you will be able to receive email notifications for
every change that is pushed to your project.
By enabling this service, you will receive email notifications for every change
that is pushed to your project.
Navigate to the [Integrations page](project_services.md#accessing-the-project-services)
and select the **Emails on push** service to configure it.
From the [Integrations page](project_services.md#accessing-the-project-services)
select **Emails on push** service to activate and configure it.
In the _Recipients_ area, provide a list of emails separated by spaces or newlines.
You can configure any of the following settings depending on your preference.
The following options are available:
+ **Push events** - Email will be triggered when a push event is received
+ **Tag push events** - Email will be triggered when a tag is created and pushed
+ **Send from committer** - Send notifications from the committer's email address if the domain is part of the domain GitLab is running on (e.g. `user@gitlab.com`).
+ **Disable code diffs** - Don't include possibly sensitive code diffs in notification body.
---
![Email on push service settings](img/emails_on_push_service.png)
| Settings | Notification |
| --- | --- |
| ![Email on push service settings](img/emails_on_push_service.png) | ![Email on push notification](img/emails_on_push_email.png) |
......@@ -31,13 +31,50 @@ For instance, consider the following workflow:
First of all, you need to define a job in your `.gitlab-ci.yml` file that generates the
[Code Quality report artifact](../../../ci/yaml/README.md#artifactsreportscodequality).
The Code Quality report artifact is a subset of the
[Code Climate spec](https://github.com/codeclimate/spec/blob/master/SPEC.md#data-types).
It must be a JSON file containing an array of objects with the following properties:
| Name | Description |
| ---------------------- | -------------------------------------------------------------------------------------- |
| `description` | A description of the code quality violation. |
| `fingerprint` | A unique fingerprint to identify the code quality violation. For example, an MD5 hash. |
| `location.path` | The relative path to the file containing the code quality violation. |
| `location.lines.begin` | The line on which the code quality violation occurred. |
Example:
```json
[
{
"description": "'unused' is assigned a value but never used.",
"fingerprint": "7815696ecbf1c96e6894b779456d330e",
"location": {
"path": "lib/index.js",
"lines": {
"begin": 42
}
}
}
]
```
Note: **Note:**
Although the Code Climate spec supports more properties, those are ignored by GitLab.
For more information on how the Code Quality job should look like, check the
example on [analyzing a project's code quality](../../../ci/examples/code_quality.md).
GitLab then checks this report, compares the metrics between the source and target
branches, and shows the information right on the merge request.
>**Note:**
Caution: **Caution:**
If multiple jobs in a pipeline generate a code quality artifact, only the artifact from
the last created job (the job with the largest job ID) is used. To avoid confusion,
configure only one job to generate a code quality artifact.
Note: **Note:**
If the Code Quality report doesn't have anything to compare to, no information
will be displayed in the merge request area. That is the case when you add the
Code Quality job in your `.gitlab-ci.yml` for the very first time.
......
......@@ -71,7 +71,7 @@ export default {
type="button"
@click="openModal({ vulnerability });"
>
<icon name="information" />
<icon name="information-o" />
</button>
<loading-button
v-if="canCreateIssue"
......
<script>
import Flash from '~/flash';
import LinkToMemberAvatar from 'ee/vue_shared/components/link_to_member_avatar.vue';
import Icon from '~/vue_shared/components/icon.vue';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
import { s__ } from '~/locale';
import eventHub from '~/vue_merge_request_widget/event_hub';
export default {
name: 'ApprovalsFooter',
components: {
LinkToMemberAvatar,
Icon,
UserAvatarLink,
},
props: {
mr: {
......@@ -84,22 +87,22 @@ export default {
<div class="approvers-prefix">
<p>{{ approvedByText }}</p>
<div class="approvers-list">
<link-to-member-avatar
v-for="(approver, index) in approvedBy"
:key="index"
:avatar-size="20"
:avatar-url="approver.user.avatar_url"
:display-name="approver.user.name"
:profile-url="approver.user.web_url"
:show-tooltip="true"
extra-link-class="approver-avatar js-approver-list-member"
<user-avatar-link
v-for="approver in approvedBy"
:key="approver.user.username"
class="js-approver-list-member"
:img-size="20"
:img-src="approver.user.avatar_url"
:img-alt="approver.user.name"
:link-href="approver.user.web_url"
:tooltip-text="approver.user.name"
tooltip-placement="bottom"
/>
<link-to-member-avatar
<icon
v-for="n in approvalsLeft"
:key="n"
:avatar-size="20"
:clickable="false"
:show-tooltip="false"
name="dotted-circle"
class="avatar avatar-placeholder s20"
/>
</div>
<button
......
......@@ -153,7 +153,15 @@ export default class MergeRequestStore extends CEMergeRequestStore {
if (issue.location.lines && issue.location.lines.begin) {
parsedIssue.line = issue.location.lines.begin;
parseCodeQualityUrl += `#L${issue.location.lines.begin}`;
} else if (
issue.location.positions &&
issue.location.positions.begin &&
issue.location.positions.begin.line
) {
parsedIssue.line = issue.location.positions.begin.line;
parseCodeQualityUrl += `#L${issue.location.positions.begin.line}`;
}
parsedIssue.urlPath = parseCodeQualityUrl;
}
}
......
<script>
// Analogue of link_to_member_avatar in app/helpers/projects_helper.rb
import pendingAvatarSvg from 'ee_icons/_icon_dotted_circle.svg';
export default {
props: {
avatarUrl: {
type: String,
required: false,
default: '',
},
profileUrl: {
type: String,
required: false,
default: '',
},
displayName: {
type: String,
required: false,
default: '',
},
extraAvatarClass: {
type: String,
required: false,
default: '',
},
extraLinkClass: {
type: String,
required: false,
default: '',
},
showTooltip: {
type: Boolean,
required: false,
default: true,
},
clickable: {
type: Boolean,
required: false,
default: true,
},
tooltipContainer: {
type: String,
required: false,
default: 'body',
},
avatarSize: {
type: Number,
required: false,
default: 32,
},
},
data() {
return {
avatarBaseClass: 'avatar avatar-inline',
pendingAvatarSvg,
};
},
computed: {
avatarSizeClass() {
return `s${this.avatarSize}`;
},
avatarHtmlClass() {
return `${this.avatarSizeClass} ${this.avatarBaseClass} avatar-placeholder`;
},
tooltipClass() {
return this.showTooltip ? 'has-tooltip' : '';
},
avatarClass() {
return `${this.avatarBaseClass} ${this.avatarSizeClass} ${this.extraAvatarClass}`;
},
disabledClass() {
return !this.clickable ? 'disabled' : '';
},
linkClass() {
return `author-link ${this.tooltipClass} ${this.extraLinkClass} ${this.disabledClass}`;
},
},
};
</script>
<template>
<div class="link-to-member-avatar">
<a
:href="profileUrl"
:class="linkClass"
:title="displayName"
:data-container="tooltipContainer"
>
<img
v-if="avatarUrl"
:class="avatarClass"
:src="avatarUrl"
:width="avatarSize"
:height="avatarSize"
:alt="displayName"
/>
<span
v-else
:class="avatarHtmlClass"
:width="avatarSize"
:height="avatarSize"
v-html="pendingAvatarSvg"
>
</span>
</a>
</div>
</template>
.link-to-member-avatar {
.disabled {
pointer-events: none;
cursor: default;
}
.avatar {
margin-bottom: 0;
margin-left: 7px;
display: block;
}
}
.approvals-body {
@include media-breakpoint-up(md) {
display: flex;
......@@ -41,12 +28,11 @@
.approvers-list {
display: flex;
align-items: center;
.link-to-member-avatar:not(:first-child) {
img {
margin-left: 0;
}
margin-left: 7px;
}
.avatar-placeholder {
color: $gray-darkest;
}
.unapprove-btn {
......@@ -63,9 +49,5 @@
outline: none;
}
}
.approver-avatar {
position: relative;
}
}
......@@ -22,7 +22,9 @@ module EE
end
def user_default_dashboard?(user = current_user)
controller_action_to_child_dashboards.any? {|dashboard| dashboard == user.dashboard }
return false unless user
controller_action_to_child_dashboards.any? { |dashboard| dashboard == user.dashboard }
end
end
end
......@@ -2,12 +2,23 @@
module EE
module UserCalloutsHelper
GOLD_TRIAL = 'gold_trial'.freeze
GOLD_TRIAL = 'gold_trial'
def show_gold_trial?(user = current_user)
!user_dismissed?(GOLD_TRIAL) &&
return false if user_dismissed?(GOLD_TRIAL)
return false unless show_gold_trial_suitable_env?
users_namespaces_clean?(user)
end
def show_gold_trial_suitable_env?
(::Gitlab.com? || Rails.env.development?) &&
!user.any_namespace_with_gold? &&
!::Gitlab::Database.read_only?
end
def users_namespaces_clean?(user)
return false if user.any_namespace_with_gold?
!user.any_namespace_with_trial?
end
end
......
......@@ -10,6 +10,9 @@ module EE
extend ::Gitlab::Utils::Override
extend ::Gitlab::Cache::RequestCache
include ::Gitlab::Utils::StrongMemoize
include ::EE::GitlabRoutingHelper
GIT_LFS_DOWNLOAD_OPERATION = 'download'.freeze
prepended do
include Elastic::ProjectsSearch
......@@ -124,8 +127,8 @@ module EE
end
def latest_pipeline_with_security_reports
pipelines.newest_first(ref: default_branch).with_security_reports.first ||
pipelines.newest_first(ref: default_branch).with_legacy_security_reports.first
ci_pipelines.newest_first(ref: default_branch).with_security_reports.first ||
ci_pipelines.newest_first(ref: default_branch).with_legacy_security_reports.first
end
def environments_for_scope(scope)
......@@ -488,6 +491,14 @@ module EE
webide_pipelines.running_or_pending.for_user(user)
end
override :lfs_http_url_to_repo
def lfs_http_url_to_repo(operation)
return super unless ::Gitlab::Geo.secondary_with_primary?
return super if operation == GIT_LFS_DOWNLOAD_OPERATION # download always comes from secondary
geo_primary_http_url_to_repo(self)
end
private
def set_override_pull_mirror_available
......
......@@ -56,7 +56,7 @@ class GithubService < Service
end
def can_test?
project.pipelines.any?
project.ci_pipelines.any?
end
def disabled_title
......@@ -72,7 +72,7 @@ class GithubService < Service
end
def test_data(project, user)
pipeline = project.pipelines.newest_first.first
pipeline = project.ci_pipelines.newest_first.first
raise disabled_title unless pipeline
......
# frozen_string_literal: true
module EE
module IssueBoardEntity
extend ActiveSupport::Concern
prepended do
expose :weight, if: ->(issue, _) { issue.supports_weight? }
end
end
end
%approvals-body{ ':user-can-approve' => 'approvals.user_can_approve', ':user-has-approved' => 'approvals.user_has_approved', ':approved-by' => 'approvals.approved_by', ':approvals-left' => 'approvals.approvals_left', ':suggested-approvers' => 'approvals.suggested_approvers' }
%approvals-footer{ 'pending-avatar-svg' => custom_icon('icon_dotted_circle'), 'checkmark-svg' => custom_icon('icon_checkmark'), ':user-can-approve' => 'approvals.user_can_approve', ':user-has-approved' => 'approvals.user_has_approved', ':approved-by' => 'approvals.approved_by', ':approvals-left' => 'approvals.approvals_left' }
- if show_gold_trial? && user_default_dashboard?
- if user_default_dashboard? && show_gold_trial?
.pt-1.d-none.d-md-block{ class: container_class }
.user-callout.promotion-callout.thin-callout.js-gold-trial-callout{ data: { uid: 'trial_callout_dismissed', feature_id: UserCalloutsHelper::GOLD_TRIAL, dismiss_endpoint: user_callouts_path } }
.bordered-box.justify-content-left.align-items-center
......
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 27 27"><path fill="#bfbfbf" fill-rule="evenodd" d="m13.5 26.5c1.412 0 2.794-.225 4.107-.662l-.316-.949c-1.212.403-2.487.611-3.792.611v1m6.06-1.495c1.234-.651 2.355-1.498 3.321-2.504l-.721-.692c-.892.929-1.928 1.711-3.067 2.312l.467.884m4.66-4.147c.79-1.149 1.391-2.418 1.777-3.762l-.961-.276c-.356 1.24-.911 2.411-1.64 3.471l.824.567m2.184-5.761c.063-.518.096-1.041.097-1.568 0-.896-.085-1.758-.255-2.603l-.98.197c.157.78.236 1.576.236 2.405-.001.486-.031.97-.09 1.448l.993.122m-.738-6.189c-.493-1.307-1.195-2.523-2.075-3.605l-.776.631c.812.999 1.46 2.122 1.916 3.327l.935-.353m-3.539-5.133c-1.043-.926-2.229-1.68-3.512-2.229l-.394.919c1.184.507 2.279 1.203 3.242 2.058l.664-.748m-5.463-2.886c-1.012-.253-2.058-.384-3.119-.388-.378 0-.717.013-1.059.039l.077.997c.316-.024.629-.036.98-.036.979.003 1.944.124 2.879.358l.243-.97m-6.238-.022c-1.361.33-2.653.878-3.832 1.619l.532.847c1.089-.684 2.281-1.189 3.536-1.494l-.236-.972m-5.517 2.878c-1.047.922-1.94 2.01-2.643 3.212l.864.504c.649-1.112 1.474-2.114 2.441-2.966l-.661-.75m-3.54 5.076c-.499 1.293-.789 2.664-.854 4.072l.999.046c.06-1.3.328-2.564.788-3.758l-.933-.36m-.78 6.202c.163 1.396.549 2.744 1.14 4l.905-.425c-.545-1.16-.902-2.404-1.052-3.692l-.993.116m2.177 5.814c.788 1.151 1.756 2.169 2.866 3.01l.606-.796c-1.025-.78-1.919-1.721-2.646-2.783l-.825.565m4.665 4.164c1.23.65 2.559 1.1 3.943 1.328l.162-.987c-1.278-.21-2.503-.625-3.638-1.225l-.468.884m6.02 1.501c.024 0 .024 0 .048 0v-1c-.022 0-.022 0-.044 0l-.004 1"/></svg>
\ No newline at end of file
---
title: Remove redundant indices for is_sample on push_rules and next_execution_timestamp on project_mirror_data
merge_request: 8695
author:
type: performance
---
title: Ensure that avatars in approvals have correct tooltip
merge_request: 6269
author:
type: fixed
---
title: Use new information-o icon for Security Dashboard
merge_request:
author:
type: other
---
title: 'Geo: Fix push to secondary over SSH for LFS'
merge_request: 8044
author:
type: fixed
---
title: Run geo check task from gitlab check
merge_request: 8616
author:
type: changed
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RemoveRedundantIndicesForProjectMirrorDataAndPushRules < ActiveRecord::Migration[5.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
remove_concurrent_index(*index_on_project_mirror_data)
remove_concurrent_index(*index_on_push_rules)
end
def down
add_concurrent_index(*index_on_push_rules)
add_concurrent_index(*index_on_project_mirror_data)
end
private
def index_on_project_mirror_data
[
:project_mirror_data,
[:next_execution_timestamp],
{ name: 'index_project_mirror_data_on_next_execution_timestamp' }
]
end
def index_on_push_rules
[
:push_rules,
[:is_sample],
{ name: 'index_push_rules_is_sample' }
]
end
end
......@@ -35,7 +35,7 @@ module EE
end
def alive_pipelines_count
@project.pipelines.alive.count
@project.ci_pipelines.alive.count
end
def max_active_pipelines_count
......
# frozen_string_literal: true
module EE
module SystemCheck
module RakeTask
module AppTask
extend ActiveSupport::Concern
class_methods do
extend ::Gitlab::Utils::Override
override :checks
def checks
super + [
::SystemCheck::App::ElasticsearchCheck
]
end
end
end
end
end
end
# frozen_string_literal: true
module EE
module SystemCheck
module RakeTask
module GitlabTask
extend ActiveSupport::Concern
class_methods do
extend ::Gitlab::Utils::Override
override :subtasks
def subtasks
existing = super
existing << ::SystemCheck::RakeTask::GeoTask if ::Gitlab::Geo.enabled?
existing
end
end
end
end
end
end
......@@ -78,6 +78,10 @@ module SystemCheck
$stdout.puts ' Reason:'.color(:blue)
$stdout.puts " #{exception.message}"
end
def see_custom_certificate_doc
'https://docs.gitlab.com/omnibus/common_installation_problems/README.html#using-self-signed-certificate-or-custom-certificate-authorities'
end
end
end
end
# frozen_string_literal: true
module SystemCheck
module RakeTask
# Used by gitlab:geo:check rake task
module GeoTask
extend RakeTaskHelpers
def self.name
'Geo'
end
def self.checks
[
SystemCheck::Geo::LicenseCheck,
SystemCheck::Geo::EnabledCheck,
SystemCheck::Geo::GeoDatabaseConfiguredCheck,
SystemCheck::Geo::DatabaseReplicationCheck,
SystemCheck::Geo::FdwEnabledCheck,
SystemCheck::Geo::FdwSchemaUpToDateCheck,
SystemCheck::Geo::HttpConnectionCheck,
SystemCheck::Geo::HTTPCloneEnabledCheck,
SystemCheck::Geo::ClocksSynchronizationCheck,
SystemCheck::App::GitUserDefaultSSHConfigCheck,
SystemCheck::Geo::AuthorizedKeysCheck,
SystemCheck::Geo::AuthorizedKeysFlagCheck
]
end
end
end
end
# frozen_string_literal: true
namespace :gitlab do
namespace :geo do
desc 'GitLab | Check Geo configuration and dependencies'
task check: :gitlab_environment do
SystemCheck::Geo.run!
end
end
end
......@@ -27,14 +27,14 @@ describe 'Merge request > User approves', :js do
approve_merge_request
expect(page).to have_content('Approved by')
expect(page).to have_css('.approver-avatar')
expect(page).to have_css('.js-approver-list-member')
end
it 'I am able to unapprove' do
approve_merge_request
unapprove_merge_request
expect(page).to have_no_css('.approver-avatar')
expect(page).to have_no_css('.js-approver-list-member')
end
end
......@@ -48,14 +48,14 @@ describe 'Merge request > User approves', :js do
approve_merge_request
expect(page).to have_content('Approved by')
expect(page).to have_css('.approver-avatar')
expect(page).to have_css('.js-approver-list-member')
end
it 'I am able to unapprove' do
approve_merge_request
unapprove_merge_request
expect(page).to have_no_css('.approver-avatar')
expect(page).to have_no_css('.js-approver-list-member')
end
end
......
......@@ -47,7 +47,7 @@ describe 'User activates GitHub Service' do
context 'with pipelines', :js do
let(:pipeline) { create(:ci_pipeline) }
let(:project) { create(:project, pipelines: [pipeline])}
let(:project) { create(:project, ci_pipelines: [pipeline])}
it 'tests service before save' do
click_button 'Test settings and save changes'
......
# frozen_string_literal: true
require 'spec_helper'
describe EE::UserCalloutsHelper do
let(:user) { create(:user) }
before do
allow(helper).to receive(:current_user).and_return(user)
end
describe '.show_gold_trial?' do
let(:suitable_env) { nil }
before do
allow(helper).to receive(:user_dismissed?).with(described_class::GOLD_TRIAL).and_return(user_dismissed)
allow(helper).to receive(:show_gold_trial_suitable_env?).and_return(suitable_env)
end
context 'when user has already dismissed the callout' do
let(:user_dismissed) { true }
it 'returns false' do
expect(helper.show_gold_trial?).to be_falsey
end
end
context 'when show_gold_trial_suitable_env? returns false' do
let(:user_dismissed) { false }
let(:suitable_env) { false }
it 'returns false' do
expect(helper.show_gold_trial?).to be_falsey
end
end
context 'when show_gold_trial_namespaces_checked?' do
let(:user_dismissed) { false }
let(:suitable_env) { true }
before do
allow(helper).to receive(:users_namespaces_clean?).and_return(namespaces_checked)
end
context 'returns false' do
let(:namespaces_checked) { false }
it 'returns false' do
expect(helper.show_gold_trial?).to be_falsey
end
end
context 'returns true' do
let(:namespaces_checked) { true }
it 'returns true' do
expect(helper.show_gold_trial?).to be_truthy
end
end
end
end
describe '.show_gold_trial_suitable_env?' do
before do
allow(Gitlab).to receive(:com?).and_return(gitlab_com)
allow(Rails.env).to receive(:development?).and_return(rails_dev_env)
allow(Gitlab::Database).to receive(:read_only?).and_return(db_read_only)
end
context 'with a writable DB' do
let(:db_read_only) { false }
context "when we're neither GitLab.com or a Rails development env" do
let(:gitlab_com) { false }
let(:rails_dev_env) { false }
it 'returns true' do
expect(helper.show_gold_trial_suitable_env?).to be_falsey
end
end
context "when we're GitLab.com" do
let(:gitlab_com) { true }
let(:rails_dev_env) { false }
it 'returns true' do
expect(helper.show_gold_trial_suitable_env?).to be_truthy
end
end
context "when we're a Rails development env" do
let(:gitlab_com) { false }
let(:rails_dev_env) { true }
it 'returns true' do
expect(helper.show_gold_trial_suitable_env?).to be_truthy
end
end
end
context 'with a readonly DB' do
let(:db_read_only) { true }
context "when we're GitLab.com" do
let(:gitlab_com) { true }
let(:rails_dev_env) { false }
it 'returns true' do
expect(helper.show_gold_trial_suitable_env?).to be_falsey
end
end
context "when we're a Rails development env" do
let(:gitlab_com) { false }
let(:rails_dev_env) { true }
it 'returns true' do
expect(helper.show_gold_trial_suitable_env?).to be_falsey
end
end
end
end
describe '.show_gold_trial_namespaces_checked?' do
let(:a_name_space_has_trial) { nil }
before do
allow(user).to receive(:any_namespace_with_gold?).and_return(a_name_space_has_gold)
allow(user).to receive(:any_namespace_with_trial?).and_return(a_name_space_has_trial)
end
context "when a user's namespace has gold" do
let(:a_name_space_has_gold) { true }
it 'returns false' do
expect(helper.users_namespaces_clean?(user)).to be_falsey
end
end
context "when a user's namespace does not have gold" do
let(:a_name_space_has_gold) { false }
context "but a user's namespace has a trial" do
let(:a_name_space_has_trial) { true }
it 'returns false' do
expect(helper.users_namespaces_clean?(user)).to be_falsey
end
end
context "and does not have a trial" do
let(:a_name_space_has_trial) { false }
it 'returns true' do
expect(helper.users_namespaces_clean?(user)).to be_truthy
end
end
end
end
end
import Vue from 'vue';
import pendingAvatarSvg from 'ee_icons/_icon_dotted_circle.svg';
import ApprovalsFooter from 'ee/vue_merge_request_widget/components/approvals/approvals_footer.vue';
import { TEST_HOST } from 'spec/test_constants';
......@@ -13,8 +12,7 @@ describe('Approvals Footer Component', () => {
userCanApprove: false,
userHasApproved: true,
approvedBy: [],
approvalsLeft: 1,
pendingAvatarSvg,
approvalsLeft: 3,
};
beforeEach(() => {
......@@ -58,17 +56,46 @@ describe('Approvals Footer Component', () => {
});
describe('approvers list', () => {
const avatarUrl = `${TEST_HOST}/dummy.jpg`;
it('shows link to member avatar for for each approver', done => {
vm.approvedBy.push({
vm.approvedBy = [
{
user: {
avatar_url: `${TEST_HOST}/dummy.jpg`,
username: 'Tanuki',
avatar_url: avatarUrl,
},
});
},
];
Vue.nextTick(() => {
const memberImage = document.querySelector('.approvers-list img');
expect(memberImage.src).toMatch(/dummy\.jpg$/);
expect(memberImage.src).toContain(avatarUrl);
done();
});
});
it('allows to add multiple approvers withoutd duplicate-key errors', done => {
vm.approvedBy = [
{
user: {
username: 'Tanuki',
avatar_url: avatarUrl,
},
},
{
user: {
username: 'Tanuki2',
avatar_url: avatarUrl,
},
},
];
Vue.nextTick(() => {
const approvers = document.querySelectorAll('.approvers-list img');
expect(approvers.length).toBe(2);
done();
});
});
......
import Vue from 'vue';
import linkToMemberAvatar from 'ee/vue_shared/components/link_to_member_avatar.vue';
describe('Link To Members Components', () => {
let vm;
const propsData = {
avatarSize: 32,
avatarUrl: 'myavatarurl.com',
profileUrl: 'profileUrl.com',
displayName: 'mydisplayname',
extraAvatarClass: 'myextraavatarclass',
extraLinkClass: 'myextralinkclass',
showTooltip: true,
};
beforeEach(() => {
setFixtures('<div id="mock-container"></div>');
const LinkToMembersComponent = Vue.extend(linkToMemberAvatar);
vm = new LinkToMembersComponent({
el: '#mock-container',
propsData,
}).$mount();
});
afterEach(() => {
vm.$destroy();
});
it('should default to the body as tooltip container', () => {
expect(vm.tooltipContainer).toBe('body');
});
it('should return a defined Vue component', () => {
expect(vm).toBeDefined();
expect(vm.$data).toBeDefined();
});
it('should have <a> children', () => {
const componentLink = vm.$el.querySelector('a');
expect(componentLink).not.toBeNull();
expect(componentLink.getAttribute('href')).toBe(propsData.profileUrl);
});
it('should show a <img> if the avatarUrl is set', () => {
const avatarImg = vm.$el.querySelector('a img');
expect(avatarImg).not.toBeNull();
expect(avatarImg.getAttribute('src')).toBe(propsData.avatarUrl);
});
it('should fallback to a <svg> if the avatarUrl is not set', done => {
vm.avatarUrl = undefined;
Vue.nextTick(() => {
const avatarImg = vm.$el.querySelector('a svg');
expect(avatarImg).not.toBeNull();
done();
});
});
it('should correctly compute computed values', () => {
const correctVals = {
disabledClass: '',
avatarSizeClass: 's32',
avatarHtmlClass: 's32 avatar avatar-inline avatar-placeholder',
avatarClass: 'avatar avatar-inline s32 myextraavatarclass',
tooltipClass: 'has-tooltip',
linkClass: 'author-link has-tooltip myextralinkclass ',
};
Object.keys(correctVals).forEach(computedKey => {
const expectedVal = correctVals[computedKey];
const actualComputed = vm[computedKey];
expect(actualComputed).toBe(expectedVal);
});
});
});
......@@ -10,7 +10,7 @@ merge_requests:
- approver_groups
- approved_by_users
- draft_notes
pipelines:
ci_pipelines:
- source_pipeline
- sourced_pipelines
- triggered_by_pipeline
......
......@@ -167,7 +167,7 @@ describe GithubService do
describe '#can_test?' do
it 'is false if there are no pipelines' do
project.pipelines.delete_all
project.ci_pipelines.delete_all
expect(subject.can_test?).to eq false
end
......@@ -184,7 +184,7 @@ describe GithubService do
let(:test_data) { subject.test_data(project, user) }
it 'raises error if no pipeline found' do
project.pipelines.delete_all
project.ci_pipelines.delete_all
expect { test_data }.to raise_error 'Please set up a pipeline on your repository.'
end
......
......@@ -2,6 +2,7 @@ require 'spec_helper'
describe Project do
include ExternalAuthorizationServiceHelpers
include ::EE::GeoHelpers
using RSpec::Parameterized::TableSyntax
describe 'associations' do
......@@ -1366,6 +1367,78 @@ describe Project do
end
end
describe '#lfs_http_url_to_repo' do
let(:project) { create(:project) }
let(:project_path) { "#{Gitlab::Routing.url_helpers.project_path(project)}.git" }
let(:primary_base_host) { 'primary.geo' }
let(:primary_base_url) { "http://#{primary_base_host}" }
let(:primary_url) { "#{primary_base_url}#{project_path}" }
context 'with a Geo setup that is a primary' do
let(:primary_node) { create(:geo_node, url: primary_base_url) }
before do
stub_current_geo_node(primary_node)
stub_default_url_options(primary_base_host)
end
context 'for an upload operation' do
it 'returns the project HTTP URL for the primary' do
expect(project.lfs_http_url_to_repo('upload')).to eq(primary_url)
end
end
end
context 'with a Geo setup that is a secondary' do
let(:secondary_base_host) { 'secondary.geo' }
let(:secondary_base_url) { "http://#{secondary_base_host}" }
let(:secondary_node) { create(:geo_node, url: secondary_base_url) }
let(:secondary_url) { "#{secondary_base_url}#{project_path}" }
before do
stub_current_geo_node(secondary_node)
stub_default_url_options(current_rails_hostname)
end
context 'and has a primary' do
let(:primary_node) { create(:geo_node, url: primary_base_url) }
context 'for an upload operation' do
let(:current_rails_hostname) { primary_base_host }
it 'returns the project HTTP URL for the primary' do
expect(project.lfs_http_url_to_repo('upload')).to eq(primary_url)
end
end
context 'for a download operation' do
let(:current_rails_hostname) { secondary_base_host }
it 'returns the project HTTP URL for the secondary' do
expect(project.lfs_http_url_to_repo('download')).to eq(secondary_url)
end
end
end
context 'without a primary' do
let(:current_rails_hostname) { secondary_base_host }
it 'returns the project HTTP URL for the secondary' do
expect(project.lfs_http_url_to_repo('operation_that_doesnt_matter')).to eq(secondary_url)
end
end
end
context 'without a Geo setup' do
it 'returns the project HTTP URL for the main node' do
project_url = "#{Gitlab::Routing.url_helpers.project_url(project)}.git"
expect(project.lfs_http_url_to_repo('operation_that_doesnt_matter')).to eq(project_url)
end
end
end
describe '#add_import_job' do
let(:project) { create(:project) }
......@@ -1469,4 +1542,13 @@ describe Project do
subject
end
end
# Despite stubbing the current node as the primary or secondary, the
# behaviour for EE::Project#lfs_http_url_to_repo() is to call
# Project#lfs_http_url_to_repo() which does not have a Geo context.
def stub_default_url_options(host)
allow(Rails.application.routes)
.to receive(:default_url_options)
.and_return(host: host)
end
end
......@@ -53,5 +53,11 @@ shared_examples 'gold trial callout' do
expect(page).not_to have_selector '.promotion-callout'
end
it 'hides promotion callout if database is in a readonly state' do
allow(Gitlab::Database).to receive(:read_only?).and_return(true)
expect(page).not_to have_selector '.promotion-callout'
end
end
end
......@@ -29,7 +29,7 @@ module API
not_found!('Commit') unless user_project.commit(params[:sha])
pipelines = user_project.pipelines.where(sha: params[:sha])
pipelines = user_project.ci_pipelines.where(sha: params[:sha])
statuses = ::CommitStatus.where(pipeline: pipelines)
statuses = statuses.latest unless to_boolean(params[:all])
statuses = statuses.where(ref: params[:ref]) if params[:ref].present?
......@@ -75,7 +75,7 @@ module API
pipeline = @project.pipeline_for(ref, commit.sha)
unless pipeline
pipeline = @project.pipelines.create!(
pipeline = @project.ci_pipelines.create!(
source: :external,
sha: commit.sha,
ref: ref,
......
......@@ -122,7 +122,7 @@ module API
{
username: token_handler.actor_name,
lfs_token: token_handler.token,
repository_http_path: project.http_url_to_repo
repository_http_path: project.lfs_http_url_to_repo(params[:operation])
}
end
# rubocop: enable CodeReuse/ActiveRecord
......
......@@ -56,7 +56,7 @@ module API
end
# rubocop: disable CodeReuse/ActiveRecord
get ':id/pipelines/:pipeline_id/jobs' do
pipeline = user_project.pipelines.find(params[:pipeline_id])
pipeline = user_project.ci_pipelines.find(params[:pipeline_id])
builds = pipeline.builds
builds = filter_builds(builds, params[:scope])
builds = builds.preload(:job_artifacts_archive, :job_artifacts, project: [:namespace])
......
......@@ -130,7 +130,7 @@ module API
helpers do
def pipeline
@pipeline ||= user_project.pipelines.find(params[:pipeline_id])
@pipeline ||= user_project.ci_pipelines.find(params[:pipeline_id])
end
end
end
......
......@@ -15,12 +15,12 @@ module Gitlab
attr_encrypted :token,
mode: :per_attribute_iv,
algorithm: 'aes-256-gcm',
key: ::Settings.attr_encrypted_db_key_base_truncated
key: ::Settings.attr_encrypted_db_key_base_32
attr_encrypted :url,
mode: :per_attribute_iv,
algorithm: 'aes-256-gcm',
key: ::Settings.attr_encrypted_db_key_base_truncated
key: ::Settings.attr_encrypted_db_key_base_32
end
end
end
......
......@@ -14,7 +14,7 @@ module Gitlab
@ref = ref
@job = job
@pipeline = @project.pipelines.latest_successful_for(@ref)
@pipeline = @project.ci_pipelines.latest_successful_for(@ref)
end
def entity
......
......@@ -22,7 +22,7 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord
def status
@project.pipelines
@project.ci_pipelines
.where(sha: @sha)
.latest_status(@ref) || 'unknown'
end
......
......@@ -54,7 +54,7 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord
def collect
query = project.pipelines
query = project.all_pipelines
.where("? > #{::Ci::Pipeline.table_name}.created_at AND #{::Ci::Pipeline.table_name}.created_at > ?", @to, @from) # rubocop:disable GitlabSecurity/SqlInjection
totals_count = grouped_count(query)
......@@ -115,7 +115,7 @@ module Gitlab
class PipelineTime < Chart
def collect
commits = project.pipelines.last(30)
commits = project.all_pipelines.last(30)
commits.each do |commit|
@labels << commit.short_sha
......
# frozen_string_literal: true
module Gitlab
module Ci
class Config
module Entry
##
# Entry that represents an only/except trigger policy for the job.
#
class ExceptPolicy < Policy
def self.default
end
end
end
end
end
end
......@@ -65,10 +65,10 @@ module Gitlab
entry :services, Entry::Services,
description: 'Services that will be used to execute this job.'
entry :only, Entry::Policy,
entry :only, Entry::OnlyPolicy,
description: 'Refs policy this job will be executed for.'
entry :except, Entry::Policy,
entry :except, Entry::ExceptPolicy,
description: 'Refs policy this job will be executed for.'
entry :variables, Entry::Variables,
......
# frozen_string_literal: true
module Gitlab
module Ci
class Config
module Entry
##
# Entry that represents an only/except trigger policy for the job.
#
class OnlyPolicy < Policy
def self.default
{ refs: %w[branches tags] }
end
end
end
end
end
end
......@@ -5,12 +5,9 @@ module Gitlab
class Config
module Entry
##
# Entry that represents an only/except trigger policy for the job.
# Base class for OnlyPolicy and ExceptPolicy
#
class Policy < ::Gitlab::Config::Entry::Simplifiable
strategy :RefsPolicy, if: -> (config) { config.is_a?(Array) }
strategy :ComplexPolicy, if: -> (config) { config.is_a?(Hash) }
class RefsPolicy < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Validatable
......@@ -66,6 +63,16 @@ module Gitlab
def self.default
end
##
# Class-level execution won't be inherited by subclasses by default.
# Therefore, we need to explicitly execute that for OnlyPolicy and ExceptPolicy
def self.inherited(klass)
super
klass.strategy :RefsPolicy, if: -> (config) { config.is_a?(Array) }
klass.strategy :ComplexPolicy, if: -> (config) { config.is_a?(Hash) }
end
end
end
end
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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