Commit 5614f50d authored by Rémy Coutable's avatar Rémy Coutable

Merge remote-tracking branch 'origin/master' into rc/ce-to-ee-wednesday

Signed-off-by: default avatarRémy Coutable <remy@rymai.me>
parents fe9a801a 30ed0e09
Please view this file on the master branch, on stable branches it's out of date.
## 8.17.2 (2017-03-01)
- No changes.
## 8.17.1 (2017-02-28)
- Fix admin email notification recipient group select list.
......
......@@ -2,6 +2,10 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 8.17.2 (2017-03-01)
- Expire all webpack assets after 8.17.1 included a badly compiled asset. !9602
## 8.17.1 (2017-02-28)
- Replace setInterval with setTimeout to prevent highly frequent requests. !9271 (Takuya Noguchi)
......
......@@ -338,8 +338,6 @@ group :test do
gem 'timecop', '~> 0.8.0'
end
gem 'newrelic_rpm', '~> 3.16'
gem 'octokit', '~> 4.6.2'
gem 'mail_room', '~> 0.9.1'
......
......@@ -458,7 +458,6 @@ GEM
net-ldap (0.12.1)
net-ssh (3.0.1)
netrc (0.11.0)
newrelic_rpm (3.16.0.318)
nokogiri (1.6.8.1)
mini_portile2 (~> 2.1.0)
numerizer (0.1.1)
......@@ -954,7 +953,6 @@ DEPENDENCIES
mysql2 (~> 0.3.16)
net-ldap
net-ssh (~> 3.0.1)
newrelic_rpm (~> 3.16)
nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.2.0)
octokit (~> 4.6.2)
......
......@@ -17,7 +17,8 @@
group_result = Api.groups(query.term, {}, function(groups) {
return groups;
});
project_result = Api.projects(query.term, 'id', function(projects) {
// Should be replaced with "Api.projects" when API v4 is frozen (9.1)
project_result = Api.allProjects(query.term, 'id', function(projects) {
return projects;
});
return $.when(project_result, group_result).done(function(projects, groups) {
......
......@@ -7,6 +7,7 @@
namespacesPath: "/api/:version/namespaces.json",
groupProjectsPath: "/api/:version/groups/:id/projects.json",
projectsPath: "/api/:version/projects.json?simple=true",
allProjectsPath: "/api/:version/projects/all.json?simple=true", // Deprecated. Valid for API v3 only.
labelsPath: "/:namespace_path/:project_path/labels",
licensePath: "/api/:version/templates/licenses/:key",
gitignorePath: "/api/:version/templates/gitignores/:key",
......@@ -67,6 +68,24 @@
return callback(projects);
});
},
// Deprecated and should be deleted for 9.1
// Also the 'Api.allProjectsPath' should be deleted as well.
// Basically, the whole commit that adds this comment should be reverted when API v4 is frozen (9.1)
// Details here: https://gitlab.com/gitlab-org/gitlab-ce/issues/28853#note_24410695
allProjects: function(query, order, callback) {
var url = Api.buildUrl(Api.allProjectsPath);
return $.ajax({
url: url,
data: {
search: query,
order_by: order,
per_page: 20
},
dataType: "json"
}).done(function(projects) {
return callback(projects);
});
},
newLabel: function(namespace_path, project_path, data, callback) {
var url = Api.buildUrl(Api.labelsPath)
.replace(':namespace_path', namespace_path)
......
......@@ -21,9 +21,9 @@
});
$(document)
.off('click', '.merge_when_build_succeeds')
.on('click', '.merge_when_build_succeeds', () => {
$('#merge_when_build_succeeds').val('1');
.off('click', '.merge_when_pipeline_succeeds')
.on('click', '.merge_when_pipeline_succeeds', () => {
$('#merge_when_pipeline_succeeds').val('1');
});
$(document)
......
......@@ -5,7 +5,7 @@ class Projects::BlobController < Projects::ApplicationController
include ActionView::Helpers::SanitizeHelper
# Raised when given an invalid file path
class InvalidPathError < StandardError; end
InvalidPathError = Class.new(StandardError)
before_action :require_non_empty_project, except: [:new, :create]
before_action :authorize_download_code!
......
......@@ -5,7 +5,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
before_action :authorize_create_deployment!, only: [:stop]
before_action :authorize_update_environment!, only: [:edit, :update]
before_action :authorize_admin_environment!, only: [:terminal, :terminal_websocket_authorize]
before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize]
before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize, :status]
before_action :verify_api_request!, only: :terminal_websocket_authorize
def index
......@@ -109,6 +109,23 @@ class Projects::EnvironmentsController < Projects::ApplicationController
end
end
# The rollout status of an enviroment
def status
unless @environment.deployment_service_ready?
render text: 'Not found', status: 404
return
end
rollout_status = @environment.rollout_status
if rollout_status.nil?
render body: nil, status: 204 # no result yet
else
serializer = RolloutStatusSerializer.new(project: @project, user: @current_user)
render json: serializer.represent(rollout_status)
end
end
private
def verify_api_request!
......
......@@ -10,13 +10,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_action :module_enabled
before_action :merge_request, only: [
:edit, :update, :show, :diffs, :commits, :conflicts, :conflict_for_path, :pipelines, :merge, :merge_check,
:ci_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues,
:ci_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_pipeline_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues,
# EE
:approve, :approvals, :unapprove, :rebase
]
before_action :validates_merge_request, only: [:show, :diffs, :commits, :pipelines]
before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :conflict_for_path, :builds, :pipelines]
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check]
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_pipeline_succeeds, :merge_check]
before_action :define_commit_vars, only: [:diffs]
before_action :define_diff_comment_vars, only: [:diffs]
before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds, :conflicts, :conflict_for_path, :pipelines]
......@@ -315,7 +315,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
render :edit
end
end
format.json do
render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
end
......@@ -337,8 +337,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
render partial: "projects/merge_requests/widget/show.html.haml", layout: false
end
def cancel_merge_when_build_succeeds
unless @merge_request.can_cancel_merge_when_build_succeeds?(current_user)
def cancel_merge_when_pipeline_succeeds
unless @merge_request.can_cancel_merge_when_pipeline_succeeds?(current_user)
return access_denied!
end
......@@ -351,9 +351,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
return access_denied! unless @merge_request.can_be_merged_by?(current_user)
return render_404 unless @merge_request.approved?
# Disable the CI check if merge_when_build_succeeds is enabled since we have
# Disable the CI check if merge_when_pipeline_succeeds is enabled since we have
# to wait until CI completes to know
unless @merge_request.mergeable?(skip_ci_check: merge_when_build_succeeds_active?)
unless @merge_request.mergeable?(skip_ci_check: merge_when_pipeline_succeeds_active?)
@status = :failed
return
end
......@@ -372,7 +372,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.update(merge_error: nil, squash: merge_params[:squash])
if params[:merge_when_build_succeeds].present?
if params[:merge_when_pipeline_succeeds].present?
unless @merge_request.head_pipeline
@status = :failed
return
......@@ -383,7 +383,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
.new(@project, current_user, merge_params)
.execute(@merge_request)
@status = :merge_when_build_succeeds
@status = :merge_when_pipeline_succeeds
elsif @merge_request.head_pipeline.success?
# This can be triggered when a user clicks the auto merge button while
# the tests finish at about the same time
......@@ -407,8 +407,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def merge_widget_refresh
@status =
if merge_request.merge_when_build_succeeds
:merge_when_build_succeeds
if merge_request.merge_when_pipeline_succeeds
:merge_when_pipeline_succeeds
else
# Only MRs that can be merged end in this action
# MR can be already picked up for merge / merged already or can be waiting for worker to be picked up
......@@ -763,8 +763,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.ensure_ref_fetched
end
def merge_when_build_succeeds_active?
params[:merge_when_build_succeeds].present? &&
def merge_when_pipeline_succeeds_active?
params[:merge_when_pipeline_succeeds].present? &&
@merge_request.head_pipeline && @merge_request.head_pipeline.active?
end
......
......@@ -315,7 +315,7 @@ class ProjectsController < Projects::ApplicationController
:name,
:namespace_id,
:only_allow_merge_if_all_discussions_are_resolved,
:only_allow_merge_if_build_succeeds,
:only_allow_merge_if_pipeline_succeeds,
:path,
:public_builds,
:request_access_enabled,
......
......@@ -185,7 +185,7 @@ module MergeRequestsHelper
def merge_params(merge_request)
{
merge_when_build_succeeds: true,
merge_when_pipeline_succeeds: true,
should_remove_source_branch: true,
sha: merge_request.diff_head_sha
}.merge(merge_params_ee(merge_request))
......
......@@ -137,12 +137,16 @@ class Environment < ActiveRecord::Base
end
end
def has_terminals?
def deployment_service_ready?
project.deployment_service.present? && available? && last_deployment.present?
end
def terminals
project.deployment_service.terminals(self) if has_terminals?
project.deployment_service.terminals(self) if deployment_service_ready?
end
def rollout_status
project.deployment_service.rollout_status(self) if deployment_service_ready?
end
# An environment name is not necessarily suitable for use in URLs, DNS
......
......@@ -102,7 +102,7 @@ class MergeRequest < ActiveRecord::Base
validates :source_branch, presence: true
validates :target_project, presence: true
validates :target_branch, presence: true
validates :merge_user, presence: true, if: :merge_when_build_succeeds?, unless: :importing?
validates :merge_user, presence: true, if: :merge_when_pipeline_succeeds?, unless: :importing?
validate :validate_branches, unless: [:allow_broken, :importing?, :closed_without_fork?]
validate :validate_fork, unless: :closed_without_fork?
validate :validate_approvals_before_merge
......@@ -459,7 +459,7 @@ class MergeRequest < ActiveRecord::Base
true
end
def can_cancel_merge_when_build_succeeds?(current_user)
def can_cancel_merge_when_pipeline_succeeds?(current_user)
can_be_merged_by?(current_user) || self.author == current_user
end
......@@ -667,10 +667,10 @@ class MergeRequest < ActiveRecord::Base
message.join("\n\n")
end
def reset_merge_when_build_succeeds
return unless merge_when_build_succeeds?
def reset_merge_when_pipeline_succeeds
return unless merge_when_pipeline_succeeds?
self.merge_when_build_succeeds = false
self.merge_when_pipeline_succeeds = false
self.merge_user = nil
if merge_params
merge_params.delete('should_remove_source_branch')
......@@ -729,7 +729,7 @@ class MergeRequest < ActiveRecord::Base
end
def mergeable_ci_state?
return true unless project.only_allow_merge_if_build_succeeds?
return true unless project.only_allow_merge_if_pipeline_succeeds?
!head_pipeline || head_pipeline.success? || head_pipeline.skipped?
end
......
......@@ -30,4 +30,10 @@ class DeploymentService < Service
def terminals(environment)
raise NotImplementedError
end
# Environments have a rollout status. This represents the current state of
# deployments to that environment.
def rollout_status(environment)
raise NotImplementedError
end
end
......@@ -104,30 +104,27 @@ class KubernetesService < DeploymentService
# short time later
def terminals(environment)
with_reactive_cache do |data|
pods = data.fetch(:pods, nil)
filter_pods(pods, app: environment.slug).
flat_map { |pod| terminals_for_pod(api_url, namespace, pod) }.
each { |terminal| add_terminal_auth(terminal, terminal_auth) }
pods = filter_by_label(data[:pods], app: environment.slug)
terminals = pods.flat_map { |pod| terminals_for_pod(api_url, namespace, pod) }
terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) }
end
end
# Caches all pods in the namespace so other calls don't need to block on
# network access.
def calculate_reactive_cache
return unless active? && project && !project.pending_delete?
kubeclient = build_kubeclient!
def rollout_status(environment)
with_reactive_cache do |data|
specs = filter_by_label(data[:deployments], app: environment.slug)
# Store as hashes, rather than as third-party types
pods = begin
kubeclient.get_pods(namespace: namespace).as_json
rescue KubeException => err
raise err unless err.error_code == 404
[]
::Gitlab::Kubernetes::RolloutStatus.from_specs(*specs)
end
end
# Caches all pods & deployments in the namespace so other calls don't need to
# block on network access.
def calculate_reactive_cache
return unless active? && project && !project.pending_delete?
# We may want to cache extra things in the future
{ pods: pods }
{ pods: read_pods, deployments: read_deployments }
end
private
......@@ -144,6 +141,25 @@ class KubernetesService < DeploymentService
)
end
# Returns a hash of all pods in the namespace
def read_pods
kubeclient = build_kubeclient!
kubeclient.get_pods(namespace: namespace).as_json
rescue KubeException => err
raise err unless err.error_code == 404
[]
end
def read_deployments
kubeclient = build_kubeclient!(api_path: 'apis/extensions', api_version: 'v1beta1')
kubeclient.get_deployments(namespace: namespace).as_json
rescue KubeException => err
raise err unless err.error_code == 404
[]
end
def kubeclient_ssl_options
opts = { verify_ssl: OpenSSL::SSL::VERIFY_PEER }
......@@ -159,11 +175,11 @@ class KubernetesService < DeploymentService
{ bearer_token: token }
end
def join_api_url(*parts)
def join_api_url(api_path)
url = URI.parse(api_url)
prefix = url.path.sub(%r{/+\z}, '')
url.path = [prefix, *parts].join("/")
url.path = [prefix, api_path].join("/")
url.to_s
end
......
......@@ -9,7 +9,7 @@ class ProjectWiki
'AsciiDoc' => :asciidoc
}.freeze unless defined?(MARKUPS)
class CouldNotCreateWikiError < StandardError; end
CouldNotCreateWikiError = Class.new(StandardError)
# Returns a string describing what went wrong after
# an operation fails.
......
......@@ -23,7 +23,7 @@ class EnvironmentEntity < Grape::Entity
environment)
end
expose :terminal_path, if: ->(environment, _) { environment.has_terminals? } do |environment|
expose :terminal_path, if: ->(environment, _) { environment.deployment_service_ready? } do |environment|
can?(request.user, :admin_environment, environment.project) &&
terminal_namespace_project_environment_path(
environment.project.namespace,
......@@ -31,5 +31,13 @@ class EnvironmentEntity < Grape::Entity
environment)
end
expose :rollout_status_path, if: ->(environment, _) { environment.deployment_service_ready? } do |environment|
status_namespace_project_environment_path(
environment.project.namespace,
environment.project,
environment,
format: :json)
end
expose :created_at, :updated_at
end
......@@ -7,7 +7,7 @@ class MergeRequestEntity < IssuableEntity
expose :merge_params
expose :merge_status
expose :merge_user_id
expose :merge_when_build_succeeds
expose :merge_when_pipeline_succeeds
expose :rebase_commit_sha
expose :rebase_in_progress?, if: { type: :full }
expose :source_branch
......
class PipelineSerializer < BaseSerializer
class InvalidResourceError < StandardError; end
InvalidResourceError = Class.new(StandardError)
entity PipelineEntity
......
class RolloutStatusEntity < Grape::Entity
include RequestAwareEntity
expose :instances
expose :completion
expose :is_completed do |rollout_status|
rollout_status.complete?
end
end
class RolloutStatusSerializer < BaseSerializer
entity RolloutStatusEntity
end
module Commits
class ChangeService < ::BaseService
class ValidationError < StandardError; end
class ChangeError < StandardError; end
ValidationError = Class.new(StandardError)
ChangeError = Class.new(StandardError)
def execute
@start_project = params[:start_project] || @project
......
module Files
class BaseService < ::BaseService
class ValidationError < StandardError; end
ValidationError = Class.new(StandardError)
def execute
@start_project = params[:start_project] || @project
......
module Files
class MultiService < Files::BaseService
class FileChangedError < StandardError; end
FileChangedError = Class.new(StandardError)
ACTIONS = %w[create update delete move].freeze
......
module Files
class UpdateService < Files::BaseService
class FileChangedError < StandardError; end
FileChangedError = Class.new(StandardError)
def commit
repository.update_file(current_user, @file_path, @file_content,
......
module Issues
class MoveService < Issues::BaseService
class MoveError < StandardError; end
MoveError = Class.new(StandardError)
def execute(issue, new_project)
@old_issue = issue
......
module MergeRequests
class MergeWhenPipelineSucceedsService < MergeRequests::BaseService
# Marks the passed `merge_request` to be merged when the build succeeds or
# Marks the passed `merge_request` to be merged when the pipeline succeeds or
# updates the params for the automatic merge
def execute(merge_request)
merge_request.merge_params.merge!(params)
# The service is also called when the merge params are updated.
already_approved = merge_request.merge_when_build_succeeds?
already_approved = merge_request.merge_when_pipeline_succeeds?
unless already_approved
merge_request.merge_when_build_succeeds = true
merge_request.merge_user = @current_user
merge_request.merge_when_pipeline_succeeds = true
merge_request.merge_user = @current_user
SystemNoteService.merge_when_build_succeeds(merge_request, @project, @current_user, merge_request.diff_head_commit)
SystemNoteService.merge_when_pipeline_succeeds(merge_request, @project, @current_user, merge_request.diff_head_commit)
end
merge_request.save
......@@ -23,7 +23,7 @@ module MergeRequests
return unless pipeline.success?
pipeline_merge_requests(pipeline) do |merge_request|
next unless merge_request.merge_when_build_succeeds?
next unless merge_request.merge_when_pipeline_succeeds?
unless merge_request.mergeable?
todo_service.merge_request_became_unmergeable(merge_request)
......@@ -36,9 +36,9 @@ module MergeRequests
# Cancels the automatic merge
def cancel(merge_request)
if merge_request.merge_when_build_succeeds? && merge_request.open?
merge_request.reset_merge_when_build_succeeds
SystemNoteService.cancel_merge_when_build_succeeds(merge_request, @project, @current_user)
if merge_request.merge_when_pipeline_succeeds? && merge_request.open?
merge_request.reset_merge_when_pipeline_succeeds
SystemNoteService.cancel_merge_when_pipeline_succeeds(merge_request, @project, @current_user)
success
else
......
......@@ -11,7 +11,7 @@ module MergeRequests
# empty diff during a manual merge
close_merge_requests
reload_merge_requests
reset_merge_when_build_succeeds
reset_merge_when_pipeline_succeeds
mark_pending_todos_done
cache_merge_requests_closing_issues
......@@ -95,8 +95,8 @@ module MergeRequests
end
end
def reset_merge_when_build_succeeds
merge_requests_for_source_branch.each(&:reset_merge_when_build_succeeds)
def reset_merge_when_pipeline_succeeds
merge_requests_for_source_branch.each(&:reset_merge_when_pipeline_succeeds)
end
def mark_pending_todos_done
......
module MergeRequests
class ResolveService < MergeRequests::BaseService
class MissingFiles < Gitlab::Conflict::ResolutionError
end
MissingFiles = Class.new(Gitlab::Conflict::ResolutionError)
attr_accessor :conflicts, :rugged, :merge_index, :merge_request
......
module MergeRequests
class WorkingCopyBaseService < MergeRequests::BaseService
class GitCommandError < StandardError; end
GitCommandError = Class.new(StandardError)
include Gitlab::Popen
......
......@@ -144,7 +144,7 @@ class NotificationService
merge_request.target_project,
current_user,
:merged_merge_request_email,
skip_current_user: !merge_request.merge_when_build_succeeds?
skip_current_user: !merge_request.merge_when_pipeline_succeeds?
)
end
......
......@@ -2,7 +2,7 @@ module Projects
class DestroyService < BaseService
include Gitlab::ShellAdapter
class DestroyError < StandardError; end
DestroyError = Class.new(StandardError)
DELETED_FLAG = '+deleted'.freeze
......
......@@ -2,7 +2,7 @@ module Projects
class ImportService < BaseService
include Gitlab::ShellAdapter
class Error < StandardError; end
Error = Class.new(StandardError)
def execute
add_repository_to_project unless project.gitlab_project_import?
......
......@@ -9,7 +9,7 @@
module Projects
class TransferService < BaseService
include Gitlab::ShellAdapter
class TransferError < StandardError; end
TransferError = Class.new(StandardError)
def execute(new_namespace)
if allowed_transfer?(current_user, project, new_namespace)
......
module Projects
class UpdateMirrorService < BaseService
class Error < StandardError; end
class UpdateError < Error; end
Error = Class.new(StandardError)
UpdateError = Class.new(Error)
def execute
unless project.mirror?
......
......@@ -59,7 +59,7 @@ module SlashCommands
@updates[:state_event] = 'reopen'
end
desc 'Merge (when build succeeds)'
desc 'Merge (when the pipeline succeeds)'
condition do
last_diff_sha = params && params[:merge_request_diff_head_sha]
issuable.is_a?(MergeRequest) &&
......
......@@ -187,14 +187,14 @@ module SystemNoteService
end
# Called when 'merge when pipeline succeeds' is executed
def merge_when_build_succeeds(noteable, project, author, last_commit)
def merge_when_pipeline_succeeds(noteable, project, author, last_commit)
body = "enabled an automatic merge when the pipeline for #{last_commit.to_reference(project)} succeeds"
create_note(noteable: noteable, project: project, author: author, note: body)
end
# Called when 'merge when pipeline succeeds' is canceled
def cancel_merge_when_build_succeeds(noteable, project, author)
def cancel_merge_when_pipeline_succeeds(noteable, project, author)
body = 'canceled the automatic merge'
create_note(noteable: noteable, project: project, author: author, note: body)
......
......@@ -103,7 +103,7 @@ class TodoService
#
def merge_request_build_failed(merge_request)
create_build_failed_todo(merge_request, merge_request.author)
create_build_failed_todo(merge_request, merge_request.merge_user) if merge_request.merge_when_build_succeeds?
create_build_failed_todo(merge_request, merge_request.merge_user) if merge_request.merge_when_pipeline_succeeds?
end
# When new approvers are added for a merge request:
......@@ -129,7 +129,7 @@ class TodoService
#
def merge_request_build_retried(merge_request)
mark_pending_todos_as_done(merge_request, merge_request.author)
mark_pending_todos_as_done(merge_request, merge_request.merge_user) if merge_request.merge_when_build_succeeds?
mark_pending_todos_as_done(merge_request, merge_request.merge_user) if merge_request.merge_when_pipeline_succeeds?
end
# When a merge request could not be automatically merged due to its unmergeable state we should:
......@@ -137,7 +137,7 @@ class TodoService
# * create a todo for a merge_user
#
def merge_request_became_unmergeable(merge_request)
create_unmergeable_todo(merge_request, merge_request.merge_user) if merge_request.merge_when_build_succeeds?
create_unmergeable_todo(merge_request, merge_request.merge_user) if merge_request.merge_when_pipeline_succeeds?
end
# When create a note we should:
......
......@@ -2,8 +2,8 @@
.form-group
.checkbox.builds-feature
= form.label :only_allow_merge_if_build_succeeds do
= form.check_box :only_allow_merge_if_build_succeeds
= form.label :only_allow_merge_if_pipeline_succeeds do
= form.check_box :only_allow_merge_if_pipeline_succeeds
%strong Only allow merge requests to be merged if the pipeline succeeds
%br
%span.descr
......
- if environment.has_terminals? && can?(current_user, :admin_environment, @project)
- if environment.deployment_service_ready? && can?(current_user, :admin_environment, @project)
= link_to terminal_namespace_project_environment_path(@project.namespace, @project, environment), class: 'btn terminal-button' do
= icon('terminal')
......@@ -2,9 +2,9 @@
- when :success
:plain
merge_request_widget.mergeInProgress(#{params[:should_remove_source_branch] == '1'});
- when :merge_when_build_succeeds
- when :merge_when_pipeline_succeeds
:plain
$('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/merge_when_build_succeeds'))}");
$('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/merge_when_pipeline_succeeds'))}");
- when :hook_validation_error
:plain
$('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/error'))}");
......
......@@ -26,8 +26,8 @@
= render 'projects/merge_requests/widget/open/conflicts'
- elsif @merge_request.work_in_progress?
= render 'projects/merge_requests/widget/open/wip'
- elsif @merge_request.merge_when_build_succeeds?
= render 'projects/merge_requests/widget/open/merge_when_build_succeeds'
- elsif @merge_request.merge_when_pipeline_succeeds?
= render 'projects/merge_requests/widget/open/merge_when_pipeline_succeeds'
- elsif !@merge_request.can_be_merged_by?(current_user)
= render 'projects/merge_requests/widget/open/not_allowed'
- elsif !@merge_request.mergeable_ci_state? && (@pipeline.failed? || @pipeline.canceled?)
......
......@@ -11,16 +11,16 @@
.accept-action
- if @pipeline && @pipeline.active?
%span.btn-group
= button_tag class: "btn btn-create js-merge-button merge_when_build_succeeds", disabled: !@merge_request.approved?, ":disabled" => "disableAcceptance" do
= button_tag class: "btn btn-create js-merge-button merge_when_pipeline_succeeds", disabled: !@merge_request.approved?, ":disabled" => "disableAcceptance" do
Merge When Pipeline Succeeds
- unless @project.only_allow_merge_if_build_succeeds?
- unless @project.only_allow_merge_if_pipeline_succeeds?
= button_tag class: "btn btn-success dropdown-toggle", 'data-toggle' => 'dropdown', disabled: !@merge_request.approved?, ":disabled" => "disableAcceptance" do
= icon('caret-down')
%span.sr-only
Select Merge Moment
%ul.js-merge-dropdown.dropdown-menu.dropdown-menu-right{ role: 'menu' }
%li
= link_to "#", class: "merge_when_build_succeeds" do
= link_to "#", class: "merge_when_pipeline_succeeds" do
= icon('check fw')
Merge When Pipeline Succeeds
%li
......@@ -61,4 +61,4 @@
text: @merge_request.merge_commit_message,
rows: 14, hint: true
= hidden_field_tag :merge_when_build_succeeds, "", autocomplete: "off"
= hidden_field_tag :merge_when_pipeline_succeeds, "", autocomplete: "off"
......@@ -20,7 +20,7 @@
The source branch will not be removed.
- remove_source_branch_button = !@merge_request.remove_source_branch? && @merge_request.can_remove_source_branch?(current_user) && @merge_request.merge_user == current_user
- user_can_cancel_automatic_merge = @merge_request.can_cancel_merge_when_build_succeeds?(current_user)
- user_can_cancel_automatic_merge = @merge_request.can_cancel_merge_when_pipeline_succeeds?(current_user)
- if remove_source_branch_button || user_can_cancel_automatic_merge
.clearfix.prepend-top-10
- if remove_source_branch_button
......@@ -29,5 +29,5 @@
Remove Source Branch When Merged
- if user_can_cancel_automatic_merge
= link_to cancel_merge_when_build_succeeds_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request), remote: true, method: :post, class: "btn btn-grouped btn-sm" do
= link_to cancel_merge_when_pipeline_succeeds_namespace_project_merge_request_path(@merge_request.target_project.namespace, @merge_request.target_project, @merge_request), remote: true, method: :post, class: "btn btn-grouped btn-sm" do
Cancel Automatic Merge
class RepositoryUpdateMirrorWorker
class UpdateMirrorError < StandardError; end
UpdateMirrorError = Class.new(StandardError)
include Sidekiq::Worker
include Gitlab::ShellAdapter
......
class RepositoryUpdateRemoteMirrorWorker
class UpdateRemoteMirrorError < StandardError; end
UpdateRemoteMirrorError = Class.new(StandardError)
include Sidekiq::Worker
include Gitlab::ShellAdapter
......
---
title: Deploy board backend
merge_request: 1278
author:
---
title: Remove the newrelic gem
merge_request: 1335
author: Robert Schilling
# New Relic configuration file
#
# This file is here to make sure the New Relic gem stays
# quiet by default.
#
# To enable and configure New Relic, please use
# environment variables, e.g. NEW_RELIC_ENABLED=true
production:
enabled: false
development:
enabled: false
test:
enabled: false
......@@ -100,7 +100,7 @@ constraints(ProjectUrlConstrainer.new) do
get :merge_check
post :merge
get :merge_widget_refresh
post :cancel_merge_when_build_succeeds
post :cancel_merge_when_pipeline_succeeds
get :ci_status
get :ci_environments_status
post :toggle_subscription
......@@ -191,6 +191,7 @@ constraints(ProjectUrlConstrainer.new) do
member do
post :stop
get :terminal
get :status, constraints: { format: :json }
get '/terminal.ws/authorize', to: 'environments#terminal_websocket_authorize', constraints: { format: nil }
end
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RenameMergeWhenBuildSucceeds < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = true
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
DOWNTIME_REASON = 'Renaming the column merge_when_build_succeeds'
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
# method is the _only_ method called in the migration, any other changes
# should go in a separate migration. This ensures that upon failure _only_ the
# index creation fails and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
# disable_ddl_transaction!
def change
rename_column :merge_requests, :merge_when_build_succeeds, :merge_when_pipeline_succeeds
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RenameOnlyAllowMergeIfBuildSucceeds < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = true
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
DOWNTIME_REASON = 'Renaming the column only_allow_merge_if_build_succeeds'
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
# method is the _only_ method called in the migration, any other changes
# should go in a separate migration. This ensures that upon failure _only_ the
# index creation fails and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
# disable_ddl_transaction!
def change
rename_column :projects, :only_allow_merge_if_build_succeeds, :only_allow_merge_if_pipeline_succeeds
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170216141440) do
ActiveRecord::Schema.define(version: 20170217151947) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -780,7 +780,7 @@ ActiveRecord::Schema.define(version: 20170216141440) do
t.integer "updated_by_id"
t.text "merge_error"
t.text "merge_params"
t.boolean "merge_when_build_succeeds", default: false, null: false
t.boolean "merge_when_pipeline_succeeds", default: false, null: false
t.integer "merge_user_id"
t.string "merge_commit_sha"
t.datetime "deleted_at"
......@@ -1109,7 +1109,7 @@ ActiveRecord::Schema.define(version: 20170216141440) do
t.boolean "last_repository_check_failed"
t.datetime "last_repository_check_at"
t.boolean "container_registry_enabled"
t.boolean "only_allow_merge_if_build_succeeds", default: false, null: false
t.boolean "only_allow_merge_if_pipeline_succeeds", default: false, null: false
t.boolean "has_external_issue_tracker"
t.string "repository_storage", default: "default", null: false
t.boolean "repository_read_only"
......
......@@ -65,7 +65,7 @@ Parameters:
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
"merge_when_build_succeeds": true,
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : false,
"sha": "8888888888888888888888888888888888888888",
......@@ -136,7 +136,7 @@ Parameters:
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
"merge_when_build_succeeds": true,
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
......@@ -243,7 +243,7 @@ Parameters:
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
"merge_when_build_succeeds": true,
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
......@@ -343,7 +343,7 @@ order for it to take effect:
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
"merge_when_build_succeeds": true,
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
......@@ -425,7 +425,7 @@ Must include at least one non-required attribute from above.
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
"merge_when_build_succeeds": true,
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
......@@ -479,7 +479,7 @@ Parameters:
- `merge_request_id` (required) - ID of MR
- `merge_commit_message` (optional) - Custom merge commit message
- `should_remove_source_branch` (optional) - if `true` removes the source branch
- `merge_when_build_succeeds` (optional) - if `true` the MR is merged when the build succeeds
- `merge_when_pipeline_succeeds` (optional) - if `true` the MR is merged when the pipeline succeeds
- `sha` (optional) - if present, then this SHA must
......@@ -489,7 +489,7 @@ Parameters:
| `merge_request_id` | integer | yes | The ID of the merge request |
| `merge_commit_message` | string | no | Custom merge commit message |
| `should_remove_source_branch` | boolean | no | Remove the source branch after merge |
| `merge_when_build_succeeds` | boolean | no | Merge when build succeeds, rather than immediately |
| `merge_when_pipeline_succeeds` | boolean | no | Merge when pipeline succeeds, rather than immediately |
| `sha` | string | no | If present, then this SHA must match the HEAD of the source branch, otherwise the merge will fail |
| `squash` | boolean | no | Squash the merge request into a single commit |
......@@ -537,7 +537,7 @@ Parameters:
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
"merge_when_build_succeeds": true,
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
......@@ -659,9 +659,9 @@ If you don't have permissions to accept this merge request - you'll get a `401`
If the merge request is already merged or closed - you get `405` and error message 'Method Not Allowed'
In case the merge request is not set to be merged when the build succeeds, you'll also get a `406` error.
In case the merge request is not set to be merged when the pipeline succeeds, you'll also get a `406` error.
```
PUT /projects/:id/merge_requests/:merge_request_id/cancel_merge_when_build_succeeds
PUT /projects/:id/merge_requests/:merge_request_id/cancel_merge_when_pipeline_succeeds
```
Parameters:
......@@ -711,7 +711,7 @@ Parameters:
"updated_at": "2015-02-02T19:49:26.013Z",
"due_date": null
},
"merge_when_build_succeeds": true,
"merge_when_pipeline_succeeds": true,
"merge_status": "can_be_merged",
"subscribed" : true,
"sha": "8888888888888888888888888888888888888888",
......@@ -869,7 +869,7 @@ Example response:
"updated_at": "2016-04-05T21:41:40.905Z",
"due_date": null
},
"merge_when_build_succeeds": false,
"merge_when_pipeline_succeeds": false,
"merge_status": "cannot_be_merged",
"subscribed": true,
"sha": "8888888888888888888888888888888888888888",
......@@ -943,7 +943,7 @@ Example response:
"updated_at": "2016-04-05T21:41:40.905Z",
"due_date": null
},
"merge_when_build_succeeds": false,
"merge_when_pipeline_succeeds": false,
"merge_status": "cannot_be_merged",
"subscribed": false,
"sha": "8888888888888888888888888888888888888888",
......@@ -1036,7 +1036,7 @@ Example response:
"updated_at": "2016-06-17T07:47:33.840Z",
"due_date": null
},
"merge_when_build_succeeds": false,
"merge_when_pipeline_succeeds": false,
"merge_status": "unchecked",
"subscribed": true,
"sha": "8888888888888888888888888888888888888888",
......
......@@ -88,7 +88,7 @@ Parameters:
"runners_token": "b8547b1dc37721d05889db52fa2f02",
"public_builds": true,
"shared_with_groups": [],
"only_allow_merge_if_build_succeeds": false,
"only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
"request_access_enabled": false
},
......@@ -149,7 +149,7 @@ Parameters:
"runners_token": "b8547b1dc37721d05889db52fa2f02",
"public_builds": true,
"shared_with_groups": [],
"only_allow_merge_if_build_succeeds": false,
"only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
"request_access_enabled": false
}
......@@ -242,7 +242,7 @@ Parameters:
}
],
"repository_storage": "default",
"only_allow_merge_if_build_succeeds": false,
"only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
"request_access_enabled": false
}
......@@ -451,7 +451,7 @@ Parameters:
| `visibility_level` | integer | no | See [project visibility level](#project-visibility-level) |
| `import_url` | string | no | URL to import repository from |
| `public_builds` | boolean | no | If `true`, builds can be viewed by non-project-members |
| `only_allow_merge_if_build_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved |
| `lfs_enabled` | boolean | no | Enable LFS |
| `request_access_enabled` | boolean | no | Allow users to request member access |
......@@ -485,7 +485,7 @@ Parameters:
| `visibility_level` | integer | no | See [project visibility level](#project-visibility-level) |
| `import_url` | string | no | URL to import repository from |
| `public_builds` | boolean | no | If `true`, builds can be viewed by non-project-members |
| `only_allow_merge_if_build_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved |
| `lfs_enabled` | boolean | no | Enable LFS |
| `request_access_enabled` | boolean | no | Allow users to request member access |
......@@ -519,7 +519,7 @@ Parameters:
| `visibility_level` | integer | no | See [project visibility level](#project-visibility-level) |
| `import_url` | string | no | URL to import repository from |
| `public_builds` | boolean | no | If `true`, builds can be viewed by non-project-members |
| `only_allow_merge_if_build_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
| `only_allow_merge_if_pipeline_succeeds` | boolean | no | Set whether merge requests can only be merged with successful builds |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | no | Set whether merge requests can only be merged when all the discussions are resolved |
| `lfs_enabled` | boolean | no | Enable LFS |
| `request_access_enabled` | boolean | no | Allow users to request member access |
......@@ -603,7 +603,7 @@ Example response:
"star_count": 1,
"public_builds": true,
"shared_with_groups": [],
"only_allow_merge_if_build_succeeds": false,
"only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
"request_access_enabled": false
}
......@@ -669,7 +669,7 @@ Example response:
"star_count": 0,
"public_builds": true,
"shared_with_groups": [],
"only_allow_merge_if_build_succeeds": false,
"only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
"request_access_enabled": false
}
......@@ -752,7 +752,7 @@ Example response:
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"public_builds": true,
"shared_with_groups": [],
"only_allow_merge_if_build_succeeds": false,
"only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
"request_access_enabled": false
}
......@@ -835,7 +835,7 @@ Example response:
"runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b",
"public_builds": true,
"shared_with_groups": [],
"only_allow_merge_if_build_succeeds": false,
"only_allow_merge_if_pipeline_succeeds": false,
"only_allow_merge_if_all_discussions_are_resolved": false,
"request_access_enabled": false
}
......
......@@ -92,7 +92,7 @@ Example Response:
"updated_at": "2016-06-17T07:47:34.163Z",
"due_date": null
},
"merge_when_build_succeeds": false,
"merge_when_pipeline_succeeds": false,
"merge_status": "cannot_be_merged",
"subscribed": true,
"user_notes_count": 7
......@@ -165,7 +165,7 @@ Example Response:
"updated_at": "2016-06-17T07:47:34.163Z",
"due_date": null
},
"merge_when_build_succeeds": false,
"merge_when_pipeline_succeeds": false,
"merge_status": "cannot_be_merged",
"subscribed": true,
"user_notes_count": 7
......@@ -263,7 +263,7 @@ Example Response:
"updated_at": "2016-06-17T07:47:34.163Z",
"due_date": null
},
"merge_when_build_succeeds": false,
"merge_when_pipeline_succeeds": false,
"merge_status": "cannot_be_merged",
"subscribed": true,
"user_notes_count": 7
......
......@@ -38,6 +38,12 @@ changes are in V4:
- POST `:id/repository/branches`
- POST `:id/repository/commits`
- POST/PUT/DELETE `:id/repository/files`
- Renamed `merge when build succeeds` to merge `when pipeline succeeds parameters` on the following endpoints: [!9335](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/)
- PUT `projects/:id/merge_requests/:merge_request_id/merge`
- POST `projects/:id/merge_requests/:merge_request_id/cancel_merge_when_pipeline_succeeds`
- POST `projects`
- POST `projects/user/:user_id`
- PUT `projects/:id`
- Renamed `branch_name` to `branch` on DELETE `id/repository/branches/:branch` response [!8936](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8936)
- Remove `public` param from create and edit actions of projects [!8736](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8736)
- Notes do not return deprecated field `upvote` and `downvote` [!9384](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9384)
......
......@@ -131,6 +131,16 @@ job_name:
variables: []
```
You are able to use other variables inside your variable definition (or escape them with `$$`):
```yaml
variables:
LS_CMD: 'ls $FLAGS $$TMP_DIR'
FLAGS: '-al'
script:
- 'eval $LS_CMD' # will execute 'ls -al $TMP_DIR'
```
## Secret variables
>**Notes:**
......
......@@ -32,7 +32,7 @@ Create a user or choose an existing user that Jenkins will use to interact
through the GitLab API. This user will need to be a global Admin or added
as a member to each Group/Project. Developer permission is required for reporting
build status. This is because a successful build status can trigger a merge
when 'Merge when build succeeds' feature is used. Some features of the GitLab
when 'Merge when pipeline succeeds' feature is used. Some features of the GitLab
Plugin may require additional privileges. For example, there is an option to
accept a merge request if the build is successful. Using this feature would
require developer, master or owner-level permission.
......
......@@ -5,7 +5,7 @@ more CI jobs running, you can set it to be merged automatically when the
jobs pipeline succeeds. This way, you don't have to wait for the jobs to
finish and remember to merge the request manually.
![Enable](img/merge_when_build_succeeds_enable.png)
![Enable](img/merge_when_pipeline_succeeds_enable.png)
When you hit the "Merge When Pipeline Succeeds" button, the status of the merge
request will be updated to represent the impending merge. If you cannot wait
......@@ -16,7 +16,7 @@ Both team developers and the author of the merge request have the option to
cancel the automatic merge if they find a reason why it shouldn't be merged
after all.
![Status](img/merge_when_build_succeeds_status.png)
![Status](img/merge_when_pipeline_succeeds_status.png)
When the pipeline succeeds, the merge request will automatically be merged.
When the pipeline fails, the author gets a chance to retry any failed jobs,
......@@ -32,15 +32,16 @@ changes to be reviewed.
> **Note:**
You need to have jobs configured to enable this feature.
You can prevent merge requests from being merged if their pipeline did not succeed.
You can prevent merge requests from being merged if their pipeline did not succeed
or if there are discussions to be resolved.
Navigate to your project's settings page, select the
**Only allow merge requests to be merged if the pipeline succeeds** check box and
hit **Save** for the changes to take effect.
![Only allow merge if pipeline succeeds settings](img/merge_when_build_succeeds_only_if_succeeds_settings.png)
![Only allow merge if pipeline succeeds settings](img/merge_when_pipeline_succeeds_only_if_succeeds_settings.png)
From now on, every time the pipeline fails you will not be able to merge the
merge request from the UI, until you make all relevant jobs pass.
![Only allow merge if pipeline succeeds message](img/merge_when_build_succeeds_only_if_succeeds_msg.png)
![Only allow merge if pipeline succeeds message](img/merge_when_pipeline_succeeds_only_if_succeeds_msg.png)
......@@ -160,13 +160,10 @@ module API
# Exceptions
#
class MissingTokenError < StandardError; end
class TokenNotFoundError < StandardError; end
class ExpiredError < StandardError; end
class RevokedError < StandardError; end
MissingTokenError = Class.new(StandardError)
TokenNotFoundError = Class.new(StandardError)
ExpiredError = Class.new(StandardError)
RevokedError = Class.new(StandardError)
class InsufficientScopeError < StandardError
attr_reader :scopes
......
......@@ -110,8 +110,8 @@ module API
expose :shared_with_groups do |project, options|
SharedGroup.represent(project.project_group_links.all, options)
end
expose :only_allow_merge_if_pipeline_succeeds
expose :repository_storage, if: lambda { |_project, options| options[:current_user].try(:admin?) }
expose :only_allow_merge_if_build_succeeds
expose :request_access_enabled
expose :only_allow_merge_if_all_discussions_are_resolved
expose :approvals_before_merge
......@@ -313,7 +313,7 @@ module API
expose :label_names, as: :labels
expose :work_in_progress?, as: :work_in_progress
expose :milestone, using: Entities::Milestone
expose :merge_when_build_succeeds
expose :merge_when_pipeline_succeeds
expose :merge_status
expose :diff_head_sha, as: :sha
expose :merge_commit_sha
......
......@@ -170,8 +170,8 @@ module API
optional :merge_commit_message, type: String, desc: 'Custom merge commit message'
optional :should_remove_source_branch, type: Boolean,
desc: 'When true, the source branch will be deleted if possible'
optional :merge_when_build_succeeds, type: Boolean,
desc: 'When true, this merge request will be merged when the pipeline succeeds'
optional :merge_when_pipeline_succeeds, type: Boolean,
desc: 'When true, this merge request will be merged when the pipeline succeeds'
optional :sha, type: String, desc: 'When present, must have the HEAD SHA of the source branch'
optional :squash, type: Boolean, desc: 'When true, the commits will be squashed into a single commit on merge'
end
......@@ -199,7 +199,7 @@ module API
should_remove_source_branch: params[:should_remove_source_branch]
}
if params[:merge_when_build_succeeds] && merge_request.head_pipeline && merge_request.head_pipeline.active?
if params[:merge_when_pipeline_succeeds] && merge_request.head_pipeline && merge_request.head_pipeline.active?
::MergeRequests::MergeWhenPipelineSucceedsService
.new(merge_request.target_project, current_user, merge_params)
.execute(merge_request)
......@@ -215,10 +215,10 @@ module API
desc 'Cancel merge if "Merge When Pipeline Succeeds" is enabled' do
success Entities::MergeRequest
end
post ':id/merge_requests/:merge_request_id/cancel_merge_when_build_succeeds' do
post ':id/merge_requests/:merge_request_id/cancel_merge_when_pipeline_succeeds' do
merge_request = find_project_merge_request(params[:merge_request_id])
unauthorized! unless merge_request.can_cancel_merge_when_build_succeeds?(current_user)
unauthorized! unless merge_request.can_cancel_merge_when_pipeline_succeeds?(current_user)
::MergeRequest::MergeWhenPipelineSucceedsService
.new(merge_request.target_project, current_user)
......
......@@ -23,7 +23,7 @@ module API
], desc: 'Create a public project. The same as visibility_level = 20.'
optional :public_builds, type: Boolean, desc: 'Perform public builds'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
optional :only_allow_merge_if_build_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed'
optional :only_allow_merge_if_pipeline_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed'
optional :only_allow_merge_if_all_discussions_are_resolved, type: Boolean, desc: 'Only allow to merge if all discussions are resolved'
# EE-specific
......@@ -213,7 +213,7 @@ module API
:wiki_enabled, :builds_enabled, :snippets_enabled,
:shared_runners_enabled, :container_registry_enabled,
:lfs_enabled, :visibility_level, :public_builds,
:request_access_enabled, :only_allow_merge_if_build_succeeds,
:request_access_enabled, :only_allow_merge_if_pipeline_succeeds,
:only_allow_merge_if_all_discussions_are_resolved, :path,
:default_branch,
## EE-specific
......
......@@ -78,6 +78,92 @@ module API
expose :plantuml_url
expose :terminal_max_session_time
end
class Project < Grape::Entity
expose :id, :description, :default_branch, :tag_list
expose :public?, as: :public
expose :archived?, as: :archived
expose :visibility_level, :ssh_url_to_repo, :http_url_to_repo, :web_url
expose :owner, using: ::API::Entities::UserBasic, unless: ->(project, options) { project.group }
expose :name, :name_with_namespace
expose :path, :path_with_namespace
expose :container_registry_enabled
# Expose old field names with the new permissions methods to keep API compatible
expose(:issues_enabled) { |project, options| project.feature_available?(:issues, options[:current_user]) }
expose(:merge_requests_enabled) { |project, options| project.feature_available?(:merge_requests, options[:current_user]) }
expose(:wiki_enabled) { |project, options| project.feature_available?(:wiki, options[:current_user]) }
expose(:builds_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) }
expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) }
expose :created_at, :last_activity_at
expose :shared_runners_enabled
expose :lfs_enabled?, as: :lfs_enabled
expose :creator_id
expose :namespace, using: 'API::Entities::Namespace'
expose :forked_from_project, using: ::API::Entities::BasicProjectDetails, if: lambda{ |project, options| project.forked? }
expose :avatar_url
expose :star_count, :forks_count
expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) && project.default_issues_tracker? }
expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
expose :public_builds
expose :shared_with_groups do |project, options|
::API::Entities::SharedGroup.represent(project.project_group_links.all, options)
end
expose :only_allow_merge_if_pipeline_succeeds, as: :only_allow_merge_if_build_succeeds
expose :repository_storage, if: lambda { |_project, options| options[:current_user].try(:admin?) }
expose :request_access_enabled
expose :only_allow_merge_if_all_discussions_are_resolved
expose :approvals_before_merge
expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics
end
class ProjectWithAccess < Project
expose :permissions do
expose :project_access, using: ::API::Entities::ProjectAccess do |project, options|
project.project_members.find_by(user_id: options[:current_user].id)
end
expose :group_access, using: ::API::Entities::GroupAccess do |project, options|
if project.group
project.group.group_members.find_by(user_id: options[:current_user].id)
end
end
end
end
class MergeRequest < Grape::Entity
expose :id, :iid
expose(:project_id) { |entity| entity.project.id }
expose :title, :description
expose :state, :created_at, :updated_at
expose :target_branch, :source_branch
expose :upvotes, :downvotes
expose :author, :assignee, using: ::API::Entities::UserBasic
expose :source_project_id, :target_project_id
expose :label_names, as: :labels
expose :work_in_progress?, as: :work_in_progress
expose :milestone, using: ::API::Entities::Milestone
expose :merge_when_pipeline_succeeds, as: :merge_when_build_succeeds
expose :merge_status
expose :diff_head_sha, as: :sha
expose :merge_commit_sha
expose :subscribed do |merge_request, options|
merge_request.subscribed?(options[:current_user], options[:project])
end
expose :user_notes_count
expose :approvals_before_merge
expose :should_remove_source_branch?, as: :should_remove_source_branch
expose :force_remove_source_branch?, as: :force_remove_source_branch
expose :squash
expose :web_url do |merge_request, options|
Gitlab::UrlBuilder.build(merge_request)
end
end
end
end
end
......@@ -41,7 +41,7 @@ module API
desc 'List merge requests' do
detail 'iid filter is deprecated have been removed on V4'
success ::API::Entities::MergeRequest
success ::API::V3::Entities::MergeRequest
end
params do
optional :state, type: String, values: %w[opened closed merged all], default: 'all',
......@@ -73,7 +73,7 @@ module API
end
desc 'Create a merge request' do
success ::API::Entities::MergeRequest
success ::API::V3::Entities::MergeRequest
end
params do
requires :title, type: String, desc: 'The title of the merge request'
......@@ -92,7 +92,7 @@ module API
merge_request = ::MergeRequests::CreateService.new(user_project, current_user, mr_params).execute
if merge_request.valid?
present merge_request, with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project
present merge_request, with: ::API::V3::Entities::MergeRequest, current_user: current_user, project: user_project
else
handle_merge_request_errors! merge_request.errors
end
......@@ -119,12 +119,12 @@ module API
if status == :deprecated
detail DEPRECATION_MESSAGE
end
success ::API::Entities::MergeRequest
success ::API::V3::Entities::MergeRequest
end
get path do
merge_request = find_merge_request_with_access(params[:merge_request_id])
present merge_request, with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project
present merge_request, with: ::API::V3::Entities::MergeRequest, current_user: current_user, project: user_project
end
desc 'Get the commits of a merge request' do
......@@ -146,7 +146,7 @@ module API
end
desc 'Update a merge request' do
success ::API::Entities::MergeRequest
success ::API::V3::Entities::MergeRequest
end
params do
optional :title, type: String, allow_blank: false, desc: 'The title of the merge request'
......@@ -167,14 +167,14 @@ module API
merge_request = ::MergeRequests::UpdateService.new(user_project, current_user, mr_params).execute(merge_request)
if merge_request.valid?
present merge_request, with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project
present merge_request, with: ::API::V3::Entities::MergeRequest, current_user: current_user, project: user_project
else
handle_merge_request_errors! merge_request.errors
end
end
desc 'Merge a merge request' do
success ::API::Entities::MergeRequest
success ::API::V3::Entities::MergeRequest
end
params do
optional :merge_commit_message, type: String, desc: 'Custom merge commit message'
......@@ -219,16 +219,16 @@ module API
.execute(merge_request)
end
present merge_request, with: ::API::Entities::MergeRequest, current_user: current_user, project: user_project
present merge_request, with: ::API::V3::Entities::MergeRequest, current_user: current_user, project: user_project
end
desc 'Cancel merge if "Merge When Pipeline Succeeds" is enabled' do
success ::API::Entities::MergeRequest
success ::API::V3::Entities::MergeRequest
end
post "#{path}/cancel_merge_when_build_succeeds" do
merge_request = find_project_merge_request(params[:merge_request_id])
unauthorized! unless merge_request.can_cancel_merge_when_build_succeeds?(current_user)
unauthorized! unless merge_request.can_cancel_merge_when_pipeline_succeeds?(current_user)
::MergeRequest::MergeWhenPipelineSucceedsService
.new(merge_request.target_project, current_user)
......
......@@ -5,6 +5,10 @@ module API
before { authenticate_non_get! }
after_validation do
set_only_allow_merge_if_pipeline_succeeds!
end
helpers do
params :optional_params do
optional :description, type: String, desc: 'The description of the project'
......@@ -25,6 +29,7 @@ module API
optional :public_builds, type: Boolean, desc: 'Perform public builds'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
optional :only_allow_merge_if_build_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed'
optional :only_allow_merge_if_pipeline_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed'
optional :only_allow_merge_if_all_discussions_are_resolved, type: Boolean, desc: 'Only allow to merge if all discussions are resolved'
# EE-specific
......@@ -41,6 +46,12 @@ module API
end
attrs
end
def set_only_allow_merge_if_pipeline_succeeds!
if params.has_key?(:only_allow_merge_if_build_succeeds)
params[:only_allow_merge_if_pipeline_succeeds] = params.delete(:only_allow_merge_if_build_succeeds)
end
end
end
resource :projects do
......@@ -79,7 +90,7 @@ module API
def present_projects(projects, options = {})
options = options.reverse_merge(
with: ::API::Entities::Project,
with: ::API::V3::Entities::Project,
current_user: current_user,
simple: params[:simple],
)
......@@ -99,7 +110,7 @@ module API
use :collection_params
end
get '/visible' do
entity = current_user ? ::API::Entities::ProjectWithAccess : ::API::Entities::BasicProjectDetails
entity = current_user ? ::API::V3::Entities::ProjectWithAccess : ::API::Entities::BasicProjectDetails
present_projects ProjectsFinder.new.execute(current_user), with: entity
end
......@@ -113,7 +124,7 @@ module API
authenticate!
present_projects current_user.authorized_projects,
with: ::API::Entities::ProjectWithAccess
with: ::API::V3::Entities::ProjectWithAccess
end
desc 'Get an owned projects list for authenticated user' do
......@@ -127,7 +138,7 @@ module API
authenticate!
present_projects current_user.owned_projects,
with: ::API::Entities::ProjectWithAccess,
with: ::API::V3::Entities::ProjectWithAccess,
statistics: params[:statistics]
end
......@@ -153,11 +164,11 @@ module API
get '/all' do
authenticated_as_admin!
present_projects Project.all, with: ::API::Entities::ProjectWithAccess, statistics: params[:statistics]
present_projects Project.all, with: ::API::V3::Entities::ProjectWithAccess, statistics: params[:statistics]
end
desc 'Search for projects the current user has access to' do
success ::API::Entities::Project
success ::API::V3::Entities::Project
end
params do
requires :query, type: String, desc: 'The project name to be searched'
......@@ -169,11 +180,11 @@ module API
projects = search_service.objects('projects', params[:page])
projects = projects.reorder(params[:order_by] => params[:sort])
present paginate(projects), with: ::API::Entities::Project
present paginate(projects), with: ::API::V3::Entities::Project
end
desc 'Create new project' do
success ::API::Entities::Project
success ::API::V3::Entities::Project
end
params do
optional :name, type: String, desc: 'The name of the project'
......@@ -187,7 +198,7 @@ module API
project = ::Projects::CreateService.new(current_user, attrs).execute
if project.saved?
present project, with: ::API::Entities::Project,
present project, with: ::API::V3::Entities::Project,
user_can_admin_project: can?(current_user, :admin_project, project)
else
if project.errors[:limit_reached].present?
......@@ -198,7 +209,7 @@ module API
end
desc 'Create new project for a specified user. Only available to admin users.' do
success ::API::Entities::Project
success ::API::V3::Entities::Project
end
params do
requires :name, type: String, desc: 'The name of the project'
......@@ -216,7 +227,7 @@ module API
project = ::Projects::CreateService.new(user, attrs).execute
if project.saved?
present project, with: ::API::Entities::Project,
present project, with: ::API::V3::Entities::Project,
user_can_admin_project: can?(current_user, :admin_project, project)
else
render_validation_error!(project)
......@@ -229,10 +240,10 @@ module API
end
resource :projects, requirements: { id: /[^\/]+/ } do
desc 'Get a single project' do
success ::API::Entities::ProjectWithAccess
success ::API::V3::Entities::ProjectWithAccess
end
get ":id" do
entity = current_user ? ::API::Entities::ProjectWithAccess : ::API::Entities::BasicProjectDetails
entity = current_user ? ::API::V3::Entities::ProjectWithAccess : ::API::Entities::BasicProjectDetails
present user_project, with: entity, current_user: current_user,
user_can_admin_project: can?(current_user, :admin_project, user_project)
end
......@@ -248,7 +259,7 @@ module API
end
desc 'Fork new project for the current user or provided namespace.' do
success ::API::Entities::Project
success ::API::V3::Entities::Project
end
params do
optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be forked into'
......@@ -274,13 +285,13 @@ module API
if forked_project.errors.any?
conflict!(forked_project.errors.messages)
else
present forked_project, with: ::API::Entities::Project,
present forked_project, with: ::API::V3::Entities::Project,
user_can_admin_project: can?(current_user, :admin_project, forked_project)
end
end
desc 'Update an existing project' do
success ::API::Entities::Project
success ::API::V3::Entities::Project
end
params do
optional :name, type: String, desc: 'The name of the project'
......@@ -306,7 +317,7 @@ module API
result = ::Projects::UpdateService.new(user_project, current_user, attrs).execute
if result[:status] == :success
present user_project, with: ::API::Entities::Project,
present user_project, with: ::API::V3::Entities::Project,
user_can_admin_project: can?(current_user, :admin_project, user_project)
else
render_validation_error!(user_project)
......@@ -314,29 +325,29 @@ module API
end
desc 'Archive a project' do
success ::API::Entities::Project
success ::API::V3::Entities::Project
end
post ':id/archive' do
authorize!(:archive_project, user_project)
user_project.archive!
present user_project, with: ::API::Entities::Project
present user_project, with: ::API::V3::Entities::Project
end
desc 'Unarchive a project' do
success ::API::Entities::Project
success ::API::V3::Entities::Project
end
post ':id/unarchive' do
authorize!(:archive_project, user_project)
user_project.unarchive!
present user_project, with: ::API::Entities::Project
present user_project, with: ::API::V3::Entities::Project
end
desc 'Star a project' do
success ::API::Entities::Project
success ::API::V3::Entities::Project
end
post ':id/star' do
if current_user.starred?(user_project)
......@@ -345,19 +356,19 @@ module API
current_user.toggle_star(user_project)
user_project.reload
present user_project, with: ::API::Entities::Project
present user_project, with: ::API::V3::Entities::Project
end
end
desc 'Unstar a project' do
success ::API::Entities::Project
success ::API::V3::Entities::Project
end
delete ':id/star' do
if current_user.starred?(user_project)
current_user.toggle_star(user_project)
user_project.reload
present user_project, with: ::API::Entities::Project
present user_project, with: ::API::V3::Entities::Project
else
not_modified!
end
......
module Bitbucket
module Error
class Unauthorized < StandardError
end
Unauthorized = Class.new(StandardError)
end
end
module Ci
class GitlabCiYamlProcessor
class ValidationError < StandardError; end
ValidationError = Class.new(StandardError)
include Gitlab::Ci::Config::Entry::LegacyValidationHelpers
......
......@@ -2,7 +2,7 @@
# file path string when combined in a request parameter
module ExtractsPath
# Raised when given an invalid file path
class InvalidPathError < StandardError; end
InvalidPathError = Class.new(StandardError)
# Given a string containing both a Git tree-ish, such as a branch or tag, and
# a filesystem path joined by forward slashes, attempts to separate the two.
......
......@@ -5,7 +5,7 @@
#
module Gitlab
module Access
class AccessDeniedError < StandardError; end
AccessDeniedError = Class.new(StandardError)
NO_ACCESS = 0
GUEST = 10
......
module Gitlab
module Auth
class MissingPersonalTokenError < StandardError; end
MissingPersonalTokenError = Class.new(StandardError)
SCOPES = [:api, :read_user].freeze
DEFAULT_SCOPES = [:api].freeze
......
......@@ -6,7 +6,7 @@ module Gitlab
module Build
module Artifacts
class Metadata
class ParserError < StandardError; end
ParserError = Class.new(StandardError)
VERSION_PATTERN = /^[\w\s]+(\d+\.\d+\.\d+)/
INVALID_PATH_PATTERN = %r{(^\.?\.?/)|(/\.?\.?/)}
......
......@@ -6,7 +6,7 @@ module Gitlab
# Factory class responsible for fabricating entry objects.
#
class Factory
class InvalidFactory < StandardError; end
InvalidFactory = Class.new(StandardError)
def initialize(entry)
@entry = entry
......
......@@ -6,7 +6,7 @@ module Gitlab
# Base abstract class for each configuration entry node.
#
class Node
class InvalidError < StandardError; end
InvalidError = Class.new(StandardError)
attr_reader :config, :metadata
attr_accessor :key, :parent, :description
......
......@@ -2,7 +2,7 @@ module Gitlab
module Ci
class Config
class Loader
class FormatError < StandardError; end
FormatError = Class.new(StandardError)
def initialize(config)
@config = YAML.safe_load(config, [Symbol], [], true)
......
......@@ -4,8 +4,7 @@ module Gitlab
include Gitlab::Routing.url_helpers
include IconsHelper
class MissingResolution < ResolutionError
end
MissingResolution = Class.new(ResolutionError)
CONTEXT_LINES = 3
......
module Gitlab
module Conflict
class FileCollection
class ConflictSideMissing < StandardError
end
ConflictSideMissing = Class.new(StandardError)
attr_reader :merge_request, :our_commit, :their_commit
......
module Gitlab
module Conflict
class Parser
class UnresolvableError < StandardError
end
class UnmergeableFile < UnresolvableError
end
class UnsupportedEncoding < UnresolvableError
end
UnresolvableError = Class.new(StandardError)
UnmergeableFile = Class.new(UnresolvableError)
UnsupportedEncoding = Class.new(UnresolvableError)
# Recoverable errors - the conflict can be resolved in an editor, but not with
# sections.
class ParserError < StandardError
end
class UnexpectedDelimiter < ParserError
end
class MissingEndDelimiter < ParserError
end
ParserError = Class.new(StandardError)
UnexpectedDelimiter = Class.new(ParserError)
MissingEndDelimiter = Class.new(ParserError)
def parse(text, our_path:, their_path:, parent_file: nil)
raise UnmergeableFile if text.blank? # Typically a binary file
......
module Gitlab
module Conflict
class ResolutionError < StandardError
end
ResolutionError = Class.new(StandardError)
end
end
......@@ -4,19 +4,19 @@ require_dependency 'gitlab/email/handler'
# Inspired in great part by Discourse's Email::Receiver
module Gitlab
module Email
class ProcessingError < StandardError; end
class EmailUnparsableError < ProcessingError; end
class SentNotificationNotFoundError < ProcessingError; end
class ProjectNotFound < ProcessingError; end
class EmptyEmailError < ProcessingError; end
class AutoGeneratedEmailError < ProcessingError; end
class UserNotFoundError < ProcessingError; end
class UserBlockedError < ProcessingError; end
class UserNotAuthorizedError < ProcessingError; end
class NoteableNotFoundError < ProcessingError; end
class InvalidNoteError < ProcessingError; end
class InvalidIssueError < ProcessingError; end
class UnknownIncomingEmail < ProcessingError; end
ProcessingError = Class.new(StandardError)
EmailUnparsableError = Class.new(ProcessingError)
SentNotificationNotFoundError = Class.new(ProcessingError)
ProjectNotFound = Class.new(ProcessingError)
EmptyEmailError = Class.new(ProcessingError)
AutoGeneratedEmailError = Class.new(ProcessingError)
UserNotFoundError = Class.new(ProcessingError)
UserBlockedError = Class.new(ProcessingError)
UserNotAuthorizedError = Class.new(ProcessingError)
NoteableNotFoundError = Class.new(ProcessingError)
InvalidNoteError = Class.new(ProcessingError)
InvalidIssueError = Class.new(ProcessingError)
UnknownIncomingEmail = Class.new(ProcessingError)
class Receiver
def initialize(raw)
......
module Gitlab
module Geo
class OauthApplicationUndefinedError < StandardError; end
OauthApplicationUndefinedError = Class.new(StandardError)
def self.current_node
self.cache_value(:geo_node_current) do
......
......@@ -2,7 +2,7 @@
module Gitlab
module Git
class Diff
class TimeoutError < StandardError; end
TimeoutError = Class.new(StandardError)
include Gitlab::Git::EncodingHelper
# Diff properties
......
......@@ -10,9 +10,9 @@ module Gitlab
SEARCH_CONTEXT_LINES = 3
class NoRepository < StandardError; end
class InvalidBlobName < StandardError; end
class InvalidRef < StandardError; end
NoRepository = Class.new(StandardError)
InvalidBlobName = Class.new(StandardError)
InvalidRef = Class.new(StandardError)
# Full path to repo
attr_reader :path
......
module Gitlab
module GonHelper
def add_gon_variables
gon.api_version = API::API.version
gon.api_version = 'v3' # v4 Is not officially released yet, therefore can't be considered as "frozen"
gon.default_avatar_url = URI.join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
gon.max_file_size = current_application_settings.max_attachment_size
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
......
module Gitlab
module ImportExport
class Error < StandardError; end
Error = Class.new(StandardError)
end
end
......@@ -8,13 +8,13 @@ module Gitlab
)
# Filters an array of pods (as returned by the kubernetes API) by their labels
def filter_pods(pods, labels = {})
pods.select do |pod|
metadata = pod.fetch("metadata", {})
pod_labels = metadata.fetch("labels", nil)
next unless pod_labels
def filter_by_label(items, labels = {})
items.select do |item|
metadata = item.fetch("metadata", {})
item_labels = metadata.fetch("labels", nil)
next unless item_labels
labels.all? { |k, v| pod_labels[k.to_s] == v }
labels.all? { |k, v| item_labels[k.to_s] == v }
end
end
......
module Gitlab
module Kubernetes
class Deployment
def initialize(attributes = {})
@attributes = attributes
end
def name
metadata['name']
end
def labels
metadata['labels']
end
def outdated?
observed_generation < generation
end
def wanted_replicas
spec.fetch('replicas', 0)
end
def finished_replicas
status.fetch('availableReplicas', 0)
end
def deploying_replicas
updated_replicas - finished_replicas
end
def waiting_replicas
wanted_replicas - updated_replicas
end
def instances
return deployment_instances(wanted_replicas, 'unknown', 'waiting') if name.nil?
return deployment_instances(wanted_replicas, name, 'waiting') if outdated?
out = deployment_instances(finished_replicas, name, 'finished')
out.push(*deployment_instances(deploying_replicas, name, 'deploying', out.size))
out.push(*deployment_instances(waiting_replicas, name, 'waiting', out.size))
out
end
private
def deployment_instances(n, name, status, offset = 0)
return [] if n < 0
Array.new(n) { |idx| deployment_instance(idx + offset, name, status) }
end
def deployment_instance(n, name, status)
{ status: status, tooltip: "#{name} (pod #{n}) #{status.capitalize}" }
end
def metadata
@attributes.fetch('metadata', {})
end
def spec
@attributes.fetch('spec', {})
end
def status
@attributes.fetch('status', {})
end
def updated_replicas
status.fetch('updatedReplicas', 0)
end
def generation
metadata.fetch('generation', 0)
end
def observed_generation
status.fetch('observedGeneration', 0)
end
end
end
end
module Gitlab
module Kubernetes
# Calculates the rollout status for a set of kubernetes deployments.
#
# A GitLab environment may be composed of several Kubernetes deployments and
# other resources, unified by an `app=` label. The rollout status sums the
# Kubernetes deployments together.
class RolloutStatus
attr_reader :deployments, :instances, :completion
def complete?
completion == 100
end
def self.from_specs(*specs)
deployments = specs.map { |spec| ::Gitlab::Kubernetes::Deployment.new(spec) }
new(deployments)
end
def initialize(deployments)
@deployments = deployments
@instances = deployments.flat_map(&:instances)
@completion =
if @instances.empty?
100
else
finished = @instances.select {|instance| instance[:status] == 'finished' }.count
(finished / @instances.count.to_f * 100).to_i
end
end
end
end
end
......@@ -4,7 +4,7 @@ module Gitlab
class Config
attr_accessor :provider, :options
class InvalidProvider < StandardError; end
InvalidProvider = Class.new(StandardError)
def self.enabled?
Gitlab.config.ldap.enabled
......
......@@ -5,7 +5,7 @@
#
module Gitlab
module OAuth
class SignupDisabledError < StandardError; end
SignupDisabledError = Class.new(StandardError)
class User
attr_accessor :auth_hash, :gl_user
......
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