rosa-build/app/models/build_list.rb

459 lines
16 KiB
Ruby
Raw Normal View History

2012-01-30 20:39:34 +00:00
# -*- encoding : utf-8 -*-
2011-04-07 14:20:21 +01:00
class BuildList < ActiveRecord::Base
2012-11-06 14:13:16 +00:00
include Modules::Models::CommitAndVersion
include Modules::Models::FileStoreClean
include AbfWorker::ModelHelper
2012-11-06 14:13:16 +00:00
2011-04-07 14:20:21 +01:00
belongs_to :project
belongs_to :arch
2012-05-04 18:12:51 +01:00
belongs_to :save_to_platform, :class_name => 'Platform'
belongs_to :save_to_repository, :class_name => 'Repository'
2012-05-04 18:12:51 +01:00
belongs_to :build_for_platform, :class_name => 'Platform'
belongs_to :user
2012-05-04 18:12:51 +01:00
belongs_to :advisory
belongs_to :mass_build, :counter_cache => true
has_many :items, :class_name => "BuildList::Item", :dependent => :destroy
has_many :packages, :class_name => "BuildList::Package", :dependent => :destroy
2012-05-04 18:12:51 +01:00
UPDATE_TYPES = %w[security bugfix enhancement recommended newpackage]
2012-05-04 18:12:51 +01:00
RELEASE_UPDATE_TYPES = %w[security bugfix]
validates :project_id, :project_version, :arch, :include_repos,
:build_for_platform_id, :save_to_platform_id, :save_to_repository_id, :presence => true
validates_numericality_of :priority, :greater_than_or_equal_to => 0
2012-05-04 18:12:51 +01:00
validates :update_type, :inclusion => UPDATE_TYPES,
:unless => Proc.new { |b| b.advisory.present? }
validates :update_type, :inclusion => {:in => RELEASE_UPDATE_TYPES, :message => I18n.t('flash.build_list.frozen_platform')},
:if => Proc.new { |b| b.advisory.present? }
validate lambda {
errors.add(:build_for_platform, I18n.t('flash.build_list.wrong_platform')) if save_to_platform.main? && save_to_platform_id != build_for_platform_id
}
validate lambda {
errors.add(:build_for_platform, I18n.t('flash.build_list.wrong_build_for_platform')) unless build_for_platform.main?
}
validate lambda {
errors.add(:save_to_repository, I18n.t('flash.build_list.wrong_repository')) unless save_to_repository_id.in? save_to_platform.repositories.map(&:id)
}
validate lambda {
include_repos.each {|ir|
errors.add(:save_to_repository, I18n.t('flash.build_list.wrong_include_repos')) unless build_for_platform.repository_ids.include? ir.to_i
}
}
validate lambda {
errors.add(:save_to_repository, I18n.t('flash.build_list.wrong_project')) unless save_to_repository.projects.exists?(project_id)
}
2013-02-04 11:43:42 +00:00
before_create :use_save_to_repository_for_main_platforms
attr_accessible :include_repos, :auto_publish, :build_for_platform_id, :commit_hash,
:arch_id, :project_id, :save_to_repository_id, :update_type,
:save_to_platform_id, :project_version, :use_save_to_repository
2012-10-11 11:55:06 +01:00
LIVE_TIME = 4.week # for unpublished
MAX_LIVE_TIME = 3.month # for published
2013-01-24 11:32:00 +00:00
SUCCESS = 0
ERROR = 1
PROJECT_VERSION_NOT_FOUND = 4
PROJECT_SOURCE_ERROR = 6
DEPENDENCIES_ERROR = 555
BUILD_ERROR = 666
BUILD_STARTED = 3000
BUILD_CANCELED = 5000
WAITING_FOR_RESPONSE = 4000
BUILD_PENDING = 2000
BUILD_PUBLISHED = 6000
BUILD_PUBLISH = 7000
FAILED_PUBLISH = 8000
REJECTED_PUBLISH = 9000
BUILD_CANCELING = 10000
TESTS_FAILED = 11000
2011-04-11 17:37:09 +01:00
STATUSES = [ WAITING_FOR_RESPONSE,
BUILD_CANCELED,
BUILD_PENDING,
BUILD_PUBLISHED,
BUILD_CANCELING,
BUILD_PUBLISH,
FAILED_PUBLISH,
REJECTED_PUBLISH,
2013-01-24 11:32:00 +00:00
SUCCESS,
BUILD_STARTED,
BUILD_ERROR,
PROJECT_VERSION_NOT_FOUND,
TESTS_FAILED
]
HUMAN_STATUSES = { WAITING_FOR_RESPONSE => :waiting_for_response,
BUILD_CANCELED => :build_canceled,
BUILD_CANCELING => :build_canceling,
BUILD_PENDING => :build_pending,
BUILD_PUBLISHED => :build_published,
BUILD_PUBLISH => :build_publish,
FAILED_PUBLISH => :failed_publish,
REJECTED_PUBLISH => :rejected_publish,
2013-01-24 11:32:00 +00:00
BUILD_ERROR => :build_error,
BUILD_STARTED => :build_started,
SUCCESS => :success,
PROJECT_VERSION_NOT_FOUND => :project_version_not_found,
TESTS_FAILED => :tests_failed
2011-04-07 14:20:21 +01:00
}
scope :recent, order("#{table_name}.updated_at DESC")
2011-04-07 14:20:21 +01:00
scope :for_status, lambda {|status| where(:status => status) }
scope :for_user, lambda { |user| where(:user_id => user.id) }
scope :for_platform, lambda { |platform| where(:build_for_platform_id => platform) }
scope :by_mass_build, lambda { |mass_build| where(:mass_build_id => mass_build) }
2011-04-07 14:20:21 +01:00
scope :scoped_to_arch, lambda {|arch| where(:arch_id => arch) }
scope :scoped_to_save_platform, lambda {|pl_id| where(:save_to_platform_id => pl_id) }
scope :scoped_to_project_version, lambda {|project_version| where(:project_version => project_version) }
scope :scoped_to_is_circle, lambda {|is_circle| where(:is_circle => is_circle) }
2011-04-07 14:20:21 +01:00
scope :for_creation_date_period, lambda{|start_date, end_date|
2012-04-13 23:27:24 +01:00
s = scoped
s = s.where(["build_lists.created_at >= ?", start_date]) if start_date
s = s.where(["build_lists.created_at <= ?", end_date]) if end_date
s
2011-04-07 14:20:21 +01:00
}
scope :for_notified_date_period, lambda{|start_date, end_date|
2012-04-13 23:27:24 +01:00
s = scoped
s = s.where(["build_lists.updated_at >= ?", start_date]) if start_date
s = s.where(["build_lists.updated_at <= ?", end_date]) if end_date
s
2011-04-07 14:20:21 +01:00
}
scope :scoped_to_project_name, lambda {|project_name| joins(:project).where('projects.name LIKE ?', "%#{project_name}%")}
2012-12-10 12:15:22 +00:00
scope :scoped_to_new_core, lambda {|new_core| where(:new_core => new_core)}
scope :outdated, where('created_at < ? AND status <> ? OR created_at < ?', Time.now - LIVE_TIME, BUILD_PUBLISHED, Time.now - MAX_LIVE_TIME)
2011-04-11 11:47:57 +01:00
serialize :additional_repos
serialize :include_repos
serialize :results, Array
after_commit :place_build
after_destroy :remove_container
2011-04-11 11:47:57 +01:00
state_machine :status, :initial => :waiting_for_response do
2012-06-29 12:01:40 +01:00
# WTF? around_transition -> infinite loop
before_transition do |build_list, transition|
2013-01-24 11:32:00 +00:00
status = HUMAN_STATUSES[build_list.status]
2012-07-30 15:49:40 +01:00
if build_list.mass_build && MassBuild::COUNT_STATUSES.include?(status)
MassBuild.decrement_counter "#{status.to_s}_count", build_list.mass_build_id
2012-06-29 12:01:40 +01:00
end
end
after_transition do |build_list, transition|
2013-01-24 11:32:00 +00:00
status = HUMAN_STATUSES[build_list.status]
2012-07-30 15:49:40 +01:00
if build_list.mass_build && MassBuild::COUNT_STATUSES.include?(status)
MassBuild.increment_counter "#{status.to_s}_count", build_list.mass_build_id
end
end
after_transition :on => :published,
:do => [:set_version_and_tag, :actualize_packages, :destroy_container]
after_transition :on => :reject_publish, :do => :destroy_container
2013-01-24 06:33:16 +00:00
after_transition :on => :cancel, :do => :cancel_job
after_transition :on => [:published, :fail_publish, :build_error, :tests_failed], :do => :notify_users
after_transition :on => :build_success, :do => :notify_users,
2012-09-19 21:54:15 +01:00
:unless => lambda { |build_list| build_list.auto_publish? }
2012-06-01 11:58:17 +01:00
event :place_build do
transition :waiting_for_response => :build_pending, :if => lambda { |build_list|
2013-01-24 11:32:00 +00:00
build_list.add_to_queue == BuildList::SUCCESS
2012-06-01 11:58:17 +01:00
}
%w[BUILD_PENDING PROJECT_VERSION_NOT_FOUND].each do |code|
2013-01-25 12:57:55 +00:00
transition :waiting_for_response => code.downcase.to_sym, :if => lambda { |build_list|
build_list.add_to_queue == BuildList.const_get(code)
2012-06-01 11:58:17 +01:00
}
end
end
event :start_build do
transition [ :build_pending, :project_version_not_found ] => :build_started
end
event :cancel do
2013-01-24 06:33:16 +00:00
transition [:build_pending, :build_started] => :build_canceling
end
# :build_canceling => :build_canceled - canceling from UI
# :build_started => :build_canceled - canceling from worker by time-out (time_living has been expired)
event :build_canceled do
2013-01-24 06:33:16 +00:00
transition [:build_canceling, :build_started] => :build_canceled
end
event :published do
transition [:build_publish, :rejected_publish] => :build_published
end
event :fail_publish do
transition [:build_publish, :rejected_publish] => :failed_publish
end
event :publish do
2013-02-06 09:42:34 +00:00
transition [:success, :failed_publish, :build_published, :tests_failed] => :build_publish
2012-05-31 15:35:37 +01:00
transition [:success, :failed_publish] => :failed_publish
end
event :reject_publish do
2013-02-08 10:44:51 +00:00
transition [:success, :failed_publish, :tests_failed] => :rejected_publish, :if => :can_reject_publish?
end
event :build_success do
transition [:build_started, :build_canceled] => :success
end
[:build_error, :tests_failed].each do |kind|
event kind do
transition [:build_started, :build_canceling] => kind
end
end
HUMAN_STATUSES.each do |code,name|
state name, :value => code
end
2012-06-18 17:19:09 +01:00
end
later :publish, :queue => :clone_build
HUMAN_CONTAINER_STATUSES = { WAITING_FOR_RESPONSE => :waiting_for_publish,
BUILD_PUBLISHED => :container_published,
BUILD_PUBLISH => :container_publish,
FAILED_PUBLISH => :container_failed_publish
}
state_machine :container_status, :initial => :waiting_for_publish do
after_transition :on => :publish_container, :do => :create_container
after_transition :on => [:fail_publish_container, :destroy_container],
:do => :remove_container
event :publish_container do
2013-02-06 13:21:47 +00:00
transition [:waiting_for_publish, :container_failed_publish] => :container_publish,
:if => :can_create_container?
end
event :published_container do
transition :container_publish => :container_published
end
event :fail_publish_container do
transition :container_publish => :container_failed_publish
end
event :destroy_container do
2013-01-29 13:17:35 +00:00
transition [:container_failed_publish, :container_published, :waiting_for_publish] => :waiting_for_publish
end
HUMAN_CONTAINER_STATUSES.each do |code,name|
state name, :value => code
end
end
2012-06-18 17:19:09 +01:00
def set_version_and_tag
pkg = self.packages.where(:package_type => 'source', :project_id => self.project_id).first
# TODO: remove 'return' after deployment ABF kernel 2.0
2012-10-11 11:55:06 +01:00
return if pkg.nil? # For old client that does not sends data about packages
2012-06-18 17:19:09 +01:00
self.package_version = "#{pkg.platform.name}-#{pkg.version}-#{pkg.release}"
system("cd #{self.project.repo.path} && git tag #{self.package_version} #{self.commit_hash}") # TODO REDO through grit
2012-06-18 17:19:09 +01:00
save
end
def actualize_packages
ActiveRecord::Base.transaction do
2012-12-03 17:43:24 +00:00
# packages from previous build_list
self.last_published.limit(2).last.packages.update_all :actual => false
2012-12-03 17:43:24 +00:00
self.packages.update_all :actual => true
end
end
def can_create_container?
2013-02-06 09:42:34 +00:00
(success? || tests_failed?) && [WAITING_FOR_RESPONSE, FAILED_PUBLISH].include?(container_status)
end
#TODO: Share this checking on product owner.
def can_cancel?
2013-01-24 06:33:16 +00:00
build_started? || build_pending?
end
def can_publish?
2013-02-06 09:42:34 +00:00
[SUCCESS, FAILED_PUBLISH, BUILD_PUBLISHED, TESTS_FAILED].include? status
end
def can_reject_publish?
can_publish? && !save_to_repository.publish_without_qa && !build_published?
end
2012-06-01 11:58:17 +01:00
def add_to_queue
2013-01-24 06:33:16 +00:00
# TODO: Investigate: why 2 tasks will be created without checking @state
unless @status
add_job_to_abf_worker_queue
update_column(:bs_id, id)
end
2013-01-24 06:33:16 +00:00
@status ||= BUILD_PENDING
2012-06-01 11:58:17 +01:00
end
2012-05-11 19:00:27 +01:00
def self.human_status(status)
I18n.t("layout.build_lists.statuses.#{HUMAN_STATUSES[status]}")
2011-04-07 14:20:21 +01:00
end
def human_status
self.class.human_status(status)
end
2012-06-29 19:31:40 +01:00
def self.status_by_human(human)
2013-01-24 11:32:00 +00:00
HUMAN_STATUSES.key human
2012-06-29 19:31:40 +01:00
end
2011-04-11 11:47:57 +01:00
def set_items(items_hash)
self.items = []
items_hash.each do |level, items|
items.each do |item|
self.items << self.items.build(:name => item['name'], :version => item['version'], :level => level.to_i)
2011-04-11 11:47:57 +01:00
end
end
end
def set_packages(pkg_hash, project_name)
prj = Project.joins(:repositories => :platform).where('platforms.id = ?', save_to_platform.id).find_by_name!(project_name)
build_package(pkg_hash['srpm'], 'source', prj) {|p| p.save!}
pkg_hash['rpm'].each do |rpm_hash|
build_package(rpm_hash, 'binary', prj) {|p| p.save!}
end
end
def event_log_message
{:project => project.name, :version => project_version, :arch => arch.name}.inspect
end
def current_duration
2012-04-13 21:49:29 +01:00
(Time.now.utc - started_at.utc).to_i
end
def human_current_duration
2012-04-13 22:20:56 +01:00
I18n.t("layout.build_lists.human_current_duration", {:hours => (current_duration/3600).to_i, :minutes => (current_duration%3600/60).to_i})
end
2012-04-12 11:29:04 +01:00
def human_duration
2012-04-13 22:20:56 +01:00
I18n.t("layout.build_lists.human_duration", {:hours => (duration/3600).to_i, :minutes => (duration%3600/60).to_i})
2012-04-12 11:29:04 +01:00
end
2012-04-13 21:49:29 +01:00
def in_work?
2013-01-24 11:32:00 +00:00
status == BUILD_STARTED
#[WAITING_FOR_RESPONSE, BUILD_PENDING, BUILD_STARTED].include?(status)
2012-04-12 11:29:04 +01:00
end
2012-10-19 08:32:12 +01:00
def associate_and_create_advisory(params)
build_advisory(params){ |a| a.update_type = update_type }
advisory.attach_build_list(self)
end
def can_attach_to_advisory?
2012-10-19 08:32:12 +01:00
!save_to_repository.publish_without_qa &&
save_to_platform.main? &&
2012-10-19 08:32:12 +01:00
save_to_platform.released &&
2012-11-30 13:10:17 +00:00
build_published?
end
def log(load_lines)
new_core? ? abf_worker_log : I18n.t('layout.build_lists.log.not_available')
end
2013-01-12 01:36:00 +00:00
def last_published
BuildList.where(:project_id => self.project_id,
:save_to_repository_id => self.save_to_repository_id)
.for_platform(self.build_for_platform_id)
.scoped_to_arch(self.arch_id)
.for_status(BUILD_PUBLISHED)
.recent
end
protected
2011-04-11 17:37:09 +01:00
def create_container
AbfWorker::BuildListsPublishTaskManager.create_container_for self
end
def remove_container
system "rm -rf #{save_to_platform.path}/container/#{id}" if save_to_platform
end
2012-12-28 14:00:37 +00:00
def abf_worker_priority
mass_build_id ? '' : 'default'
end
def abf_worker_base_queue
'rpm_worker'
end
def abf_worker_args
# TODO: remove when this will be not necessary
# "rosa2012.1/main" repository should be used in "conectiva" platform
include_repos << 146 if build_for_platform_id == 376
include_repos_hash = {}.tap do |h|
include_repos.each do |r|
repo = Repository.find r
path = repo.platform.public_downloads_url(nil, arch.name, repo.name)
2013-02-06 13:21:47 +00:00
# path.gsub!(/^http:\/\/(0\.0\.0\.0|localhost)\:[\d]+/, 'https://abf.rosalinux.ru') unless Rails.env.production?
# Path looks like:
# http://abf.rosalinux.ru/downloads/rosa-server2012/repository/x86_64/base/
# so, we should append:
# - release
# - updates
%w(release updates).each do |kind|
h["#{repo.platform.name}_#{repo.name}_#{kind}"] = path + kind
end
end
end
if save_to_platform.personal? && use_save_to_repository
include_repos_hash["#{save_to_platform.name}_release"] = save_to_platform.
2012-12-13 12:30:37 +00:00
urpmi_list(nil, nil, false, save_to_repository.name)["#{build_for_platform.name}"]["#{arch.name}"]
end
2013-02-06 10:18:51 +00:00
git_project_address = project.git_project_address(user)
2013-02-06 13:21:47 +00:00
# git_project_address.gsub!(/^http:\/\/(0\.0\.0\.0|localhost)\:[\d]+/, 'https://abf.rosalinux.ru') unless Rails.env.production?
{
2013-02-06 10:18:51 +00:00
:id => id,
:arch => arch.name,
:time_living => 43200, # 12 hours
:distrib_type => build_for_platform.distrib_type,
:git_project_address => git_project_address,
:commit_hash => commit_hash,
:include_repos => include_repos_hash,
:bplname => build_for_platform.name,
:user => {:uname => user.uname, :email => user.email}
}
end
def notify_users
unless mass_build_id
users = []
if project # find associated users
users = project.all_members.
select{ |user| user.notifier.can_notify? && user.notifier.new_associated_build? }
end
if user.notifier.can_notify? && user.notifier.new_build?
users = users | [user]
end
users.each do |user|
UserMailer.build_list_notification(self, user).deliver
end
end
end # notify_users
def build_package(pkg_hash, package_type, prj)
packages.create(pkg_hash) do |p|
p.project = prj
p.platform = save_to_platform
p.package_type = package_type
yield p
end
end
2013-02-04 11:43:42 +00:00
def use_save_to_repository_for_main_platforms
self.use_save_to_repository = true if save_to_platform.main?
end
end