# == Schema Information # # Table name: 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 # module Ci class Commit < ActiveRecord::Base extend Ci::Model belongs_to :gl_project, class_name: '::Project', foreign_key: :gl_project_id 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 validate :valid_commit_sha def self.truncate_sha(sha) sha[0...8] end def to_param sha end def project unless @project gl_project.ensure_ci_project @project = gl_project.gitlab_ci_project end @project end def project_id project.id end def last_build builds.order(:id).last end def retry builds_without_retry.each do |build| Ci::Build.retry(build) end end def valid_commit_sha if self.sha == Ci::Git::BLANK_SHA self.errors.add(:sha, " cant be 00000000 (branch removal)") 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] end def git_author_email commit_data[:author][:email] if commit_data && commit_data[:author] 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) end def short_sha Ci::Commit.truncate_sha(sha) end def commit_data push_data[:commits].find do |commit| commit[:id] == sha end 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 } end def create_builds_for_stage(stage, trigger_request) 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, }) end end def create_next_builds(trigger_request) return if skip_ci? && trigger_request.blank? return unless config_processor stages = builds.where(trigger_request: trigger_request).group_by(&:stage) config_processor.stages.any? do |stage| !stages.include?(stage) && create_builds_for_stage(stage, trigger_request).present? end end def create_builds(trigger_request = nil) return if skip_ci? && trigger_request.blank? return unless config_processor config_processor.stages.any? do |stage| create_builds_for_stage(stage, trigger_request).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 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 end def retried_builds @retried_builds ||= (builds.order(id: :desc) - builds_without_retry) end def status if skip_ci? return 'skipped' elsif 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 end def pending? builds_without_retry.all? do |build| build.pending? end end def running? builds_without_retry.any? do |build| build.running? || build.pending? end end def success? builds_without_retry.all? do |build| build.success? || build.ignored? end end def failed? status == 'failed' end def canceled? builds_without_retry.all? do |build| build.canceled? end end def duration @duration ||= builds_without_retry.select(&:duration).sum(&:duration).to_i end def finished_at @finished_at ||= builds.order('finished_at DESC').first.try(:finished_at) end def coverage if project.coverage_enabled? coverage_array = builds_without_retry.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 end def config_processor @config_processor ||= Ci::GitlabCiYamlProcessor.new(push_data[:ci_yaml_file]) rescue Ci::GitlabCiYamlProcessor::ValidationError => 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") nil end def skip_ci? return false if builds.any? commits = push_data[:commits] commits.present? && commits.last[:message] =~ /(\[ci skip\])/ end def update_committed! update!(committed_at: DateTime.now) end private def save_yaml_error(error) return if self.yaml_errors? self.yaml_errors = error save end end end