Commit d2406668 authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch 'ci-commit-as-pipeline' into 'master'

Ci::Commit becomes a Pipeline object

1. Ci::Commit receives context: ref, :tag.
1. One Ci::Commit describes a one Pipeline
1. Pipeline is created from `.gitlab-ci.yml`
1. Pipeline is a ordered group of builds
1. We test MR against Pipeline
1. Pipelines have a separate view (https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3703)
1. Pipeline can be triggered from UI (https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3703)
1. Later we change `Trigger -> TriggerRequest -> Build` to `Trigger -> Pipeline` (future)
1. We add a Pipeline Hook that will be triggered on Pipeline status change  (future)
1. We extend notifications to use `Pipeline Hook` to send summary on pipeline changes (future)

After merging that I'll prepare a separate MR that will unify naming, database columns, table names:
```
Ci::Commit -> Pipeline
Ci::Build -> Build
CommitStatus -> Job
GenericCommitStatus -> ExternalJob

ci_commits -> pipelines
ci_builds -> jobs
```

This MR implements first 5 points.

This is made to solve this issue https://gitlab.com/gitlab-org/gitlab-ce/issues/14149.

See merge request !3653
parents 2ade37e2 27e0c772
...@@ -68,6 +68,7 @@ v 8.7.0 (unreleased) ...@@ -68,6 +68,7 @@ v 8.7.0 (unreleased)
- Hide `Create a group` help block when creating a new project in a group - Hide `Create a group` help block when creating a new project in a group
- Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.) - Implement 'TODOs View' as an option for dashboard preferences !3379 (Elias W.)
- Allow issues and merge requests to be assigned to the author !2765 - Allow issues and merge requests to be assigned to the author !2765
- Make Ci::Commit to group only similar builds and make it stateful (ref, tag)
- Gracefully handle notes on deleted commits in merge requests (Stan Hu) - Gracefully handle notes on deleted commits in merge requests (Stan Hu)
- Decouple membership and notifications - Decouple membership and notifications
- Fix creation of merge requests for orphaned branches (Stan Hu) - Fix creation of merge requests for orphaned branches (Stan Hu)
......
...@@ -38,13 +38,13 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -38,13 +38,13 @@ class Projects::CommitController < Projects::ApplicationController
end end
def cancel_builds def cancel_builds
ci_commit.builds.running_or_pending.each(&:cancel) ci_builds.running_or_pending.each(&:cancel)
redirect_back_or_default default: builds_namespace_project_commit_path(project.namespace, project, commit.sha) redirect_back_or_default default: builds_namespace_project_commit_path(project.namespace, project, commit.sha)
end end
def retry_builds def retry_builds
ci_commit.builds.latest.failed.each do |build| ci_builds.latest.failed.each do |build|
if build.retryable? if build.retryable?
Ci::Build.retry(build) Ci::Build.retry(build)
end end
...@@ -99,8 +99,12 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -99,8 +99,12 @@ class Projects::CommitController < Projects::ApplicationController
@commit ||= @project.commit(params[:id]) @commit ||= @project.commit(params[:id])
end end
def ci_commit def ci_commits
@ci_commit ||= project.ci_commit(commit.sha) @ci_commits ||= project.ci_commits.where(sha: commit.sha)
end
def ci_builds
@ci_builds ||= Ci::Build.where(commit: ci_commits)
end end
def define_show_vars def define_show_vars
...@@ -113,7 +117,8 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -113,7 +117,8 @@ class Projects::CommitController < Projects::ApplicationController
@diff_refs = [commit.parent || commit, commit] @diff_refs = [commit.parent || commit, commit]
@notes_count = commit.notes.count @notes_count = commit.notes.count
@statuses = ci_commit.statuses if ci_commit @statuses = CommitStatus.where(commit: ci_commits)
@builds = Ci::Build.where(commit: ci_commits)
end end
def assign_change_commit_vars(mr_source_branch) def assign_change_commit_vars(mr_source_branch)
......
...@@ -321,6 +321,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -321,6 +321,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def define_widget_vars def define_widget_vars
@ci_commit = @merge_request.ci_commit @ci_commit = @merge_request.ci_commit
@ci_commits = [@ci_commit].compact
closes_issues closes_issues
end end
......
...@@ -4,14 +4,6 @@ module CiStatusHelper ...@@ -4,14 +4,6 @@ module CiStatusHelper
builds_namespace_project_commit_path(project.namespace, project, ci_commit.sha) builds_namespace_project_commit_path(project.namespace, project, ci_commit.sha)
end end
def ci_status_icon(ci_commit)
ci_icon_for_status(ci_commit.status)
end
def ci_status_label(ci_commit)
ci_label_for_status(ci_commit.status)
end
def ci_status_with_icon(status, target = nil) def ci_status_with_icon(status, target = nil)
content = ci_icon_for_status(status) + '&nbsp;'.html_safe + ci_label_for_status(status) content = ci_icon_for_status(status) + '&nbsp;'.html_safe + ci_label_for_status(status)
klass = "ci-status ci-#{status}" klass = "ci-status ci-#{status}"
...@@ -47,10 +39,13 @@ module CiStatusHelper ...@@ -47,10 +39,13 @@ module CiStatusHelper
end end
def render_ci_status(ci_commit, tooltip_placement: 'auto left') def render_ci_status(ci_commit, tooltip_placement: 'auto left')
link_to ci_status_icon(ci_commit), # TODO: split this method into
# - render_commit_status
# - render_pipeline_status
link_to ci_icon_for_status(ci_commit.status),
ci_status_path(ci_commit), ci_status_path(ci_commit),
class: "ci-status-link ci-status-icon-#{ci_commit.status.dasherize}", class: "ci-status-link ci-status-icon-#{ci_commit.status.dasherize}",
title: "Build #{ci_status_label(ci_commit)}", title: "Build #{ci_label_for_status(ci_commit.status)}",
data: { toggle: 'tooltip', placement: tooltip_placement } data: { toggle: 'tooltip', placement: tooltip_placement }
end end
......
...@@ -25,6 +25,10 @@ module GitlabRoutingHelper ...@@ -25,6 +25,10 @@ module GitlabRoutingHelper
namespace_project_commits_path(project.namespace, project, @ref || project.repository.root_ref) namespace_project_commits_path(project.namespace, project, @ref || project.repository.root_ref)
end end
def project_pipelines_path(project, *args)
namespace_project_pipelines_path(project.namespace, project, *args)
end
def project_builds_path(project, *args) def project_builds_path(project, *args)
namespace_project_builds_path(project.namespace, project, *args) namespace_project_builds_path(project.namespace, project, *args)
end end
......
...@@ -37,8 +37,6 @@ ...@@ -37,8 +37,6 @@
module Ci module Ci
class Build < CommitStatus class Build < CommitStatus
LAZY_ATTRIBUTES = ['trace']
belongs_to :runner, class_name: 'Ci::Runner' belongs_to :runner, class_name: 'Ci::Runner'
belongs_to :trigger_request, class_name: 'Ci::TriggerRequest' belongs_to :trigger_request, class_name: 'Ci::TriggerRequest'
belongs_to :erased_by, class_name: 'User' belongs_to :erased_by, class_name: 'User'
...@@ -50,25 +48,17 @@ module Ci ...@@ -50,25 +48,17 @@ module Ci
scope :unstarted, ->() { where(runner_id: nil) } scope :unstarted, ->() { where(runner_id: nil) }
scope :ignore_failures, ->() { where(allow_failure: false) } scope :ignore_failures, ->() { where(allow_failure: false) }
scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) }
mount_uploader :artifacts_file, ArtifactUploader mount_uploader :artifacts_file, ArtifactUploader
mount_uploader :artifacts_metadata, ArtifactUploader mount_uploader :artifacts_metadata, ArtifactUploader
acts_as_taggable acts_as_taggable
# To prevent db load megabytes of data from trace
default_scope -> { select(Ci::Build.columns_without_lazy) }
before_destroy { project } before_destroy { project }
class << self after_create :execute_hooks
def columns_without_lazy
(column_names - LAZY_ATTRIBUTES).map do |column_name|
"#{table_name}.#{column_name}"
end
end
class << self
def last_month def last_month
where('created_at > ?', Date.today - 1.month) where('created_at > ?', Date.today - 1.month)
end end
...@@ -126,12 +116,16 @@ module Ci ...@@ -126,12 +116,16 @@ module Ci
end end
def retried? def retried?
!self.commit.latest_statuses_for_ref(self.ref).include?(self) !self.commit.statuses.latest.include?(self)
end
def retry
Ci::Build.retry(self)
end end
def depends_on_builds def depends_on_builds
# Get builds of the same type # Get builds of the same type
latest_builds = self.commit.builds.similar(self).latest latest_builds = self.commit.builds.latest
# Return builds from previous stages # Return builds from previous stages
latest_builds.where('stage_idx < ?', stage_idx) latest_builds.where('stage_idx < ?', stage_idx)
......
...@@ -19,21 +19,28 @@ ...@@ -19,21 +19,28 @@
module Ci module Ci
class Commit < ActiveRecord::Base class Commit < ActiveRecord::Base
extend Ci::Model extend Ci::Model
include Statuseable
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
has_many :statuses, class_name: 'CommitStatus' has_many :statuses, class_name: 'CommitStatus'
has_many :builds, class_name: 'Ci::Build' has_many :builds, class_name: 'Ci::Build'
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest' has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
delegate :stages, to: :statuses
validates_presence_of :sha validates_presence_of :sha
validates_presence_of :status
validate :valid_commit_sha validate :valid_commit_sha
# Invalidate object and save if when touched
after_touch :update_state
def self.truncate_sha(sha) def self.truncate_sha(sha)
sha[0...8] sha[0...8]
end end
def to_param def self.stages
sha CommitStatus.where(commit: all).stages
end end
def project_id def project_id
...@@ -68,15 +75,20 @@ module Ci ...@@ -68,15 +75,20 @@ module Ci
nil nil
end end
def stage def branch?
running_or_pending = statuses.latest.running_or_pending.ordered !tag?
running_or_pending.first.try(:stage) end
def retryable?
builds.latest.any? do |build|
build.failed? && build.retryable?
end
end end
def create_builds(ref, tag, user, trigger_request = nil) def create_builds(user, trigger_request = nil)
return unless config_processor return unless config_processor
config_processor.stages.any? do |stage| config_processor.stages.any? do |stage|
CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request, 'success').present? CreateBuildsService.new(self).execute(stage, user, 'success', trigger_request).present?
end end
end end
...@@ -84,7 +96,7 @@ module Ci ...@@ -84,7 +96,7 @@ module Ci
return unless config_processor return unless config_processor
# don't create other builds if this one is retried # don't create other builds if this one is retried
latest_builds = builds.similar(build).latest latest_builds = builds.latest
return unless latest_builds.exists?(build.id) return unless latest_builds.exists?(build.id)
# get list of stages after this build # get list of stages after this build
...@@ -92,88 +104,21 @@ module Ci ...@@ -92,88 +104,21 @@ module Ci
next_stages.delete(build.stage) next_stages.delete(build.stage)
# get status for all prior builds # get status for all prior builds
prior_builds = latest_builds.reject { |other_build| next_stages.include?(other_build.stage) } prior_builds = latest_builds.where.not(stage: next_stages)
status = Ci::Status.get_status(prior_builds) prior_status = prior_builds.status
# create builds for next stages based # create builds for next stages based
next_stages.any? do |stage| next_stages.any? do |stage|
CreateBuildsService.new.execute(self, stage, build.ref, build.tag, build.user, build.trigger_request, status).present? CreateBuildsService.new(self).execute(stage, build.user, prior_status, build.trigger_request).present?
end
end
def refs
statuses.order(:ref).pluck(:ref).uniq
end
def latest_statuses
@latest_statuses ||= statuses.latest.to_a
end
def latest_statuses_for_ref(ref)
latest_statuses.select { |status| status.ref == ref }
end end
def matrix_builds(build = nil)
matrix_builds = builds.latest.ordered
matrix_builds = matrix_builds.similar(build) if build
matrix_builds.to_a
end end
def retried def retried
@retried ||= (statuses.order(id: :desc) - statuses.latest) @retried ||= (statuses.order(id: :desc) - statuses.latest)
end end
def status
if yaml_errors.present?
return 'failed'
end
@status ||= Ci::Status.get_status(latest_statuses)
end
def pending?
status == 'pending'
end
def running?
status == 'running'
end
def success?
status == 'success'
end
def failed?
status == 'failed'
end
def canceled?
status == 'canceled'
end
def active?
running? || pending?
end
def complete?
canceled? || success? || failed?
end
def duration
duration_array = statuses.map(&:duration).compact
duration_array.reduce(:+).to_i
end
def started_at
@started_at ||= statuses.order('started_at ASC').first.try(:started_at)
end
def finished_at
@finished_at ||= statuses.order('finished_at DESC').first.try(:finished_at)
end
def coverage def coverage
coverage_array = latest_statuses.map(&:coverage).compact coverage_array = statuses.latest.map(&:coverage).compact
if coverage_array.size >= 1 if coverage_array.size >= 1
'%.2f' % (coverage_array.reduce(:+) / coverage_array.size) '%.2f' % (coverage_array.reduce(:+) / coverage_array.size)
end end
...@@ -181,7 +126,10 @@ module Ci ...@@ -181,7 +126,10 @@ module Ci
def config_processor def config_processor
return nil unless ci_yaml_file return nil unless ci_yaml_file
@config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, project.path_with_namespace) return @config_processor if defined?(@config_processor)
@config_processor ||= begin
Ci::GitlabCiYamlProcessor.new(ci_yaml_file, project.path_with_namespace)
rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e
save_yaml_error(e.message) save_yaml_error(e.message)
nil nil
...@@ -189,16 +137,19 @@ module Ci ...@@ -189,16 +137,19 @@ module Ci
save_yaml_error("Undefined error") save_yaml_error("Undefined error")
nil nil
end end
end
def ci_yaml_file def ci_yaml_file
return @ci_yaml_file if defined?(@ci_yaml_file)
@ci_yaml_file ||= begin @ci_yaml_file ||= begin
blob = project.repository.blob_at(sha, '.gitlab-ci.yml') blob = project.repository.blob_at(sha, '.gitlab-ci.yml')
blob.load_all_data!(project.repository) blob.load_all_data!(project.repository)
blob.data blob.data
end
rescue rescue
nil nil
end end
end
def skip_ci? def skip_ci?
git_commit_message =~ /(\[ci skip\])/ if git_commit_message git_commit_message =~ /(\[ci skip\])/ if git_commit_message
...@@ -206,10 +157,23 @@ module Ci ...@@ -206,10 +157,23 @@ module Ci
private private
def update_state
statuses.reload
self.status = if yaml_errors.blank?
statuses.latest.status || 'skipped'
else
'failed'
end
self.started_at = statuses.started_at
self.finished_at = statuses.finished_at
self.duration = statuses.latest.duration
save
end
def save_yaml_error(error) def save_yaml_error(error)
return if self.yaml_errors? return if self.yaml_errors?
self.yaml_errors = error self.yaml_errors = error
save update_state
end end
end end
end end
...@@ -207,12 +207,13 @@ class Commit ...@@ -207,12 +207,13 @@ class Commit
@raw.short_id(7) @raw.short_id(7)
end end
def ci_commit def ci_commits
project.ci_commit(sha) @ci_commits ||= project.ci_commits.where(sha: sha)
end end
def status def status
ci_commit.try(:status) || :not_found return @status if defined?(@status)
@status ||= ci_commits.status
end end
def revert_branch_name def revert_branch_name
......
...@@ -33,30 +33,23 @@ ...@@ -33,30 +33,23 @@
# #
class CommitStatus < ActiveRecord::Base class CommitStatus < ActiveRecord::Base
include Statuseable
self.table_name = 'ci_builds' self.table_name = 'ci_builds'
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
belongs_to :commit, class_name: 'Ci::Commit' belongs_to :commit, class_name: 'Ci::Commit', touch: true
belongs_to :user belongs_to :user
validates :commit, presence: true validates :commit, presence: true
validates :status, inclusion: { in: %w(pending running failed success canceled) }
validates_presence_of :name validates_presence_of :name
alias_attribute :author, :user alias_attribute :author, :user
scope :running, -> { where(status: 'running') } scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :commit_id)) }
scope :pending, -> { where(status: 'pending') }
scope :success, -> { where(status: 'success') }
scope :failed, -> { where(status: 'failed') }
scope :running_or_pending, -> { where(status: [:running, :pending]) }
scope :finished, -> { where(status: [:success, :failed, :canceled]) }
scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :ref)) }
scope :ordered, -> { order(:ref, :stage_idx, :name) } scope :ordered, -> { order(:ref, :stage_idx, :name) }
scope :for_ref, ->(ref) { where(ref: ref) } scope :ignored, -> { where(allow_failure: true, status: [:failed, :canceled]) }
AVAILABLE_STATUSES = ['pending', 'running', 'success', 'failed', 'canceled']
state_machine :status, initial: :pending do state_machine :status, initial: :pending do
event :run do event :run do
...@@ -86,31 +79,24 @@ class CommitStatus < ActiveRecord::Base ...@@ -86,31 +79,24 @@ class CommitStatus < ActiveRecord::Base
after_transition [:pending, :running] => :success do |commit_status| after_transition [:pending, :running] => :success do |commit_status|
MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.commit.project, nil).trigger(commit_status) MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.commit.project, nil).trigger(commit_status)
end end
state :pending, value: 'pending'
state :running, value: 'running'
state :failed, value: 'failed'
state :success, value: 'success'
state :canceled, value: 'canceled'
end end
delegate :sha, :short_sha, to: :commit, prefix: false delegate :sha, :short_sha, to: :commit
# TODO: this should be removed with all references
def before_sha def before_sha
Gitlab::Git::BLANK_SHA commit.before_sha || Gitlab::Git::BLANK_SHA
end end
def started? def self.stages
!pending? && !canceled? && started_at order_by = 'max(stage_idx)'
group('stage').order(order_by).pluck(:stage, order_by).map(&:first).compact
end end
def active? def self.stages_status
running? || pending? all.stages.inject({}) do |h, stage|
h[stage] = all.where(stage: stage).status
h
end end
def complete?
canceled? || success? || failed?
end end
def ignored? def ignored?
...@@ -118,11 +104,13 @@ class CommitStatus < ActiveRecord::Base ...@@ -118,11 +104,13 @@ class CommitStatus < ActiveRecord::Base
end end
def duration def duration
duration =
if started_at && finished_at if started_at && finished_at
finished_at - started_at finished_at - started_at
elsif started_at elsif started_at
Time.now - started_at Time.now - started_at
end end
duration
end end
def stuck? def stuck?
......
module Statuseable
extend ActiveSupport::Concern
AVAILABLE_STATUSES = %w(pending running success failed canceled skipped)
class_methods do
def status_sql
builds = all.select('count(*)').to_sql
success = all.success.select('count(*)').to_sql
ignored = all.ignored.select('count(*)').to_sql if all.respond_to?(:ignored)
ignored ||= '0'
pending = all.pending.select('count(*)').to_sql
running = all.running.select('count(*)').to_sql
canceled = all.canceled.select('count(*)').to_sql
skipped = all.skipped.select('count(*)').to_sql
deduce_status = "(CASE
WHEN (#{builds})=0 THEN NULL
WHEN (#{builds})=(#{success})+(#{ignored}) THEN 'success'
WHEN (#{builds})=(#{pending}) THEN 'pending'
WHEN (#{builds})=(#{canceled}) THEN 'canceled'
WHEN (#{builds})=(#{skipped}) THEN 'skipped'
WHEN (#{running})+(#{pending})>0 THEN 'running'
ELSE 'failed'
END)"
deduce_status
end
def status
all.pluck(self.status_sql).first
end
def duration
duration_array = all.map(&:duration).compact
duration_array.reduce(:+)
end
def started_at
all.minimum(:started_at)
end
def finished_at
all.maximum(:finished_at)
end
end
included do
validates :status, inclusion: { in: AVAILABLE_STATUSES }
state_machine :status, initial: :pending do
state :pending, value: 'pending'
state :running, value: 'running'
state :failed, value: 'failed'
state :success, value: 'success'
state :canceled, value: 'canceled'
state :skipped, value: 'skipped'
end
scope :running, -> { where(status: 'running') }
scope :pending, -> { where(status: 'pending') }
scope :success, -> { where(status: 'success') }
scope :failed, -> { where(status: 'failed') }
scope :canceled, -> { where(status: 'canceled') }
scope :skipped, -> { where(status: 'skipped') }
scope :running_or_pending, -> { where(status: [:running, :pending]) }
scope :finished, -> { where(status: [:success, :failed, :canceled]) }
end
def started?
!pending? && !canceled? && started_at
end
def active?
running? || pending?
end
def complete?
canceled? || success? || failed?
end
end
...@@ -586,7 +586,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -586,7 +586,7 @@ class MergeRequest < ActiveRecord::Base
end end
def ci_commit def ci_commit
@ci_commit ||= source_project.ci_commit(last_commit.id) if last_commit && source_project @ci_commit ||= source_project.ci_commit(last_commit.id, source_branch) if last_commit && source_project
end end
def diff_refs def diff_refs
......
...@@ -963,12 +963,12 @@ class Project < ActiveRecord::Base ...@@ -963,12 +963,12 @@ class Project < ActiveRecord::Base
!namespace.share_with_group_lock !namespace.share_with_group_lock
end end
def ci_commit(sha) def ci_commit(sha, ref)
ci_commits.find_by(sha: sha) ci_commits.order(id: :desc).find_by(sha: sha, ref: ref)
end end
def ensure_ci_commit(sha) def ensure_ci_commit(sha, ref)
ci_commit(sha) || ci_commits.create(sha: sha) ci_commit(sha, ref) || ci_commits.create(sha: sha, ref: ref)
end end
def enable_ci def enable_ci
......
module Ci module Ci
class CreateBuildsService class CreateBuildsService
def execute(commit, stage, ref, tag, user, trigger_request, status) def initialize(commit)
builds_attrs = commit.config_processor.builds_for_stage_and_ref(stage, ref, tag, trigger_request) @commit = commit
end
def execute(stage, user, status, trigger_request = nil)
builds_attrs = config_processor.builds_for_stage_and_ref(stage, @commit.ref, @commit.tag, trigger_request)
# check when to create next build # check when to create next build
builds_attrs = builds_attrs.select do |build_attrs| builds_attrs = builds_attrs.select do |build_attrs|
...@@ -17,7 +21,8 @@ module Ci ...@@ -17,7 +21,8 @@ module Ci
builds_attrs.map do |build_attrs| builds_attrs.map do |build_attrs|
# don't create the same build twice # don't create the same build twice
unless commit.builds.find_by(ref: ref, tag: tag, trigger_request: trigger_request, name: build_attrs[:name]) unless @commit.builds.find_by(ref: @commit.ref, tag: @commit.tag,
trigger_request: trigger_request, name: build_attrs[:name])
build_attrs.slice!(:name, build_attrs.slice!(:name,
:commands, :commands,
:tag_list, :tag_list,
...@@ -26,17 +31,21 @@ module Ci ...@@ -26,17 +31,21 @@ module Ci
:stage, :stage,
:stage_idx) :stage_idx)
build_attrs.merge!(ref: ref, build_attrs.merge!(ref: @commit.ref,
tag: tag, tag: @commit.tag,
trigger_request: trigger_request, trigger_request: trigger_request,
user: user, user: user,
project: commit.project) project: @commit.project)
build = commit.builds.create!(build_attrs) @commit.builds.create!(build_attrs)
build.execute_hooks end
build
end end
end end
private
def config_processor
@config_processor ||= @commit.config_processor
end end
end end
end end
...@@ -7,14 +7,14 @@ module Ci ...@@ -7,14 +7,14 @@ module Ci
# check if ref is tag # check if ref is tag
tag = project.repository.find_tag(ref).present? tag = project.repository.find_tag(ref).present?
ci_commit = project.ensure_ci_commit(commit.sha) ci_commit = project.ci_commits.create(sha: commit.sha, ref: ref, tag: tag)
trigger_request = trigger.trigger_requests.create!( trigger_request = trigger.trigger_requests.create!(
variables: variables, variables: variables,
commit: ci_commit, commit: ci_commit,
) )
if ci_commit.create_builds(ref, tag, nil, trigger_request) if ci_commit.create_builds(nil, trigger_request)
trigger_request trigger_request
end end
end end
......
...@@ -3,8 +3,9 @@ module Ci ...@@ -3,8 +3,9 @@ module Ci
def execute(project, opts) def execute(project, opts)
sha = opts[:sha] || ref_sha(project, opts[:ref]) sha = opts[:sha] || ref_sha(project, opts[:ref])
commit = project.ci_commits.find_by(sha: sha) ci_commits = project.ci_commits.where(sha: sha)
image_name = image_for_commit(commit) ci_commits = ci_commits.where(ref: opts[:ref]) if opts[:ref]
image_name = image_for_status(ci_commits.status)
image_path = Rails.root.join('public/ci', image_name) image_path = Rails.root.join('public/ci', image_name)
OpenStruct.new(path: image_path, name: image_name) OpenStruct.new(path: image_path, name: image_name)
...@@ -16,9 +17,9 @@ module Ci ...@@ -16,9 +17,9 @@ module Ci
project.commit(ref).try(:sha) if ref project.commit(ref).try(:sha) if ref
end end
def image_for_commit(commit) def image_for_status(status)
return 'build-unknown.svg' unless commit status ||= 'unknown'
'build-' + commit.status + ".svg" 'build-' + status + ".svg"
end end
end end
end end
...@@ -2,6 +2,7 @@ class CreateCommitBuildsService ...@@ -2,6 +2,7 @@ class CreateCommitBuildsService
def execute(project, user, params) def execute(project, user, params)
return false unless project.builds_enabled? return false unless project.builds_enabled?
before_sha = params[:checkout_sha] || params[:before]
sha = params[:checkout_sha] || params[:after] sha = params[:checkout_sha] || params[:after]
origin_ref = params[:ref] origin_ref = params[:ref]
...@@ -10,15 +11,16 @@ class CreateCommitBuildsService ...@@ -10,15 +11,16 @@ class CreateCommitBuildsService
end end
ref = Gitlab::Git.ref_name(origin_ref) ref = Gitlab::Git.ref_name(origin_ref)
tag = Gitlab::Git.tag_ref?(origin_ref)
# Skip branch removal # Skip branch removal
if sha == Gitlab::Git::BLANK_SHA if sha == Gitlab::Git::BLANK_SHA
return false return false
end end
commit = project.ci_commit(sha) commit = project.ci_commit(sha, ref)
unless commit unless commit
commit = project.ci_commits.new(sha: sha) commit = project.ci_commits.new(sha: sha, ref: ref, before_sha: before_sha, tag: tag)
# Skip creating ci_commit when no gitlab-ci.yml is found # Skip creating ci_commit when no gitlab-ci.yml is found
unless commit.ci_yaml_file unless commit.ci_yaml_file
...@@ -32,10 +34,10 @@ class CreateCommitBuildsService ...@@ -32,10 +34,10 @@ class CreateCommitBuildsService
# Skip creating builds for commits that have [ci skip] # Skip creating builds for commits that have [ci skip]
unless commit.skip_ci? unless commit.skip_ci?
# Create builds for commit # Create builds for commit
tag = Gitlab::Git.tag_ref?(origin_ref) commit.create_builds(user)
commit.create_builds(ref, tag, user)
end end
commit.touch
commit commit
end end
end end
.project-last-commit .project-last-commit
- ci_commit = project.ci_commit(commit.sha) - if commit.status
- if ci_commit = link_to builds_namespace_project_commit_path(commit.project.namespace, commit.project, commit), class: "ci-status ci-#{commit.status}" do
= link_to ci_status_path(ci_commit), class: "ci-status ci-#{ci_commit.status}" do = ci_icon_for_status(commit.status)
= ci_status_icon(ci_commit) = ci_label_for_status(commit.status)
= ci_status_label(ci_commit)
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id" = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
= link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit), class: "commit-row-message" = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit), class: "commit-row-message"
......
...@@ -58,6 +58,6 @@ ...@@ -58,6 +58,6 @@
%th Coverage %th Coverage
%th %th
= render @builds, commit_sha: true, stage: true, allow_retry: true, coverage: @project.build_coverage_enabled? = render @builds, commit_sha: true, ref: true, stage: true, allow_retry: true, coverage: @project.build_coverage_enabled?
= paginate @builds, theme: 'gitlab' = paginate @builds, theme: 'gitlab'
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
= link_to "merge request #{merge_request.to_reference}", merge_request_path(merge_request) = link_to "merge request #{merge_request.to_reference}", merge_request_path(merge_request)
#up-build-trace #up-build-trace
- builds = @build.commit.matrix_builds(@build) - builds = @build.commit.builds.latest.to_a
- if builds.size > 1 - if builds.size > 1
%ul.nav-links.no-top.no-bottom %ul.nav-links.no-top.no-bottom
- builds.each do |build| - builds.each do |build|
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
%td %td
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace" = link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace"
- if defined?(ref) && ref
%td %td
- if build.ref - if build.ref
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref) = link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref)
...@@ -48,6 +49,8 @@ ...@@ -48,6 +49,8 @@
%span.label.label-info triggered %span.label.label-info triggered
- if build.try(:allow_failure) - if build.try(:allow_failure)
%span.label.label-danger allowed to fail %span.label.label-danger allowed to fail
- if defined?(retried) && retried
%span.label.label-warning retried
%td.duration %td.duration
- if build.duration - if build.duration
......
.gray-content-block.middle-block - @ci_commits.each do |ci_commit|
.pull-right = render "ci_commit", ci_commit: ci_commit
- if can?(current_user, :update_build, @ci_commit.project)
- if @ci_commit.builds.latest.failed.any?(&:retryable?)
= link_to "Retry failed", retry_builds_namespace_project_commit_path(@ci_commit.project.namespace, @ci_commit.project, @ci_commit.sha), class: 'btn btn-grouped btn-primary', method: :post
- if @ci_commit.builds.running_or_pending.any?
= link_to "Cancel running", cancel_builds_namespace_project_commit_path(@ci_commit.project.namespace, @ci_commit.project, @ci_commit.sha), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post
.oneline
= pluralize @statuses.count(:id), "build"
- if defined?(link_to_commit) && link_to_commit
for commit
= link_to @ci_commit.short_sha, namespace_project_commit_path(@ci_commit.project.namespace, @ci_commit.project, @ci_commit.sha), class: "monospace"
- if @ci_commit.duration > 0
in
= time_interval_in_words @ci_commit.duration
- if @ci_commit.yaml_errors.present?
.bs-callout.bs-callout-danger
%h4 Found errors in your .gitlab-ci.yml:
%ul
- @ci_commit.yaml_errors.split(",").each do |error|
%li= error
You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path}
- if @ci_commit.project.builds_enabled? && !@ci_commit.ci_yaml_file
.bs-callout.bs-callout-warning
\.gitlab-ci.yml not found in this commit
.table-holder
%table.table.builds
%thead
%tr
%th Status
%th Build ID
%th Ref
%th Stage
%th Name
%th Duration
%th Finished at
- if @ci_commit.project.build_coverage_enabled?
%th Coverage
%th
- @ci_commit.refs.each do |ref|
- builds = @ci_commit.statuses.for_ref(ref).latest.ordered
= render builds, coverage: @ci_commit.project.build_coverage_enabled?, stage: true, allow_retry: true
- if @ci_commit.retried.any?
.gray-content-block.second-block
Retried builds
.table-holder
%table.table.builds
%thead
%tr
%th Status
%th Build ID
%th Ref
%th Stage
%th Name
%th Duration
%th Finished at
- if @ci_commit.project.build_coverage_enabled?
%th Coverage
%th
= render @ci_commit.retried, coverage: @ci_commit.project.build_coverage_enabled?, stage: true
.gray-content-block.middle-block
.pull-right
- if can?(current_user, :update_build, @project)
- if ci_commit.builds.latest.failed.any?(&:retryable?)
= link_to "Retry failed", retry_builds_namespace_project_commit_path(@project.namespace, @project, ci_commit.sha), class: 'btn btn-grouped btn-primary', method: :post
- if ci_commit.builds.running_or_pending.any?
= link_to "Cancel running", cancel_builds_namespace_project_commit_path(@project.namespace, @project, ci_commit.sha), data: { confirm: 'Are you sure?' }, class: 'btn btn-grouped btn-danger', method: :post
.oneline
= pluralize ci_commit.statuses.count(:id), "build"
- if ci_commit.ref
for
%span.label.label-info
= ci_commit.ref
- if defined?(link_to_commit) && link_to_commit
for commit
= link_to ci_commit.short_sha, namespace_project_commit_path(@project.namespace, @project, ci_commit.sha), class: "monospace"
- if ci_commit.duration > 0
in
= time_interval_in_words ci_commit.duration
- if ci_commit.yaml_errors.present?
.bs-callout.bs-callout-danger
%h4 Found errors in your .gitlab-ci.yml:
%ul
- ci_commit.yaml_errors.split(",").each do |error|
%li= error
You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path}
- if @project.builds_enabled? && !ci_commit.ci_yaml_file
.bs-callout.bs-callout-warning
\.gitlab-ci.yml not found in this commit
.table-holder
%table.table.builds
%thead
%tr
%th Status
%th Build ID
%th Stage
%th Name
%th Duration
%th Finished at
- if @project.build_coverage_enabled?
%th Coverage
%th
- builds = ci_commit.statuses.latest.ordered
= render builds, coverage: @project.build_coverage_enabled?, stage: true, ref: false, allow_retry: true
- if ci_commit.retried.any?
.gray-content-block.second-block
Retried builds
.table-holder
%table.table.builds
%thead
%tr
%th Status
%th Build ID
%th Ref
%th Stage
%th Name
%th Duration
%th Finished at
- if @project.build_coverage_enabled?
%th Coverage
%th
= render ci_commit.retried, coverage: @project.build_coverage_enabled?, stage: true, ref: false
...@@ -43,12 +43,12 @@ ...@@ -43,12 +43,12 @@
- @commit.parents.each do |parent| - @commit.parents.each do |parent|
= link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "monospace" = link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "monospace"
- if @ci_commit - if @commit.status
.pull-right .pull-right
= link_to ci_status_path(@ci_commit), class: "ci-status ci-#{@ci_commit.status}" do = link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id), class: "ci-status ci-#{@commit.status}" do
= ci_status_icon(@ci_commit) = ci_icon_for_status(@commit.status)
build: build:
= ci_status_label(@ci_commit) = ci_label_for_status(@commit.status)
.commit-info-row.branches .commit-info-row.branches
%i.fa.fa-spinner.fa-spin %i.fa.fa-spinner.fa-spin
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
.prepend-top-default .prepend-top-default
= render "commit_box" = render "commit_box"
- if @ci_commit - if @commit.status
= render "ci_menu" = render "ci_menu"
- else - else
%div.block-connector %div.block-connector
......
...@@ -4,9 +4,8 @@ ...@@ -4,9 +4,8 @@
- notes = commit.notes - notes = commit.notes
- note_count = notes.user.count - note_count = notes.user.count
- ci_commit = project.ci_commit(commit.sha)
- cache_key = [project.path_with_namespace, commit.id, current_application_settings, note_count] - cache_key = [project.path_with_namespace, commit.id, current_application_settings, note_count]
- cache_key.push(ci_commit.status) if ci_commit - cache_key.push(commit.status) if commit.status
= cache(cache_key) do = cache(cache_key) do
%li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" } %li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" }
...@@ -17,8 +16,8 @@ ...@@ -17,8 +16,8 @@
%a.text-expander.js-toggle-button ... %a.text-expander.js-toggle-button ...
.pull-right .pull-right
- if ci_commit - if commit.status
= render_ci_status(ci_commit) = render_ci_status(commit)
= clipboard_button(clipboard_text: commit.id) = clipboard_button(clipboard_text: commit.id)
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id" = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
%td %td
= link_to generic_commit_status.short_sha, namespace_project_commit_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.sha), class: "monospace" = link_to generic_commit_status.short_sha, namespace_project_commit_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.sha), class: "monospace"
- if defined?(ref) && ref
%td %td
- if generic_commit_status.ref - if generic_commit_status.ref
= link_to generic_commit_status.ref, namespace_project_commits_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.ref) = link_to generic_commit_status.ref, namespace_project_commits_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.ref)
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
- @related_branches.each do |branch| - @related_branches.each do |branch|
%li %li
- sha = @project.repository.find_branch(branch).target - sha = @project.repository.find_branch(branch).target
- ci_commit = @project.ci_commit(sha) if sha - ci_commit = @project.ci_commit(sha, branch) if sha
- if ci_commit - if ci_commit
%span.related-branch-ci-status %span.related-branch-ci-status
= render_ci_status(ci_commit) = render_ci_status(ci_commit)
......
= render "projects/commit/builds", link_to_commit: true = render "projects/commit/ci_commit", ci_commit: @ci_commit, link_to_commit: true
...@@ -6,9 +6,8 @@ ...@@ -6,9 +6,8 @@
- css_class = '' unless local_assigns[:css_class] - css_class = '' unless local_assigns[:css_class]
- show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true && project.commit - show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true && project.commit
- css_class += " no-description" if project.description.blank? && !show_last_commit_as_description - css_class += " no-description" if project.description.blank? && !show_last_commit_as_description
- ci_commit = project.ci_commit(project.commit.sha) if ci && !project.empty_repo? && project.commit
- cache_key = [project.namespace, project, controller.controller_name, controller.action_name, current_application_settings, 'v2.3'] - cache_key = [project.namespace, project, controller.controller_name, controller.action_name, current_application_settings, 'v2.3']
- cache_key.push(ci_commit.status) if ci_commit - cache_key.push(project.commit.status) if project.commit.try(:status)
%li.project-row{ class: css_class } %li.project-row{ class: css_class }
= cache(cache_key) do = cache(cache_key) do
...@@ -16,9 +15,9 @@ ...@@ -16,9 +15,9 @@
- if project.main_language - if project.main_language
%span %span
= project.main_language = project.main_language
- if ci_commit - if project.commit.try(:status)
%span %span
= render_ci_status(ci_commit) = render_ci_status(project.commit)
- if forks - if forks
%span %span
= icon('code-fork') = icon('code-fork')
......
...@@ -19,7 +19,7 @@ class Gitlab::Seeder::Builds ...@@ -19,7 +19,7 @@ class Gitlab::Seeder::Builds
commits = @project.repository.commits('master', nil, 5) commits = @project.repository.commits('master', nil, 5)
commits_sha = commits.map { |commit| commit.raw.id } commits_sha = commits.map { |commit| commit.raw.id }
commits_sha.map do |sha| commits_sha.map do |sha|
@project.ensure_ci_commit(sha) @project.ensure_ci_commit(sha, 'master')
end end
rescue rescue
[] []
......
class AddFieldsToCiCommit < ActiveRecord::Migration
def change
add_column :ci_commits, :status, :string
add_column :ci_commits, :started_at, :timestamp
add_column :ci_commits, :finished_at, :timestamp
add_column :ci_commits, :duration, :integer
end
end
class UpdateCiCommit < ActiveRecord::Migration
# This migration can be run online, but needs to be executed for the second time after restarting Unicorn workers
# Otherwise Offline migration should be used.
def change
execute("UPDATE ci_commits SET status=#{status}, ref=#{ref}, tag=#{tag} WHERE status IS NULL")
end
private
def status
builds = '(SELECT COUNT(*) FROM ci_builds WHERE ci_builds.commit_id=ci_commits.id)'
success = "(SELECT COUNT(*) FROM ci_builds WHERE ci_builds.commit_id=ci_commits.id AND status='success')"
ignored = "(SELECT COUNT(*) FROM ci_builds WHERE ci_builds.commit_id=ci_commits.id AND (status='failed' OR status='canceled') AND allow_failure)"
pending = "(SELECT COUNT(*) FROM ci_builds WHERE ci_builds.commit_id=ci_commits.id AND status='pending')"
running = "(SELECT COUNT(*) FROM ci_builds WHERE ci_builds.commit_id=ci_commits.id AND status='running')"
canceled = "(SELECT COUNT(*) FROM ci_builds WHERE ci_builds.commit_id=ci_commits.id AND status='canceled')"
"(CASE
WHEN #{builds}=0 THEN 'skipped'
WHEN #{builds}=#{success}+#{ignored} THEN 'success'
WHEN #{builds}=#{pending} THEN 'pending'
WHEN #{builds}=#{canceled} THEN 'canceled'
WHEN #{running}+#{pending}>0 THEN 'running'
ELSE 'failed'
END)"
end
def ref
'(SELECT ref FROM ci_builds WHERE ci_builds.commit_id=ci_commits.id ORDER BY id DESC LIMIT 1)'
end
def tag
'(SELECT tag FROM ci_builds WHERE ci_builds.commit_id=ci_commits.id ORDER BY id DESC LIMIT 1)'
end
end
class AddCiCommitIndexes < ActiveRecord::Migration
disable_ddl_transaction!
def change
add_index :ci_commits, [:gl_project_id, :sha], index_options
add_index :ci_commits, [:gl_project_id, :status], index_options
add_index :ci_commits, [:status], index_options
end
private
def index_options
if Gitlab::Database.postgresql?
{ algorithm: :concurrently }
else
{ }
end
end
end
...@@ -171,14 +171,21 @@ ActiveRecord::Schema.define(version: 20160419120017) do ...@@ -171,14 +171,21 @@ ActiveRecord::Schema.define(version: 20160419120017) do
t.text "yaml_errors" t.text "yaml_errors"
t.datetime "committed_at" t.datetime "committed_at"
t.integer "gl_project_id" t.integer "gl_project_id"
t.string "status"
t.datetime "started_at"
t.datetime "finished_at"
t.integer "duration"
end end
add_index "ci_commits", ["gl_project_id", "sha"], name: "index_ci_commits_on_gl_project_id_and_sha", using: :btree
add_index "ci_commits", ["gl_project_id", "status"], name: "index_ci_commits_on_gl_project_id_and_status", using: :btree
add_index "ci_commits", ["gl_project_id"], name: "index_ci_commits_on_gl_project_id", using: :btree add_index "ci_commits", ["gl_project_id"], name: "index_ci_commits_on_gl_project_id", using: :btree
add_index "ci_commits", ["project_id", "committed_at", "id"], name: "index_ci_commits_on_project_id_and_committed_at_and_id", using: :btree add_index "ci_commits", ["project_id", "committed_at", "id"], name: "index_ci_commits_on_project_id_and_committed_at_and_id", using: :btree
add_index "ci_commits", ["project_id", "committed_at"], name: "index_ci_commits_on_project_id_and_committed_at", using: :btree add_index "ci_commits", ["project_id", "committed_at"], name: "index_ci_commits_on_project_id_and_committed_at", using: :btree
add_index "ci_commits", ["project_id", "sha"], name: "index_ci_commits_on_project_id_and_sha", using: :btree add_index "ci_commits", ["project_id", "sha"], name: "index_ci_commits_on_project_id_and_sha", using: :btree
add_index "ci_commits", ["project_id"], name: "index_ci_commits_on_project_id", using: :btree add_index "ci_commits", ["project_id"], name: "index_ci_commits_on_project_id", using: :btree
add_index "ci_commits", ["sha"], name: "index_ci_commits_on_sha", using: :btree add_index "ci_commits", ["sha"], name: "index_ci_commits_on_sha", using: :btree
add_index "ci_commits", ["status"], name: "index_ci_commits_on_status", using: :btree
create_table "ci_events", force: :cascade do |t| create_table "ci_events", force: :cascade do |t|
t.integer "project_id" t.integer "project_id"
......
...@@ -519,7 +519,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps ...@@ -519,7 +519,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
step '"Bug NS-05" has CI status' do step '"Bug NS-05" has CI status' do
project = merge_request.source_project project = merge_request.source_project
project.enable_ci project.enable_ci
ci_commit = create :ci_commit, project: project, sha: merge_request.last_commit.id ci_commit = create :ci_commit, project: project, sha: merge_request.last_commit.id, ref: merge_request.source_branch
create :ci_build, commit: ci_commit create :ci_build, commit: ci_commit
end end
......
...@@ -10,16 +10,16 @@ module SharedBuilds ...@@ -10,16 +10,16 @@ module SharedBuilds
end end
step 'project has a recent build' do step 'project has a recent build' do
@ci_commit = create(:ci_commit, project: @project, sha: @project.commit.sha) @ci_commit = create(:ci_commit, project: @project, sha: @project.commit.sha, ref: 'master')
@build = create(:ci_build_with_coverage, commit: @ci_commit) @build = create(:ci_build_with_coverage, commit: @ci_commit)
end end
step 'recent build is successful' do step 'recent build is successful' do
@build.update_column(:status, 'success') @build.update(status: 'success')
end end
step 'recent build failed' do step 'recent build failed' do
@build.update_column(:status, 'failed') @build.update(status: 'failed')
end end
step 'project has another build that is running' do step 'project has another build that is running' do
......
...@@ -230,7 +230,7 @@ module SharedProject ...@@ -230,7 +230,7 @@ module SharedProject
step 'project "Shop" has CI build' do step 'project "Shop" has CI build' do
project = Project.find_by(name: "Shop") project = Project.find_by(name: "Shop")
create :ci_commit, project: project, sha: project.commit.sha create :ci_commit, project: project, sha: project.commit.sha, ref: 'master', status: 'skipped'
end end
step 'I should see last commit with CI status' do step 'I should see last commit with CI status' do
......
...@@ -21,10 +21,9 @@ module API ...@@ -21,10 +21,9 @@ module API
authorize!(:read_commit_status, user_project) authorize!(:read_commit_status, user_project)
not_found!('Commit') unless user_project.commit(params[:sha]) not_found!('Commit') unless user_project.commit(params[:sha])
ci_commit = user_project.ci_commit(params[:sha])
return [] unless ci_commit
statuses = ci_commit.statuses ci_commits = user_project.ci_commits.where(sha: params[:sha])
statuses = ::CommitStatus.where(commit: ci_commits)
statuses = statuses.latest unless parse_boolean(params[:all]) statuses = statuses.latest unless parse_boolean(params[:all])
statuses = statuses.where(ref: params[:ref]) if params[:ref].present? statuses = statuses.where(ref: params[:ref]) if params[:ref].present?
statuses = statuses.where(stage: params[:stage]) if params[:stage].present? statuses = statuses.where(stage: params[:stage]) if params[:stage].present?
...@@ -51,7 +50,21 @@ module API ...@@ -51,7 +50,21 @@ module API
commit = @project.commit(params[:sha]) commit = @project.commit(params[:sha])
not_found! 'Commit' unless commit not_found! 'Commit' unless commit
ci_commit = @project.ensure_ci_commit(commit.sha) # Since the CommitStatus is attached to Ci::Commit (in the future Pipeline)
# We need to always have the pipeline object
# To have a valid pipeline object that can be attached to specific MR
# Other CI service needs to send `ref`
# If we don't receive it, we will attach the CommitStatus to
# the first found branch on that commit
ref = params[:ref]
unless ref
branches = @project.repository.branch_names_contains(commit.sha)
not_found! 'References for commit' if branches.none?
ref = branches.first
end
ci_commit = @project.ensure_ci_commit(commit.sha, ref)
name = params[:name] || params[:context] name = params[:name] || params[:context]
status = GenericCommitStatus.running_or_pending.find_by(commit: ci_commit, name: name, ref: params[:ref]) status = GenericCommitStatus.running_or_pending.find_by(commit: ci_commit, name: name, ref: params[:ref])
......
module Ci
class Status
def self.get_status(statuses)
if statuses.none?
'skipped'
elsif statuses.all? { |status| status.success? || status.ignored? }
'success'
elsif statuses.all?(&:pending?)
'pending'
elsif statuses.any?(&:running?) || statuses.any?(&:pending?)
'running'
elsif statuses.all?(&:canceled?)
'canceled'
else
'failed'
end
end
end
end
...@@ -6,8 +6,8 @@ describe CiStatusHelper do ...@@ -6,8 +6,8 @@ describe CiStatusHelper do
let(:success_commit) { double("Ci::Commit", status: 'success') } let(:success_commit) { double("Ci::Commit", status: 'success') }
let(:failed_commit) { double("Ci::Commit", status: 'failed') } let(:failed_commit) { double("Ci::Commit", status: 'failed') }
describe 'ci_status_icon' do describe 'ci_icon_for_status' do
it { expect(helper.ci_status_icon(success_commit)).to include('fa-check') } it { expect(helper.ci_icon_for_status(success_commit.status)).to include('fa-check') }
it { expect(helper.ci_status_icon(failed_commit)).to include('fa-close') } it { expect(helper.ci_icon_for_status(failed_commit.status)).to include('fa-close') }
end end
end end
...@@ -42,7 +42,7 @@ describe Gitlab::Badge::Build do ...@@ -42,7 +42,7 @@ describe Gitlab::Badge::Build do
end end
context 'build exists' do context 'build exists' do
let(:ci_commit) { create(:ci_commit, project: project, sha: sha) } let(:ci_commit) { create(:ci_commit, project: project, sha: sha, ref: branch) }
let!(:build) { create(:ci_build, commit: ci_commit) } let!(:build) { create(:ci_build, commit: ci_commit) }
...@@ -57,7 +57,7 @@ describe Gitlab::Badge::Build do ...@@ -57,7 +57,7 @@ describe Gitlab::Badge::Build do
describe '#data' do describe '#data' do
let(:data) { badge.data } let(:data) { badge.data }
it 'contains infromation about success' do it 'contains information about success' do
expect(status_node(data, 'success')).to be_truthy expect(status_node(data, 'success')).to be_truthy
end end
end end
...@@ -74,7 +74,7 @@ describe Gitlab::Badge::Build do ...@@ -74,7 +74,7 @@ describe Gitlab::Badge::Build do
describe '#data' do describe '#data' do
let(:data) { badge.data } let(:data) { badge.data }
it 'contains infromation about failure' do it 'contains information about failure' do
expect(status_node(data, 'failed')).to be_truthy expect(status_node(data, 'failed')).to be_truthy
end end
end end
......
...@@ -27,6 +27,8 @@ describe Ci::Commit, models: true do ...@@ -27,6 +27,8 @@ describe Ci::Commit, models: true do
it { is_expected.to have_many(:trigger_requests) } it { is_expected.to have_many(:trigger_requests) }
it { is_expected.to have_many(:builds) } it { is_expected.to have_many(:builds) }
it { is_expected.to validate_presence_of :sha } it { is_expected.to validate_presence_of :sha }
it { is_expected.to validate_presence_of :status }
it { is_expected.to delegate_method(:stages).to(:statuses) }
it { is_expected.to respond_to :git_author_name } it { is_expected.to respond_to :git_author_name }
it { is_expected.to respond_to :git_author_email } it { is_expected.to respond_to :git_author_email }
...@@ -52,57 +54,9 @@ describe Ci::Commit, models: true do ...@@ -52,57 +54,9 @@ describe Ci::Commit, models: true do
it { expect(commit.sha).to start_with(subject) } it { expect(commit.sha).to start_with(subject) }
end end
describe :stage do
subject { commit.stage }
before do
@second = FactoryGirl.create :commit_status, commit: commit, name: 'deploy', stage: 'deploy', stage_idx: 1, status: 'pending'
@first = FactoryGirl.create :commit_status, commit: commit, name: 'test', stage: 'test', stage_idx: 0, status: 'pending'
end
it 'returns first running stage' do
is_expected.to eq('test')
end
context 'first build succeeded' do
before do
@first.success
end
it 'returns last running stage' do
is_expected.to eq('deploy')
end
end
context 'all builds succeeded' do
before do
@first.success
@second.success
end
it 'returns nil' do
is_expected.to be_nil
end
end
end
describe :create_next_builds do describe :create_next_builds do
end end
describe :refs do
subject { commit.refs }
before do
FactoryGirl.create :commit_status, commit: commit, name: 'deploy'
FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'develop'
FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'master'
end
it 'returns all refs' do
is_expected.to contain_exactly('master', 'develop', nil)
end
end
describe :retried do describe :retried do
subject { commit.retried } subject { commit.retried }
...@@ -117,10 +71,10 @@ describe Ci::Commit, models: true do ...@@ -117,10 +71,10 @@ describe Ci::Commit, models: true do
end end
describe :create_builds do describe :create_builds do
let!(:commit) { FactoryGirl.create :ci_commit, project: project } let!(:commit) { FactoryGirl.create :ci_commit, project: project, ref: 'master', tag: false }
def create_builds(trigger_request = nil) def create_builds(trigger_request = nil)
commit.create_builds('master', false, nil, trigger_request) commit.create_builds(nil, trigger_request)
end end
def create_next_builds def create_next_builds
...@@ -143,67 +97,6 @@ describe Ci::Commit, models: true do ...@@ -143,67 +97,6 @@ describe Ci::Commit, models: true do
expect(create_next_builds).to be_falsey expect(create_next_builds).to be_falsey
end end
context 'for different ref' do
def create_develop_builds
commit.create_builds('develop', false, nil, nil)
end
it 'creates builds' do
expect(create_builds).to be_truthy
commit.builds.update_all(status: "success")
expect(commit.builds.count(:all)).to eq(2)
expect(create_develop_builds).to be_truthy
commit.builds.update_all(status: "success")
expect(commit.builds.count(:all)).to eq(4)
expect(commit.refs.size).to eq(2)
expect(commit.builds.pluck(:name).uniq.size).to eq(2)
end
end
context 'for build triggers' do
let(:trigger) { FactoryGirl.create :ci_trigger, project: project }
let(:trigger_request) { FactoryGirl.create :ci_trigger_request, commit: commit, trigger: trigger }
it 'creates builds' do
expect(create_builds(trigger_request)).to be_truthy
expect(commit.builds.count(:all)).to eq(2)
end
it 'rebuilds commit' do
expect(create_builds).to be_truthy
expect(commit.builds.count(:all)).to eq(2)
expect(create_builds(trigger_request)).to be_truthy
expect(commit.builds.count(:all)).to eq(4)
end
it 'creates next builds' do
expect(create_builds(trigger_request)).to be_truthy
expect(commit.builds.count(:all)).to eq(2)
commit.builds.update_all(status: "success")
expect(create_next_builds).to be_truthy
expect(commit.builds.count(:all)).to eq(4)
end
context 'for [ci skip]' do
before do
allow(commit).to receive(:git_commit_message) { 'message [ci skip]' }
end
it 'rebuilds commit' do
expect(commit.status).to eq('skipped')
expect(create_builds).to be_truthy
# since everything in Ci::Commit is cached we need to fetch a new object
new_commit = Ci::Commit.find_by_id(commit.id)
expect(new_commit.status).to eq('pending')
end
end
end
context 'custom stage with first job allowed to fail' do context 'custom stage with first job allowed to fail' do
let(:yaml) do let(:yaml) do
{ {
...@@ -284,6 +177,7 @@ describe Ci::Commit, models: true do ...@@ -284,6 +177,7 @@ describe Ci::Commit, models: true do
commit.builds.running_or_pending.each(&:success) commit.builds.running_or_pending.each(&:success)
expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'success') expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'success')
commit.reload
expect(commit.status).to eq('success') expect(commit.status).to eq('success')
end end
...@@ -306,6 +200,7 @@ describe Ci::Commit, models: true do ...@@ -306,6 +200,7 @@ describe Ci::Commit, models: true do
commit.builds.running_or_pending.each(&:success) commit.builds.running_or_pending.each(&:success)
expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'success') expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'success')
commit.reload
expect(commit.status).to eq('failed') expect(commit.status).to eq('failed')
end end
...@@ -329,6 +224,7 @@ describe Ci::Commit, models: true do ...@@ -329,6 +224,7 @@ describe Ci::Commit, models: true do
expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup') expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup')
expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'success') expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'success')
commit.reload
expect(commit.status).to eq('failed') expect(commit.status).to eq('failed')
end end
...@@ -351,6 +247,7 @@ describe Ci::Commit, models: true do ...@@ -351,6 +247,7 @@ describe Ci::Commit, models: true do
commit.builds.running_or_pending.each(&:success) commit.builds.running_or_pending.each(&:success)
expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'success') expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'success')
commit.reload
expect(commit.status).to eq('failed') expect(commit.status).to eq('failed')
end end
end end
...@@ -402,4 +299,98 @@ describe Ci::Commit, models: true do ...@@ -402,4 +299,98 @@ describe Ci::Commit, models: true do
expect(commit.coverage).to be_nil expect(commit.coverage).to be_nil
end end
end end
describe '#retryable?' do
subject { commit.retryable? }
context 'no failed builds' do
before do
FactoryGirl.create :ci_build, name: "rspec", commit: commit, status: 'success'
end
it 'be not retryable' do
is_expected.to be_falsey
end
end
context 'with failed builds' do
before do
FactoryGirl.create :ci_build, name: "rspec", commit: commit, status: 'running'
FactoryGirl.create :ci_build, name: "rubocop", commit: commit, status: 'failed'
end
it 'be retryable' do
is_expected.to be_truthy
end
end
end
describe '#stages' do
let(:commit2) { FactoryGirl.create :ci_commit, project: project }
subject { CommitStatus.where(commit: [commit, commit2]).stages }
before do
FactoryGirl.create :ci_build, commit: commit2, stage: 'test', stage_idx: 1
FactoryGirl.create :ci_build, commit: commit, stage: 'build', stage_idx: 0
end
it 'return all stages' do
is_expected.to eq(%w(build test))
end
end
describe '#update_state' do
it 'execute update_state after touching object' do
expect(commit).to receive(:update_state).and_return(true)
commit.touch
end
context 'dependent objects' do
let(:commit_status) { build :commit_status, commit: commit }
it 'execute update_state after saving dependent object' do
expect(commit).to receive(:update_state).and_return(true)
commit_status.save
end
end
context 'update state' do
let(:current) { Time.now.change(usec: 0) }
let(:build) { FactoryGirl.create :ci_build, :success, commit: commit, started_at: current - 120, finished_at: current - 60 }
before do
build
end
[:status, :started_at, :finished_at, :duration].each do |param|
it "update #{param}" do
expect(commit.send(param)).to eq(build.send(param))
end
end
end
end
describe '#branch?' do
subject { commit.branch? }
context 'is not a tag' do
before do
commit.tag = false
end
it 'return true when tag is set to false' do
is_expected.to be_truthy
end
end
context 'is not a tag' do
before do
commit.tag = true
end
it 'return false when tag is set to true' do
is_expected.to be_falsey
end
end
end
end end
...@@ -163,4 +163,12 @@ eos ...@@ -163,4 +163,12 @@ eos
it { expect(commit.reverts_commit?(another_commit)).to be_truthy } it { expect(commit.reverts_commit?(another_commit)).to be_truthy }
end end
end end
describe '#ci_commits' do
# TODO: kamil
end
describe '#status' do
# TODO: kamil
end
end end
...@@ -163,37 +163,73 @@ describe CommitStatus, models: true do ...@@ -163,37 +163,73 @@ describe CommitStatus, models: true do
end end
it 'return unique statuses' do it 'return unique statuses' do
is_expected.to eq([@commit2, @commit3, @commit4, @commit5]) is_expected.to eq([@commit4, @commit5])
end end
end end
describe :for_ref do describe :running_or_pending do
subject { CommitStatus.for_ref('bb').order(:id) } subject { CommitStatus.running_or_pending.order(:id) }
before do before do
@commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running' @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running'
@commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending' @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending'
@commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success' @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success'
@commit4 = FactoryGirl.create :commit_status, commit: commit, name: 'dd', ref: nil, status: 'failed'
@commit5 = FactoryGirl.create :commit_status, commit: commit, name: 'ee', ref: nil, status: 'canceled'
end end
it 'return statuses with equal and nil ref set' do it 'return statuses that are running or pending' do
is_expected.to eq([@commit1]) is_expected.to eq([@commit1, @commit2])
end end
end end
describe :running_or_pending do describe '#before_sha' do
subject { CommitStatus.running_or_pending.order(:id) } subject { commit_status.before_sha }
context 'when no before_sha is set for ci::commit' do
before { commit.before_sha = nil }
it 'return blank sha' do
is_expected.to eq(Gitlab::Git::BLANK_SHA)
end
end
context 'for before_sha set for ci::commit' do
let(:value) { '1234' }
before { commit.before_sha = value }
it 'return the set value' do
is_expected.to eq(value)
end
end
end
describe '#stages' do
before do before do
@commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running' FactoryGirl.create :commit_status, commit: commit, stage: 'build', stage_idx: 0, status: 'success'
@commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending' FactoryGirl.create :commit_status, commit: commit, stage: 'build', stage_idx: 0, status: 'failed'
@commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success' FactoryGirl.create :commit_status, commit: commit, stage: 'deploy', stage_idx: 2, status: 'running'
@commit4 = FactoryGirl.create :commit_status, commit: commit, name: 'dd', ref: nil, status: 'failed' FactoryGirl.create :commit_status, commit: commit, stage: 'test', stage_idx: 1, status: 'success'
@commit5 = FactoryGirl.create :commit_status, commit: commit, name: 'ee', ref: nil, status: 'canceled'
end end
it 'return statuses that are running or pending' do context 'stages list' do
is_expected.to eq([@commit1, @commit2]) subject { CommitStatus.where(commit: commit).stages }
it 'return ordered list of stages' do
is_expected.to eq(%w(build test deploy))
end
end
context 'stages with statuses' do
subject { CommitStatus.where(commit: commit).stages_status }
it 'return list of stages with statuses' do
is_expected.to eq({
'build' => 'failed',
'test' => 'success',
'deploy' => 'running'
})
end
end end
end end
end end
require 'spec_helper' require 'spec_helper'
describe Ci::Status do describe Statuseable do
describe '.get_status' do before do
subject { described_class.get_status(statuses) } @object = Object.new
@object.extend(Statuseable::ClassMethods)
end
describe '.status' do
before do
allow(@object).to receive(:all).and_return(CommitStatus.where(id: statuses))
end
subject { @object.status }
shared_examples 'build status summary' do shared_examples 'build status summary' do
context 'all successful' do context 'all successful' do
......
...@@ -404,12 +404,12 @@ describe MergeRequest, models: true do ...@@ -404,12 +404,12 @@ describe MergeRequest, models: true do
describe 'when the source project exists' do describe 'when the source project exists' do
it 'returns the latest commit' do it 'returns the latest commit' do
commit = double(:commit, id: '123abc') commit = double(:commit, id: '123abc')
ci_commit = double(:ci_commit) ci_commit = double(:ci_commit, ref: 'master')
allow(subject).to receive(:last_commit).and_return(commit) allow(subject).to receive(:last_commit).and_return(commit)
expect(subject.source_project).to receive(:ci_commit). expect(subject.source_project).to receive(:ci_commit).
with('123abc'). with('123abc', 'master').
and_return(ci_commit) and_return(ci_commit)
expect(subject.ci_commit).to eq(ci_commit) expect(subject.ci_commit).to eq(ci_commit)
......
...@@ -441,9 +441,22 @@ describe Project, models: true do ...@@ -441,9 +441,22 @@ describe Project, models: true do
describe :ci_commit do describe :ci_commit do
let(:project) { create :project } let(:project) { create :project }
let(:commit) { create :ci_commit, project: project } let(:commit) { create :ci_commit, project: project, ref: 'master' }
it { expect(project.ci_commit(commit.sha)).to eq(commit) } subject { project.ci_commit(commit.sha, 'master') }
it { is_expected.to eq(commit) }
context 'return latest' do
let(:commit2) { create :ci_commit, project: project, ref: 'master' }
before do
commit
commit2
end
it { is_expected.to eq(commit2) }
end
end end
describe :builds_enabled do describe :builds_enabled do
......
...@@ -59,7 +59,7 @@ describe API::API, api: true do ...@@ -59,7 +59,7 @@ describe API::API, api: true do
describe 'GET /projects/:id/repository/commits/:sha/builds' do describe 'GET /projects/:id/repository/commits/:sha/builds' do
before do before do
project.ensure_ci_commit(commit.sha) project.ensure_ci_commit(commit.sha, 'master')
get api("/projects/#{project.id}/repository/commits/#{commit.sha}/builds", api_user) get api("/projects/#{project.id}/repository/commits/#{commit.sha}/builds", api_user)
end end
......
...@@ -16,7 +16,8 @@ describe API::CommitStatus, api: true do ...@@ -16,7 +16,8 @@ describe API::CommitStatus, api: true do
let(:get_url) { "/projects/#{project.id}/repository/commits/#{sha}/statuses" } let(:get_url) { "/projects/#{project.id}/repository/commits/#{sha}/statuses" }
context 'ci commit exists' do context 'ci commit exists' do
let!(:ci_commit) { project.ensure_ci_commit(commit.id) } let!(:master) { project.ci_commits.create(sha: commit.id, ref: 'master') }
let!(:develop) { project.ci_commits.create(sha: commit.id, ref: 'develop') }
it_behaves_like 'a paginated resources' do it_behaves_like 'a paginated resources' do
let(:request) { get api(get_url, reporter) } let(:request) { get api(get_url, reporter) }
...@@ -25,16 +26,16 @@ describe API::CommitStatus, api: true do ...@@ -25,16 +26,16 @@ describe API::CommitStatus, api: true do
context "reporter user" do context "reporter user" do
let(:statuses_id) { json_response.map { |status| status['id'] } } let(:statuses_id) { json_response.map { |status| status['id'] } }
def create_status(opts = {}) def create_status(commit, opts = {})
create(:commit_status, { commit: ci_commit }.merge(opts)) create(:commit_status, { commit: commit, ref: commit.ref }.merge(opts))
end end
let!(:status1) { create_status(status: 'running') } let!(:status1) { create_status(master, status: 'running') }
let!(:status2) { create_status(name: 'coverage', status: 'pending') } let!(:status2) { create_status(master, name: 'coverage', status: 'pending') }
let!(:status3) { create_status(ref: 'develop', status: 'running', allow_failure: true) } let!(:status3) { create_status(develop, status: 'running', allow_failure: true) }
let!(:status4) { create_status(name: 'coverage', status: 'success') } let!(:status4) { create_status(master, name: 'coverage', status: 'success') }
let!(:status5) { create_status(name: 'coverage', ref: 'develop', status: 'success') } let!(:status5) { create_status(develop, name: 'coverage', status: 'success') }
let!(:status6) { create_status(status: 'success') } let!(:status6) { create_status(master, status: 'success') }
context 'latest commit statuses' do context 'latest commit statuses' do
before { get api(get_url, reporter) } before { get api(get_url, reporter) }
......
...@@ -48,14 +48,14 @@ describe API::API, api: true do ...@@ -48,14 +48,14 @@ describe API::API, api: true do
expect(response.status).to eq(404) expect(response.status).to eq(404)
end end
it "should return not_found for CI status" do it "should return nil for commit without CI" do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(json_response['status']).to eq('not_found') expect(json_response['status']).to be_nil
end end
it "should return status for CI" do it "should return status for CI" do
ci_commit = project.ensure_ci_commit(project.repository.commit.sha) ci_commit = project.ensure_ci_commit(project.repository.commit.sha, 'master')
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(json_response['status']).to eq(ci_commit.status) expect(json_response['status']).to eq(ci_commit.status)
......
...@@ -20,8 +20,8 @@ describe Ci::API::API do ...@@ -20,8 +20,8 @@ describe Ci::API::API do
describe "POST /builds/register" do describe "POST /builds/register" do
it "should start a build" do it "should start a build" do
commit = FactoryGirl.create(:ci_commit, project: project) commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master')
commit.create_builds('master', false, nil) commit.create_builds(nil)
build = commit.builds.first build = commit.builds.first
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
...@@ -56,8 +56,8 @@ describe Ci::API::API do ...@@ -56,8 +56,8 @@ describe Ci::API::API do
end end
it "returns options" do it "returns options" do
commit = FactoryGirl.create(:ci_commit, project: project) commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master')
commit.create_builds('master', false, nil) commit.create_builds(nil)
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
...@@ -66,8 +66,8 @@ describe Ci::API::API do ...@@ -66,8 +66,8 @@ describe Ci::API::API do
end end
it "returns variables" do it "returns variables" do
commit = FactoryGirl.create(:ci_commit, project: project) commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master')
commit.create_builds('master', false, nil) commit.create_builds(nil)
project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value")
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
...@@ -83,10 +83,10 @@ describe Ci::API::API do ...@@ -83,10 +83,10 @@ describe Ci::API::API do
it "returns variables for triggers" do it "returns variables for triggers" do
trigger = FactoryGirl.create(:ci_trigger, project: project) trigger = FactoryGirl.create(:ci_trigger, project: project)
commit = FactoryGirl.create(:ci_commit, project: project) commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master')
trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger) trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger)
commit.create_builds('master', false, nil, trigger_request) commit.create_builds(nil, trigger_request)
project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value")
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
...@@ -103,8 +103,8 @@ describe Ci::API::API do ...@@ -103,8 +103,8 @@ describe Ci::API::API do
end end
it "returns dependent builds" do it "returns dependent builds" do
commit = FactoryGirl.create(:ci_commit, project: project) commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master')
commit.create_builds('master', false, nil, nil) commit.create_builds(nil, nil)
commit.builds.where(stage: 'test').each(&:success) commit.builds.where(stage: 'test').each(&:success)
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
......
require 'spec_helper' require 'spec_helper'
describe Ci::CreateBuildsService, services: true do describe Ci::CreateBuildsService, services: true do
let(:commit) { create(:ci_commit) } let(:commit) { create(:ci_commit, ref: 'master') }
let(:user) { create(:user) } let(:user) { create(:user) }
describe '#execute' do describe '#execute' do
...@@ -9,7 +9,7 @@ describe Ci::CreateBuildsService, services: true do ...@@ -9,7 +9,7 @@ describe Ci::CreateBuildsService, services: true do
# #
subject do subject do
described_class.new.execute(commit, 'test', 'master', nil, user, nil, status) described_class.new(commit).execute(commit, nil, user, status)
end end
context 'next builds available' do context 'next builds available' do
......
...@@ -5,7 +5,7 @@ module Ci ...@@ -5,7 +5,7 @@ module Ci
let(:service) { ImageForBuildService.new } let(:service) { ImageForBuildService.new }
let(:project) { FactoryGirl.create(:empty_project) } let(:project) { FactoryGirl.create(:empty_project) }
let(:commit_sha) { '01234567890123456789' } let(:commit_sha) { '01234567890123456789' }
let(:commit) { project.ensure_ci_commit(commit_sha) } let(:commit) { project.ensure_ci_commit(commit_sha, 'master') }
let(:build) { FactoryGirl.create(:ci_build, commit: commit) } let(:build) { FactoryGirl.create(:ci_build, commit: commit) }
describe :execute do describe :execute do
......
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