2012-01-30 20:39:34 +00:00
# -*- encoding : utf-8 -*-
2011-03-09 17:38:21 +00:00
class Project < ActiveRecord :: Base
2011-10-23 22:39:44 +01:00
VISIBILITIES = [ 'open' , 'hidden' ]
2012-01-27 00:16:07 +00:00
MAX_OWN_PROJECTS = 32000
2012-05-14 14:24:01 +01:00
NAME_REGEXP = / [a-zA-Z0-9_ \ - \ + \ .]+ /
2011-10-23 22:39:44 +01:00
2012-01-27 06:13:56 +00:00
belongs_to :owner , :polymorphic = > true , :counter_cache = > :own_projects_count
2011-10-18 16:00:06 +01:00
2011-12-19 15:30:14 +00:00
has_many :issues , :dependent = > :destroy
2012-05-02 10:18:07 +01:00
has_many :labels , :dependent = > :destroy
2011-04-07 14:20:21 +01:00
has_many :build_lists , :dependent = > :destroy
2011-03-10 12:35:23 +00:00
2012-01-27 15:35:18 +00:00
has_many :project_imports , :dependent = > :destroy
2011-10-26 21:57:51 +01:00
has_many :project_to_repositories , :dependent = > :destroy
2011-10-13 23:35:16 +01:00
has_many :repositories , :through = > :project_to_repositories
2011-10-13 16:55:03 +01:00
2011-10-26 21:57:51 +01:00
has_many :relations , :as = > :target , :dependent = > :destroy
2012-04-26 02:38:33 +01:00
has_many :collaborators , :through = > :relations , :source = > :actor , :source_type = > 'User'
has_many :groups , :through = > :relations , :source = > :actor , :source_type = > 'Group'
2011-10-13 16:55:03 +01:00
2012-05-14 21:00:04 +01:00
has_many :advisories # should be without :dependent => :destroy
2012-05-14 20:08:31 +01:00
has_many :packages , :class_name = > " BuildList::Package " , :dependent = > :destroy
2012-05-04 18:12:51 +01:00
2012-05-16 11:01:41 +01:00
validates :name , :uniqueness = > { :scope = > [ :owner_id , :owner_type ] , :case_sensitive = > false } , :presence = > true , :format = > { :with = > / ^ #{ NAME_REGEXP } $ / , :message = > I18n . t ( " activerecord.errors.project.uname " ) }
2011-10-26 21:57:51 +01:00
validates :owner , :presence = > true
2012-01-27 06:13:56 +00:00
validate { errors . add ( :base , :can_have_less_or_equal , :count = > MAX_OWN_PROJECTS ) if owner . projects . size > = MAX_OWN_PROJECTS }
2012-04-04 13:13:10 +01:00
2012-01-24 20:35:16 +00:00
validates_attachment_size :srpm , :less_than = > 500 . megabytes
2012-01-27 15:35:18 +00:00
validates_attachment_content_type :srpm , :content_type = > [ 'application/octet-stream' , " application/x-rpm " , " application/x-redhat-package-manager " ] , :message = > I18n . t ( 'layout.invalid_content_type' )
2011-03-09 17:38:21 +00:00
2012-05-15 11:00:57 +01:00
attr_accessible :name , :description , :visibility , :srpm , :is_package , :default_branch , :has_issues , :has_wiki
2011-11-28 15:41:42 +00:00
attr_readonly :name
2011-03-10 12:38:42 +00:00
2011-03-31 02:15:17 +01:00
scope :recent , order ( " name ASC " )
2012-03-07 21:34:49 +00:00
scope :search_order , order ( " CHAR_LENGTH(name) ASC " )
2012-04-05 18:11:02 +01:00
scope :search , lambda { | q | by_name ( " % #{ q . to_s . strip } % " ) }
2012-01-28 01:04:11 +00:00
scope :by_name , lambda { | name | where ( 'projects.name ILIKE ?' , name ) }
2012-03-06 15:53:04 +00:00
scope :by_visibilities , lambda { | v | where ( :visibility = > v ) }
2012-03-28 00:58:03 +01:00
scope :opened , where ( :visibility = > 'open' )
2011-11-01 13:22:41 +00:00
scope :addable_to_repository , lambda { | repository_id | where ( " projects.id NOT IN (SELECT project_to_repositories.project_id FROM project_to_repositories WHERE (project_to_repositories.repository_id = #{ repository_id } )) " ) }
2011-10-23 22:39:44 +01:00
2011-10-27 14:04:03 +01:00
after_create :attach_to_personal_repository
2011-11-01 00:52:24 +00:00
after_create :create_git_repo
2012-04-05 17:52:21 +01:00
after_create { | p | p . delay ( :queue = > 'fork' , :priority = > 20 ) . fork_git_repo unless is_root? }
2011-12-23 17:01:47 +00:00
after_save :create_wiki
2011-10-28 18:15:35 +01:00
after_destroy :destroy_git_repo
2011-12-23 17:01:47 +00:00
after_destroy :destroy_wiki
2012-04-04 13:13:10 +01:00
after_save { | p | p . delay ( :queue = > 'import' , :priority = > 10 ) . import_attached_srpm if p . srpm? } # should be after create_git_repo
2011-11-23 15:52:33 +00:00
# after_rollback lambda { destroy_git_repo rescue true if new_record? }
has_ancestry
2011-10-28 00:18:04 +01:00
2012-01-24 20:35:16 +00:00
has_attached_file :srpm
2011-11-24 21:46:19 +00:00
include Modules :: Models :: Owner
2012-05-02 10:18:07 +01:00
def to_param
name
end
2012-04-19 20:45:50 +01:00
def self . find_by_owner_and_name ( owner_name , project_name )
2012-05-02 10:18:07 +01:00
owner = User . find_by_uname ( owner_name ) || Group . find_by_uname ( owner_name ) || User . by_uname ( owner_name ) . first || Group . by_uname ( owner_name ) . first and
2012-04-19 20:45:50 +01:00
scoped = where ( :owner_id = > owner . id , :owner_type = > owner . class ) and
scoped . find_by_name ( project_name ) || scoped . by_name ( project_name ) . first
2012-05-03 15:08:02 +01:00
# owner.projects.find_by_name(project_name) || owner.projects.by_name(project_name).first # TODO force this work?
2012-04-19 20:45:50 +01:00
end
def self . find_by_owner_and_name! ( owner_name , project_name )
find_by_owner_and_name ( owner_name , project_name ) or raise ActiveRecord :: RecordNotFound
end
2012-05-18 16:12:51 +01:00
def build_for ( platform , user , arch = 'i586' , auto_publish = false , mass_build_id = nil , priority = 0 )
2012-04-14 14:46:39 +01:00
# Select main and project platform repository(contrib, non-free and etc)
2012-04-14 17:10:18 +01:00
# If main does not exist, will connect only project platform repository
# If project platform repository is main, only main will be connect
2012-04-14 14:46:39 +01:00
build_reps = [ platform . repositories . find_by_name ( 'main' ) ]
build_reps += platform . repositories . select { | rep | self . repository_ids . include? rep . id }
build_ids = build_reps . compact . map ( & :id ) . uniq
2012-03-07 23:25:17 +00:00
arch = Arch . find_by_name ( arch ) if arch . acts_like? ( :string )
2011-11-30 21:43:01 +00:00
build_lists . create do | bl |
2012-05-04 18:12:51 +01:00
bl . save_to_platform = platform
2012-05-18 16:12:51 +01:00
bl . build_for_platform = platform
2012-02-23 20:20:44 +00:00
bl . update_type = 'newpackage'
2012-03-07 23:25:17 +00:00
bl . arch = arch
2012-04-14 14:46:39 +01:00
bl . project_version = " latest_ #{ platform . name } "
2011-11-30 21:43:01 +00:00
bl . build_requires = false # already set as db default
2011-12-20 17:09:29 +00:00
bl . user = user
2012-01-23 16:19:10 +00:00
bl . auto_publish = true # already set as db default
2012-04-14 14:46:39 +01:00
bl . include_repos = build_ids
2012-04-24 20:03:45 +01:00
bl . priority = priority
2012-05-18 16:12:51 +01:00
bl . mass_build_id = mass_build_id
2011-11-30 21:43:01 +00:00
end
end
2011-10-29 20:09:14 +01:00
def tags
2011-11-30 00:22:26 +00:00
self . git_repository . tags #.sort_by{|t| t.name.gsub(/[a-zA-Z.]+/, '').to_i}
end
def branches
self . git_repository . branches
end
2012-02-18 22:47:57 +00:00
def last_active_branch
@last_active_branch || = branches . inject do | r , c |
r_last = r . commit . committed_date || r . commit . authored_date unless r . nil?
c_last = c . commit . committed_date || c . commit . authored_date
if r . nil? or r_last < c_last
r = c
end
r
end
@last_active_branch
end
2012-02-21 15:41:16 +00:00
def branch ( name = nil )
name = default_branch if name . blank?
branches . select { | b | b . name == name } . first
end
def tree_info ( tree , treeish = nil , path = nil )
treeish = tree . id unless treeish . present?
# initialize result as hash of <tree_entry> => nil
res = ( tree . trees . sort + tree . blobs . sort ) . inject ( { } ) { | h , e | h . merge! ( { e = > nil } ) }
# fills result vith commits that describes this file
res = res . inject ( res ) do | h , ( entry , commit ) |
# only if commit == nil ...
if commit . nil? and entry . respond_to? :name
# ... find last commit corresponds to this file ...
c = git_repository . log ( treeish , File . join ( [ path , entry . name ] . compact ) , :max_count = > 1 ) . first
# ... and add it to result.
h [ entry ] = c
# find another files, that linked to this commit and set them their commit
2012-03-21 19:55:14 +00:00
# c.diffs.map{|diff| diff.b_path.split(File::SEPARATOR, 2).first}.each do |name|
# h.each_pair do |k, v|
# h[k] = c if k.name == name and v.nil?
# end
# end
2012-02-21 15:41:16 +00:00
end
h
end
end
2011-11-30 00:22:26 +00:00
def versions
tags . map ( & :name ) + branches . map { | b | " latest_ #{ b . name } " }
2011-10-23 11:34:42 +01:00
end
2011-10-23 22:39:44 +01:00
2012-04-10 13:03:33 +01:00
def versions_for_group_select
[
2012-04-10 16:40:28 +01:00
[ 'Branches' , branches . map { | b | " latest_ #{ b . name } " } ] ,
[ 'Tags' , tags . map ( & :name ) ]
2012-04-10 13:03:33 +01:00
]
end
2011-10-18 16:00:06 +01:00
def members
2011-11-17 21:57:30 +00:00
collaborators + groups . map ( & :members ) . flatten
2011-10-18 16:00:06 +01:00
end
2011-11-10 09:55:50 +00:00
def git_repository
2011-11-23 15:52:33 +00:00
@git_repository || = Git :: Repository . new ( path )
2011-10-26 21:57:51 +01:00
end
2011-11-23 15:52:33 +00:00
2011-10-26 21:57:51 +01:00
def git_repo_name
2011-11-28 15:41:42 +00:00
File . join owner . uname , name
2011-10-26 21:57:51 +01:00
end
2011-03-10 13:38:50 +00:00
2012-01-30 14:17:00 +00:00
def wiki_repo_name
File . join owner . uname , " #{ name } .wiki "
end
2011-10-26 21:57:51 +01:00
def public?
visibility == 'open'
2011-03-11 09:39:34 +00:00
end
2011-11-23 15:52:33 +00:00
def fork ( new_owner )
2012-03-07 21:34:49 +00:00
dup . tap do | c |
2011-11-29 22:23:09 +00:00
c . parent_id = id
2011-11-23 15:52:33 +00:00
c . owner = new_owner
c . updated_at = nil ; c . created_at = nil # :id = nil
c . save
end
2011-03-11 17:38:28 +00:00
end
2011-10-28 00:18:04 +01:00
def path
build_path ( git_repo_name )
end
2011-12-23 17:01:47 +00:00
def wiki_path
build_wiki_path ( git_repo_name )
end
2011-10-28 14:53:46 +01:00
def xml_rpc_create ( repository )
2011-11-28 15:41:42 +00:00
result = BuildServer . create_project name , repository . platform . name , repository . name , path
2011-10-28 14:31:26 +01:00
if result == BuildServer :: SUCCESS
return true
else
2011-11-28 15:41:42 +00:00
raise " Failed to create project #{ name } (repo #{ repository . name } ) inside platform #{ repository . platform . name } in path #{ path } with code #{ result } . "
2012-01-11 18:15:35 +00:00
end
2011-10-28 14:31:26 +01:00
end
2011-10-28 14:53:46 +01:00
def xml_rpc_destroy ( repository )
2011-11-28 15:41:42 +00:00
result = BuildServer . delete_project name , repository . platform . name
2011-10-28 14:31:26 +01:00
if result == BuildServer :: SUCCESS
return true
else
raise " Failed to delete repository #{ name } (repo main) inside platform #{ owner . uname } _personal with code #{ result } . "
end
end
2011-12-21 14:01:50 +00:00
def platforms
@platforms || = repositories . map ( & :platform ) . uniq
end
2012-01-27 15:35:18 +00:00
def import_srpm ( srpm_path = srpm . path , branch_name = 'import' )
system ( " #{ Rails . root . join ( 'bin' , 'import_srpm.sh' ) } #{ srpm_path } #{ path } #{ branch_name } >> /dev/null 2>&1 " )
2012-01-24 20:35:16 +00:00
end
2012-01-25 17:33:26 +00:00
def owner? ( user )
2012-01-29 20:18:14 +00:00
owner == user
2012-01-25 17:33:26 +00:00
end
2012-02-06 09:31:22 +00:00
def self . process_hook ( owner_uname , repo , newrev , oldrev , ref , newrev_type , oldrev_type )
rec = GitHook . new ( owner_uname , repo , newrev , oldrev , ref , newrev_type , oldrev_type )
2012-02-10 17:33:05 +00:00
ActivityFeedObserver . instance . after_create rec
2012-02-06 09:31:22 +00:00
end
2012-02-14 16:46:08 +00:00
def owner_and_admin_ids
2012-04-26 02:38:33 +01:00
recipients = self . relations . by_role ( 'admin' ) . where ( :actor_type = > 'User' ) . map { | rel | rel . read_attribute ( :actor_id ) }
2012-02-14 16:46:08 +00:00
recipients = recipients | [ self . owner_id ] if self . owner_type == 'User'
recipients
2012-02-06 09:31:22 +00:00
end
2012-04-12 15:55:32 +01:00
def human_average_build_time
time = average_build_time
2012-04-14 17:10:18 +01:00
I18n . t ( " layout.projects.human_average_build_time " , { :hours = > ( time / 3600 ) . to_i , :minutes = > ( time % 3600 / 60 ) . to_i } )
2012-04-12 15:55:32 +01:00
end
2011-03-09 17:38:21 +00:00
protected
2012-01-27 15:35:18 +00:00
def build_path ( dir )
File . join ( APP_CONFIG [ 'root_path' ] , 'git_projects' , " #{ dir } .git " )
end
2011-10-28 00:18:04 +01:00
2012-01-30 09:27:22 +00:00
def build_wiki_path ( dir )
File . join ( APP_CONFIG [ 'root_path' ] , 'git_projects' , " #{ dir } .wiki.git " )
end
2011-12-23 17:01:47 +00:00
2012-01-27 15:35:18 +00:00
def attach_to_personal_repository
repositories << self . owner . personal_repository if ! repositories . exists? ( :id = > self . owner . personal_repository )
end
2011-10-28 00:18:04 +01:00
2012-01-27 15:35:18 +00:00
def create_git_repo
2012-04-05 17:52:21 +01:00
if is_root?
2012-04-03 12:32:00 +01:00
Grit :: Repo . init_bare ( path )
2012-04-04 13:13:10 +01:00
write_hook . delay ( :queue = > 'fork' , :priority = > 15 )
2012-04-03 12:32:00 +01:00
end
end
def fork_git_repo
dummy = Grit :: Repo . new ( path ) rescue parent . git_repository . repo . fork_bare ( path )
2012-04-02 20:29:06 +01:00
write_hook
2012-01-27 15:35:18 +00:00
end
2011-03-15 11:24:45 +00:00
2012-01-27 15:35:18 +00:00
def destroy_git_repo
FileUtils . rm_rf path
end
2011-12-23 17:01:47 +00:00
2012-01-27 15:35:18 +00:00
def import_attached_srpm
if srpm?
import_srpm # srpm.path
self . srpm = nil ; save # clear srpm
2011-12-23 17:01:47 +00:00
end
2012-01-27 15:35:18 +00:00
end
2011-12-23 17:01:47 +00:00
2012-02-02 20:55:16 +00:00
2012-01-30 09:27:22 +00:00
def create_wiki
if has_wiki && ! FileTest . exist? ( wiki_path )
Grit :: Repo . init_bare ( wiki_path )
2012-05-02 10:18:07 +01:00
wiki = Gollum :: Wiki . new ( wiki_path , { :base_path = > Rails . application . routes . url_helpers . project_wiki_index_path ( owner , self ) } )
2012-01-30 09:27:22 +00:00
wiki . write_page ( 'Home' , :markdown , I18n . t ( " wiki.seed.welcome_content " ) ,
{ :name = > owner . name , :email = > owner . email , :message = > 'Initial commit' } )
2011-12-23 17:01:47 +00:00
end
2012-01-30 09:27:22 +00:00
end
def destroy_wiki
FileUtils . rm_rf wiki_path
end
2012-02-02 20:55:16 +00:00
2012-02-06 09:31:22 +00:00
def write_hook
2012-03-15 12:10:10 +00:00
is_production = Rails . env == " production "
2012-02-16 15:08:08 +00:00
hook = File . join ( :: Rails . root . to_s , 'tmp' , " post-receive-hook " )
FileUtils . cp ( File . join ( :: Rails . root . to_s , 'bin' , " post-receive-hook.partial " ) , hook )
File . open ( hook , 'a' ) do | f |
2012-04-02 20:29:06 +01:00
s = " \n /bin/bash -l -c \" cd #{ is_production ? '/srv/rosa_build/current' : Rails . root . to_s } && #{ is_production ? 'RAILS_ENV=production' : '' } bundle exec rails runner 'Project.delay(:queue => \\ \" hook \\ \" ).process_hook( \\ \" $owner \\ \" , \\ \" $reponame \\ \" , \\ \" $newrev \\ \" , \\ \" $oldrev \\ \" , \\ \" $ref \\ \" , \\ \" $newrev_type \\ \" , \\ \" $oldrev_type \\ \" )' \" "
2012-02-16 15:08:08 +00:00
s << " > /dev/null 2>&1 " if is_production
s << " \n done \n "
f . write ( s )
2012-03-06 09:39:23 +00:00
f . chmod ( 0755 )
2012-02-16 15:08:08 +00:00
end
2012-02-06 09:31:22 +00:00
hook_file = File . join ( path , 'hooks' , 'post-receive' )
2012-02-16 15:08:08 +00:00
FileUtils . cp ( hook , hook_file )
FileUtils . rm_rf ( hook )
2012-02-06 09:31:22 +00:00
rescue Exception # FIXME
end
2011-03-09 17:38:21 +00:00
end