Merge branch 'refactor-build-service' into 'master'

Refactor Ci::Commit and Ci::Build to have all builds for same :sha on single page

This makes Ci::Commit to have only :sha and simplifies routing to have only :sha in path. The :ref and :push_data is now parameter of Ci::Build.

All commit related data (git author, message and .gitlab-ci.yml) is read directly from repository.

All code related for creating builds is moved to CreateBuildsService.

Status deduction is rewritten to make if more efficient and easier to integrate with Commit Status API.

This is partially working, tests are not yet touched.

This slightly changes view of Commit:
![Screen_Shot_2015-10-02_at_15.21.47](https://gitlab.com/gitlab-org/gitlab-ce/uploads/ad3f1ccdcc87659ea437d8db6c5b9f94/Screen_Shot_2015-10-02_at_15.21.47.png)

@dzaporozhets What do you think?


See merge request !1502
This commit is contained in:
Kamil Trzciński 2015-10-05 17:42:50 +00:00
commit 3eef0e18e0
55 changed files with 573 additions and 607 deletions

View file

@ -18,7 +18,7 @@ module Ci
if commit
# Redirect to commit page
redirect_to ci_project_ref_commit_path(@project, @build.commit.ref, @build.commit.sha)
redirect_to ci_project_commit_path(@project, @build.commit)
return
end
end

View file

@ -13,7 +13,7 @@ module Ci
end
def status
commit = Ci::Project.find(params[:project_id]).commits.find_by_sha_and_ref!(params[:id], params[:ref_id])
commit = Ci::Project.find(params[:project_id]).commits.find_by_sha!(params[:id])
render json: commit.to_json(only: [:id, :sha], methods: [:status, :coverage])
rescue ActiveRecord::RecordNotFound
render json: { status: "not_found" }
@ -22,7 +22,7 @@ module Ci
def cancel
commit.builds.running_or_pending.each(&:cancel)
redirect_to ci_project_ref_commits_path(project, commit.ref, commit.sha)
redirect_to ci_project_commits_path(project, commit.sha)
end
private
@ -32,7 +32,7 @@ module Ci
end
def commit
@commit ||= Ci::Project.find(params[:project_id]).commits.find_by_sha_and_ref!(params[:id], params[:ref_id])
@commit ||= Ci::Project.find(params[:project_id]).commits.find_by_sha!(params[:id])
end
end
end

View file

@ -19,7 +19,8 @@ module Ci
@ref = params[:ref]
@commits = @project.commits.reverse_order
@commits = @commits.where(ref: @ref) if @ref
# TODO: this is broken
# @commits = @commits.where(ref: @ref) if @ref
@commits = @commits.page(params[:page]).per(20)
end

View file

@ -3,10 +3,6 @@ module BuildsHelper
gitlab_ref_link build.project, build.ref
end
def build_compare_link build
gitlab_compare_link build.project, build.commit.short_before_sha, build.short_sha
end
def build_commit_link build
gitlab_commit_link build.project, build.short_sha
end

View file

@ -1,7 +1,7 @@
module Ci
module CommitsHelper
def ci_commit_path(commit)
ci_project_ref_commits_path(commit.project, commit.ref, commit.sha)
ci_project_commits_path(commit.project, commit)
end
def commit_link(commit)

View file

@ -26,7 +26,7 @@ module Ci
def yaml_web_editor_link(project)
commits = project.commits
if commits.any? && commits.last.push_data[:ci_yaml_file]
if commits.any? && commits.last.ci_yaml_file
"#{project.gitlab_url}/edit/master/.gitlab-ci.yml"
else
"#{project.gitlab_url}/new/master"

View file

@ -1,6 +1,6 @@
module CiStatusHelper
def ci_status_path(ci_commit)
ci_project_ref_commits_path(ci_commit.project, ci_commit.ref, ci_commit)
ci_project_commits_path(ci_commit.project, ci_commit)
end
def ci_status_icon(ci_commit)

View file

@ -32,12 +32,14 @@ module Ci
belongs_to :commit, class_name: 'Ci::Commit'
belongs_to :runner, class_name: 'Ci::Runner'
belongs_to :trigger_request, class_name: 'Ci::TriggerRequest'
belongs_to :user
serialize :options
validates :commit, presence: true
validates :status, presence: true
validates :coverage, numericality: true, allow_blank: true
validates_presence_of :ref
scope :running, ->() { where(status: "running") }
scope :pending, ->() { where(status: "pending") }
@ -45,6 +47,10 @@ module Ci
scope :failed, ->() { where(status: "failed") }
scope :unstarted, ->() { where(runner_id: nil) }
scope :running_or_pending, ->() { where(status:[:running, :pending]) }
scope :latest, ->() { where(id: unscope(:select).select('max(id)').group(:name)).order(stage_idx: :asc) }
scope :ignore_failures, ->() { where(allow_failure: false) }
scope :for_ref, ->(ref) { where(ref: ref) }
scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) }
acts_as_taggable
@ -75,6 +81,8 @@ module Ci
def retry(build)
new_build = Ci::Build.new(status: :pending)
new_build.ref = build.ref
new_build.tag = build.tag
new_build.options = build.options
new_build.commands = build.commands
new_build.tag_list = build.tag_list
@ -82,6 +90,7 @@ module Ci
new_build.name = build.name
new_build.allow_failure = build.allow_failure
new_build.stage = build.stage
new_build.stage_idx = build.stage_idx
new_build.trigger_request = build.trigger_request
new_build.save
new_build
@ -117,8 +126,8 @@ module Ci
Ci::WebHookService.new.build_end(build)
end
if build.commit.success?
build.commit.create_next_builds(build.trigger_request)
if build.commit.should_create_next_builds?(build)
build.commit.create_next_builds(build.ref, build.tag, build.user, build.trigger_request)
end
project.execute_services(build)
@ -135,9 +144,13 @@ module Ci
state :canceled, value: 'canceled'
end
delegate :sha, :short_sha, :before_sha, :ref, :project,
delegate :sha, :short_sha, :project,
to: :commit, prefix: false
def before_sha
Gitlab::Git::BLANK_SHA
end
def trace_html
html = Ci::Ansi2html::convert(trace) if trace.present?
html || ''
@ -187,6 +200,16 @@ module Ci
project.name
end
def project_recipients
recipients = project.email_recipients.split(' ')
if project.email_add_pusher? && user.present? && user.notification_email.present?
recipients << user.notification_email
end
recipients.uniq
end
def repo_url
project.repo_url_with_auth
end

View file

@ -23,9 +23,7 @@ module Ci
has_many :builds, dependent: :destroy, class_name: 'Ci::Build'
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
serialize :push_data
validates_presence_of :ref, :sha, :before_sha, :push_data
validates_presence_of :sha
validate :valid_commit_sha
def self.truncate_sha(sha)
@ -60,28 +58,16 @@ module Ci
end
end
def new_branch?
before_sha == Ci::Git::BLANK_SHA
end
def compare?
!new_branch?
end
def git_author_name
commit_data[:author][:name] if commit_data && commit_data[:author]
commit_data.author_name if commit_data
end
def git_author_email
commit_data[:author][:email] if commit_data && commit_data[:author]
commit_data.author_email if commit_data
end
def git_commit_message
commit_data[:message] if commit_data && commit_data[:message]
end
def short_before_sha
Ci::Commit.truncate_sha(before_sha)
commit_data.message if commit_data
end
def short_sha
@ -89,84 +75,51 @@ module Ci
end
def commit_data
push_data[:commits].find do |commit|
commit[:id] == sha
end
@commit ||= gl_project.commit(sha)
rescue
nil
end
def project_recipients
recipients = project.email_recipients.split(' ')
if project.email_add_pusher? && push_data[:user_email].present?
recipients << push_data[:user_email]
end
recipients.uniq
end
def stage
return unless config_processor
stages = builds_without_retry.select(&:active?).map(&:stage)
config_processor.stages.find { |stage| stages.include? stage }
running_or_pending = builds_without_retry.running_or_pending
running_or_pending.limit(1).pluck(:stage).first
end
def create_builds_for_stage(stage, trigger_request)
def create_builds(ref, tag, user, trigger_request = nil)
return if skip_ci? && trigger_request.blank?
return unless config_processor
builds_attrs = config_processor.builds_for_stage_and_ref(stage, ref, tag)
builds_attrs.map do |build_attrs|
builds.create!({
name: build_attrs[:name],
commands: build_attrs[:script],
tag_list: build_attrs[:tags],
options: build_attrs[:options],
allow_failure: build_attrs[:allow_failure],
stage: build_attrs[:stage],
trigger_request: trigger_request,
})
config_processor.stages.any? do |stage|
CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request).present?
end
end
def create_next_builds(trigger_request)
def create_next_builds(ref, tag, user, trigger_request)
return if skip_ci? && trigger_request.blank?
return unless config_processor
stages = builds.where(trigger_request: trigger_request).group_by(&:stage)
stages = builds.where(ref: ref, tag: tag, trigger_request: trigger_request).group_by(&:stage)
config_processor.stages.any? do |stage|
!stages.include?(stage) && create_builds_for_stage(stage, trigger_request).present?
unless stages.include?(stage)
CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request).present?
end
end
end
def create_builds(trigger_request = nil)
return if skip_ci? && trigger_request.blank?
return unless config_processor
def refs
builds.group(:ref).pluck(:ref)
end
config_processor.stages.any? do |stage|
create_builds_for_stage(stage, trigger_request).present?
end
def last_ref
builds.latest.first.try(:ref)
end
def builds_without_retry
@builds_without_retry ||=
begin
grouped_builds = builds.group_by(&:name)
grouped_builds.map do |name, builds|
builds.sort_by(&:id).last
end
end
builds.latest
end
def builds_without_retry_sorted
return builds_without_retry unless config_processor
stages = config_processor.stages
builds_without_retry.sort_by do |build|
[stages.index(build.stage) || -1, build.name || ""]
end
def builds_without_retry_for_ref(ref)
builds.for_ref(ref).latest
end
def retried_builds
@ -225,6 +178,10 @@ module Ci
@duration ||= builds_without_retry.select(&:duration).sum(&:duration).to_i
end
def duration_for_ref(ref)
builds_without_retry_for_ref(ref).select(&:duration).sum(&:duration).to_i
end
def finished_at
@finished_at ||= builds.order('finished_at DESC').first.try(:finished_at)
end
@ -238,12 +195,12 @@ module Ci
end
end
def matrix?
builds_without_retry.size > 1
def matrix_for_ref?(ref)
builds_without_retry_for_ref(ref).pluck(:id).size > 1
end
def config_processor
@config_processor ||= Ci::GitlabCiYamlProcessor.new(push_data[:ci_yaml_file])
@config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file)
rescue Ci::GitlabCiYamlProcessor::ValidationError => e
save_yaml_error(e.message)
nil
@ -253,16 +210,31 @@ module Ci
nil
end
def ci_yaml_file
gl_project.repository.blob_at(sha, '.gitlab-ci.yml')
rescue
nil
end
def skip_ci?
return false if builds.any?
commits = push_data[:commits]
commits.present? && commits.last[:message] =~ /(\[ci skip\])/
git_commit_message =~ /(\[ci skip\])/ if git_commit_message
end
def update_committed!
update!(committed_at: DateTime.now)
end
def should_create_next_builds?(build)
# don't create other builds if this one is retried
other_builds = builds.similar(build).latest
return false unless other_builds.include?(build)
other_builds.all? do |build|
build.success? || build.ignored?
end
end
private
def save_yaml_error(error)

View file

@ -28,18 +28,6 @@ module Ci
status
end
# only check for toggling build status within same ref.
def last_commit_changed_status?
ref = last_commit.ref
last_commits = commits.where(ref: ref).last(2)
if last_commits.size < 2
false
else
last_commits[0].status != last_commits[1].status
end
end
def last_commit_for_ref(ref)
commits.where(ref: ref).last
end

View file

@ -744,7 +744,11 @@ class Project < ActiveRecord::Base
end
def ci_commit(sha)
gitlab_ci_project.commits.find_by(sha: sha) if gitlab_ci?
ci_commits.find_by(sha: sha)
end
def ensure_ci_commit(sha)
ci_commit(sha) || ci_commits.create(sha: sha)
end
def ensure_gitlab_ci_project

View file

@ -11,14 +11,7 @@ module Ci
def to_s
lines = Array.new
lines.push("<a href=\"#{ci_project_url(project)}\">#{project.name}</a> - ")
if commit.matrix?
lines.push("<a href=\"#{ci_project_ref_commits_url(project, commit.ref, commit.sha)}\">Commit ##{commit.id}</a></br>")
else
first_build = commit.builds_without_retry.first
lines.push("<a href=\"#{ci_project_build_url(project, first_build)}\">Build '#{first_build.name}' ##{first_build.id}</a></br>")
end
lines.push("<a href=\"#{ci_project_commits_url(project, commit.sha)}\">Commit ##{commit.id}</a></br>")
lines.push("#{commit.short_sha} #{commit.git_author_name} - #{commit.git_commit_message}</br>")
lines.push("#{humanized_status(commit_status)} in #{commit.duration} second(s).")
lines.join('')

View file

@ -61,7 +61,7 @@ module Ci
end
def execute(build)
build.commit.project_recipients.each do |recipient|
build.project_recipients.each do |recipient|
case build.status.to_sym
when :success
mailer.build_success_email(build.id, recipient)

View file

@ -23,15 +23,13 @@ module Ci
def attachments
fields = []
if commit.matrix?
commit.builds_without_retry.each do |build|
next if build.allow_failure?
next unless build.failed?
fields << {
title: build.name,
value: "Build <#{ci_project_build_url(project, build)}|\##{build.id}> failed in #{build.duration.to_i} second(s)."
}
end
commit.builds_without_retry.each do |build|
next if build.allow_failure?
next unless build.failed?
fields << {
title: build.name,
value: "Build <#{ci_project_build_url(project, build)}|\##{build.id}> failed in #{build.duration.to_i} second(s)."
}
end
[{
@ -47,12 +45,7 @@ module Ci
def attachment_message
out = "<#{ci_project_url(project)}|#{project_name}>: "
if commit.matrix?
out << "Commit <#{ci_project_ref_commits_url(project, commit.ref, commit.sha)}|\##{commit.id}> "
else
build = commit.builds_without_retry.first
out << "Build <#{ci_project_build_url(project, build)}|\##{build.id}> "
end
out << "Commit <#{ci_project_commits_url(project, commit.sha)}|\##{commit.id}> "
out << "(<#{commit_sha_link}|#{commit.short_sha}>) "
out << "of <#{commit_ref_link}|#{commit.ref}> "
out << "by #{commit.git_author_name} " if commit.git_author_name

View file

@ -40,19 +40,10 @@ class GitlabCiService < CiService
def execute(data)
return unless supported_events.include?(data[:object_kind])
sha = data[:checkout_sha]
if sha.present?
file = ci_yaml_file(sha)
if file && file.data
data.merge!(ci_yaml_file: file.data)
end
end
ci_project = Ci::Project.find_by(gitlab_id: project.id)
ci_project = project.gitlab_ci_project
if ci_project
Ci::CreateCommitService.new.execute(ci_project, data)
current_user = User.find_by(id: data[:user_id])
Ci::CreateCommitService.new.execute(ci_project, current_user, data)
end
end
@ -63,7 +54,7 @@ class GitlabCiService < CiService
end
def get_ci_commit(sha, ref)
Ci::Project.find(project.gitlab_ci_project).commits.find_by_sha_and_ref!(sha, ref)
Ci::Project.find(project.gitlab_ci_project).commits.find_by_sha!(sha)
end
def commit_status(sha, ref)
@ -80,7 +71,7 @@ class GitlabCiService < CiService
def build_page(sha, ref)
if project.gitlab_ci_project.present?
ci_project_ref_commits_url(project.gitlab_ci_project, ref, sha)
ci_project_commits_url(project.gitlab_ci_project, sha)
end
end
@ -99,14 +90,4 @@ class GitlabCiService < CiService
def fields
[]
end
private
def ci_yaml_file(sha)
repository.blob_at(sha, '.gitlab-ci.yml')
end
def repository
project.repository
end
end

View file

@ -130,6 +130,7 @@ class User < ActiveRecord::Base
has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy
has_one :abuse_report, dependent: :destroy
has_many :ci_builds, dependent: :nullify, class_name: 'Ci::Build'
#

View file

@ -0,0 +1,27 @@
module Ci
class CreateBuildsService
def execute(commit, stage, ref, tag, user, trigger_request)
builds_attrs = commit.config_processor.builds_for_stage_and_ref(stage, ref, tag)
builds_attrs.map do |build_attrs|
# don't create the same build twice
unless commit.builds.find_by(ref: ref, tag: tag, trigger_request: trigger_request, name: build_attrs[:name])
build_attrs.slice!(:name,
:commands,
:tag_list,
:options,
:allow_failure,
:stage,
:stage_idx)
build_attrs.merge!(ref: ref,
tag: tag,
trigger_request: trigger_request,
user: user)
commit.builds.create!(build_attrs)
end
end
end
end
end

View file

@ -1,7 +1,6 @@
module Ci
class CreateCommitService
def execute(project, params)
before_sha = params[:before]
def execute(project, user, params)
sha = params[:checkout_sha] || params[:after]
origin_ref = params[:ref]
@ -16,33 +15,10 @@ module Ci
return false
end
commit = project.commits.find_by_sha_and_ref(sha, ref)
# Create commit if not exists yet
unless commit
data = {
ref: ref,
sha: sha,
tag: origin_ref.start_with?('refs/tags/'),
before_sha: before_sha,
push_data: {
before: before_sha,
after: sha,
ref: ref,
user_name: params[:user_name],
user_email: params[:user_email],
repository: params[:repository],
commits: params[:commits],
total_commits_count: params[:total_commits_count],
ci_yaml_file: params[:ci_yaml_file]
}
}
commit = project.commits.create(data)
end
tag = origin_ref.start_with?('refs/tags/')
commit = project.gl_project.ensure_ci_commit(sha)
commit.update_committed!
commit.create_builds unless commit.builds.any?
commit.create_builds(ref, tag, user)
commit
end

View file

@ -1,15 +1,20 @@
module Ci
class CreateTriggerRequestService
def execute(project, trigger, ref, variables = nil)
commit = project.commits.where(ref: ref).last
commit = project.gl_project.commit(ref)
return unless commit
# check if ref is tag
tag = project.gl_project.repository.find_tag(ref).present?
ci_commit = project.gl_project.ensure_ci_commit(commit.sha)
trigger_request = trigger.trigger_requests.create!(
commit: commit,
variables: variables
variables: variables,
commit: ci_commit,
)
if commit.create_builds(trigger_request)
if ci_commit.create_builds(ref, tag, nil, trigger_request)
trigger_request
end
end

View file

@ -27,9 +27,8 @@ module Ci
project_name: project.name,
gitlab_url: project.gitlab_url,
ref: build.ref,
sha: build.sha,
before_sha: build.before_sha,
push_data: build.commit.push_data
sha: build.sha,
})
end
end

View file

@ -6,6 +6,10 @@
= link_to ci_project_build_path(build.project, build) do
%strong Build ##{build.id}
- if defined?(ref)
%td
= build.ref
%td
= build.stage

View file

@ -1,7 +1,7 @@
#up-build-trace
- if @commit.matrix?
- if @commit.matrix_for_ref?(@build.ref)
%ul.center-top-menu
- @commit.builds_without_retry_sorted.each do |build|
- @commit.builds_without_retry_for_ref(build.ref).each do |build|
%li{class: ('active' if build == @build) }
= link_to ci_project_build_url(@project, build) do
= ci_icon_for_status(build.status)
@ -12,7 +12,7 @@
= build.id
- unless @commit.builds_without_retry.include?(@build)
- unless @commit.builds_without_retry_for_ref(@build.ref).include?(@build)
%li.active
%a
Build ##{@build.id}
@ -122,11 +122,6 @@
Commit
.pull-right
%small #{build_commit_link @build}
- if @build.commit.compare?
%p
%span.attr-name Compare:
#{build_compare_link @build}
%p
%span.attr-name Branch:
#{build_ref_link @build}

View file

@ -7,7 +7,7 @@
%td.build-link
= link_to ci_project_ref_commits_path(commit.project, commit.ref, commit.sha) do
= link_to ci_project_commits_path(commit.project, commit.sha) do
%strong #{commit.short_sha}
%td.build-message
@ -16,7 +16,7 @@
%td.build-branch
- unless @ref
%span
= link_to truncate(commit.ref, length: 25), ci_project_path(@project, ref: commit.ref)
= link_to truncate(commit.last_ref, length: 25), ci_project_path(@project, ref: commit.last_ref)
%td.duration
- if commit.duration > 0

View file

@ -9,22 +9,14 @@
.gray-content-block.second-block
.row
.col-sm-6
- if @commit.compare?
%p
%span.attr-name Compare:
#{gitlab_compare_link(@project, @commit.short_before_sha, @commit.short_sha)}
- else
%p
%span.attr-name Commit:
#{gitlab_commit_link(@project, @commit.sha)}
%p
%span.attr-name Branch:
#{gitlab_ref_link(@project, @commit.ref)}
%p
%span.attr-name Commit:
#{gitlab_commit_link(@project, @commit.sha)}
.col-sm-6
%p
%span.attr-name Author:
#{@commit.git_author_name} (#{@commit.git_author_email})
- if @commit.git_author_name || @commit.git_author_email
%p
%span.attr-name Author:
#{@commit.git_author_name} (#{@commit.git_author_email})
- if @commit.created_at
%p
%span.attr-name Created at:
@ -33,7 +25,7 @@
- if current_user && can?(current_user, :manage_builds, gl_project)
.pull-right
- if @commit.builds.running_or_pending.any?
= link_to "Cancel", cancel_ci_project_ref_commits_path(@project, @commit.ref, @commit.sha), class: 'btn btn-sm btn-danger'
= link_to "Cancel", cancel_ci_project_commits_path(@project, @commit), class: 'btn btn-sm btn-danger'
- if @commit.yaml_errors.present?
@ -43,34 +35,17 @@
- @commit.yaml_errors.split(",").each do |error|
%li= error
- unless @commit.push_data[:ci_yaml_file]
- unless @commit.ci_yaml_file
.bs-callout.bs-callout-warning
\.gitlab-ci.yml not found in this commit
%h3
Builds
- if @commit.duration > 0
%small.pull-right
%i.fa.fa-time
#{time_interval_in_words @commit.duration}
%table.table.builds
%thead
%tr
%th Status
%th Build ID
%th Stage
%th Name
%th Duration
%th Finished at
- if @project.coverage_enabled?
%th Coverage
%th
= render @commit.builds_without_retry_sorted, controls: true
- if @commit.retried_builds.any?
- @commit.refs.each do |ref|
%h3
Retried builds
Builds for #{ref}
- if @commit.duration_for_ref(ref) > 0
%small.pull-right
%i.fa.fa-time
#{time_interval_in_words @commit.duration_for_ref(ref)}
%table.table.builds
%thead
@ -84,4 +59,23 @@
- if @project.coverage_enabled?
%th Coverage
%th
= render @commit.retried_builds
= render @commit.builds_without_retry.for_ref(ref), controls: true
- if @commit.retried_builds.any?
%h3
Retried builds
%table.table.builds
%thead
%tr
%th Status
%th Build ID
%th Ref
%th Stage
%th Name
%th Duration
%th Finished at
- if @project.coverage_enabled?
%th Coverage
%th
= render @commit.retried_builds, ref: true

View file

@ -11,7 +11,7 @@
%p
Author: #{@build.commit.git_author_name}
%p
Branch: #{@build.commit.ref}
Branch: #{@build.ref}
%p
Message: #{@build.commit.git_commit_message}

View file

@ -3,7 +3,7 @@ Build failed for <%= @project.name %>
Status: <%= @build.status %>
Commit: <%= @build.commit.short_sha %>
Author: <%= @build.commit.git_author_name %>
Branch: <%= @build.commit.ref %>
Branch: <%= @build.ref %>
Message: <%= @build.commit.git_commit_message %>
Url: <%= ci_project_build_url(@build.project, @build) %>

View file

@ -12,7 +12,7 @@
%p
Author: #{@build.commit.git_author_name}
%p
Branch: #{@build.commit.ref}
Branch: #{@build.ref}
%p
Message: #{@build.commit.git_commit_message}

View file

@ -3,7 +3,7 @@ Build successful for <%= @project.name %>
Status: <%= @build.status %>
Commit: <%= @build.commit.short_sha %>
Author: <%= @build.commit.git_author_name %>
Branch: <%= @build.commit.ref %>
Branch: <%= @build.ref %>
Message: <%= @build.commit.git_commit_message %>
Url: <%= ci_project_build_url(@build.project, @build) %>

View file

@ -30,12 +30,10 @@ Gitlab::Application.routes.draw do
resource :charts, only: [:show]
resources :refs, constraints: { ref_id: /.*/ }, only: [] do
resources :commits, only: [:show] do
member do
get :status
get :cancel
end
resources :commits, only: [:show] do
member do
get :status
get :cancel
end
end

View file

@ -0,0 +1,5 @@
class AddStageIdxToBuilds < ActiveRecord::Migration
def change
add_column :ci_builds, :stage_idx, :integer
end
end

View file

@ -0,0 +1,5 @@
class AddIndexForBuilds < ActiveRecord::Migration
def up
add_index :ci_builds, [:commit_id, :stage_idx, :created_at]
end
end

View file

@ -0,0 +1,6 @@
class AddRefAndTagToBuilds < ActiveRecord::Migration
def change
add_column :ci_builds, :tag, :boolean
add_column :ci_builds, :ref, :string
end
end

View file

@ -0,0 +1,6 @@
class MigrateRefAndTagToBuild < ActiveRecord::Migration
def change
execute('UPDATE ci_builds SET ref=(SELECT ref FROM ci_commits WHERE ci_commits.id = ci_builds.commit_id) WHERE ref IS NULL')
execute('UPDATE ci_builds SET tag=(SELECT tag FROM ci_commits WHERE ci_commits.id = ci_builds.commit_id) WHERE tag IS NULL')
end
end

View file

@ -0,0 +1,5 @@
class AddUserIdToBuild < ActiveRecord::Migration
def change
add_column :ci_builds, :user_id, :integer
end
end

View file

@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150930095736) do
ActiveRecord::Schema.define(version: 20151005075649) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -100,8 +100,13 @@ ActiveRecord::Schema.define(version: 20150930095736) do
t.boolean "allow_failure", default: false, null: false
t.string "stage"
t.integer "trigger_request_id"
t.integer "stage_idx"
t.boolean "tag"
t.string "ref"
t.integer "user_id"
end
add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree
add_index "ci_builds", ["commit_id"], name: "index_ci_builds_on_commit_id", using: :btree
add_index "ci_builds", ["project_id", "commit_id"], name: "index_ci_builds_on_project_id_and_commit_id", using: :btree
add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree

View file

@ -51,7 +51,7 @@ module Ci
required_attributes! [:project_id, :data, :project_token]
project = Ci::Project.find(params[:project_id])
authenticate_project_token!(project)
commit = Ci::CreateCommitService.new.execute(project, params[:data])
commit = Ci::CreateCommitService.new.execute(project, current_user, params[:data])
if commit.persisted?
present commit, with: Entities::CommitWithBuilds

View file

@ -85,9 +85,10 @@ module Ci
def build_job(name, job)
{
stage_idx: stages.index(job[:stage]),
stage: job[:stage],
script: "#{@before_script.join("\n")}\n#{normalize_script(job[:script])}",
tags: job[:tags] || [],
commands: "#{@before_script.join("\n")}\n#{normalize_script(job[:script])}",
tag_list: job[:tags] || [],
name: name,
only: job[:only],
except: job[:except],

View file

@ -27,6 +27,8 @@
FactoryGirl.define do
factory :ci_build, class: Ci::Build do
ref 'master'
tag false
started_at 'Di 29. Okt 09:51:28 CET 2013'
finished_at 'Di 29. Okt 09:53:28 CET 2013'
commands 'ls -a'
@ -43,5 +45,9 @@ FactoryGirl.define do
started_at nil
finished_at nil
end
factory :ci_build_tag do
tag true
end
end
end

View file

@ -17,60 +17,32 @@
# Read about factories at https://github.com/thoughtbot/factory_girl
FactoryGirl.define do
factory :ci_commit, class: Ci::Commit do
ref 'master'
before_sha '76de212e80737a608d939f648d959671fb0a0142'
factory :ci_empty_commit, class: Ci::Commit do
sha '97de212e80737a608d939f648d959671fb0a0142'
push_data do
{
ref: 'refs/heads/master',
before: '76de212e80737a608d939f648d959671fb0a0142',
after: '97de212e80737a608d939f648d959671fb0a0142',
user_name: 'Git User',
user_email: 'git@example.com',
repository: {
name: 'test-data',
url: 'ssh://git@gitlab.com/test/test-data.git',
description: '',
homepage: 'http://gitlab.com/test/test-data'
},
commits: [
{
id: '97de212e80737a608d939f648d959671fb0a0142',
message: 'Test commit message',
timestamp: '2014-09-23T13:12:25+02:00',
url: 'https://gitlab.com/test/test-data/commit/97de212e80737a608d939f648d959671fb0a0142',
author: {
name: 'Git User',
email: 'git@user.com'
}
}
],
total_commits_count: 1,
ci_yaml_file: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
}
end
gl_project factory: :empty_project
factory :ci_commit_without_jobs do
after(:create) do |commit, evaluator|
commit.push_data[:ci_yaml_file] = YAML.dump({})
commit.save
after(:build) do |commit|
allow(commit).to receive(:ci_yaml_file) { YAML.dump({}) }
end
end
factory :ci_commit_with_one_job do
after(:create) do |commit, evaluator|
commit.push_data[:ci_yaml_file] = YAML.dump({ rspec: { script: "ls" } })
commit.save
after(:build) do |commit|
allow(commit).to receive(:ci_yaml_file) { YAML.dump({ rspec: { script: "ls" } }) }
end
end
factory :ci_commit_with_two_jobs do
after(:create) do |commit, evaluator|
commit.push_data[:ci_yaml_file] = YAML.dump({ rspec: { script: "ls" }, spinach: { script: "ls" } })
commit.save
after(:build) do |commit|
allow(commit).to receive(:ci_yaml_file) { YAML.dump({ rspec: { script: "ls" }, spinach: { script: "ls" } }) }
end
end
factory :ci_commit do
after(:build) do |commit|
allow(commit).to receive(:ci_yaml_file) { File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) }
end
end
end

View file

@ -11,6 +11,10 @@ describe "Commits" do
@commit.project.gl_project.team << [@user, :master]
end
before do
stub_ci_commit_to_return_yaml_file
end
describe "GET /:project/commits/:sha" do
before do
visit ci_commit_path(@commit)
@ -38,8 +42,7 @@ describe "Commits" do
end
it "shows warning" do
@commit.push_data[:ci_yaml_file] = nil
@commit.save
stub_ci_commit_yaml_file(nil)
visit ci_commit_path(@commit)

View file

@ -17,11 +17,12 @@ module Ci
expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
expect(config_processor.builds_for_stage_and_ref(type, "master").first).to eq({
stage: "test",
stage_idx: 1,
except: nil,
name: :rspec,
only: nil,
script: "pwd\nrspec",
tags: [],
commands: "pwd\nrspec",
tag_list: [],
options: {},
allow_failure: false
})
@ -115,10 +116,11 @@ module Ci
expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
except: nil,
stage: "test",
stage_idx: 1,
name: :rspec,
only: nil,
script: "pwd\nrspec",
tags: [],
commands: "pwd\nrspec",
tag_list: [],
options: {
image: "ruby:2.1",
services: ["mysql"]
@ -141,10 +143,11 @@ module Ci
expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
except: nil,
stage: "test",
stage_idx: 1,
name: :rspec,
only: nil,
script: "pwd\nrspec",
tags: [],
commands: "pwd\nrspec",
tag_list: [],
options: {
image: "ruby:2.5",
services: ["postgresql"]

View file

@ -30,9 +30,12 @@ describe Ci::Build do
let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
let(:build) { FactoryGirl.create :ci_build, commit: commit }
subject { build }
it { is_expected.to belong_to(:commit) }
it { is_expected.to belong_to(:user) }
it { is_expected.to validate_presence_of :status }
it { is_expected.to validate_presence_of :ref }
it { is_expected.to respond_to :success? }
it { is_expected.to respond_to :failed? }
@ -236,12 +239,6 @@ describe Ci::Build do
it { is_expected.to eq(options) }
end
describe :ref do
subject { build.ref }
it { is_expected.to eq(commit.ref) }
end
describe :sha do
subject { build.sha }
@ -254,12 +251,6 @@ describe Ci::Build do
it { is_expected.to eq(commit.short_sha) }
end
describe :before_sha do
subject { build.before_sha }
it { is_expected.to eq(commit.before_sha) }
end
describe :allow_git_fetch do
subject { build.allow_git_fetch }
@ -359,4 +350,38 @@ describe Ci::Build do
end
end
end
describe :project_recipients do
let(:pusher_email) { 'pusher@gitlab.test' }
let(:user) { User.new(notification_email: pusher_email) }
subject { build.project_recipients }
before do
build.update_attributes(user: user)
end
it 'should return pusher_email as only recipient when no additional recipients are given' do
project.update_attributes(email_add_pusher: true,
email_recipients: '')
is_expected.to eq([pusher_email])
end
it 'should return pusher_email and additional recipients' do
project.update_attributes(email_add_pusher: true,
email_recipients: 'rec1 rec2')
is_expected.to eq(['rec1', 'rec2', pusher_email])
end
it 'should return recipients' do
project.update_attributes(email_add_pusher: false,
email_recipients: 'rec1 rec2')
is_expected.to eq(['rec1', 'rec2'])
end
it 'should return unique recipients only' do
project.update_attributes(email_add_pusher: true,
email_recipients: "rec1 rec1 #{pusher_email}")
is_expected.to eq(['rec1', pusher_email])
end
end
end

View file

@ -21,15 +21,10 @@ describe Ci::Commit do
let(:project) { FactoryGirl.create :ci_project }
let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
let(:commit_with_project) { FactoryGirl.create :ci_commit, gl_project: gl_project }
let(:config_processor) { Ci::GitlabCiYamlProcessor.new(gitlab_ci_yaml) }
it { is_expected.to belong_to(:gl_project) }
it { is_expected.to have_many(:builds) }
it { is_expected.to validate_presence_of :before_sha }
it { is_expected.to validate_presence_of :sha }
it { is_expected.to validate_presence_of :ref }
it { is_expected.to validate_presence_of :push_data }
it { is_expected.to respond_to :git_author_name }
it { is_expected.to respond_to :git_author_email }
@ -59,53 +54,6 @@ describe Ci::Commit do
end
end
describe :project_recipients do
context 'always sending notification' do
it 'should return commit_pusher_email as only recipient when no additional recipients are given' do
project = FactoryGirl.create :ci_project,
email_add_pusher: true,
email_recipients: ''
gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project
commit = FactoryGirl.create :ci_commit, gl_project: gl_project
expected = 'commit_pusher_email'
allow(commit).to receive(:push_data) { { user_email: expected } }
expect(commit.project_recipients).to eq([expected])
end
it 'should return commit_pusher_email and additional recipients' do
project = FactoryGirl.create :ci_project,
email_add_pusher: true,
email_recipients: 'rec1 rec2'
gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project
commit = FactoryGirl.create :ci_commit, gl_project: gl_project
expected = 'commit_pusher_email'
allow(commit).to receive(:push_data) { { user_email: expected } }
expect(commit.project_recipients).to eq(['rec1', 'rec2', expected])
end
it 'should return recipients' do
project = FactoryGirl.create :ci_project,
email_add_pusher: false,
email_recipients: 'rec1 rec2'
gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project
commit = FactoryGirl.create :ci_commit, gl_project: gl_project
expect(commit.project_recipients).to eq(['rec1', 'rec2'])
end
it 'should return unique recipients only' do
project = FactoryGirl.create :ci_project,
email_add_pusher: true,
email_recipients: 'rec1 rec1 rec2'
gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project
commit = FactoryGirl.create :ci_commit, gl_project: gl_project
expected = 'rec2'
allow(commit).to receive(:push_data) { { user_email: expected } }
expect(commit.project_recipients).to eq(['rec1', 'rec2'])
end
end
end
describe :valid_commit_sha do
context 'commit.sha can not start with 00000000' do
before do
@ -117,23 +65,6 @@ describe Ci::Commit do
end
end
describe :compare? do
subject { commit_with_project.compare? }
context 'if commit.before_sha are not nil' do
it { is_expected.to be_truthy }
end
end
describe :short_sha do
subject { commit.short_before_sha }
it 'has 8 items' do
expect(subject.size).to eq(8)
end
it { expect(commit.before_sha).to start_with(subject) }
end
describe :short_sha do
subject { commit.short_sha }
@ -143,37 +74,86 @@ describe Ci::Commit do
it { expect(commit.sha).to start_with(subject) }
end
describe :create_next_builds do
describe :stage do
subject { commit.stage }
before do
allow(commit).to receive(:config_processor).and_return(config_processor)
@second = FactoryGirl.create :ci_build, commit: commit, name: 'deploy', stage: 'deploy', stage_idx: 1, status: :pending
@first = FactoryGirl.create :ci_build, commit: commit, name: 'test', stage: 'test', stage_idx: 0, status: :pending
end
it "creates builds for next type" do
expect(commit.create_builds).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(2)
it 'returns first running stage' do
is_expected.to eq('test')
end
expect(commit.create_next_builds(nil)).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(4)
context 'first build succeeded' do
before do
@first.update_attributes(status: :success)
end
expect(commit.create_next_builds(nil)).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(5)
it 'returns last running stage' do
is_expected.to eq('deploy')
end
end
expect(commit.create_next_builds(nil)).to be_falsey
context 'all builds succeeded' do
before do
@first.update_attributes(status: :success)
@second.update_attributes(status: :success)
end
it 'returns nil' do
is_expected.to be_nil
end
end
end
describe :create_next_builds do
end
describe :create_builds do
before do
allow(commit).to receive(:config_processor).and_return(config_processor)
let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
def create_builds(trigger_request = nil)
commit.create_builds('master', false, nil, trigger_request)
end
def create_next_builds(trigger_request = nil)
commit.create_next_builds('master', false, nil, trigger_request)
end
it 'creates builds' do
expect(commit.create_builds).to be_truthy
expect(create_builds).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(2)
expect(create_next_builds).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(4)
expect(create_next_builds).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(5)
expect(create_next_builds).to be_falsey
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.reload
expect(commit.builds.size).to eq(2)
expect(create_develop_builds).to be_truthy
commit.builds.reload
expect(commit.builds.size).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
@ -181,40 +161,39 @@ describe Ci::Commit do
let(:trigger_request) { FactoryGirl.create :ci_trigger_request, commit: commit, trigger: trigger }
it 'creates builds' do
expect(commit.create_builds(trigger_request)).to be_truthy
expect(create_builds(trigger_request)).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(2)
end
it 'rebuilds commit' do
expect(commit.create_builds).to be_truthy
expect(create_builds).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(2)
expect(commit.create_builds(trigger_request)).to be_truthy
expect(create_builds(trigger_request)).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(4)
end
it 'creates next builds' do
expect(commit.create_builds(trigger_request)).to be_truthy
expect(create_builds(trigger_request)).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(2)
expect(commit.create_next_builds(trigger_request)).to be_truthy
expect(create_next_builds(trigger_request)).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(4)
end
context 'for [ci skip]' do
before do
commit.push_data[:commits][0][:message] = 'skip this commit [ci skip]'
commit.save
allow(commit).to receive(:git_commit_message) { 'message [ci skip]' }
end
it 'rebuilds commit' do
expect(commit.status).to eq('skipped')
expect(commit.create_builds(trigger_request)).to be_truthy
expect(create_builds(trigger_request)).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(2)
expect(commit.status).to eq('pending')
@ -270,4 +249,59 @@ describe Ci::Commit do
expect(commit.coverage).to be_nil
end
end
describe :should_create_next_builds? do
before do
@build1 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: false, status: :success
@build2 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'develop', tag: false, status: :failed
@build3 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: true, status: :failed
@build4 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: :success
end
context 'for success' do
it 'to create if all succeeded' do
expect(commit.should_create_next_builds?(@build4)).to be_truthy
end
end
context 'for failed' do
before do
@build4.update_attributes(status: :failed)
end
it 'to not create' do
expect(commit.should_create_next_builds?(@build4)).to be_falsey
end
context 'and ignore failures for current' do
before do
@build4.update_attributes(allow_failure: true)
end
it 'to create' do
expect(commit.should_create_next_builds?(@build4)).to be_truthy
end
end
end
context 'for running' do
before do
@build4.update_attributes(status: :running)
end
it 'to not create' do
expect(commit.should_create_next_builds?(@build4)).to be_falsey
end
end
context 'for retried' do
before do
@build5 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: :failed
end
it 'to not create' do
expect(commit.should_create_next_builds?(@build4)).to be_falsey
end
end
end
end

View file

@ -3,70 +3,37 @@ require 'spec_helper'
describe Ci::HipChatMessage do
subject { Ci::HipChatMessage.new(build) }
context "One build" do
let(:commit) { FactoryGirl.create(:ci_commit_with_one_job) }
let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
let(:build) do
commit.create_builds
commit.builds.first
end
let(:build) do
commit.builds.first
end
context 'when build succeeds' do
it 'returns a successful message' do
build.update(status: "success")
context 'when all matrix builds succeed' do
it 'returns a successful message' do
commit.create_builds('master', false, nil)
commit.builds.update_all(status: "success")
commit.reload
expect( subject.status_color ).to eq 'green'
expect( subject.notify? ).to be_falsey
expect( subject.to_s ).to match(/Build '[^']+' #\d+/)
expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./)
end
end
context 'when build fails' do
it 'returns a failure message' do
build.update(status: "failed")
expect( subject.status_color ).to eq 'red'
expect( subject.notify? ).to be_truthy
expect( subject.to_s ).to match(/Build '[^']+' #\d+/)
expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./)
end
expect(subject.status_color).to eq 'green'
expect(subject.notify?).to be_falsey
expect(subject.to_s).to match(/Commit #\d+/)
expect(subject.to_s).to match(/Successful in \d+ second\(s\)\./)
end
end
context "Several builds" do
let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
context 'when at least one matrix build fails' do
it 'returns a failure message' do
commit.create_builds('master', false, nil)
first_build = commit.builds.first
second_build = commit.builds.last
first_build.update(status: "success")
second_build.update(status: "failed")
let(:build) do
commit.builds.first
end
context 'when all matrix builds succeed' do
it 'returns a successful message' do
commit.create_builds
commit.builds.update_all(status: "success")
commit.reload
expect( subject.status_color ).to eq 'green'
expect( subject.notify? ).to be_falsey
expect( subject.to_s ).to match(/Commit #\d+/)
expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./)
end
end
context 'when at least one matrix build fails' do
it 'returns a failure message' do
commit.create_builds
first_build = commit.builds.first
second_build = commit.builds.last
first_build.update(status: "success")
second_build.update(status: "failed")
expect( subject.status_color ).to eq 'red'
expect( subject.notify? ).to be_truthy
expect( subject.to_s ).to match(/Commit #\d+/)
expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./)
end
expect(subject.status_color).to eq 'red'
expect(subject.notify?).to be_truthy
expect(subject.to_s).to match(/Commit #\d+/)
expect(subject.to_s).to match(/Failed in \d+ second\(s\)\./)
end
end
end

View file

@ -29,12 +29,13 @@ describe Ci::MailService do
describe 'Sends email for' do
let(:mail) { Ci::MailService.new }
let(:user) { User.new(notification_email: 'git@example.com')}
describe 'failed build' do
let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true) }
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) }
let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit, user: user) }
before do
allow(mail).to receive_messages(
@ -57,7 +58,7 @@ describe Ci::MailService do
let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true, email_only_broken_builds: false) }
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) }
let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
before do
allow(mail).to receive_messages(
@ -85,7 +86,7 @@ describe Ci::MailService do
end
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) }
let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
before do
allow(mail).to receive_messages(
@ -114,7 +115,7 @@ describe Ci::MailService do
end
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) }
let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
before do
allow(mail).to receive_messages(
@ -143,7 +144,7 @@ describe Ci::MailService do
end
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) }
let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
before do
allow(mail).to receive_messages(
@ -166,7 +167,7 @@ describe Ci::MailService do
end
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) }
let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit, user: user) }
before do
allow(mail).to receive_messages(

View file

@ -3,80 +3,41 @@ require 'spec_helper'
describe Ci::SlackMessage do
subject { Ci::SlackMessage.new(commit) }
context "One build" do
let(:commit) { FactoryGirl.create(:ci_commit_with_one_job) }
let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
let(:build) do
commit.create_builds
commit.builds.first
end
context 'when all matrix builds succeeded' do
let(:color) { 'good' }
context 'when build succeeded' do
let(:color) { 'good' }
it 'returns a message with success' do
commit.create_builds('master', false, nil)
commit.builds.update_all(status: "success")
commit.reload
it 'returns a message with succeeded build' do
build.update(status: "success")
expect(subject.color).to eq(color)
expect(subject.fallback).to include('Build')
expect(subject.fallback).to include("\##{build.id}")
expect(subject.fallback).to include('succeeded')
expect(subject.attachments.first[:fields]).to be_empty
end
end
context 'when build failed' do
let(:color) { 'danger' }
it 'returns a message with failed build' do
build.update(status: "failed")
expect(subject.color).to eq(color)
expect(subject.fallback).to include('Build')
expect(subject.fallback).to include("\##{build.id}")
expect(subject.fallback).to include('failed')
expect(subject.attachments.first[:fields]).to be_empty
end
expect(subject.color).to eq(color)
expect(subject.fallback).to include('Commit')
expect(subject.fallback).to include("\##{commit.id}")
expect(subject.fallback).to include('succeeded')
expect(subject.attachments.first[:fields]).to be_empty
end
end
context "Several builds" do
let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
context 'when one of matrix builds failed' do
let(:color) { 'danger' }
context 'when all matrix builds succeeded' do
let(:color) { 'good' }
it 'returns a message with information about failed build' do
commit.create_builds('master', false, nil)
first_build = commit.builds.first
second_build = commit.builds.last
first_build.update(status: "success")
second_build.update(status: "failed")
it 'returns a message with success' do
commit.create_builds
commit.builds.update_all(status: "success")
commit.reload
expect(subject.color).to eq(color)
expect(subject.fallback).to include('Commit')
expect(subject.fallback).to include("\##{commit.id}")
expect(subject.fallback).to include('succeeded')
expect(subject.attachments.first[:fields]).to be_empty
end
end
context 'when one of matrix builds failed' do
let(:color) { 'danger' }
it 'returns a message with information about failed build' do
commit.create_builds
first_build = commit.builds.first
second_build = commit.builds.last
first_build.update(status: "success")
second_build.update(status: "failed")
expect(subject.color).to eq(color)
expect(subject.fallback).to include('Commit')
expect(subject.fallback).to include("\##{commit.id}")
expect(subject.fallback).to include('failed')
expect(subject.attachments.first[:fields].size).to eq(1)
expect(subject.attachments.first[:fields].first[:title]).to eq(second_build.name)
expect(subject.attachments.first[:fields].first[:value]).to include("\##{second_build.id}")
end
expect(subject.color).to eq(color)
expect(subject.fallback).to include('Commit')
expect(subject.fallback).to include("\##{commit.id}")
expect(subject.fallback).to include('failed')
expect(subject.attachments.first[:fields].size).to eq(1)
expect(subject.attachments.first[:fields].first[:title]).to eq(second_build.name)
expect(subject.attachments.first[:fields].first[:value]).to include("\##{second_build.id}")
end
end
end

View file

@ -39,8 +39,8 @@ describe GitlabCiService do
end
describe :build_page do
it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://localhost/ci/projects/#{@ci_project.id}/refs/master/commits/2ab7834c")}
it { expect(@service.build_page("issue#2", 'master')).to eq("http://localhost/ci/projects/#{@ci_project.id}/refs/master/commits/issue%232")}
it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://localhost/ci/projects/#{@ci_project.id}/commits/2ab7834c")}
it { expect(@service.build_page("issue#2", 'master')).to eq("http://localhost/ci/projects/#{@ci_project.id}/commits/issue%232")}
end
describe "execute" do
@ -48,8 +48,8 @@ describe GitlabCiService do
let(:project) { create(:project, name: 'project') }
let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) }
it "calls ci_yaml_file" do
expect(@service).to receive(:ci_yaml_file).with(push_sample_data[:checkout_sha])
it "calls CreateCommitService" do
expect_any_instance_of(Ci::CreateCommitService).to receive(:execute).with(@ci_project, user, push_sample_data)
@service.execute(push_sample_data)
end

View file

@ -7,6 +7,10 @@ describe Ci::API::API do
let(:project) { FactoryGirl.create(:ci_project) }
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
before do
stub_ci_commit_to_return_yaml_file
end
describe "Builds API for runners" do
let(:shared_runner) { FactoryGirl.create(:ci_runner, token: "SharedRunner") }
let(:shared_project) { FactoryGirl.create(:ci_project, name: "SharedProject") }
@ -19,7 +23,7 @@ describe Ci::API::API do
describe "POST /builds/register" do
it "should start a build" do
commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
commit.create_builds
commit.create_builds('master', false, nil)
build = commit.builds.first
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
@ -55,7 +59,7 @@ describe Ci::API::API do
it "returns options" do
commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
commit.create_builds
commit.create_builds('master', false, nil)
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
@ -65,7 +69,7 @@ describe Ci::API::API do
it "returns variables" do
commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
commit.create_builds
commit.create_builds('master', false, nil)
project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value")
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
@ -82,7 +86,7 @@ describe Ci::API::API do
commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger)
commit.create_builds(trigger_request)
commit.create_builds('master', false, nil, trigger_request)
project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value")
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }

View file

@ -44,8 +44,7 @@ describe Ci::API::API, 'Commits' do
"email" => "jordi@softcatala.org",
}
}
],
ci_yaml_file: gitlab_ci_yaml
]
}
end

View file

@ -5,8 +5,8 @@ describe Ci::API::API do
describe 'POST /projects/:project_id/refs/:ref/trigger' do
let!(:trigger_token) { 'secure token' }
let!(:project) { FactoryGirl.create(:ci_project) }
let!(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
let!(:gl_project) { FactoryGirl.create(:project) }
let!(:project) { FactoryGirl.create(:ci_project, gl_project: gl_project) }
let!(:project2) { FactoryGirl.create(:ci_project) }
let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) }
let(:options) do
@ -15,6 +15,10 @@ describe Ci::API::API do
}
end
before do
stub_ci_commit_to_return_yaml_file
end
context 'Handles errors' do
it 'should return bad request if token is missing' do
post ci_api("/projects/#{project.id}/refs/master/trigger")
@ -33,15 +37,13 @@ describe Ci::API::API do
end
context 'Have a commit' do
before do
@commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
end
let(:commit) { project.commits.last }
it 'should create builds' do
post ci_api("/projects/#{project.id}/refs/master/trigger"), options
expect(response.status).to eq(201)
@commit.builds.reload
expect(@commit.builds.size).to eq(2)
commit.builds.reload
expect(commit.builds.size).to eq(2)
end
it 'should return bad request with no builds created if there\'s no commit for that ref' do
@ -70,8 +72,8 @@ describe Ci::API::API do
it 'create trigger request with variables' do
post ci_api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: variables)
expect(response.status).to eq(201)
@commit.builds.reload
expect(@commit.builds.first.trigger_request.variables).to eq(variables)
commit.builds.reload
expect(commit.builds.first.trigger_request.variables).to eq(variables)
end
end
end

View file

@ -7,7 +7,7 @@ describe "Commits" do
describe "GET /:project/refs/:ref_name/commits/:id/status.json" do
before do
get status_ci_project_ref_commits_path(@commit.project, @commit.ref, @commit.sha), format: :json
get status_ci_project_commits_path(@commit.project, @commit.sha), format: :json
end
it { expect(response.status).to eq(200) }

View file

@ -4,15 +4,19 @@ module Ci
describe CreateCommitService do
let(:service) { CreateCommitService.new }
let(:project) { FactoryGirl.create(:ci_project) }
let(:user) { nil }
before do
stub_ci_commit_to_return_yaml_file
end
describe :execute do
context 'valid params' do
let(:commit) do
service.execute(project,
service.execute(project, user,
ref: 'refs/heads/master',
before: '00000000',
after: '31das312',
ci_yaml_file: gitlab_ci_yaml,
commits: [ { message: "Message" } ]
)
end
@ -26,11 +30,10 @@ module Ci
context "skip tag if there is no build for it" do
it "creates commit if there is appropriate job" do
result = service.execute(project,
result = service.execute(project, user,
ref: 'refs/tags/0_1',
before: '00000000',
after: '31das312',
ci_yaml_file: gitlab_ci_yaml,
commits: [ { message: "Message" } ]
)
expect(result).to be_persisted
@ -38,12 +41,12 @@ module Ci
it "creates commit if there is no appropriate job but deploy job has right ref setting" do
config = YAML.dump({ deploy: { deploy: "ls", only: ["0_1"] } })
stub_ci_commit_yaml_file(config)
result = service.execute(project,
result = service.execute(project, user,
ref: 'refs/heads/0_1',
before: '00000000',
after: '31das312',
ci_yaml_file: config,
commits: [ { message: "Message" } ]
)
expect(result).to be_persisted
@ -51,11 +54,11 @@ module Ci
end
it 'fails commits without .gitlab-ci.yml' do
result = service.execute(project,
stub_ci_commit_yaml_file(nil)
result = service.execute(project, user,
ref: 'refs/heads/0_1',
before: '00000000',
after: '31das312',
ci_yaml_file: config,
commits: [ { message: 'Message' } ]
)
expect(result).to be_persisted
@ -64,41 +67,46 @@ module Ci
end
describe :ci_skip? do
let(:message) { "some message[ci skip]" }
before do
allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message }
end
it "skips builds creation if there is [ci skip] tag in commit message" do
commits = [{ message: "some message[ci skip]" }]
commit = service.execute(project,
commits = [{ message: message }]
commit = service.execute(project, user,
ref: 'refs/tags/0_1',
before: '00000000',
after: '31das312',
commits: commits,
ci_yaml_file: gitlab_ci_yaml
commits: commits
)
expect(commit.builds.any?).to be false
expect(commit.status).to eq("skipped")
end
it "does not skips builds creation if there is no [ci skip] tag in commit message" do
commits = [{ message: "some message" }]
allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { "some message" }
commit = service.execute(project,
commits = [{ message: "some message" }]
commit = service.execute(project, user,
ref: 'refs/tags/0_1',
before: '00000000',
after: '31das312',
commits: commits,
ci_yaml_file: gitlab_ci_yaml
commits: commits
)
expect(commit.builds.first.name).to eq("staging")
end
it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do
commits = [{ message: "some message[ci skip]" }]
commit = service.execute(project,
stub_ci_commit_yaml_file('invalid: file')
commits = [{ message: message }]
commit = service.execute(project, user,
ref: 'refs/tags/0_1',
before: '00000000',
after: '31das312',
commits: commits,
ci_yaml_file: "invalid: file"
commits: commits
)
expect(commit.builds.any?).to be false
expect(commit.status).to eq("skipped")
@ -106,35 +114,36 @@ module Ci
end
it "skips build creation if there are already builds" do
allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { gitlab_ci_yaml }
commits = [{ message: "message" }]
commit = service.execute(project,
commit = service.execute(project, user,
ref: 'refs/heads/master',
before: '00000000',
after: '31das312',
commits: commits,
ci_yaml_file: gitlab_ci_yaml
commits: commits
)
expect(commit.builds.count(:all)).to eq(2)
commit = service.execute(project,
commit = service.execute(project, user,
ref: 'refs/heads/master',
before: '00000000',
after: '31das312',
commits: commits,
ci_yaml_file: gitlab_ci_yaml
commits: commits
)
expect(commit.builds.count(:all)).to eq(2)
end
it "creates commit with failed status if yaml is invalid" do
stub_ci_commit_yaml_file('invalid: file')
commits = [{ message: "some message" }]
commit = service.execute(project,
commit = service.execute(project, user,
ref: 'refs/tags/0_1',
before: '00000000',
after: '31das312',
commits: commits,
ci_yaml_file: "invalid: file"
commits: commits
)
expect(commit.status).to eq("failed")

View file

@ -2,20 +2,20 @@ require 'spec_helper'
describe Ci::CreateTriggerRequestService do
let(:service) { Ci::CreateTriggerRequestService.new }
let(:project) { FactoryGirl.create :ci_project }
let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
let(:trigger) { FactoryGirl.create :ci_trigger, project: project }
let(:gl_project) { create(:project) }
let(:project) { create(:ci_project, gl_project: gl_project) }
let(:trigger) { create(:ci_trigger, project: project) }
before do
stub_ci_commit_to_return_yaml_file
end
describe :execute do
context 'valid params' do
subject { service.execute(project, trigger, 'master') }
before do
@commit = FactoryGirl.create :ci_commit, gl_project: gl_project
end
it { expect(subject).to be_kind_of(Ci::TriggerRequest) }
it { expect(subject.commit).to eq(@commit) }
it { expect(subject.builds.first).to be_kind_of(Ci::Build) }
end
context 'no commit for ref' do
@ -28,26 +28,11 @@ describe Ci::CreateTriggerRequestService do
subject { service.execute(project, trigger, 'master') }
before do
FactoryGirl.create :ci_commit_without_jobs, gl_project: gl_project
stub_ci_commit_yaml_file('{}')
FactoryGirl.create :ci_commit, gl_project: gl_project
end
it { expect(subject).to be_nil }
end
context 'for multiple commits' do
subject { service.execute(project, trigger, 'master') }
before do
@commit1 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: gl_project
@commit2 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: gl_project
@commit3 = FactoryGirl.create :ci_commit, committed_at: 3.hour.ago, gl_project: gl_project
end
context 'retries latest one' do
it { expect(subject).to be_kind_of(Ci::TriggerRequest) }
it { expect(subject).to be_persisted }
it { expect(subject.commit).to eq(@commit2) }
end
end
end
end

View file

@ -43,4 +43,8 @@ RSpec.configure do |config|
end
end
FactoryGirl::SyntaxRunner.class_eval do
include RSpec::Mocks::ExampleMethods
end
ActiveRecord::Migration.maintain_test_schema!

View file

@ -13,6 +13,14 @@ module StubGitlabCalls
allow_any_instance_of(Network).to receive(:projects) { project_hash_array }
end
def stub_ci_commit_to_return_yaml_file
stub_ci_commit_yaml_file(gitlab_ci_yaml)
end
def stub_ci_commit_yaml_file(ci_yaml)
allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { ci_yaml }
end
private
def gitlab_url