diff options
Diffstat (limited to 'app/models/ci/commit.rb')
-rw-r--r-- | app/models/ci/commit.rb | 220 |
1 files changed, 88 insertions, 132 deletions
diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb index f102d0a7679..971e899de84 100644 --- a/app/models/ci/commit.rb +++ b/app/models/ci/commit.rb @@ -1,31 +1,33 @@ # == Schema Information # -# Table name: commits +# Table name: ci_commits # -# id :integer not null, primary key -# project_id :integer -# ref :string(255) -# sha :string(255) -# before_sha :string(255) -# push_data :text -# created_at :datetime -# updated_at :datetime -# tag :boolean default(FALSE) -# yaml_errors :text -# committed_at :datetime +# id :integer not null, primary key +# project_id :integer +# ref :string(255) +# sha :string(255) +# before_sha :string(255) +# push_data :text +# created_at :datetime +# updated_at :datetime +# tag :boolean default(FALSE) +# yaml_errors :text +# committed_at :datetime +# gl_project_id :integer # module Ci class Commit < ActiveRecord::Base extend Ci::Model - - belongs_to :project, class_name: 'Ci::Project' - has_many :builds, dependent: :destroy, class_name: 'Ci::Build' + + belongs_to :gl_project, class_name: '::Project', foreign_key: :gl_project_id + has_many :statuses, dependent: :destroy, class_name: 'CommitStatus' + has_many :builds, class_name: 'Ci::Build' has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest' - serialize :push_data + scope :ordered, -> { order('CASE WHEN ci_commits.committed_at IS NULL THEN 0 ELSE 1 END', :committed_at, :id) } - validates_presence_of :ref, :sha, :before_sha, :push_data + validates_presence_of :sha validate :valid_commit_sha def self.truncate_sha(sha) @@ -36,12 +38,20 @@ module Ci sha end + def project + @project ||= gl_project.ensure_gitlab_ci_project + end + + def project_id + project.id + end + def last_build builds.order(:id).last end def retry - builds_without_retry.each do |build| + latest_builds.each do |build| Ci::Build.retry(build) end end @@ -52,28 +62,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 @@ -81,127 +79,82 @@ 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 = statuses.latest.running_or_pending.ordered + running_or_pending.first.try(:stage) end - def create_builds_for_stage(stage, trigger_request) - return if skip_ci? && trigger_request.blank? + def create_builds(ref, tag, user, trigger_request = nil) return unless config_processor - - builds_attrs = config_processor.builds_for_stage_and_ref(stage, ref, tag) - builds_attrs.map do |build_attrs| - builds.create!({ - project: project, - 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, 'success').present? end end - def create_next_builds(trigger_request) - return if skip_ci? && trigger_request.blank? + def create_next_builds(build) return unless config_processor - stages = builds.where(trigger_request: trigger_request).group_by(&:stage) + # don't create other builds if this one is retried + latest_builds = builds.similar(build).latest + return unless latest_builds.exists?(build.id) - config_processor.stages.any? do |stage| - !stages.include?(stage) && create_builds_for_stage(stage, trigger_request).present? - end - end + # get list of stages after this build + next_stages = config_processor.stages.drop_while { |stage| stage != build.stage } + next_stages.delete(build.stage) - def create_builds(trigger_request = nil) - return if skip_ci? && trigger_request.blank? - return unless config_processor + # get status for all prior builds + prior_builds = latest_builds.reject { |other_build| next_stages.include?(other_build.stage) } + status = Ci::Status.get_status(prior_builds) - config_processor.stages.any? do |stage| - create_builds_for_stage(stage, trigger_request).present? + # create builds for next stages based + next_stages.any? do |stage| + CreateBuildsService.new.execute(self, stage, build.ref, build.tag, build.user, build.trigger_request, status).present? end 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 + def refs + statuses.order(:ref).pluck(:ref).uniq end - def builds_without_retry_sorted - return builds_without_retry unless config_processor + def latest_statuses + @latest_statuses ||= statuses.latest.to_a + end - stages = config_processor.stages - builds_without_retry.sort_by do |build| - [stages.index(build.stage) || -1, build.name || ""] - end + def latest_builds + @latest_builds ||= builds.latest.to_a + end + + def latest_builds_for_ref(ref) + latest_builds.select { |build| build.ref == ref } end - def retried_builds - @retried_builds ||= (builds.order(id: :desc) - builds_without_retry) + def retried + @retried ||= (statuses.order(id: :desc) - statuses.latest) end def status - if skip_ci? - return 'skipped' - elsif yaml_errors.present? + if yaml_errors.present? return 'failed' - elsif builds.none? - return 'skipped' - elsif success? - 'success' - elsif pending? - 'pending' - elsif running? - 'running' - elsif canceled? - 'canceled' - else - 'failed' end + + @status ||= Ci::Status.get_status(latest_statuses) end def pending? - builds_without_retry.all? do |build| - build.pending? - end + status == 'pending' end def running? - builds_without_retry.any? do |build| - build.running? || build.pending? - end + status == 'running' end def success? - builds_without_retry.all? do |build| - build.success? || build.ignored? - end + status == 'success' end def failed? @@ -209,47 +162,50 @@ module Ci end def canceled? - builds_without_retry.all? do |build| - build.canceled? - end + status == 'canceled' end def duration - @duration ||= builds_without_retry.select(&:duration).sum(&:duration).to_i + duration_array = latest_statuses.map(&:duration).compact + duration_array.reduce(:+).to_i end def finished_at - @finished_at ||= builds.order('finished_at DESC').first.try(:finished_at) + @finished_at ||= statuses.order('finished_at DESC').first.try(:finished_at) end def coverage if project.coverage_enabled? - coverage_array = builds_without_retry.map(&:coverage).compact + coverage_array = latest_builds.map(&:coverage).compact if coverage_array.size >= 1 '%.2f' % (coverage_array.reduce(:+) / coverage_array.size) end end end - def matrix? - builds_without_retry.size > 1 + def matrix_for_ref?(ref) + latest_builds_for_ref(ref).size > 1 end def config_processor - @config_processor ||= Ci::GitlabCiYamlProcessor.new(push_data[:ci_yaml_file]) - rescue Ci::GitlabCiYamlProcessor::ValidationError => e + return nil unless ci_yaml_file + @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace) + rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e save_yaml_error(e.message) nil - rescue Exception => e - logger.error e.message + "\n" + e.backtrace.join("\n") - save_yaml_error("Undefined yaml error") + rescue + save_yaml_error("Undefined error") + nil + end + + def ci_yaml_file + gl_project.repository.blob_at(sha, '.gitlab-ci.yml').data + 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! |