2012-11-19 13:24:05 -05:00
# == Schema Information
#
# Table name: projects
#
# id :integer not null, primary key
# name :string(255)
# path :string(255)
# description :text
2014-04-09 08:05:03 -04:00
# created_at :datetime
# updated_at :datetime
2013-01-03 14:09:18 -05:00
# creator_id :integer
2012-11-19 13:24:05 -05:00
# issues_enabled :boolean default(TRUE), not null
# wall_enabled :boolean default(TRUE), not null
# merge_requests_enabled :boolean default(TRUE), not null
# wiki_enabled :boolean default(TRUE), not null
2012-11-24 15:16:51 -05:00
# namespace_id :integer
2015-02-02 13:24:40 -05:00
# issues_tracker :string(255) default("gitlab"), not null
2013-03-15 09:16:02 -04:00
# issues_tracker_id :string(255)
2013-03-27 12:26:37 -04:00
# snippets_enabled :boolean default(TRUE), not null
2013-04-04 15:11:51 -04:00
# last_activity_at :datetime
2013-08-21 05:34:02 -04:00
# import_url :string(255)
2013-11-06 10:13:21 -05:00
# visibility_level :integer default(0), not null
2014-04-09 08:05:03 -04:00
# archived :boolean default(FALSE), not null
2015-08-02 12:52:54 -04:00
# avatar :string(255)
2014-04-09 08:05:03 -04:00
# import_status :string(255)
2014-08-25 05:25:02 -04:00
# repository_size :float default(0.0)
# star_count :integer default(0), not null
2015-01-22 12:40:03 -05:00
# import_type :string(255)
# import_source :string(255)
2015-08-02 12:52:54 -04:00
# commit_count :integer default(0)
2012-11-19 13:24:05 -05:00
#
2015-01-19 15:37:20 -05:00
require 'carrierwave/orm/activerecord'
require 'file_size_validator'
2011-10-08 17:36:38 -04:00
class Project < ActiveRecord :: Base
2015-05-02 23:11:21 -04:00
include Gitlab :: ConfigHelper
2013-03-21 15:01:14 -04:00
include Gitlab :: ShellAdapter
2013-11-06 10:13:21 -05:00
include Gitlab :: VisibilityLevel
2015-05-02 23:11:21 -04:00
include Referable
include Sortable
2015-09-25 04:46:13 -04:00
include AfterCommitQueue
2015-10-07 11:37:39 -04:00
include CaseSensitivity
2015-02-28 12:07:53 -05:00
2014-06-26 16:24:17 -04:00
extend Gitlab :: ConfigHelper
2013-01-23 09:13:28 -05:00
extend Enumerize
2013-11-27 03:49:59 -05:00
2015-08-04 18:21:12 -04:00
UNKNOWN_IMPORT_URL = 'http://unknown.git'
2014-02-28 02:57:19 -05:00
default_value_for :archived , false
2014-06-26 16:24:17 -04:00
default_value_for :visibility_level , gitlab_config_features . visibility_level
default_value_for :issues_enabled , gitlab_config_features . issues
default_value_for :merge_requests_enabled , gitlab_config_features . merge_requests
default_value_for :wiki_enabled , gitlab_config_features . wiki
2014-06-15 02:40:24 -04:00
default_value_for :wall_enabled , false
2014-06-26 16:24:17 -04:00
default_value_for :snippets_enabled , gitlab_config_features . snippets
2014-02-27 08:55:22 -05:00
2015-02-22 22:03:12 -05:00
# set last_activity_at to the same as created_at
after_create :set_last_activity_at
2015-02-22 20:31:12 -05:00
def set_last_activity_at
2015-02-22 22:03:12 -05:00
update_column ( :last_activity_at , self . created_at )
2015-02-22 20:31:12 -05:00
end
2013-10-02 09:18:28 -04:00
ActsAsTaggableOn . strict_case_match = true
2014-07-29 12:19:26 -04:00
acts_as_taggable_on :tags
2013-04-16 05:45:45 -04:00
2013-11-06 11:45:39 -05:00
attr_accessor :new_default_branch
2012-06-07 08:44:57 -04:00
# Relations
2015-02-05 17:20:55 -05:00
belongs_to :creator , foreign_key : 'creator_id' , class_name : 'User'
2015-01-19 15:37:20 -05:00
belongs_to :group , - > { where ( type : Group ) } , foreign_key : 'namespace_id'
2012-11-22 13:34:16 -05:00
belongs_to :namespace
2012-12-05 10:06:15 -05:00
2013-12-09 13:29:39 -05:00
has_one :last_event , - > { order 'events.created_at DESC' } , class_name : 'Event' , foreign_key : 'project_id'
2014-05-28 04:35:43 -04:00
# Project services
has_many :services
2012-11-19 14:34:05 -05:00
has_one :gitlab_ci_service , dependent : :destroy
2013-05-22 10:59:59 -04:00
has_one :campfire_service , dependent : :destroy
2015-08-26 19:58:49 -04:00
has_one :drone_ci_service , dependent : :destroy
2013-12-17 05:42:40 -05:00
has_one :emails_on_push_service , dependent : :destroy
2015-02-04 15:31:55 -05:00
has_one :irker_service , dependent : :destroy
2013-09-24 18:56:25 -04:00
has_one :pivotaltracker_service , dependent : :destroy
2013-05-23 14:10:32 -04:00
has_one :hipchat_service , dependent : :destroy
2013-08-19 05:11:36 -04:00
has_one :flowdock_service , dependent : :destroy
2013-11-21 07:18:02 -05:00
has_one :assembla_service , dependent : :destroy
2014-07-29 11:41:55 -04:00
has_one :asana_service , dependent : :destroy
2014-02-18 18:09:16 -05:00
has_one :gemnasium_service , dependent : :destroy
2014-03-18 13:27:03 -04:00
has_one :slack_service , dependent : :destroy
2015-04-11 14:02:13 -04:00
has_one :buildkite_service , dependent : :destroy
2014-10-16 12:34:19 -04:00
has_one :bamboo_service , dependent : :destroy
2014-12-08 16:54:09 -05:00
has_one :teamcity_service , dependent : :destroy
2014-09-30 20:48:41 -04:00
has_one :pushover_service , dependent : :destroy
2015-01-20 19:47:29 -05:00
has_one :jira_service , dependent : :destroy
has_one :redmine_service , dependent : :destroy
2015-01-20 19:55:35 -05:00
has_one :custom_issue_tracker_service , dependent : :destroy
2015-01-28 12:28:17 -05:00
has_one :gitlab_issue_tracker_service , dependent : :destroy
2015-03-18 01:59:45 -04:00
has_one :external_wiki_service , dependent : :destroy
2015-01-20 19:47:29 -05:00
2013-03-19 11:37:50 -04:00
has_one :forked_project_link , dependent : :destroy , foreign_key : " forked_to_project_id "
2015-01-27 01:12:53 -05:00
2013-03-19 11:37:50 -04:00
has_one :forked_from_project , through : :forked_project_link
2013-12-12 04:32:16 -05:00
# Merge Requests for target project should be removed with it
2015-01-19 15:37:20 -05:00
has_many :merge_requests , dependent : :destroy , foreign_key : 'target_project_id'
2013-12-12 04:32:16 -05:00
# Merge requests from source project should be kept when source project was removed
2015-01-19 15:37:20 -05:00
has_many :fork_merge_requests , foreign_key : 'source_project_id' , class_name : MergeRequest
2015-02-05 17:20:55 -05:00
has_many :issues , dependent : :destroy
2014-07-29 12:19:26 -04:00
has_many :labels , dependent : :destroy
2013-12-12 04:32:16 -05:00
has_many :services , dependent : :destroy
has_many :events , dependent : :destroy
2013-01-19 12:06:50 -05:00
has_many :milestones , dependent : :destroy
has_many :notes , dependent : :destroy
2015-01-19 15:37:20 -05:00
has_many :snippets , dependent : :destroy , class_name : 'ProjectSnippet'
has_many :hooks , dependent : :destroy , class_name : 'ProjectHook'
2013-01-19 12:06:50 -05:00
has_many :protected_branches , dependent : :destroy
2014-09-14 12:51:54 -04:00
has_many :project_members , dependent : :destroy , as : :source , class_name : 'ProjectMember'
has_many :users , through : :project_members
2013-05-06 08:10:55 -04:00
has_many :deploy_keys_projects , dependent : :destroy
has_many :deploy_keys , through : :deploy_keys_projects
2014-06-26 03:49:14 -04:00
has_many :users_star_projects , dependent : :destroy
has_many :starrers , through : :users_star_projects , source : :user
2015-09-28 09:33:19 -04:00
has_many :ci_commits , - > ( ) { order ( 'CASE WHEN ci_commits.committed_at IS NULL THEN 0 ELSE 1 END' , :committed_at , :id ) } , dependent : :destroy , class_name : 'Ci::Commit' , foreign_key : :gl_project_id
has_many :ci_builds , through : :ci_commits , source : :builds , dependent : :destroy , class_name : 'Ci::Build'
2013-05-06 08:10:55 -04:00
2015-04-17 08:31:24 -04:00
has_one :import_data , dependent : :destroy , class_name : " ProjectImportData "
2015-09-16 11:54:38 -04:00
has_one :gitlab_ci_project , dependent : :destroy , class_name : " Ci::Project " , foreign_key : :gitlab_id
2015-04-17 08:31:24 -04:00
2012-10-02 12:01:40 -04:00
delegate :name , to : :owner , allow_nil : true , prefix : true
2013-06-21 17:06:21 -04:00
delegate :members , to : :team , prefix : true
2012-10-02 12:01:40 -04:00
2012-10-08 20:10:04 -04:00
# Validations
2013-12-18 13:58:16 -05:00
validates :creator , presence : true , on : :create
2013-12-06 10:05:34 -05:00
validates :description , length : { maximum : 2000 } , allow_blank : true
2015-02-03 00:15:44 -05:00
validates :name ,
presence : true ,
length : { within : 0 .. 255 } ,
format : { with : Gitlab :: Regex . project_name_regex ,
2015-03-24 09:55:14 -04:00
message : Gitlab :: Regex . project_name_regex_message }
2015-02-03 00:15:44 -05:00
validates :path ,
presence : true ,
length : { within : 0 .. 255 } ,
2015-03-24 09:55:14 -04:00
format : { with : Gitlab :: Regex . project_path_regex ,
message : Gitlab :: Regex . project_path_regex_message }
2014-06-13 07:24:54 -04:00
validates :issues_enabled , :merge_requests_enabled ,
2012-10-08 20:10:04 -04:00
:wiki_enabled , inclusion : { in : [ true , false ] }
2013-12-06 10:05:34 -05:00
validates :issues_tracker_id , length : { maximum : 255 } , allow_blank : true
2013-09-17 09:12:10 -04:00
validates :namespace , presence : true
2012-11-23 13:11:09 -05:00
validates_uniqueness_of :name , scope : :namespace_id
validates_uniqueness_of :path , scope : :namespace_id
2013-02-11 16:13:21 -05:00
validates :import_url ,
2015-04-11 05:37:58 -04:00
format : { with : / \ A #{ URI . regexp ( %w( ssh git http https ) ) } \ z / , message : 'should be a valid url' } ,
2015-09-01 03:56:40 -04:00
if : :external_import?
2014-07-14 09:17:59 -04:00
validates :star_count , numericality : { greater_than_or_equal_to : 0 }
2013-08-13 05:04:11 -04:00
validate :check_limit , on : :create
2014-01-25 12:15:44 -05:00
validate :avatar_type ,
2015-05-06 17:33:39 -04:00
if : - > ( project ) { project . avatar . present? && project . avatar_changed? }
2015-01-19 15:37:20 -05:00
validates :avatar , file_size : { maximum : 200 . kilobytes . to_i }
2014-01-25 12:15:44 -05:00
2015-02-20 09:19:50 -05:00
mount_uploader :avatar , AvatarUploader
2012-10-08 20:10:04 -04:00
2012-06-07 08:44:57 -04:00
# Scopes
2015-02-05 23:21:21 -05:00
scope :sorted_by_activity , - > { reorder ( last_activity_at : :desc ) }
2015-02-05 17:20:55 -05:00
scope :sorted_by_stars , - > { reorder ( 'projects.star_count DESC' ) }
scope :sorted_by_names , - > { joins ( :namespace ) . reorder ( 'namespaces.name ASC, projects.name ASC' ) }
2015-01-19 15:37:20 -05:00
scope :without_user , - > ( user ) { where ( 'projects.id NOT IN (:ids)' , ids : user . authorized_projects . map ( & :id ) ) }
scope :without_team , - > ( team ) { team . projects . present? ? where ( 'projects.id NOT IN (:ids)' , ids : team . projects . map ( & :id ) ) : scoped }
scope :not_in_group , - > ( group ) { where ( 'projects.id NOT IN (:ids)' , ids : group . project_ids ) }
2015-06-09 07:28:11 -04:00
scope :in_namespace , - > ( namespace_ids ) { where ( namespace_id : namespace_ids ) }
2013-04-02 18:28:12 -04:00
scope :in_group_namespace , - > { joins ( :group ) }
2012-11-29 22:14:05 -05:00
scope :personal , - > ( user ) { where ( namespace_id : user . namespace_id ) }
2015-01-19 15:37:20 -05:00
scope :joined , - > ( user ) { where ( 'namespace_id != ?' , user . namespace_id ) }
2014-02-25 07:36:36 -05:00
scope :public_only , - > { where ( visibility_level : Project :: PUBLIC ) }
scope :public_and_internal_only , - > { where ( visibility_level : Project . public_and_internal_levels ) }
2013-11-29 11:10:59 -05:00
scope :non_archived , - > { where ( archived : false ) }
2014-03-12 11:15:03 -04:00
state_machine :import_status , initial : :none do
event :import_start do
2014-11-29 17:50:25 -05:00
transition [ :none , :finished ] = > :started
2014-03-12 11:15:03 -04:00
end
event :import_finish do
2015-02-02 22:30:09 -05:00
transition started : :finished
2014-03-12 11:15:03 -04:00
end
event :import_fail do
2015-02-02 22:30:09 -05:00
transition started : :failed
2014-03-12 13:26:09 -04:00
end
event :import_retry do
2015-02-02 22:30:09 -05:00
transition failed : :started
2014-03-12 11:15:03 -04:00
end
state :started
state :finished
2014-03-12 13:26:09 -04:00
state :failed
2015-09-25 04:46:13 -04:00
after_transition any = > :started , do : :schedule_add_import_job
2015-04-03 09:29:27 -04:00
after_transition any = > :finished , do : :clear_import_data
2014-03-12 11:15:03 -04:00
end
2012-10-08 20:10:04 -04:00
class << self
2014-02-25 07:36:36 -05:00
def public_and_internal_levels
[ Project :: PUBLIC , Project :: INTERNAL ]
end
2013-01-14 02:37:29 -05:00
def abandoned
2013-06-11 09:57:52 -04:00
where ( 'projects.last_activity_at < ?' , 6 . months . ago )
2013-01-14 02:37:29 -05:00
end
2014-02-25 07:36:36 -05:00
2014-02-13 15:45:51 -05:00
def publicish ( user )
visibility_levels = [ Project :: PUBLIC ]
2014-10-09 03:47:47 -04:00
visibility_levels << Project :: INTERNAL if user
2014-02-13 15:45:51 -05:00
where ( visibility_level : visibility_levels )
end
2014-02-25 07:36:36 -05:00
2013-01-14 02:44:27 -05:00
def with_push
2014-10-20 10:22:39 -04:00
joins ( :events ) . where ( 'events.action = ?' , Event :: PUSHED )
2013-01-14 02:44:27 -05:00
end
2012-10-08 20:10:04 -04:00
def active
2015-01-19 15:37:20 -05:00
joins ( :issues , :notes , :merge_requests ) . order ( 'issues.created_at, notes.created_at, merge_requests.created_at DESC' )
2012-10-08 20:10:04 -04:00
end
2012-06-11 01:52:44 -04:00
2014-08-27 05:47:30 -04:00
def search ( query )
2015-08-10 13:42:09 -04:00
joins ( :namespace ) .
2015-01-19 15:37:20 -05:00
where ( ' LOWER ( projects . name ) LIKE :query OR
2015-01-18 22:13:41 -05:00
LOWER ( projects . path ) LIKE :query OR
LOWER ( namespaces . name ) LIKE :query OR
2015-01-19 15:37:20 -05:00
LOWER ( projects . description ) LIKE :query ' ,
2015-01-18 22:13:41 -05:00
query : " % #{ query . try ( :downcase ) } % " )
2012-10-08 20:10:04 -04:00
end
2012-06-11 01:52:44 -04:00
2014-08-27 05:47:30 -04:00
def search_by_title ( query )
2015-01-19 15:37:20 -05:00
where ( 'projects.archived = ?' , false ) . where ( 'LOWER(projects.name) LIKE :query' , query : " % #{ query . downcase } % " )
2014-01-18 07:16:46 -05:00
end
2012-11-24 15:00:30 -05:00
def find_with_namespace ( id )
2015-10-07 11:37:39 -04:00
namespace_path , project_path = id . split ( '/' )
return nil if ! namespace_path || ! project_path
# Use of unscoped ensures we're not secretly adding any ORDER BYs, which
# have a negative impact on performance (and aren't needed for this
# query).
unscoped .
joins ( :namespace ) .
iwhere ( 'namespaces.path' = > namespace_path ) .
iwhere ( 'projects.path' = > project_path ) .
take
2012-11-24 15:00:30 -05:00
end
2013-11-27 04:51:46 -05:00
2013-11-06 10:13:21 -05:00
def visibility_levels
Gitlab :: VisibilityLevel . options
end
2013-12-29 05:58:00 -05:00
def sort ( method )
2015-02-05 22:15:05 -05:00
if method == 'repository_size_desc'
reorder ( repository_size : :desc , id : :desc )
else
order_by ( method )
2013-12-29 05:58:00 -05:00
end
end
2015-05-14 16:59:39 -04:00
def reference_pattern
name_pattern = Gitlab :: Regex :: NAMESPACE_REGEX_STR
%r{ (?<project> #{ name_pattern } / #{ name_pattern } ) }
end
2015-10-06 10:35:51 -04:00
def trending ( since = 1 . month . ago )
# By counting in the JOIN we don't expose the GROUP BY to the outer query.
# This means that calls such as "any?" and "count" just return a number of
# the total count, instead of the counts grouped per project as a Hash.
join_body = " INNER JOIN (
SELECT project_id , COUNT ( * ) AS amount
FROM notes
WHERE created_at > = #{sanitize(since)}
GROUP BY project_id
) join_note_counts ON projects . id = join_note_counts . project_id "
joins ( join_body ) . reorder ( 'join_note_counts.amount DESC' )
end
2012-07-05 14:59:37 -04:00
end
2013-01-03 14:09:18 -05:00
def team
2013-01-19 13:52:55 -05:00
@team || = ProjectTeam . new ( self )
2013-01-03 14:09:18 -05:00
end
def repository
2015-04-21 09:09:15 -04:00
@repository || = Repository . new ( path_with_namespace , nil , self )
end
2015-04-21 09:13:40 -04:00
def commit ( id = 'HEAD' )
2015-04-21 09:09:15 -04:00
repository . commit ( id )
2013-01-03 14:09:18 -05:00
end
2012-07-05 14:59:37 -04:00
def saved?
2013-02-14 06:58:33 -05:00
id && persisted?
2012-06-11 01:52:44 -04:00
end
2015-09-25 04:46:13 -04:00
def schedule_add_import_job
run_after_commit ( :add_import_job )
end
2014-03-12 13:26:09 -04:00
def add_import_job
2015-09-01 03:56:40 -04:00
if forked?
unless RepositoryForkWorker . perform_async ( id , forked_from_project . path_with_namespace , self . namespace . path )
import_fail
end
else
2015-09-25 04:46:13 -04:00
RepositoryImportWorker . perform_async ( id )
2015-09-01 03:56:40 -04:00
end
2014-03-12 13:26:09 -04:00
end
2015-04-03 09:29:27 -04:00
def clear_import_data
2015-04-17 08:31:24 -04:00
self . import_data . destroy if self . import_data
2015-04-03 09:29:27 -04:00
end
2013-02-11 16:13:21 -05:00
def import?
2015-09-01 03:56:40 -04:00
external_import? || forked?
end
def external_import?
2013-02-11 16:13:21 -05:00
import_url . present?
end
2013-08-12 11:21:47 -04:00
def imported?
2014-03-12 13:26:09 -04:00
import_finished?
end
def import_in_progress?
import? && import_status == 'started'
end
def import_failed?
import_status == 'failed'
end
def import_finished?
import_status == 'finished'
2013-08-12 11:21:47 -04:00
end
2012-06-07 08:44:57 -04:00
def check_limit
2014-04-09 11:27:34 -04:00
unless creator . can_create_project? or namespace . kind == 'group'
2014-04-04 16:17:21 -04:00
errors [ :limit_reached ] << ( " Your project limit is #{ creator . projects_limit } projects! Please contact your administrator to increase it " )
2012-06-07 08:44:57 -04:00
end
rescue
2012-08-10 19:47:54 -04:00
errors [ :base ] << ( " Can't check your ability to create project " )
2011-10-08 17:36:38 -04:00
end
2012-06-07 08:44:57 -04:00
def to_param
2015-01-24 13:02:58 -05:00
path
2011-11-06 15:38:08 -05:00
end
2015-05-02 23:11:21 -04:00
def to_reference ( _from_project = nil )
path_with_namespace
end
2012-06-07 08:44:57 -04:00
def web_url
2015-08-25 21:42:46 -04:00
Gitlab :: Application . routes . url_helpers . namespace_project_url ( self . namespace , self )
2011-12-13 16:24:31 -05:00
end
2014-01-20 06:26:34 -05:00
def web_url_without_protocol
2015-01-19 15:37:20 -05:00
web_url . split ( '://' ) [ 1 ]
2013-12-30 15:14:15 -05:00
end
2011-11-10 18:28:26 -05:00
def build_commit_note ( commit )
2015-01-19 15:37:20 -05:00
notes . new ( commit_id : commit . id , noteable_type : 'Commit' )
2011-10-08 17:36:38 -04:00
end
2011-10-26 09:46:25 -04:00
2011-11-15 03:34:30 -05:00
def last_activity
2012-10-17 15:02:52 -04:00
last_event
2011-10-31 16:57:16 -04:00
end
def last_activity_date
2013-04-02 23:06:18 -04:00
last_activity_at || updated_at
2012-03-01 13:40:32 -05:00
end
2011-12-20 01:24:14 -05:00
2012-03-05 17:26:40 -05:00
def project_id
self . id
end
2012-10-09 13:39:06 -04:00
2015-04-30 16:10:39 -04:00
def get_issue ( issue_id )
2015-01-23 14:01:09 -05:00
if default_issues_tracker?
2015-04-30 16:10:39 -04:00
issues . find_by ( iid : issue_id )
2013-02-11 06:41:12 -05:00
else
2015-04-30 16:10:39 -04:00
ExternalIssue . new ( issue_id , self )
2013-02-11 06:41:12 -05:00
end
end
2015-04-30 16:10:39 -04:00
def issue_exists? ( issue_id )
2015-05-01 13:29:36 -04:00
get_issue ( issue_id )
2015-04-30 16:10:39 -04:00
end
2015-01-28 12:28:17 -05:00
def default_issue_tracker
2015-02-09 17:33:47 -05:00
gitlab_issue_tracker_service || create_gitlab_issue_tracker_service
2015-01-28 12:28:17 -05:00
end
def issues_tracker
if external_issue_tracker
external_issue_tracker
else
default_issue_tracker
end
end
2015-01-23 14:01:09 -05:00
def default_issues_tracker?
2015-04-30 17:27:51 -04:00
! external_issue_tracker
2015-01-23 13:28:38 -05:00
end
def external_issues_trackers
2015-01-28 12:28:17 -05:00
services . select ( & :issue_tracker? ) . reject ( & :default? )
2015-01-23 13:28:38 -05:00
end
def external_issue_tracker
@external_issues_tracker || = external_issues_trackers . select ( & :activated? ) . first
end
2013-02-11 09:17:43 -05:00
def can_have_issues_tracker_id?
2015-01-23 14:01:09 -05:00
self . issues_enabled && ! self . default_issues_tracker?
2013-02-11 09:17:43 -05:00
end
2013-05-22 09:58:44 -04:00
def build_missing_services
2015-02-11 20:34:41 -05:00
services_templates = Service . where ( template : true )
2015-02-12 12:19:55 -05:00
Service . available_services_names . each do | service_name |
2015-02-11 20:34:41 -05:00
service = find_service ( services , service_name )
2013-05-22 09:58:44 -04:00
# If service is available but missing in db
2015-02-11 20:34:41 -05:00
if service . nil?
# We should check if template for the service exists
template = find_service ( services_templates , service_name )
if template . nil?
# If no template, we should create an instance. Ex `create_gitlab_ci_service`
2015-10-03 01:56:37 -04:00
self . send :" create_ #{ service_name } _service "
2015-02-11 20:34:41 -05:00
else
Service . create_from_template ( self . id , template )
end
end
2013-05-22 09:58:44 -04:00
end
end
2015-09-03 10:12:15 -04:00
def create_labels
Label . templates . each do | label |
label = label . dup
label . template = nil
label . project_id = self . id
label . save
end
end
2015-02-11 20:34:41 -05:00
def find_service ( list , name )
list . find { | service | service . to_param == name }
end
2012-11-20 12:34:05 -05:00
def gitlab_ci?
2015-09-17 17:54:52 -04:00
gitlab_ci_service && gitlab_ci_service . active && gitlab_ci_project . present?
2012-11-20 12:34:05 -05:00
end
2012-11-22 13:34:16 -05:00
2014-05-28 04:35:43 -04:00
def ci_services
services . select { | service | service . category == :ci }
end
def ci_service
2014-06-03 11:46:47 -04:00
@ci_service || = ci_services . select ( & :activated? ) . first
2014-05-28 04:35:43 -04:00
end
2014-01-25 12:15:44 -05:00
def avatar_type
2015-01-19 15:37:20 -05:00
unless self . avatar . image?
self . errors . add :avatar , 'only images allowed'
2014-01-25 12:15:44 -05:00
end
end
def avatar_in_git
@avatar_file || = 'logo.png' if repository . blob_at_branch ( 'master' , 'logo.png' )
@avatar_file || = 'logo.jpg' if repository . blob_at_branch ( 'master' , 'logo.jpg' )
@avatar_file || = 'logo.gif' if repository . blob_at_branch ( 'master' , 'logo.gif' )
@avatar_file
end
2015-02-28 12:07:53 -05:00
def avatar_url
if avatar . present?
[ gitlab_config . url , avatar . url ] . join
elsif avatar_in_git
2015-08-25 21:42:46 -04:00
Gitlab :: Application . routes . url_helpers . namespace_project_avatar_url ( namespace , self )
2015-02-28 12:07:53 -05:00
end
end
2014-01-25 12:15:44 -05:00
# For compatibility with old code
def code
path
end
2014-09-25 18:07:40 -04:00
def items_for ( entity )
2012-11-21 00:24:05 -05:00
case entity
when 'issue' then
issues
when 'merge_request' then
merge_requests
end
end
2012-12-20 15:16:51 -05:00
2015-09-29 09:37:50 -04:00
def send_move_instructions ( old_path_with_namespace )
NotificationService . new . project_was_moved ( self , old_path_with_namespace )
2012-12-20 15:16:51 -05:00
end
2013-01-02 12:00:00 -05:00
def owner
2013-09-26 07:49:22 -04:00
if group
group
2013-01-02 12:00:00 -05:00
else
2013-09-26 07:49:22 -04:00
namespace . try ( :owner )
2013-01-02 12:00:00 -05:00
end
end
2013-01-02 16:35:11 -05:00
2015-03-13 11:22:03 -04:00
def project_member_by_name_or_email ( name = nil , email = nil )
2015-01-19 15:37:20 -05:00
user = users . where ( 'name like ? or email like ?' , name , email ) . first
2014-09-14 12:51:54 -04:00
project_members . where ( user : user ) if user
2013-01-02 16:35:11 -05:00
end
# Get Team Member record by user id
2015-03-13 11:22:03 -04:00
def project_member_by_id ( user_id )
2014-09-14 12:51:54 -04:00
project_members . find_by ( user_id : user_id )
2013-01-02 16:35:11 -05:00
end
def name_with_namespace
@name_with_namespace || = begin
if namespace
2015-01-19 15:37:20 -05:00
namespace . human_name + ' / ' + name
2013-01-02 16:35:11 -05:00
else
name
end
end
end
def path_with_namespace
if namespace
namespace . path + '/' + path
else
path
end
end
2013-12-03 04:31:56 -05:00
def execute_hooks ( data , hooks_scope = :push_hooks )
hooks . send ( hooks_scope ) . each do | hook |
2015-01-23 19:10:43 -05:00
hook . async_execute ( data , hooks_scope . to_s )
2013-12-03 04:31:56 -05:00
end
2013-01-02 16:35:11 -05:00
end
2015-02-19 00:02:57 -05:00
def execute_services ( data , hooks_scope = :push_hooks )
# Call only service hooks that are active for this scope
services . send ( hooks_scope ) . each do | service |
2014-12-07 05:29:37 -05:00
service . async_execute ( data )
2013-01-02 16:35:11 -05:00
end
end
def update_merge_requests ( oldrev , newrev , ref , user )
2014-11-11 09:49:26 -05:00
MergeRequests :: RefreshService . new ( self , user ) .
execute ( oldrev , newrev , ref )
2013-01-02 16:35:11 -05:00
end
def valid_repo?
2013-04-01 09:56:25 -04:00
repository . exists?
2013-01-02 16:35:11 -05:00
rescue
2015-01-19 15:37:20 -05:00
errors . add ( :path , 'Invalid repository path' )
2013-01-02 16:35:11 -05:00
false
end
def empty_repo?
2013-04-01 09:56:25 -04:00
! repository . exists? || repository . empty?
2013-01-02 16:35:11 -05:00
end
def repo
2013-01-03 14:09:18 -05:00
repository . raw
2013-01-02 16:35:11 -05:00
end
def url_to_repo
2013-02-11 12:16:59 -05:00
gitlab_shell . url_to_repo ( path_with_namespace )
2013-01-02 16:35:11 -05:00
end
def namespace_dir
namespace . try ( :path ) || ''
end
def repo_exists?
2013-04-01 09:56:25 -04:00
@repo_exists || = repository . exists?
2013-01-02 16:35:11 -05:00
rescue
@repo_exists = false
end
def open_branches
2013-03-31 10:08:10 -04:00
all_branches = repository . branches
if protected_branches . present?
all_branches . reject! do | branch |
protected_branches_names . include? ( branch . name )
end
end
all_branches
end
def protected_branches_names
@protected_branches_names || = protected_branches . map ( & :name )
2013-01-02 16:35:11 -05:00
end
def root_ref? ( branch )
2013-01-03 14:09:18 -05:00
repository . root_ref == branch
2013-01-02 16:35:11 -05:00
end
def ssh_url_to_repo
url_to_repo
end
def http_url_to_repo
2015-07-29 07:23:28 -04:00
" #{ web_url } .git "
2013-01-02 16:35:11 -05:00
end
# Check if current branch name is marked as protected in the system
2014-09-25 18:07:40 -04:00
def protected_branch? ( branch_name )
2013-03-31 10:08:10 -04:00
protected_branches_names . include? ( branch_name )
2013-01-02 16:35:11 -05:00
end
2013-03-19 11:37:50 -04:00
2014-12-26 03:35:49 -05:00
def developers_can_push_to_protected_branch? ( branch_name )
2014-12-29 03:04:31 -05:00
protected_branches . any? { | pb | pb . name == branch_name && pb . developers_can_push }
2014-12-26 03:35:49 -05:00
end
2013-03-19 11:37:50 -04:00
def forked?
! ( forked_project_link . nil? || forked_project_link . forked_from_project . nil? )
end
2013-05-24 17:07:19 -04:00
2013-06-19 14:58:52 -04:00
def personal?
! group
end
2013-05-24 17:07:19 -04:00
def rename_repo
2014-07-31 08:39:28 -04:00
path_was = previous_changes [ 'path' ] . first
2013-05-24 17:07:19 -04:00
old_path_with_namespace = File . join ( namespace_dir , path_was )
new_path_with_namespace = File . join ( namespace_dir , path )
if gitlab_shell . mv_repository ( old_path_with_namespace , new_path_with_namespace )
2015-08-11 08:33:31 -04:00
# If repository moved successfully we need to send update instructions to users.
2013-05-24 17:07:19 -04:00
# However we cannot allow rollback since we moved repository
# So we basically we mute exceptions in next actions
begin
2013-08-20 10:44:15 -04:00
gitlab_shell . mv_repository ( " #{ old_path_with_namespace } .wiki " , " #{ new_path_with_namespace } .wiki " )
2015-09-29 09:37:50 -04:00
send_move_instructions ( old_path_with_namespace )
2013-08-29 12:12:22 -04:00
reset_events_cache
2013-05-24 17:07:19 -04:00
rescue
2013-07-29 06:46:00 -04:00
# Returning false does not rollback after_* transaction but gives
2013-05-24 17:07:19 -04:00
# us information about failing some of tasks
false
end
else
# if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs
raise Exception . new ( 'repository cannot be renamed' )
end
2015-10-06 10:09:03 -04:00
Gitlab :: UploadsTransfer . new . rename_project ( path_was , path , namespace . path )
2013-05-24 17:07:19 -04:00
end
2013-08-29 12:12:22 -04:00
2014-09-15 03:10:35 -04:00
def hook_attrs
{
name : name ,
ssh_url : ssh_url_to_repo ,
http_url : http_url_to_repo ,
2015-08-26 19:58:49 -04:00
web_url : web_url ,
2014-09-15 03:10:35 -04:00
namespace : namespace . name ,
visibility_level : visibility_level
}
end
2013-08-29 12:12:22 -04:00
# Reset events cache related to this project
#
# Since we do cache @event we need to reset cache in special cases:
# * when project was moved
# * when project was renamed
2014-01-25 12:15:44 -05:00
# * when the project avatar changes
2013-08-29 12:12:22 -04:00
# Events cache stored like events/23-20130109142513.
# The cache key includes updated_at timestamp.
# Thus it will automatically generate a new fragment
# when the event is updated because the key changes.
def reset_events_cache
Event . where ( project_id : self . id ) .
order ( 'id DESC' ) . limit ( 100 ) .
update_all ( updated_at : Time . now )
end
2013-11-05 09:41:29 -05:00
def project_member ( user )
2014-09-14 12:51:54 -04:00
project_members . where ( user_id : user ) . first
2013-11-05 09:41:29 -05:00
end
2013-11-06 11:45:39 -05:00
def default_branch
@default_branch || = repository . root_ref if repository . exists?
end
2013-11-27 03:49:59 -05:00
def reload_default_branch
@default_branch = nil
default_branch
end
2013-11-27 04:51:46 -05:00
2013-11-06 10:13:21 -05:00
def visibility_level_field
visibility_level
end
2013-11-29 11:10:59 -05:00
def archive!
update_attribute ( :archived , true )
end
def unarchive!
update_attribute ( :archived , false )
end
2013-12-16 14:30:24 -05:00
2013-12-16 12:34:56 -05:00
def change_head ( branch )
gitlab_shell . update_repository_head ( self . path_with_namespace , branch )
reload_default_branch
end
2014-04-02 14:51:53 -04:00
def forked_from? ( project )
forked? && project == forked_from_project
end
2014-05-02 09:05:08 -04:00
def update_repository_size
update_attribute ( :repository_size , repository . size )
end
2014-07-24 15:14:12 -04:00
2015-07-17 09:22:56 -04:00
def update_commit_count
update_attribute ( :commit_count , repository . commit_count )
end
2014-07-24 15:14:12 -04:00
def forks_count
ForkedProjectLink . where ( forked_from_project_id : self . id ) . count
end
2014-08-12 08:16:25 -04:00
def find_label ( name )
labels . find_by ( name : name )
end
2014-09-23 07:20:21 -04:00
def origin_merge_requests
merge_requests . where ( source_project_id : self . id )
end
2014-11-29 14:34:18 -05:00
def create_repository
2015-09-01 03:56:40 -04:00
# Forked import is handled asynchronously
unless forked?
2015-04-24 15:37:12 -04:00
if gitlab_shell . add_repository ( path_with_namespace )
true
else
2015-07-24 02:52:21 -04:00
errors . add ( :base , 'Failed to create repository via gitlab-shell' )
2015-04-24 15:37:12 -04:00
false
end
2014-11-29 14:34:18 -05:00
end
end
def repository_exists?
! ! repository . exists?
end
def create_wiki
ProjectWiki . new ( self , self . owner ) . wiki
true
2015-10-03 01:56:37 -04:00
rescue ProjectWiki :: CouldNotCreateWikiError
2015-01-19 15:37:20 -05:00
errors . add ( :base , 'Failed create wiki' )
2014-11-29 14:34:18 -05:00
false
end
2015-09-23 06:18:16 -04:00
def ci_commit ( sha )
2015-10-02 07:46:38 -04:00
ci_commits . find_by ( sha : sha )
end
def ensure_ci_commit ( sha )
ci_commit ( sha ) || ci_commits . create ( sha : sha )
2015-09-23 06:18:16 -04:00
end
2015-09-23 07:11:40 -04:00
2015-09-29 04:44:53 -04:00
def ensure_gitlab_ci_project
gitlab_ci_project || create_gitlab_ci_project
2015-09-28 07:35:26 -04:00
end
2015-10-02 04:08:16 -04:00
def enable_ci
2015-09-23 07:11:40 -04:00
service = gitlab_ci_service || create_gitlab_ci_service
service . active = true
service . save
end
2011-10-08 17:36:38 -04:00
end