summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKamil Trzcinski <ayufan@ayufan.eu>2016-03-31 19:51:28 +0200
committerKamil Trzcinski <ayufan@ayufan.eu>2016-04-07 13:28:40 +0200
commit57431a3cf6bc1deeb6c9b50ed8b5516b8c3b575f (patch)
tree91572d63ecb97b67561ace78f32f62679179b28f
parenta918e8bf277418048776a5d9c34a64b39f4e56f3 (diff)
downloadgitlab-ce-57431a3cf6bc1deeb6c9b50ed8b5516b8c3b575f.tar.gz
WIP
-rw-r--r--app/controllers/projects/ci_commits_controller.rb43
-rw-r--r--app/controllers/projects/commit_controller.rb7
-rw-r--r--app/controllers/projects/merge_requests_controller.rb3
-rw-r--r--app/helpers/gitlab_routing_helper.rb4
-rw-r--r--app/models/ci/build.rb4
-rw-r--r--app/models/ci/commit.rb115
-rw-r--r--app/models/commit.rb10
-rw-r--r--app/models/commit_status.rb28
-rw-r--r--app/models/concerns/ci_status.rb50
-rw-r--r--app/models/merge_request.rb2
-rw-r--r--app/models/project.rb8
-rw-r--r--app/services/ci/create_trigger_request_service.rb2
-rw-r--r--app/services/create_commit_builds_service.rb9
-rw-r--r--app/views/layouts/nav/_project.html.haml8
-rw-r--r--app/views/projects/_last_commit.html.haml9
-rw-r--r--app/views/projects/ci/builds/_build.html.haml15
-rw-r--r--app/views/projects/ci/commits/_commit.html.haml141
-rw-r--r--app/views/projects/ci_commits/_header_title.html.haml1
-rw-r--r--app/views/projects/ci_commits/index.html.haml51
-rw-r--r--app/views/projects/ci_commits/show.html.haml216
-rw-r--r--app/views/projects/commit/_builds.html.haml69
-rw-r--r--app/views/projects/commit/_ci_commit.html.haml69
-rw-r--r--app/views/projects/commit/_commit_box.html.haml8
-rw-r--r--app/views/projects/commit/show.html.haml2
-rw-r--r--app/views/projects/commits/_commit.html.haml7
-rw-r--r--app/views/projects/issues/_related_branches.html.haml2
-rw-r--r--app/views/shared/projects/_project.html.haml7
-rw-r--r--config/routes.rb2
-rw-r--r--db/migrate/20160331153918_add_fields_to_ci_commit.rb7
-rw-r--r--lib/api/commit_statuses.rb2
30 files changed, 718 insertions, 183 deletions
diff --git a/app/controllers/projects/ci_commits_controller.rb b/app/controllers/projects/ci_commits_controller.rb
new file mode 100644
index 00000000000..e4797833b06
--- /dev/null
+++ b/app/controllers/projects/ci_commits_controller.rb
@@ -0,0 +1,43 @@
+class Projects::CiCommitsController < Projects::ApplicationController
+ before_action :ci_commit, except: [:index]
+ before_action :authorize_read_build!
+ layout 'project'
+
+ def index
+ @scope = params[:scope]
+ @all_commits = project.ci_commits
+ @commits = @all_commits.order(id: :desc)
+ @commits =
+ case @scope
+ when 'latest'
+ @commits
+ when 'branches'
+ refs = project.repository.branches.map(&:name)
+ ids = @all_commits.where(ref: refs).group(:ref).select('max(id)')
+ @commits.where(id: ids)
+ when 'tags'
+ refs = project.repository.tags.map(&:name)
+ ids = @all_commits.where(ref: refs).group(:ref).select('max(id)')
+ @commits.where(id: ids)
+ else
+ @commits
+ end
+ @commits = @commits.page(params[:page]).per(30)
+ end
+
+ def show
+ @commit = @ci_commit.commit
+ @builds = @ci_commit.builds
+ @statuses = @ci_commit.statuses
+
+ respond_to do |format|
+ format.html
+ end
+ end
+
+ private
+
+ def ci_commit
+ @ci_commit ||= project.ci_commits.find_by!(id: params[:id])
+ end
+end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 576fa3cedb2..ef20281e82f 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -94,8 +94,8 @@ class Projects::CommitController < Projects::ApplicationController
@commit ||= @project.commit(params[:id])
end
- def ci_commit
- @ci_commit ||= project.ci_commit(commit.sha)
+ def ci_commits
+ @ci_commits ||= project.ci_commits.where(sha: commit.sha)
end
def define_show_vars
@@ -108,7 +108,8 @@ class Projects::CommitController < Projects::ApplicationController
@diff_refs = [commit.parent || commit, commit]
@notes_count = commit.notes.count
- @statuses = ci_commit.statuses if ci_commit
+ @statuses = ci_commits.statuses
+ @builds = @statuses
end
def assign_revert_commit_vars
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 49064f5d505..ed5a1901056 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -118,6 +118,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@diffs = @merge_request.compare.diffs(diff_options) if @merge_request.compare
@ci_commit = @merge_request.ci_commit
+ @ci_commits = [@ci_commit].compact
@statuses = @ci_commit.statuses if @ci_commit
@note_counts = Note.where(commit_id: @commits.map(&:id)).
@@ -308,6 +309,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request_diff = @merge_request.merge_request_diff
@ci_commit = @merge_request.ci_commit
+ @ci_commits = [@ci_commit].compact
@statuses = @ci_commit.statuses if @ci_commit
if @merge_request.locked_long_ago?
@@ -318,6 +320,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def define_widget_vars
@ci_commit = @merge_request.ci_commit
+ @ci_commits = [@ci_commit].compact
closes_issues
end
diff --git a/app/helpers/gitlab_routing_helper.rb b/app/helpers/gitlab_routing_helper.rb
index f3fddef01cb..e4580eea524 100644
--- a/app/helpers/gitlab_routing_helper.rb
+++ b/app/helpers/gitlab_routing_helper.rb
@@ -25,6 +25,10 @@ module GitlabRoutingHelper
namespace_project_commits_path(project.namespace, project, @ref || project.repository.root_ref)
end
+ def project_ci_commits_path(project, *args)
+ namespace_project_ci_commits_path(project.namespace, project, *args)
+ end
+
def project_builds_path(project, *args)
namespace_project_builds_path(project.namespace, project, *args)
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 7d33838044b..c99aeff6f1c 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -126,12 +126,12 @@ module Ci
end
def retried?
- !self.commit.latest_statuses_for_ref(self.ref).include?(self)
+ !self.commit.latest.include?(self)
end
def depends_on_builds
# 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
latest_builds.where('stage_idx < ?', stage_idx)
diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb
index f4cf7034b14..cd9ce0a283d 100644
--- a/app/models/ci/commit.rb
+++ b/app/models/ci/commit.rb
@@ -19,19 +19,54 @@
module Ci
class Commit < ActiveRecord::Base
extend Ci::Model
+ include CiStatus
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
has_many :statuses, class_name: 'CommitStatus'
has_many :builds, class_name: 'Ci::Build'
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
+ scope :running, -> { where(status: 'running') }
+ 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]) }
+
validates_presence_of :sha
+ validates_presence_of :before_sha
+ validates_presence_of :ref
validate :valid_commit_sha
+ # Make sure that status is saved
+ before_save :status
+
+ def ignored?
+ false
+ end
+
def self.truncate_sha(sha)
sha[0...8]
end
+ def self.retried
+ all.map { |commit| commit.retried }.flatten
+ end
+
+ def self.statuses
+ CommitStatus.where(commit: all)
+ end
+
+ def self.builds
+ Ci::Build.where(commit: all)
+ end
+
+ def self.stages
+ CommitStatus.where(commit: all)
+ .group(:stage, :stage_idx).order(:stage_idx)
+ .pluck(:stage, :stage_idx).map(&:first)
+ end
+
def to_param
sha
end
@@ -68,9 +103,16 @@ module Ci
nil
end
- def stage
- running_or_pending = statuses.latest.running_or_pending.ordered
- running_or_pending.first.try(:stage)
+ def origin_ref
+ ref
+ end
+
+ def tag?
+ Gitlab::Git::tag_ref?(origin_ref)
+ end
+
+ def branch?
+ Gitlab::Git::branch_ref?(origin_ref)
end
def create_builds(ref, tag, user, trigger_request = nil)
@@ -101,16 +143,8 @@ module Ci
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 }
+ def latest
+ statuses.latest
end
def matrix_builds(build = nil)
@@ -124,52 +158,39 @@ module Ci
end
def status
- if yaml_errors.present?
- return 'failed'
- end
-
- @status ||= Ci::Status.get_status(latest_statuses)
- end
-
- def pending?
- status == 'pending'
+ read_attribute(:status) || update_status
end
- def running?
- status == 'running'
- end
-
- def success?
- status == 'success'
- end
-
- def failed?
- status == 'failed'
- end
-
- def canceled?
- status == 'canceled'
+ def duration
+ duration_array = latest.map(&:duration).compact
+ duration_array.reduce(:+).to_i
end
- def active?
- running? || pending?
+ def started_at
+ read_attribute(:started_at) || update_started_at
end
- def complete?
- canceled? || success? || failed?
+ def finished_at
+ read_attribute(:finished_at) || update_finished_at
end
- def duration
- duration_array = statuses.map(&:duration).compact
- duration_array.reduce(:+).to_i
+ def update_status
+ status =
+ if yaml_errors.present?
+ 'failed'
+ else
+ latest.status
+ end
end
- def started_at
- @started_at ||= statuses.order('started_at ASC').first.try(:started_at)
+ def update_started_at
+ started_at =
+ statuses.order(:id).first.try(:started_at)
end
- def finished_at
- @finished_at ||= statuses.order('finished_at DESC').first.try(:finished_at)
+ def update_finished_at
+ finished_at =
+ statuses.order(id: :desc).first.try(:finished_at)
end
def coverage
diff --git a/app/models/commit.rb b/app/models/commit.rb
index d09876a07d9..89331768159 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -10,6 +10,9 @@ class Commit
attr_mentionable :safe_message, pipeline: :single_line
participant :author, :committer, :notes
+ alias_attribute :sha, :id
+ alias_attribute :short_sha, :short_id
+
attr_accessor :project
DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines]
@@ -209,12 +212,13 @@ class Commit
@raw.short_id(7)
end
- def ci_commit
- project.ci_commit(sha)
+ def ci_commits
+ @ci_commits ||= project.ci_commits.where(sha: sha)
end
def status
- ci_commit.try(:status) || :not_found
+ return @status if defined?(@status)
+ @status ||= ci_commits.status
end
def revert_branch_name
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 3377a85a55a..3a059f01d49 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -33,6 +33,8 @@
#
class CommitStatus < ActiveRecord::Base
+ include CiStatus
+
self.table_name = 'ci_builds'
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
@@ -40,7 +42,6 @@ class CommitStatus < ActiveRecord::Base
belongs_to :user
validates :commit, presence: true
- validates :status, inclusion: { in: %w(pending running failed success canceled) }
validates_presence_of :name
@@ -86,32 +87,9 @@ class CommitStatus < ActiveRecord::Base
after_transition [:pending, :running] => :success do |commit_status|
MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.commit.project, nil).trigger(commit_status)
end
-
- state :pending, value: 'pending'
- state :running, value: 'running'
- state :failed, value: 'failed'
- state :success, value: 'success'
- state :canceled, value: 'canceled'
- end
-
- delegate :sha, :short_sha, to: :commit, prefix: false
-
- # TODO: this should be removed with all references
- def before_sha
- Gitlab::Git::BLANK_SHA
end
- def started?
- !pending? && !canceled? && started_at
- end
-
- def active?
- running? || pending?
- end
-
- def complete?
- canceled? || success? || failed?
- end
+ delegate :before_sha, :sha, :short_sha, to: :commit, prefix: false
def ignored?
allow_failure? && (failed? || canceled?)
diff --git a/app/models/concerns/ci_status.rb b/app/models/concerns/ci_status.rb
new file mode 100644
index 00000000000..494db025048
--- /dev/null
+++ b/app/models/concerns/ci_status.rb
@@ -0,0 +1,50 @@
+module CiStatus
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ def status
+ if all.none?
+ nil
+ elsif all.all? { |status| status.success? || status.ignored? }
+ 'success'
+ elsif all.all?(&:pending?)
+ 'pending'
+ elsif all.any?(&:running?) || all.any?(&:pending?)
+ 'running'
+ elsif all.all?(&:canceled?)
+ 'canceled'
+ else
+ 'failed'
+ end
+ end
+
+ def duration
+ duration_array = all.map(&:duration).compact
+ duration_array.reduce(:+).to_i
+ end
+ end
+
+ included do
+ validates :status, inclusion: { in: %w(pending running failed success canceled) }
+
+ 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'
+ end
+ end
+
+ def started?
+ !pending? && !canceled? && started_at
+ end
+
+ def active?
+ running? || pending?
+ end
+
+ def complete?
+ canceled? || success? || failed?
+ end
+end \ No newline at end of file
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index bf185cb5dd8..33869e215c9 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -589,7 +589,7 @@ class MergeRequest < ActiveRecord::Base
end
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
def diff_refs
diff --git a/app/models/project.rb b/app/models/project.rb
index 3e1f04b4158..b9d589a4594 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -919,12 +919,12 @@ class Project < ActiveRecord::Base
!namespace.share_with_group_lock
end
- def ci_commit(sha)
- ci_commits.find_by(sha: sha)
+ def ci_commit(sha, ref)
+ ci_commits.find_by(sha: sha, ref: ref)
end
- def ensure_ci_commit(sha)
- ci_commit(sha) || ci_commits.create(sha: sha)
+ def ensure_ci_commit(sha, ref)
+ ci_commit(sha, ref) || ci_commits.create(sha: sha, ref: ref)
end
def enable_ci
diff --git a/app/services/ci/create_trigger_request_service.rb b/app/services/ci/create_trigger_request_service.rb
index b3dfc707221..483cc0a72a3 100644
--- a/app/services/ci/create_trigger_request_service.rb
+++ b/app/services/ci/create_trigger_request_service.rb
@@ -7,7 +7,7 @@ module Ci
# check if ref is tag
tag = project.repository.find_tag(ref).present?
- ci_commit = project.ensure_ci_commit(commit.sha)
+ ci_commit = project.ensure_ci_commit(commit.sha, ref)
trigger_request = trigger.trigger_requests.create!(
variables: variables,
diff --git a/app/services/create_commit_builds_service.rb b/app/services/create_commit_builds_service.rb
index 69d5c42a877..e7e1134ce4b 100644
--- a/app/services/create_commit_builds_service.rb
+++ b/app/services/create_commit_builds_service.rb
@@ -2,6 +2,7 @@ class CreateCommitBuildsService
def execute(project, user, params)
return false unless project.builds_enabled?
+ before_sha = params[:checkout_sha] || params[:before]
sha = params[:checkout_sha] || params[:after]
origin_ref = params[:ref]
@@ -10,15 +11,16 @@ class CreateCommitBuildsService
end
ref = Gitlab::Git.ref_name(origin_ref)
+ tag = Gitlab::Git.tag_ref?(origin_ref)
# Skip branch removal
if sha == Gitlab::Git::BLANK_SHA
return false
end
- commit = project.ci_commit(sha)
+ commit = project.ci_commit(sha, ref)
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
unless commit.ci_yaml_file
@@ -32,8 +34,7 @@ class CreateCommitBuildsService
# Skip creating builds for commits that have [ci skip]
unless commit.skip_ci?
# Create builds for commit
- tag = Gitlab::Git.tag_ref?(origin_ref)
- commit.create_builds(ref, tag, user)
+ commit.create_builds(user)
end
commit
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index d0f82b5f57f..862a3e087f1 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -24,6 +24,14 @@
Commits
- if project_nav_tab? :builds
+ = nav_link(controller: %w(ci_commits)) do
+ = link_to project_ci_commits_path(@project), title: 'CI', class: 'shortcuts-ci' do
+ = icon('cubes fw')
+ %span
+ CI
+ %span.count.ci_counter= number_with_delimiter(@project.ci_commits.running_or_pending.count(:all))
+
+ - if project_nav_tab? :builds
= nav_link(controller: %w(builds)) do
= link_to project_builds_path(@project), title: 'Builds', class: 'shortcuts-builds' do
= icon('cubes fw')
diff --git a/app/views/projects/_last_commit.html.haml b/app/views/projects/_last_commit.html.haml
index 386d72e7787..47d9239eaa2 100644
--- a/app/views/projects/_last_commit.html.haml
+++ b/app/views/projects/_last_commit.html.haml
@@ -1,9 +1,8 @@
.project-last-commit
- - ci_commit = project.ci_commit(commit.sha)
- - if ci_commit
- = link_to ci_status_path(ci_commit), class: "ci-status ci-#{ci_commit.status}" do
- = ci_status_icon(ci_commit)
- = ci_status_label(ci_commit)
+ - if commit.status
+ = link_to ci_status_path(commit), class: "ci-status ci-#{commit.status}" do
+ = ci_status_icon(commit)
+ = ci_status_label(commit)
= 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"
diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml
index 2cf9115e4dd..c109b6cdd20 100644
--- a/app/views/projects/ci/builds/_build.html.haml
+++ b/app/views/projects/ci/builds/_build.html.haml
@@ -1,4 +1,4 @@
-%tr.build
+%tr.build{style:"text-decoration: #{if defined?(retried) && retried then "line-through" else "inherit" end}"}
%td.status
- if can?(current_user, :read_build, build)
= ci_status_with_icon(build.status, namespace_project_build_url(build.project.namespace, build.project, build))
@@ -19,11 +19,12 @@
%td
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace"
- %td
- - if build.ref
- = link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref)
- - else
- .light none
+ - if !defined?(ref) || ref
+ %td
+ - if build.ref
+ = link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref)
+ - else
+ .light none
- if defined?(runner) && runner
%td
@@ -48,6 +49,8 @@
%span.label.label-info triggered
- if build.try(:allow_failure)
%span.label.label-danger allowed to fail
+ - if defined?(retried) && retried
+ %span.label.label-warning retried
%td.duration
- if build.duration
diff --git a/app/views/projects/ci/commits/_commit.html.haml b/app/views/projects/ci/commits/_commit.html.haml
new file mode 100644
index 00000000000..671c5abcbed
--- /dev/null
+++ b/app/views/projects/ci/commits/_commit.html.haml
@@ -0,0 +1,141 @@
+%tr.commit
+ %td.commit-link
+ - if can?(current_user, :read_commit, commit)
+ = link_to namespace_project_commit_url(commit.project.namespace, commit.project, commit) do
+ %strong ##{commit.id}
+ - else
+ %strong ##{commit.id}
+
+ %td.status
+ %div
+ - if can?(current_user, :read_commit, commit)
+ = ci_status_with_icon(commit.status, namespace_project_commit_url(commit.project.namespace, commit.project, commit))
+ - else
+ = ci_status_with_icon(commit.status)
+
+ %td
+ %div
+ - if commit.ref
+ = link_to commit.ref, namespace_project_commits_path(commit.project.namespace, commit.project, commit.ref)
+ &nbsp;
+ - if commit.tag?
+ %span.label.label-primary tag
+ - if commit.branch?
+ %span.label.label-primary branch
+ - if commit.trigger_requests.any?
+ %span.label.label-primary triggered
+ - if commit.yaml_errors.present?
+ %span.label.label-danger.has-tooltip(title="#{commit.yaml_errors}") yaml invalid
+ - if commit.builds.any?(&:stuck?)
+ %span.label.label-warning stuck
+
+ - if commit_data = commit.commit_data
+ = render 'projects/branches/commit', commit: commit_data, project: @project
+ - else
+ %p
+ Cant find HEAD commit for this branch
+
+ %td
+ %div
+ Duration:
+ &nbsp;
+ - if commit.started_at && commit.finished_at
+ #{duration_in_words(commit.finished_at, commit.started_at)}
+ - else
+ \-
+ %p
+ Finished:
+ &nbsp;
+ - if commit.finished_at
+ #{time_ago_with_tooltip(commit.finished_at)}
+ - else
+ \-
+
+ %td.content
+ .controls.hidden-xs
+ = link_to project_builds_path(commit.project), class: 'btn btn-grouped btn-xs' do
+ = icon('cubes fw')
+ Details
+
+ - @project = commit.project
+ - ref = 'master'
+ %span.btn-group.btn-grouped
+ %a.btn.btn-default.dropdown-toggle{ 'data-toggle' => 'dropdown' }
+ %span.caret
+ %span.sr-only
+ Select Archive Format
+ %ul.col-xs-10.dropdown-menu.dropdown-menu-align-right{ role: 'menu' }
+ %li
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), rel: 'nofollow' do
+ %i.fa.fa-download
+ %span Download zip
+ %li
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar.gz'), rel: 'nofollow' do
+ %i.fa.fa-download
+ %span Download tar.gz
+ %li
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar.bz2'), rel: 'nofollow' do
+ %i.fa.fa-download
+ %span Download tar.bz2
+ %li
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'tar'), rel: 'nofollow' do
+ %i.fa.fa-download
+ %span Download tar
+-#%tr.commit
+-# %td.status
+-# - if can?(current_user, :read_commit, commit)
+-# = ci_status_with_icon(commit.status, namespace_project_commit_url(commit.project.namespace, commit.project, commit))
+-# - else
+-# = ci_status_with_icon(commit.status)
+-#
+-# %td.commit-link
+-# - if can?(current_user, :read_commit, commit)
+-# = link_to namespace_project_commit_url(commit.project.namespace, commit.project, commit) do
+-# %strong ##{commit.id}
+-# - else
+-# %strong ##{commit.id}
+-#
+-# - if defined?(commit_sha) && commit_sha
+-# %td
+-# = link_to commit.short_sha, namespace_project_commit_path(commit.project.namespace, commit.project, commit.sha), class: "monospace"
+-#
+-# %td
+-# - if commit.ref
+-# = link_to commit.ref, namespace_project_commits_path(commit.project.namespace, commit.project, commit.ref)
+-# - else
+-# .light none
+-#
+-# %td
+-# = commit.git_commit_message
+-#
+-# %td.duration
+-# - if commit.started_at && commit.finished_at
+-# #{duration_in_words(commit.finished_at, commit.started_at)}
+-#
+-# %td.timestamp
+-# - if commit.finished_at
+-# %span #{time_ago_with_tooltip(commit.finished_at)}
+-#
+-# - if defined?(coverage) && coverage
+-# %td.coverage
+-# - if commit.try(:coverage)
+-# #{commit.coverage}%
+-#
+-# %td
+-# .pull-right
+-# - if can?(current_user, :read_commit, commit) && commit.artifacts?
+-# = link_to download_namespace_project_commit_artifacts_path(commit.project.namespace, commit.project, commit), title: 'Download artifacts' do
+-# %i.fa.fa-download
+-# - if can?(current_user, :update_commit, commit)
+-# - if commit.active?
+-# = link_to cancel_namespace_project_commit_path(commit.project.namespace, commit.project, commit, return_to: request.original_url), method: :post, title: 'Cancel' do
+-# %i.fa.fa-remove.cred
+-# - elsif defined?(allow_retry) && allow_retry && commit.retryable?
+-# = link_to retry_namespace_project_commit_path(commit.project.namespace, commit.project, commit, return_to: request.original_url), method: :post, title: 'Retry' do
+-# %i.fa.fa-repeat
+-#
+-#- if commit.yaml_errors.present?
+-# %tr
+-# %td{colspan: 7}
+-# .light
+-# = commit.yaml_errors \ No newline at end of file
diff --git a/app/views/projects/ci_commits/_header_title.html.haml b/app/views/projects/ci_commits/_header_title.html.haml
new file mode 100644
index 00000000000..c04fc6be9f1
--- /dev/null
+++ b/app/views/projects/ci_commits/_header_title.html.haml
@@ -0,0 +1 @@
+- header_title project_title(@project, "CI", project_ci_commits_path(@project))
diff --git a/app/views/projects/ci_commits/index.html.haml b/app/views/projects/ci_commits/index.html.haml
new file mode 100644
index 00000000000..7955e5e9abf
--- /dev/null
+++ b/app/views/projects/ci_commits/index.html.haml
@@ -0,0 +1,51 @@
+- page_title "CI Changes"
+= render "header_title"
+
+.top-area
+ %ul.nav-links
+ %li{class: ('active' if @scope.nil? || @scope == 'latest')}
+ = link_to project_ci_commits_path(@project, scope: :latest) do
+ Latest
+ %span.badge.js-totalbuilds-count
+ = number_with_delimiter(@all_commits.count(:id))
+
+ %li{class: ('active' if @scope == 'branches')}
+ = link_to project_ci_commits_path(@project, scope: :branches) do
+ Branches
+ %span.badge.js-running-count
+ = number_with_delimiter(@all_commits.running_or_pending.count(:id))
+
+ %li{class: ('active' if @scope == 'tags')}
+ = link_to project_ci_commits_path(@project, scope: :tags) do
+ Tags
+ %span.badge.js-running-count
+ = number_with_delimiter(@all_commits.running_or_pending.count(:id))
+
+ %li{class: ('active' if @scope == 'failed')}
+ = link_to project_ci_commits_path(@project, scope: :failed) do
+ Failed
+ %span.badge.js-running-count
+ = number_with_delimiter(@all_commits.finished.count(:id))
+
+ .nav-controls
+ - if can?(current_user, :update_build, @project)
+ - unless @repository.gitlab_ci_yml
+ = link_to 'Get started with Builds', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info'
+
+ = link_to ci_lint_path, class: 'btn btn-default' do
+ = icon('wrench')
+ %span CI Lint
+
+.gray-content-block
+ #{(@scope || 'running').capitalize} changes on this project
+
+%ul.content-list
+ - if @commits.blank?
+ %li
+ .nothing-here-block No commits to show
+ - else
+ .table-holder
+ %table.table.builds
+ = render @commits, commit_sha: true, stage: true, allow_retry: true
+
+ = paginate @commits, theme: 'gitlab'
diff --git a/app/views/projects/ci_commits/show.html.haml b/app/views/projects/ci_commits/show.html.haml
new file mode 100644
index 00000000000..b02aee3db21
--- /dev/null
+++ b/app/views/projects/ci_commits/show.html.haml
@@ -0,0 +1,216 @@
+- page_title "#{@build.name} (##{@build.id})", "Builds"
+= render "header_title"
+
+.build-page
+ .gray-content-block.top-block
+ Build ##{@build.id} for commit
+ %strong.monospace= link_to @build.commit.short_sha, ci_status_path(@build.commit)
+ from
+ = link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref)
+ - merge_request = @build.merge_request
+ - if merge_request
+ via
+ = link_to "merge request ##{merge_request.iid}", merge_request_path(merge_request)
+
+ #up-build-trace
+ - builds = @build.commit.matrix_builds(@build)
+ - if builds.size > 1
+ %ul.nav-links.no-top.no-bottom
+ - builds.each do |build|
+ %li{class: ('active' if build == @build) }
+ = link_to namespace_project_build_path(@project.namespace, @project, build) do
+ = ci_icon_for_status(build.status)
+ %span
+ - if build.name
+ = build.name
+ - else
+ = build.id
+
+ - if @build.retried?
+ %li.active
+ %a
+ Build ##{@build.id}
+ &middot;
+ %i.fa.fa-warning
+ This build was retried.
+
+ .gray-content-block.middle-block
+ .build-head
+ .clearfix
+ = ci_status_with_icon(@build.status)
+ - if @build.duration
+ %span
+ %i.fa.fa-time
+ #{duration_in_words(@build.finished_at, @build.started_at)}
+ .pull-right
+ #{time_ago_with_tooltip(@build.finished_at) if @build.finished_at}
+
+ - if @build.stuck?
+ - unless @build.any_runners_online?
+ .bs-callout.bs-callout-warning
+ %p
+ - if no_runners_for_project?(@build.project)
+ This build is stuck, because the project doesn't have any runners online assigned to it.
+ - elsif @build.tags.any?
+ This build is stuck, because you don't have any active runners online with any of these tags assigned to them:
+ - @build.tags.each do |tag|
+ %span.label.label-primary
+ = tag
+ - else
+ This build is stuck, because you don't have any active runners that can run this build.
+
+ %br
+ Go to
+ = link_to namespace_project_runners_path(@build.project.namespace, @build.project) do
+ Runners page
+
+ .row.prepend-top-default
+ .col-md-9
+ .clearfix
+ - if @build.active?
+ .autoscroll-container
+ %button.btn.btn-success.btn-sm#autoscroll-button{:type => "button", :data => {:state => 'disabled'}} enable autoscroll
+ .clearfix
+ #js-build-scroll.scroll-controls
+ = link_to '#up-build-trace', class: 'btn' do
+ %i.fa.fa-angle-up
+ = link_to '#down-build-trace', class: 'btn' do
+ %i.fa.fa-angle-down
+
+ - if @build.erased?
+ .erased.alert.alert-warning
+ - erased_by = "by #{link_to @build.erased_by.name, user_path(@build.erased_by)}" if @build.erased_by
+ Build has been erased #{erased_by.html_safe} #{time_ago_with_tooltip(@build.erased_at)}
+ - else
+ %pre.trace#build-trace
+ %code.bash
+ = preserve do
+ = raw @build.trace_html
+
+ %div#down-build-trace
+
+ .col-md-3
+ - if @build.coverage
+ .build-widget
+ %h4.title
+ Test coverage
+ %h1 #{@build.coverage}%
+
+ - if can?(current_user, :read_build, @project) && @build.artifacts?
+ .build-widget.artifacts
+ %h4.title Build artifacts
+ .center
+ .btn-group{ role: :group }
+ = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary' do
+ = icon('download')
+ Download
+
+ - if @build.artifacts_metadata?
+ = link_to browse_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary' do
+ = icon('folder-open')
+ Browse
+
+ .build-widget
+ %h4.title
+ Build ##{@build.id}
+ - if can?(current_user, :update_build, @project)
+ .center
+ .btn-group{ role: :group }
+ - if @build.active?
+ = link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-danger', method: :post
+ - elsif @build.retryable?
+ = link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary', method: :post
+
+ - if @build.erasable?
+ = link_to erase_namespace_project_build_path(@project.namespace, @project, @build),
+ class: 'btn btn-sm btn-warning', method: :post,
+ data: { confirm: 'Are you sure you want to erase this build?' } do
+ = icon('eraser')
+ Erase
+
+ .clearfix
+ - if @build.duration
+ %p
+ %span.attr-name Duration:
+ #{duration_in_words(@build.finished_at, @build.started_at)}
+ %p
+ %span.attr-name Created:
+ #{time_ago_with_tooltip(@build.created_at)}
+ - if @build.finished_at
+ %p
+ %span.attr-name Finished:
+ #{time_ago_with_tooltip(@build.finished_at)}
+ - if @build.erased_at
+ %p
+ %span.attr-name Erased:
+ #{time_ago_with_tooltip(@build.erased_at)}
+ %p
+ %span.attr-name Runner:
+ - if @build.runner && current_user && current_user.admin
+ = link_to "##{@build.runner.id}", admin_runner_path(@build.runner.id)
+ - elsif @build.runner
+ \##{@build.runner.id}
+
+ - if @build.trigger_request
+ .build-widget
+ %h4.title
+ Trigger
+
+ %p
+ %span.attr-name Token:
+ #{@build.trigger_request.trigger.short_token}
+
+ - if @build.trigger_request.variables
+ %p
+ %span.attr-name Variables:
+
+ %code
+ - @build.trigger_request.variables.each do |key, value|
+ #{key}=#{value}
+
+ .build-widget
+ %h4.title
+ Commit
+ .pull-right
+ %small
+ = link_to @build.commit.short_sha, ci_status_path(@build.commit), class: "monospace"
+ %p
+ %span.attr-name Branch:
+ = link_to @build.ref, namespace_project_commits_path(@project.namespace, @project, @build.ref)
+ %p
+ %span.attr-name Author:
+ #{@build.commit.git_author_name}
+ %p
+ %span.attr-name Message:
+ #{@build.commit.git_commit_message}
+
+ - if @build.tags.any?
+ .build-widget
+ %h4.title
+ Tags
+ - @build.tag_list.each do |tag|
+ %span.label.label-primary
+ = tag
+
+ - if @builds.present?
+ .build-widget
+ %h4.title #{pluralize(@builds.count(:id), "other build")} for
+ = succeed ":" do
+ = link_to @build.commit.short_sha, ci_status_path(@build.commit), class: "monospace"
+ %table.table.builds
+ - @builds.each_with_index do |build, i|
+ %tr.build
+ %td
+ = ci_icon_for_status(build.status)
+ %td
+ = link_to namespace_project_build_path(@project.namespace, @project, build) do
+ - if build.name
+ = build.name
+ - else
+ %span ##{build.id}
+
+ %td.status= build.status
+
+
+ :javascript
+ new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{@build.status}")
diff --git a/app/views/projects/commit/_builds.html.haml b/app/views/projects/commit/_builds.html.haml
index 003b7c18d0e..5c9a319edeb 100644
--- a/app/views/projects/commit/_builds.html.haml
+++ b/app/views/projects/commit/_builds.html.haml
@@ -1,67 +1,2 @@
-.gray-content-block.middle-block
- .pull-right
- - 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
+- @ci_commits.each do |ci_commit|
+ = render "ci_commit", ci_commit: ci_commit
diff --git a/app/views/projects/commit/_ci_commit.html.haml b/app/views/projects/commit/_ci_commit.html.haml
new file mode 100644
index 00000000000..06520e40bd9
--- /dev/null
+++ b/app/views/projects/commit/_ci_commit.html.haml
@@ -0,0 +1,69 @@
+.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, @commit.id), 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, @commit.id), 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 @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit.id), 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
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 71995fcc487..f1da308b47b 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -42,12 +42,12 @@
- @commit.parents.each do |parent|
= link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "monospace"
-- if @ci_commit
+- if @commit.status
.pull-right
- = link_to ci_status_path(@ci_commit), class: "ci-status ci-#{@ci_commit.status}" do
- = ci_status_icon(@ci_commit)
+ = link_to ci_status_path(@commit), class: "ci-status ci-#{@commit.status}" do
+ = ci_status_icon(@commit)
build:
- = ci_status_label(@ci_commit)
+ = ci_status_label(@commit)
.commit-info-row.branches
%i.fa.fa-spinner.fa-spin
diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml
index 21e186120c3..096f7058bd4 100644
--- a/app/views/projects/commit/show.html.haml
+++ b/app/views/projects/commit/show.html.haml
@@ -5,7 +5,7 @@
.prepend-top-default
= render "commit_box"
-- if @ci_commit
+- if @commit.status
= render "ci_menu"
- else
%div.block-connector
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 7f2903589a9..fa34f7b7d61 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -4,9 +4,8 @@
- notes = commit.notes
- 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.push(ci_commit.status) if ci_commit
+- cache_key.push(commit.status) if commit.status
= cache(cache_key) do
%li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" }
@@ -17,8 +16,8 @@
%a.text-expander.js-toggle-button ...
.pull-right
- - if ci_commit
- = render_ci_status(ci_commit)
+ - if commit.status
+ = render_ci_status(commit)
&nbsp;
= clipboard_button(clipboard_text: commit.id)
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
diff --git a/app/views/projects/issues/_related_branches.html.haml b/app/views/projects/issues/_related_branches.html.haml
index b10cd03515f..bdfa0c7009e 100644
--- a/app/views/projects/issues/_related_branches.html.haml
+++ b/app/views/projects/issues/_related_branches.html.haml
@@ -5,7 +5,7 @@
- @related_branches.each do |branch|
%li
- 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
%span.related-branch-ci-status
= render_ci_status(ci_commit)
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index 53ff8959bc8..53261fcace7 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -6,9 +6,8 @@
- css_class = '' unless local_assigns[:css_class]
- 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
-- 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.push(ci_commit.status) if ci_commit
+- cache_key.push(project.commit.status) if project.commit.status
%li.project-row{ class: css_class }
= cache(cache_key) do
@@ -16,9 +15,9 @@
- if project.main_language
%span
= project.main_language
- - if ci_commit
+ - if project.commit.status
%span
- = render_ci_status(ci_commit)
+ = render_ci_status(project.commit)
- if forks
%span
= icon('code-fork')
diff --git a/config/routes.rb b/config/routes.rb
index 842fbb99843..c55d9637299 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -654,6 +654,8 @@ Rails.application.routes.draw do
resource :variables, only: [:show, :update]
resources :triggers, only: [:index, :create, :destroy]
+ resources :ci_commits, only: [:index, :show]
+
resources :builds, only: [:index, :show], constraints: { id: /\d+/ } do
collection do
post :cancel_all
diff --git a/db/migrate/20160331153918_add_fields_to_ci_commit.rb b/db/migrate/20160331153918_add_fields_to_ci_commit.rb
new file mode 100644
index 00000000000..03eb9ba4e53
--- /dev/null
+++ b/db/migrate/20160331153918_add_fields_to_ci_commit.rb
@@ -0,0 +1,7 @@
+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
+ end
+end
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index 8e74e177ea0..e7d76764ff5 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -21,7 +21,7 @@ module API
authorize!(:read_commit_status, user_project)
not_found!('Commit') unless user_project.commit(params[:sha])
- ci_commit = user_project.ci_commit(params[:sha])
+ ci_commit = user_project.ci_commit(params[:sha], params[:ref])
return [] unless ci_commit
statuses = ci_commit.statuses