2015-01-19 15:37:20 -05:00
require 'carrierwave/orm/activerecord'
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-11-03 08:45:41 -05:00
include Gitlab :: CurrentSettings
2016-04-18 12:53:32 -04:00
include AccessRequestable
2016-10-06 17:17:11 -04:00
include CacheMarkdownField
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
2016-01-05 02:18:23 -05:00
include TokenAuthenticatable
2016-08-01 18:31:21 -04:00
include ProjectFeaturesCompatibility
2015-11-25 17:06:04 -05:00
2014-06-26 16:24:17 -04:00
extend Gitlab :: ConfigHelper
2013-11-27 03:49:59 -05:00
2016-10-10 10:22:30 -04:00
class BoardLimitExceeded < StandardError ; end
2016-10-06 14:58:28 -04:00
NUMBER_OF_PERMITTED_BOARDS = 1
2015-08-04 18:21:12 -04:00
UNKNOWN_IMPORT_URL = 'http://unknown.git'
2016-10-06 17:17:11 -04:00
cache_markdown_field :description , pipeline : :description
2016-08-01 18:31:21 -04:00
delegate :feature_available? , :builds_enabled? , :wiki_enabled? , :merge_requests_enabled? , to : :project_feature , allow_nil : true
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
2016-05-09 13:29:57 -04:00
default_value_for :container_registry_enabled , gitlab_config_features . container_registry
2016-06-29 23:35:00 -04:00
default_value_for ( :repository_storage ) { current_application_settings . repository_storage }
2015-12-04 06:55:23 -05:00
default_value_for ( :shared_runners_enabled ) { current_application_settings . shared_runners_enabled }
2016-10-28 16:56:13 -04:00
default_value_for :issues_enabled , gitlab_config_features . issues
default_value_for :merge_requests_enabled , gitlab_config_features . merge_requests
default_value_for :builds_enabled , gitlab_config_features . builds
default_value_for :wiki_enabled , gitlab_config_features . wiki
default_value_for :snippets_enabled , gitlab_config_features . snippets
2014-02-27 08:55:22 -05:00
2016-06-22 17:04:51 -04:00
after_create :ensure_dir_exist
2016-10-14 18:38:41 -04:00
after_create :create_project_feature , unless : :project_feature
2016-06-22 17:04:51 -04:00
after_save :ensure_dir_exist , if : :namespace_id_changed?
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
2016-03-18 08:28:16 -04:00
# update visibility_level of forks
2015-11-19 18:13:56 -05:00
after_update :update_forks_visibility_level
def update_forks_visibility_level
return unless visibility_level < visibility_level_was
forks . each do | forked_project |
if forked_project . visibility_level > visibility_level
forked_project . visibility_level = visibility_level
forked_project . save!
end
end
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
2015-05-05 11:13:11 -04:00
attr_accessor :old_path_with_namespace
2013-11-06 11:45:39 -05:00
2016-05-12 17:47:45 -04:00
alias_attribute :title , :name
2012-06-07 08:44:57 -04:00
# Relations
2016-10-18 17:20:36 -04:00
belongs_to :creator , class_name : 'User'
2016-09-05 22:58:27 -04: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
2016-10-18 17:20:36 -04:00
has_one :last_event , - > { order 'events.created_at DESC' } , class_name : 'Event'
2016-10-06 14:58:28 -04:00
has_many :boards , before_add : :validate_board_limit , dependent : :destroy
2016-08-01 18:31:21 -04:00
2014-05-28 04:35:43 -04:00
# Project services
has_many :services
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-12-07 07:23:23 -05:00
has_one :builds_email_service , dependent : :destroy
2016-08-31 14:16:24 -04:00
has_one :pipelines_email_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
2016-06-26 13:36:43 -04:00
has_one :bugzilla_service , dependent : :destroy
2016-06-08 12:10:46 -04:00
has_one :gitlab_issue_tracker_service , dependent : :destroy , inverse_of : :project
2015-03-18 01:59:45 -04:00
has_one :external_wiki_service , dependent : :destroy
2015-01-20 19:47:29 -05:00
2015-11-18 16:11:15 -05:00
has_one :forked_project_link , dependent : :destroy , foreign_key : " forked_to_project_id "
has_one :forked_from_project , through : :forked_project_link
has_many :forked_project_links , foreign_key : " forked_from_project_id "
has_many :forks , through : :forked_project_links , source : :forked_to_project
2015-01-27 01:12:53 -05:00
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
2016-09-20 16:07:56 -04:00
has_many :labels , dependent : :destroy , class_name : 'ProjectLabel'
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
2016-06-27 10:20:57 -04:00
2016-10-18 17:20:36 -04:00
has_many :project_members , - > { where ( requested_at : nil ) } , dependent : :destroy , as : :source
2016-06-02 12:05:06 -04:00
alias_method :members , :project_members
2016-06-27 10:20:57 -04:00
has_many :users , through : :project_members
has_many :requesters , - > { where . not ( requested_at : nil ) } , dependent : :destroy , as : :source , class_name : 'ProjectMember'
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-11-05 05:16:41 -05:00
has_many :releases , dependent : :destroy
2015-10-12 10:42:14 -04:00
has_many :lfs_objects_projects , dependent : :destroy
has_many :lfs_objects , through : :lfs_objects_projects
2016-03-11 11:47:05 -05:00
has_many :project_group_links , dependent : :destroy
has_many :invited_groups , through : :project_group_links , source : :group
2016-03-09 09:06:58 -05:00
has_many :todos , dependent : :destroy
2016-03-28 14:31:36 -04:00
has_many :notification_settings , dependent : :destroy , as : :source
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 "
2016-08-01 18:31:21 -04:00
has_one :project_feature , dependent : :destroy
2015-12-04 06:55:23 -05:00
2016-10-18 17:20:36 -04:00
has_many :commit_statuses , dependent : :destroy , foreign_key : :gl_project_id
2016-06-02 10:19:18 -04:00
has_many :pipelines , dependent : :destroy , class_name : 'Ci::Pipeline' , foreign_key : :gl_project_id
2015-12-10 11:44:06 -05:00
has_many :builds , class_name : 'Ci::Build' , foreign_key : :gl_project_id # the builds are created from the commit_statuses
has_many :runner_projects , dependent : :destroy , class_name : 'Ci::RunnerProject' , foreign_key : :gl_project_id
has_many :runners , through : :runner_projects , source : :runner , class_name : 'Ci::Runner'
has_many :variables , dependent : :destroy , class_name : 'Ci::Variable' , foreign_key : :gl_project_id
has_many :triggers , dependent : :destroy , class_name : 'Ci::Trigger' , foreign_key : :gl_project_id
2016-06-10 17:36:54 -04:00
has_many :environments , dependent : :destroy
has_many :deployments , dependent : :destroy
2015-12-04 06:55:23 -05:00
2015-12-10 11:44:06 -05:00
accepts_nested_attributes_for :variables , allow_destroy : true
2016-08-01 18:31:21 -04:00
accepts_nested_attributes_for :project_feature
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
2016-09-16 11:54:21 -04:00
delegate :add_user , to : :team
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 }
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
2016-07-18 02:54:25 -04:00
validates :import_url , addressable_url : true , 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 }
2016-03-20 16:03:53 -04:00
validate :visibility_level_allowed_by_group
2016-03-20 19:42:30 -04:00
validate :visibility_level_allowed_as_fork
2016-06-27 14:23:19 -04:00
validate :check_wiki_path_conflict
2016-06-22 17:04:51 -04:00
validates :repository_storage ,
presence : true ,
inclusion : { in : - > ( _object ) { Gitlab . config . repositories . storages . keys } }
2014-01-25 12:15:44 -05:00
2016-01-05 02:18:23 -05:00
add_authentication_token_field :runners_token
2016-01-08 11:00:45 -05:00
before_save :ensure_runners_token
2015-12-04 06:55:23 -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
2016-03-29 10:57:24 -04:00
default_scope { where ( pending_delete : false ) }
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' ) }
2015-06-09 07:28:11 -04:00
scope :in_namespace , - > ( namespace_ids ) { where ( namespace_id : namespace_ids ) }
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 ) }
2016-05-18 18:13:54 -04:00
scope :visible_to_user , - > ( user ) { where ( id : user . authorized_projects . select ( :id ) . reorder ( nil ) ) }
2013-11-29 11:10:59 -05:00
scope :non_archived , - > { where ( archived : false ) }
2016-03-06 23:07:19 -05:00
scope :for_milestones , - > ( ids ) { joins ( :milestones ) . where ( 'milestones.id' = > ids ) . distinct }
2016-05-18 18:13:54 -04:00
scope :with_push , - > { joins ( :events ) . where ( 'events.action = ?' , Event :: PUSHED ) }
2016-08-01 18:31:21 -04:00
scope :with_builds_enabled , - > { joins ( 'LEFT JOIN project_features ON projects.id = project_features.project_id' ) . where ( 'project_features.builds_access_level IS NULL or project_features.builds_access_level > 0' ) }
scope :with_issues_enabled , - > { joins ( 'LEFT JOIN project_features ON projects.id = project_features.project_id' ) . where ( 'project_features.issues_access_level IS NULL or project_features.issues_access_level > 0' ) }
2016-05-18 18:13:54 -04:00
scope :active , - > { joins ( :issues , :notes , :merge_requests ) . order ( 'issues.created_at, notes.created_at, merge_requests.created_at DESC' ) }
scope :abandoned , - > { where ( 'projects.last_activity_at < ?' , 6 . months . ago ) }
2013-11-29 11:10:59 -05:00
2016-08-05 09:29:20 -04:00
scope :excluding_project , - > ( project ) { where . not ( id : project ) }
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
2016-05-20 08:55:27 -04:00
after_transition any = > :finished , do : :reset_cache_and_import_attrs
2014-03-12 11:15:03 -04:00
end
2012-10-08 20:10:04 -04:00
class << self
2016-03-01 06:02:06 -05:00
# Searches for a list of projects based on the query given in `query`.
#
# On PostgreSQL this method uses "ILIKE" to perform a case-insensitive
# search. On MySQL a regular "LIKE" is used as it's already
# case-insensitive.
#
# query - The search query as a String.
2014-08-27 05:47:30 -04:00
def search ( query )
2016-03-04 06:01:21 -05:00
ptable = arel_table
2016-03-01 06:02:06 -05:00
ntable = Namespace . arel_table
pattern = " % #{ query } % "
projects = select ( :id ) . where (
ptable [ :path ] . matches ( pattern ) .
or ( ptable [ :name ] . matches ( pattern ) ) .
or ( ptable [ :description ] . matches ( pattern ) )
)
2016-03-15 11:46:17 -04:00
# We explicitly remove any eager loading clauses as they're:
#
# 1. Not needed by this query
# 2. Combined with .joins(:namespace) lead to all columns from the
# projects & namespaces tables being selected, leading to a SQL error
# due to the columns of all UNION'd queries no longer being the same.
2016-03-01 06:02:06 -05:00
namespaces = select ( :id ) .
2016-03-15 11:46:17 -04:00
except ( :includes ) .
2016-03-01 06:02:06 -05:00
joins ( :namespace ) .
where ( ntable [ :name ] . matches ( pattern ) )
union = Gitlab :: SQL :: Union . new ( [ projects , namespaces ] )
where ( " projects.id IN ( #{ union . to_sql } ) " )
2012-10-08 20:10:04 -04:00
end
2012-06-11 01:52:44 -04:00
2016-01-18 12:41:49 -05:00
def search_by_visibility ( level )
2016-01-19 09:53:40 -05:00
where ( visibility_level : Gitlab :: VisibilityLevel . const_get ( level . upcase ) )
2016-01-18 12:41:49 -05:00
end
2014-08-27 05:47:30 -04:00
def search_by_title ( query )
2016-03-01 06:16:15 -05:00
pattern = " % #{ query } % "
table = Project . arel_table
non_archived . where ( table [ :name ] . matches ( pattern ) )
2014-01-18 07:16:46 -05:00
end
2016-06-08 11:03:51 -04:00
# Finds a single project for the given path.
#
# path - The full project path (including namespace path).
#
# Returns a Project, or nil if no project could be found.
def find_with_namespace ( path )
2016-06-15 12:07:04 -04:00
namespace_path , project_path = path . split ( '/' , 2 )
return unless namespace_path && project_path
namespace_path = connection . quote ( namespace_path )
project_path = connection . quote ( project_path )
# On MySQL we want to ensure the ORDER BY uses a case-sensitive match so
# any literal matches come first, for this we have to use "BINARY".
# Without this there's still no guarantee in what order MySQL will return
# rows.
binary = Gitlab :: Database . mysql? ? 'BINARY' : ''
order_sql = " (CASE WHEN #{ binary } namespaces.path = #{ namespace_path } " \
" AND #{ binary } projects.path = #{ project_path } THEN 0 ELSE 1 END) "
where_paths_in ( [ path ] ) . reorder ( order_sql ) . take
2016-06-08 11:03:51 -04:00
end
2015-10-07 11:37:39 -04:00
2016-06-08 11:03:51 -04:00
# Builds a relation to find multiple projects by their full paths.
#
# Each path must be in the following format:
#
# namespace_path/project_path
#
# For example:
#
# gitlab-org/gitlab-ce
#
# Usage:
#
# Project.where_paths_in(%w{gitlab-org/gitlab-ce gitlab-org/gitlab-ee})
#
# This would return the projects with the full paths matching the values
# given.
#
# paths - An Array of full paths (namespace path + project path) for which
# to find the projects.
#
# Returns an ActiveRecord::Relation.
def where_paths_in ( paths )
wheres = [ ]
cast_lower = Gitlab :: Database . postgresql?
2015-10-07 11:37:39 -04:00
2016-06-08 11:03:51 -04:00
paths . each do | path |
namespace_path , project_path = path . split ( '/' , 2 )
2015-10-07 11:37:39 -04:00
2016-06-08 11:03:51 -04:00
next unless namespace_path && project_path
namespace_path = connection . quote ( namespace_path )
project_path = connection . quote ( project_path )
2015-10-20 10:16:08 -04:00
2016-06-08 11:03:51 -04:00
where = " (namespaces.path = #{ namespace_path }
AND projects . path = #{project_path})"
2015-10-20 10:16:08 -04:00
2016-06-08 11:03:51 -04:00
if cast_lower
where = " (
#{where}
OR (
LOWER ( namespaces . path ) = LOWER ( #{namespace_path})
AND LOWER ( projects . path ) = LOWER ( #{project_path})
)
) "
end
wheres << where
end
if wheres . empty?
none
else
joins ( :namespace ) . where ( wheres . join ( ' OR ' ) )
end
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
2016-10-07 09:24:09 -04:00
def trending
joins ( 'INNER JOIN trending_projects ON projects.id = trending_projects.project_id' ) .
reorder ( 'trending_projects.id ASC' )
2015-10-06 10:35:51 -04:00
end
2016-08-09 17:08:33 -04:00
def cached_count
Rails . cache . fetch ( 'total_project_count' , expires_in : 5 . minutes ) do
Project . count
end
end
2016-09-29 14:50:14 -04:00
def group_ids
2016-10-28 14:20:46 -04:00
joins ( :namespace ) . where ( namespaces : { type : 'Group' } ) . select ( :namespace_id )
2016-09-29 14:50:14 -04:00
end
2012-07-05 14:59:37 -04:00
end
2016-08-24 18:08:23 -04:00
def lfs_enabled?
2016-09-01 19:49:48 -04:00
return namespace . lfs_enabled? if self [ :lfs_enabled ] . nil?
2016-08-25 18:59:31 -04:00
2016-09-01 19:49:48 -04:00
self [ :lfs_enabled ] && Gitlab . config . lfs . enabled
2016-08-24 18:08:23 -04:00
end
2016-06-22 17:04:51 -04:00
def repository_storage_path
Gitlab . config . repositories . storages [ repository_storage ]
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
2016-02-16 06:13:13 -05:00
@repository || = Repository . new ( path_with_namespace , self )
2015-04-21 09:09:15 -04:00
end
2016-06-01 16:57:50 -04:00
def container_registry_path_with_namespace
path_with_namespace . downcase
end
2016-05-09 15:27:06 -04:00
def container_registry_repository
2016-05-18 12:22:28 -04:00
return unless Gitlab . config . registry . enabled
2016-05-09 15:14:46 -04:00
@container_registry_repository || = begin
2016-06-01 16:57:50 -04:00
token = Auth :: ContainerRegistryAuthenticationService . full_access_token ( container_registry_path_with_namespace )
2016-05-09 15:27:06 -04:00
url = Gitlab . config . registry . api_url
2016-05-16 10:48:39 -04:00
host_port = Gitlab . config . registry . host_port
registry = ContainerRegistry :: Registry . new ( url , token : token , path : host_port )
2016-06-01 16:57:50 -04:00
registry . repository ( container_registry_path_with_namespace )
2016-05-09 13:29:57 -04:00
end
2016-04-18 08:13:16 -04:00
end
2016-05-09 15:27:06 -04:00
def container_registry_repository_url
2016-05-14 15:22:10 -04:00
if Gitlab . config . registry . enabled
2016-06-01 16:57:50 -04:00
" #{ Gitlab . config . registry . host_port } / #{ container_registry_path_with_namespace } "
2016-05-09 13:29:57 -04:00
end
2016-04-18 08:13:16 -04:00
end
2016-05-09 15:41:48 -04:00
def has_container_registry_tags?
2016-05-18 12:22:28 -04:00
return unless container_registry_repository
container_registry_repository . tags . any?
2016-05-09 15:41:48 -04:00
end
2016-07-07 07:25:05 -04:00
def commit ( ref = 'HEAD' )
repository . commit ( ref )
2013-01-03 14:09:18 -05:00
end
2016-07-19 08:59:38 -04:00
# ref can't be HEAD, can only be branch/tag name or SHA
2016-07-19 12:52:16 -04:00
def latest_successful_builds_for ( ref = default_branch )
2016-08-11 06:09:26 -04:00
latest_pipeline = pipelines . latest_successful_for ( ref )
2016-07-21 05:02:28 -04:00
if latest_pipeline
latest_pipeline . builds . latest . with_artifacts
else
builds . none
end
2016-07-19 08:59:38 -04:00
end
2016-01-27 11:23:59 -05:00
def merge_base_commit ( first_commit_id , second_commit_id )
2016-01-28 09:10:48 -05:00
sha = repository . merge_base ( first_commit_id , second_commit_id )
repository . commit ( sha ) if sha
2016-01-27 11:23:59 -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
2014-03-12 13:26:09 -04:00
def add_import_job
2015-09-01 03:56:40 -04:00
if forked?
2016-07-26 17:22:13 -04:00
job_id = RepositoryForkWorker . perform_async ( id , forked_from_project . repository_storage_path ,
forked_from_project . path_with_namespace ,
self . namespace . path )
2015-09-01 03:56:40 -04:00
else
2016-04-12 02:07:06 -04:00
job_id = RepositoryImportWorker . perform_async ( self . id )
end
if job_id
Rails . logger . info " Import job started for #{ path_with_namespace } with job ID #{ job_id } "
else
Rails . logger . error " Import job failed to start for #{ path_with_namespace } "
2015-09-01 03:56:40 -04:00
end
2014-03-12 13:26:09 -04:00
end
2016-05-20 08:55:27 -04:00
def reset_cache_and_import_attrs
2015-11-11 10:22:51 -05:00
ProjectCacheWorker . perform_async ( self . id )
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
2016-03-21 08:15:51 -04:00
def import_url = ( value )
2016-06-30 07:17:37 -04:00
return super ( value ) unless Gitlab :: UrlSanitizer . valid? ( value )
2016-05-18 22:16:36 -04:00
import_url = Gitlab :: UrlSanitizer . new ( value )
2016-03-21 10:11:05 -04:00
super ( import_url . sanitized_url )
2016-07-13 07:57:43 -04:00
create_or_update_import_data ( credentials : import_url . credentials )
2016-03-21 08:15:51 -04:00
end
def import_url
2016-10-05 08:31:33 -04:00
if import_data && super . present?
2016-05-18 22:16:36 -04:00
import_url = Gitlab :: UrlSanitizer . new ( super , credentials : import_data . credentials )
2016-03-21 10:11:05 -04:00
import_url . full_url
else
super
2016-03-21 08:15:51 -04:00
end
end
2016-03-21 13:09:47 -04:00
2016-07-13 04:40:03 -04:00
def valid_import_url?
valid? || errors . messages [ :import_url ] . nil?
end
2016-04-07 09:08:38 -04:00
def create_or_update_import_data ( data : nil , credentials : nil )
2016-07-17 23:12:32 -04:00
return unless import_url . present? && valid_import_url?
2016-07-13 07:57:43 -04:00
2016-03-21 13:20:25 -04:00
project_import_data = import_data || build_import_data
2016-04-07 10:53:15 -04:00
if data
project_import_data . data || = { }
project_import_data . data = project_import_data . data . merge ( data )
end
2016-04-07 09:08:38 -04:00
if credentials
project_import_data . credentials || = { }
project_import_data . credentials = project_import_data . credentials . merge ( credentials )
end
2016-04-07 09:00:20 -04:00
project_import_data . save
2016-03-21 13:09:47 -04:00
end
2016-03-21 08:15:51 -04:00
2013-02-11 16:13:21 -05:00
def import?
2016-06-14 14:32:19 -04:00
external_import? || forked? || gitlab_project_import?
2015-09-01 03:56:40 -04:00
end
2016-02-17 09:05:44 -05:00
def no_import?
import_status == 'none'
end
2015-09-01 03:56:40 -04:00
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
2015-11-11 10:23:51 -05:00
def safe_import_url
2016-05-20 08:55:27 -04:00
Gitlab :: UrlSanitizer . new ( import_url ) . masked_url
2015-11-11 10:23:51 -05:00
end
2016-06-14 06:47:07 -04:00
def gitlab_project_import?
import_type == 'gitlab_project'
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'
2016-05-24 12:39:56 -04:00
projects_limit = creator . projects_limit
if projects_limit == 0
2016-05-25 03:52:13 -04:00
self . errors . add ( :limit_reached , " Personal project creation is not allowed. Please contact your administrator with questions " )
2016-05-24 12:39:56 -04:00
else
2016-05-25 03:52:13 -04:00
self . errors . add ( :limit_reached , " Your project limit is #{ projects_limit } projects! Please contact your administrator to increase it " )
2016-05-24 12:39:56 -04:00
end
2012-06-07 08:44:57 -04:00
end
rescue
2016-03-20 16:03:53 -04:00
self . errors . add ( :base , " Can't check your ability to create project " )
2011-10-08 17:36:38 -04:00
end
2016-03-20 16:03:53 -04:00
def visibility_level_allowed_by_group
return if visibility_level_allowed_by_group?
level_name = Gitlab :: VisibilityLevel . level_name ( self . visibility_level ) . downcase
group_level_name = Gitlab :: VisibilityLevel . level_name ( self . group . visibility_level ) . downcase
self . errors . add ( :visibility_level , " #{ level_name } is not allowed in a #{ group_level_name } group. " )
end
def visibility_level_allowed_as_fork
return if visibility_level_allowed_as_fork?
level_name = Gitlab :: VisibilityLevel . level_name ( self . visibility_level ) . downcase
self . errors . add ( :visibility_level , " #{ level_name } is not allowed since the fork source project has lower visibility. " )
2016-03-18 08:28:16 -04:00
end
2016-06-27 14:23:19 -04:00
def check_wiki_path_conflict
return if path . blank?
path_to_check = path . ends_with? ( '.wiki' ) ? path . chomp ( '.wiki' ) : " #{ path } .wiki "
if Project . where ( namespace_id : namespace_id , path : path_to_check ) . exists?
errors . add ( :name , 'has already been taken' )
end
end
2012-06-07 08:44:57 -04:00
def to_param
2016-07-26 14:27:42 -04:00
if persisted? && errors . include? ( :path )
path_was
else
path
end
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
2016-03-24 12:00:26 -04:00
Gitlab :: Routing . 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
2016-06-23 06:36:44 -04:00
def new_issue_address ( author )
2016-08-19 20:07:02 -04:00
# This feature is disabled for the time being.
return nil
if Gitlab :: IncomingEmail . enabled? && author # rubocop:disable Lint/UnreachableCode
2016-06-24 04:05:05 -04:00
Gitlab :: IncomingEmail . reply_address (
" #{ path_with_namespace } + #{ author . authentication_token } " )
end
2016-06-23 06:36:44 -04: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
2016-10-06 18:05:27 -04:00
def issue_reference_pattern
issues_tracker . reference_pattern
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_issue_tracker
2016-06-03 04:21:18 -04:00
if has_external_issue_tracker . nil? # To populate existing projects
cache_has_external_issue_tracker
end
if has_external_issue_tracker?
return @external_issue_tracker if defined? ( @external_issue_tracker )
@external_issue_tracker = services . external_issue_trackers . first
else
nil
end
end
def cache_has_external_issue_tracker
update_column ( :has_external_issue_tracker , services . external_issue_trackers . any? )
2015-01-23 13:28:38 -05:00
end
2016-08-26 16:20:00 -04:00
def has_wiki?
wiki_enabled? || has_external_wiki?
end
2016-07-17 10:32:11 -04:00
def external_wiki
if has_external_wiki . nil?
cache_has_external_wiki # Populate
end
if has_external_wiki
@external_wiki || = services . external_wikis . first
else
nil
end
end
def cache_has_external_wiki
update_column ( :has_external_wiki , services . external_wikis . any? )
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`
2016-09-14 11:59:21 -04:00
public_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 |
2016-10-17 22:27:10 -04:00
params = label . attributes . except ( 'id' , 'template' , 'created_at' , 'updated_at' )
2016-10-28 05:31:45 -04:00
Labels :: FindOrCreateService . new ( nil , self , params ) . execute ( skip_authorization : true )
2015-09-03 10:12:15 -04:00
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
2014-05-28 04:35:43 -04:00
def ci_services
2016-03-10 09:32:31 -05:00
services . where ( category : :ci )
2014-05-28 04:35:43 -04:00
end
def ci_service
2016-03-10 09:32:31 -05:00
@ci_service || = ci_services . reorder ( nil ) . find_by ( active : true )
2014-05-28 04:35:43 -04:00
end
2015-12-17 17:08:14 -05:00
def jira_tracker?
issues_tracker . to_param == 'jira'
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
2016-03-17 11:53:05 -04:00
repository . avatar
2014-01-25 12:15:44 -05:00
end
2015-02-28 12:07:53 -05:00
def avatar_url
2016-07-05 10:51:11 -04:00
if self [ :avatar ] . present?
2015-02-28 12:07:53 -05:00
[ gitlab_config . url , avatar . url ] . join
elsif avatar_in_git
2016-03-24 12:00:26 -04:00
Gitlab :: Routing . 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 )
2015-12-28 21:10:46 -05:00
# New project path needs to be committed to the DB or notification will
# retrieve stale information
run_after_commit { 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
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
2016-06-02 12:05:06 -04:00
alias_method :human_name , :name_with_namespace
2013-01-02 16:35:11 -05:00
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 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?
2015-10-20 14:47:29 -04:00
! repository . exists? || ! repository . has_visible_content?
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
2016-07-06 05:37:30 -04:00
# Branches that are not _exactly_ matched by a protected branch.
2013-01-02 16:35:11 -05:00
def open_branches
2016-07-06 05:37:30 -04:00
exact_protected_branch_names = protected_branches . reject ( & :wildcard? ) . map ( & :name )
branch_names = repository . branches . map ( & :name )
non_open_branch_names = Set . new ( exact_protected_branch_names ) . intersection ( Set . new ( branch_names ) )
repository . branches . reject { | branch | non_open_branch_names . include? branch . 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 )
2016-08-01 05:06:57 -04:00
return true if empty_repo? && default_branch_protected?
2016-06-15 01:45:01 -04:00
@protected_branches || = self . protected_branches . to_a
ProtectedBranch . matching ( branch_name , protected_branches : @protected_branches ) . present?
2013-01-02 16:35:11 -05:00
end
2013-03-19 11:37:50 -04:00
2016-08-01 11:48:15 -04:00
def user_can_push_to_empty_repo? ( user )
! default_branch_protected? || team . max_member_access ( user . id ) > Gitlab :: Access :: DEVELOPER
2016-08-01 05:06:57 -04: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 )
2016-07-23 09:35:00 -04:00
Rails . logger . error " Attempting to rename #{ old_path_with_namespace } -> #{ new_path_with_namespace } "
2016-02-25 11:43:47 -05:00
expire_caches_before_rename ( old_path_with_namespace )
2016-05-09 15:41:48 -04:00
if has_container_registry_tags?
2016-07-23 09:35:00 -04:00
Rails . logger . error " Project #{ old_path_with_namespace } cannot be renamed because container registry tags are present "
2016-05-09 15:41:48 -04:00
# we currently doesn't support renaming repository if it contains tags in container registry
2016-05-12 14:03:04 -04:00
raise Exception . new ( 'Project cannot be renamed, because tags are present in its container registry' )
2016-05-09 15:41:48 -04:00
end
2016-06-22 17:04:51 -04:00
if gitlab_shell . mv_repository ( repository_storage_path , 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
2016-06-22 17:04:51 -04:00
gitlab_shell . mv_repository ( repository_storage_path , " #{ 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
2015-05-05 11:13:11 -04:00
@old_path_with_namespace = old_path_with_namespace
SystemHooksService . new . execute_hooks_for ( self , :rename )
2015-10-27 10:22:48 -04:00
@repository = nil
2016-07-23 09:35:00 -04:00
rescue = > e
Rails . logger . error " Exception renaming #{ old_path_with_namespace } -> #{ new_path_with_namespace } : #{ e } "
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
2016-07-23 09:35:00 -04:00
Rails . logger . error " Repository could not be renamed: #{ old_path_with_namespace } -> #{ new_path_with_namespace } "
2013-05-24 17:07:19 -04:00
# 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
2016-07-23 09:35:00 -04:00
Gitlab :: AppLogger . info " Project was renamed: #{ old_path_with_namespace } -> #{ new_path_with_namespace } "
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
2016-02-25 11:43:47 -05:00
# Expires various caches before a project is renamed.
def expire_caches_before_rename ( old_path )
repo = Repository . new ( old_path , self )
wiki = Repository . new ( " #{ old_path } .wiki " , self )
if repo . exists?
2016-04-22 03:39:31 -04:00
repo . before_delete
2016-02-25 11:43:47 -05:00
end
if wiki . exists?
2016-04-22 03:39:31 -04:00
wiki . before_delete
2016-02-25 11:43:47 -05:00
end
end
2016-04-15 07:08:22 -04:00
def hook_attrs ( backward : true )
attrs = {
2014-09-15 03:10:35 -04:00
name : name ,
Add new data to project in push, issue, merge-request and note webhooks data
- Add `avatar_url`, `description`, `git_ssh_url`, `git_http_url`,
`path_with_namespace` and `default_branch` in `project` in push, issue,
merge-request and note webhooks data
- Deprecate the `ssh_url` in favor of `git_ssh_url` and `http_url` in
favor of `git_http_url` in `project` for push, issue, merge-request and
note webhooks data
- Deprecate the `repository` key in push, issue, merge-request and
note webhooks data, use `project` instead
2016-02-06 09:20:21 -05:00
description : description ,
2015-08-26 19:58:49 -04:00
web_url : web_url ,
Add new data to project in push, issue, merge-request and note webhooks data
- Add `avatar_url`, `description`, `git_ssh_url`, `git_http_url`,
`path_with_namespace` and `default_branch` in `project` in push, issue,
merge-request and note webhooks data
- Deprecate the `ssh_url` in favor of `git_ssh_url` and `http_url` in
favor of `git_http_url` in `project` for push, issue, merge-request and
note webhooks data
- Deprecate the `repository` key in push, issue, merge-request and
note webhooks data, use `project` instead
2016-02-06 09:20:21 -05:00
avatar_url : avatar_url ,
git_ssh_url : ssh_url_to_repo ,
git_http_url : http_url_to_repo ,
2014-09-15 03:10:35 -04:00
namespace : namespace . name ,
Add new data to project in push, issue, merge-request and note webhooks data
- Add `avatar_url`, `description`, `git_ssh_url`, `git_http_url`,
`path_with_namespace` and `default_branch` in `project` in push, issue,
merge-request and note webhooks data
- Deprecate the `ssh_url` in favor of `git_ssh_url` and `http_url` in
favor of `git_http_url` in `project` for push, issue, merge-request and
note webhooks data
- Deprecate the `repository` key in push, issue, merge-request and
note webhooks data, use `project` instead
2016-02-06 09:20:21 -05:00
visibility_level : visibility_level ,
path_with_namespace : path_with_namespace ,
default_branch : default_branch ,
2014-09-15 03:10:35 -04:00
}
2016-04-15 07:08:22 -04:00
# Backward compatibility
if backward
attrs . merge! ( {
homepage : web_url ,
url : url_to_repo ,
ssh_url : ssh_url_to_repo ,
http_url : http_url_to_repo
} )
end
attrs
2014-09-15 03:10:35 -04:00
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 )
2015-12-14 21:53:52 -05:00
project_members . find_by ( user_id : user )
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 )
2016-02-23 06:02:59 -05:00
repository . before_change_head
2016-02-17 06:31:54 -05:00
repository . rugged . references . create ( 'HEAD' ,
" refs/heads/ #{ branch } " ,
force : true )
2016-03-14 19:33:00 -04:00
repository . copy_gitattributes ( branch )
2016-08-12 10:29:13 -04:00
repository . expire_avatar_cache ( branch )
2013-12-16 12:34:56 -05:00
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
2015-11-04 17:40:43 -05:00
forks . count
2014-07-24 15:14:12 -04:00
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?
2016-06-22 17:04:51 -04:00
if gitlab_shell . add_repository ( repository_storage_path , path_with_namespace )
2016-03-18 10:31:19 -04:00
repository . after_create
2015-04-24 15:37:12 -04:00
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
2015-12-17 17:08:14 -05:00
def jira_tracker_active?
jira_tracker? && jira_service . active
end
2016-03-11 12:37:46 -05:00
def allowed_to_share_with_group?
2016-03-11 13:03:19 -05:00
! namespace . share_with_group_lock
2016-03-11 12:37:46 -05:00
end
2016-08-19 05:42:49 -04:00
def pipeline_for ( ref , sha = nil )
sha || = commit ( ref ) . try ( :sha )
2016-08-11 05:55:23 -04:00
return unless sha
2016-08-19 05:42:49 -04:00
2016-06-02 10:19:18 -04:00
pipelines . order ( id : :desc ) . find_by ( sha : sha , ref : ref )
2015-10-02 07:46:38 -04:00
end
2016-08-11 05:26:04 -04:00
def ensure_pipeline ( ref , sha , current_user = nil )
pipeline_for ( ref , sha ) ||
pipelines . create ( sha : sha , ref : ref , user : current_user )
2015-09-23 06:18:16 -04:00
end
2015-09-23 07:11:40 -04:00
2015-11-09 10:48:03 -05:00
def enable_ci
2016-08-01 18:31:21 -04:00
project_feature . update_attribute ( :builds_access_level , ProjectFeature :: ENABLED )
2015-11-09 10:48:03 -05:00
end
2015-10-12 10:42:14 -04:00
2015-12-04 06:55:23 -05:00
def any_runners? ( & block )
2015-12-10 11:44:06 -05:00
if runners . active . any? ( & block )
2015-12-04 06:55:23 -05:00
return true
end
shared_runners_enabled? && Ci :: Runner . shared . active . any? ( & block )
end
2016-05-30 06:08:53 -04:00
def valid_runners_token? ( token )
2016-03-08 09:57:45 -05:00
self . runners_token && ActiveSupport :: SecurityUtils . variable_size_secure_compare ( token , self . runners_token )
2015-12-10 11:29:44 -05:00
end
2015-12-04 06:55:23 -05:00
def build_coverage_enabled?
build_coverage_regex . present?
end
def build_timeout_in_minutes
build_timeout / 60
end
def build_timeout_in_minutes = ( value )
self . build_timeout = value . to_i * 60
end
2015-12-16 10:08:35 -05:00
2015-12-12 02:17:36 -05:00
def open_issues_count
issues . opened . count
end
2015-12-21 07:27:34 -05:00
2016-03-20 16:03:53 -04:00
def visibility_level_allowed_as_fork? ( level = self . visibility_level )
2016-03-20 17:55:08 -04:00
return true unless forked?
2016-03-20 16:03:53 -04:00
2016-03-20 19:42:30 -04:00
# self.forked_from_project will be nil before the project is saved, so
# we need to go through the relation
original_project = forked_project_link . forked_from_project
return true unless original_project
level < = original_project . visibility_level
2016-03-20 16:03:53 -04:00
end
2016-03-18 08:28:16 -04:00
2016-03-20 16:03:53 -04:00
def visibility_level_allowed_by_group? ( level = self . visibility_level )
return true unless group
2016-03-08 19:01:33 -05:00
2016-03-20 16:03:53 -04:00
level < = group . visibility_level
2015-10-12 10:42:14 -04:00
end
2016-01-05 02:18:23 -05:00
2016-03-20 16:03:53 -04:00
def visibility_level_allowed? ( level = self . visibility_level )
visibility_level_allowed_as_fork? ( level ) && visibility_level_allowed_by_group? ( level )
2016-03-18 20:04:53 -04:00
end
2016-01-05 02:18:23 -05:00
def runners_token
ensure_runners_token!
end
2016-01-22 04:24:38 -05:00
def wiki
@wiki || = ProjectWiki . new ( self , self . owner )
end
2016-05-01 03:38:53 -04:00
2016-06-01 10:50:32 -04:00
def running_or_pending_build_count ( force : false )
Rails . cache . fetch ( [ 'projects' , id , 'running_or_pending_build_count' ] , force : force ) do
2016-05-31 08:51:13 -04:00
builds . running_or_pending . count ( :all )
end
end
2016-05-31 05:00:59 -04:00
def mark_import_as_failed ( error_message )
2016-05-31 08:53:33 -04:00
original_errors = errors . dup
sanitized_message = Gitlab :: UrlSanitizer . sanitize ( error_message )
2016-05-31 05:00:59 -04:00
import_fail
2016-05-31 08:53:33 -04:00
update_column ( :import_error , sanitized_message )
rescue ActiveRecord :: ActiveRecordError = > e
Rails . logger . error ( " Error setting import status to failed: #{ e . message } . Original error: #{ sanitized_message } " )
ensure
@errors = original_errors
2016-05-31 05:00:59 -04:00
end
2016-06-03 05:17:46 -04:00
2016-06-15 11:31:00 -04:00
def add_export_job ( current_user : )
job_id = ProjectExportWorker . perform_async ( current_user . id , self . id )
2016-05-04 04:59:33 -04:00
if job_id
Rails . logger . info " Export job started for project ID #{ self . id } with job ID #{ job_id } "
else
Rails . logger . error " Export job failed to start for project ID #{ self . id } "
end
end
2016-06-14 16:11:21 -04:00
def export_path
2016-06-15 03:03:54 -04:00
File . join ( Gitlab :: ImportExport . storage_path , path_with_namespace )
2016-06-14 16:11:21 -04:00
end
2016-06-15 11:31:00 -04:00
def export_project_path
Dir . glob ( " #{ export_path } /*export.tar.gz " ) . max_by { | f | File . ctime ( f ) }
end
def remove_exports
_ , status = Gitlab :: Popen . popen ( %W( find #{ export_path } -not -path #{ export_path } -delete ) )
status . zero?
end
2016-06-22 17:04:51 -04:00
def ensure_dir_exist
gitlab_shell . add_namespace ( repository_storage_path , namespace . path )
end
2016-07-20 07:17:21 -04:00
def predefined_variables
[
{ key : 'CI_PROJECT_ID' , value : id . to_s , public : true } ,
{ key : 'CI_PROJECT_NAME' , value : path , public : true } ,
{ key : 'CI_PROJECT_PATH' , value : path_with_namespace , public : true } ,
{ key : 'CI_PROJECT_NAMESPACE' , value : namespace . path , public : true } ,
{ key : 'CI_PROJECT_URL' , value : web_url , public : true }
]
end
def container_registry_variables
return [ ] unless Gitlab . config . registry . enabled
variables = [
{ key : 'CI_REGISTRY' , value : Gitlab . config . registry . host_port , public : true }
]
2016-07-20 08:00:25 -04:00
if container_registry_enabled?
variables << { key : 'CI_REGISTRY_IMAGE' , value : container_registry_repository_url , public : true }
end
2016-07-20 07:17:21 -04:00
variables
end
def secret_variables
variables . map do | variable |
{ key : variable . key , value : variable . value , public : false }
end
end
2016-07-07 16:08:06 -04:00
# Checks if `user` is authorized for this project, with at least the
# `min_access_level` (if given).
#
# If you change the logic of this method, please also update `User#authorized_projects`
def authorized_for_user? ( user , min_access_level = nil )
return false unless user
return true if personal? && namespace_id == user . namespace_id
authorized_for_user_by_group? ( user , min_access_level ) ||
authorized_for_user_by_members? ( user , min_access_level ) ||
authorized_for_user_by_shared_projects? ( user , min_access_level )
end
2016-07-22 06:10:45 -04:00
def append_or_update_attribute ( name , value )
old_values = public_send ( name . to_s )
if Project . reflect_on_association ( name ) . try ( :macro ) == :has_many && old_values . any?
update_attribute ( name , old_values + value )
else
update_attribute ( name , value )
end
end
2016-09-13 12:28:45 -04:00
def pushes_since_gc
Gitlab :: Redis . with { | redis | redis . get ( pushes_since_gc_redis_key ) . to_i }
end
def increment_pushes_since_gc
Gitlab :: Redis . with { | redis | redis . incr ( pushes_since_gc_redis_key ) }
end
def reset_pushes_since_gc
Gitlab :: Redis . with { | redis | redis . del ( pushes_since_gc_redis_key ) }
end
2016-09-20 16:42:07 -04:00
def environments_for ( ref , commit , with_tags : false )
environment_ids = deployments . group ( :environment_id ) .
select ( :environment_id )
environment_ids =
if with_tags
environment_ids . where ( 'ref=? OR tag IS TRUE' , ref )
else
environment_ids . where ( ref : ref )
end
2016-10-17 06:45:31 -04:00
environments . available . where ( id : environment_ids ) . select do | environment |
2016-09-20 16:42:07 -04:00
environment . includes_commit? ( commit )
end
end
2016-07-07 16:08:06 -04:00
private
2016-09-13 12:28:45 -04:00
def pushes_since_gc_redis_key
" projects/ #{ id } /pushes_since_gc "
end
2016-08-01 05:06:57 -04:00
def default_branch_protected?
2016-08-01 11:48:15 -04:00
current_application_settings . default_branch_protection == Gitlab :: Access :: PROTECTION_FULL ||
current_application_settings . default_branch_protection == Gitlab :: Access :: PROTECTION_DEV_CAN_MERGE
2016-08-01 05:06:57 -04:00
end
2016-07-07 16:08:06 -04:00
def authorized_for_user_by_group? ( user , min_access_level )
member = user . group_members . find_by ( source_id : group )
member && ( ! min_access_level || member . access_level > = min_access_level )
end
def authorized_for_user_by_members? ( user , min_access_level )
member = members . find_by ( user_id : user )
member && ( ! min_access_level || member . access_level > = min_access_level )
end
def authorized_for_user_by_shared_projects? ( user , min_access_level )
shared_projects = user . group_members . joins ( group : :shared_projects ) .
where ( project_group_links : { project_id : self } )
if min_access_level
members_scope = { access_level : Gitlab :: Access . values . select { | access | access > = min_access_level } }
shared_projects = shared_projects . where ( members : members_scope )
end
shared_projects . any?
end
2016-10-06 14:58:28 -04:00
2016-10-20 12:15:39 -04:00
# Similar to the normal callbacks that hook into the life cycle of an
# Active Record object, you can also define callbacks that get triggered
# when you add an object to an association collection. If any of these
# callbacks throw an exception, the object will not be added to the
# collection. Before you add a new board to the boards collection if you
# already have 1, 2, or n it will fail, but it if you have 0 that is lower
# than the number of permitted boards per project it won't fail.
2016-10-06 14:58:28 -04:00
def validate_board_limit ( board )
2016-10-10 10:22:30 -04:00
raise BoardLimitExceeded , 'Number of permitted boards exceeded' if boards . size > = NUMBER_OF_PERMITTED_BOARDS
2016-10-06 14:58:28 -04:00
end
2011-10-08 17:36:38 -04:00
end