2015-04-26 12:48:37 +05:30
require 'carrierwave/orm/activerecord'
2014-09-02 18:07:02 +05:30
class Project < ActiveRecord :: Base
2015-09-11 14:41:01 +05:30
include Gitlab :: ConfigHelper
2014-09-02 18:07:02 +05:30
include Gitlab :: ShellAdapter
include Gitlab :: VisibilityLevel
2015-11-26 14:37:03 +05:30
include Gitlab :: CurrentSettings
2016-06-16 23:09:34 +05:30
include AccessRequestable
2015-09-11 14:41:01 +05:30
include Referable
include Sortable
2015-10-24 18:46:33 +05:30
include AfterCommitQueue
include CaseSensitivity
2016-01-14 18:37:52 +05:30
include TokenAuthenticatable
2016-09-29 09:46:39 +05:30
include ProjectFeaturesCompatibility
2015-12-23 02:04:40 +05:30
2014-09-02 18:07:02 +05:30
extend Gitlab :: ConfigHelper
2015-09-25 12:07:36 +05:30
UNKNOWN_IMPORT_URL = 'http://unknown.git'
2016-09-29 09:46:39 +05:30
delegate :feature_available? , :builds_enabled? , :wiki_enabled? , :merge_requests_enabled? , to : :project_feature , allow_nil : true
2014-09-02 18:07:02 +05:30
default_value_for :archived , false
default_value_for :visibility_level , gitlab_config_features . visibility_level
2016-06-02 11:05:42 +05:30
default_value_for :container_registry_enabled , gitlab_config_features . container_registry
2016-08-24 12:49:21 +05:30
default_value_for ( :repository_storage ) { current_application_settings . repository_storage }
2015-12-23 02:04:40 +05:30
default_value_for ( :shared_runners_enabled ) { current_application_settings . shared_runners_enabled }
2014-09-02 18:07:02 +05:30
2016-08-24 12:49:21 +05:30
after_create :ensure_dir_exist
after_save :ensure_dir_exist , if : :namespace_id_changed?
2016-09-29 09:46:39 +05:30
after_initialize :setup_project_feature
2016-08-24 12:49:21 +05:30
2015-04-26 12:48:37 +05:30
# set last_activity_at to the same as created_at
after_create :set_last_activity_at
def set_last_activity_at
update_column ( :last_activity_at , self . created_at )
end
2016-06-02 11:05:42 +05:30
# update visibility_level of forks
2016-01-14 18:37:52 +05:30
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
2014-09-02 18:07:02 +05:30
ActsAsTaggableOn . strict_case_match = true
acts_as_taggable_on :tags
attr_accessor :new_default_branch
2016-01-14 18:37:52 +05:30
attr_accessor :old_path_with_namespace
2014-09-02 18:07:02 +05:30
2016-06-02 11:05:42 +05:30
alias_attribute :title , :name
2014-09-02 18:07:02 +05:30
# Relations
2015-04-26 12:48:37 +05:30
belongs_to :creator , foreign_key : 'creator_id' , class_name : 'User'
2016-09-29 09:46:39 +05:30
belongs_to :group , - > { where ( type : 'Group' ) } , foreign_key : 'namespace_id'
2014-09-02 18:07:02 +05:30
belongs_to :namespace
has_one :last_event , - > { order 'events.created_at DESC' } , class_name : 'Event' , foreign_key : 'project_id'
2016-09-29 09:46:39 +05:30
has_one :board , dependent : :destroy
2014-09-02 18:07:02 +05:30
# Project services
has_many :services
has_one :campfire_service , dependent : :destroy
2015-09-25 12:07:36 +05:30
has_one :drone_ci_service , dependent : :destroy
2014-09-02 18:07:02 +05:30
has_one :emails_on_push_service , dependent : :destroy
2015-12-23 02:04:40 +05:30
has_one :builds_email_service , dependent : :destroy
2015-04-26 12:48:37 +05:30
has_one :irker_service , dependent : :destroy
2014-09-02 18:07:02 +05:30
has_one :pivotaltracker_service , dependent : :destroy
has_one :hipchat_service , dependent : :destroy
has_one :flowdock_service , dependent : :destroy
has_one :assembla_service , dependent : :destroy
2015-04-26 12:48:37 +05:30
has_one :asana_service , dependent : :destroy
2014-09-02 18:07:02 +05:30
has_one :gemnasium_service , dependent : :destroy
has_one :slack_service , dependent : :destroy
2015-04-26 12:48:37 +05:30
has_one :buildkite_service , dependent : :destroy
has_one :bamboo_service , dependent : :destroy
has_one :teamcity_service , dependent : :destroy
has_one :pushover_service , dependent : :destroy
has_one :jira_service , dependent : :destroy
has_one :redmine_service , dependent : :destroy
has_one :custom_issue_tracker_service , dependent : :destroy
2016-08-24 12:49:21 +05:30
has_one :bugzilla_service , dependent : :destroy
has_one :gitlab_issue_tracker_service , dependent : :destroy , inverse_of : :project
2015-04-26 12:48:37 +05:30
has_one :external_wiki_service , dependent : :destroy
2016-01-14 18:37:52 +05:30
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-04-26 12:48:37 +05:30
2014-09-02 18:07:02 +05:30
# Merge Requests for target project should be removed with it
2015-04-26 12:48:37 +05:30
has_many :merge_requests , dependent : :destroy , foreign_key : 'target_project_id'
2014-09-02 18:07:02 +05:30
# Merge requests from source project should be kept when source project was removed
2015-04-26 12:48:37 +05:30
has_many :fork_merge_requests , foreign_key : 'source_project_id' , class_name : MergeRequest
has_many :issues , dependent : :destroy
2014-09-02 18:07:02 +05:30
has_many :labels , dependent : :destroy
has_many :services , dependent : :destroy
has_many :events , dependent : :destroy
has_many :milestones , dependent : :destroy
has_many :notes , dependent : :destroy
2015-04-26 12:48:37 +05:30
has_many :snippets , dependent : :destroy , class_name : 'ProjectSnippet'
has_many :hooks , dependent : :destroy , class_name : 'ProjectHook'
2014-09-02 18:07:02 +05:30
has_many :protected_branches , dependent : :destroy
2016-08-24 12:49:21 +05:30
has_many :project_members , - > { where ( requested_at : nil ) } , dependent : :destroy , as : :source , class_name : 'ProjectMember'
2016-06-16 23:09:34 +05:30
alias_method :members , :project_members
2016-08-24 12:49:21 +05:30
has_many :users , through : :project_members
has_many :requesters , - > { where . not ( requested_at : nil ) } , dependent : :destroy , as : :source , class_name : 'ProjectMember'
2014-09-02 18:07:02 +05:30
has_many :deploy_keys_projects , dependent : :destroy
has_many :deploy_keys , through : :deploy_keys_projects
has_many :users_star_projects , dependent : :destroy
has_many :starrers , through : :users_star_projects , source : :user
2015-11-26 14:37:03 +05:30
has_many :releases , dependent : :destroy
has_many :lfs_objects_projects , dependent : :destroy
has_many :lfs_objects , through : :lfs_objects_projects
2016-06-02 11:05:42 +05:30
has_many :project_group_links , dependent : :destroy
has_many :invited_groups , through : :project_group_links , source : :group
2016-04-02 18:10:28 +05:30
has_many :todos , dependent : :destroy
2016-06-02 11:05:42 +05:30
has_many :notification_settings , dependent : :destroy , as : :source
2014-09-02 18:07:02 +05:30
2015-04-26 12:48:37 +05:30
has_one :import_data , dependent : :destroy , class_name : " ProjectImportData "
2016-09-29 09:46:39 +05:30
has_one :project_feature , dependent : :destroy
2015-12-23 02:04:40 +05:30
has_many :commit_statuses , dependent : :destroy , class_name : 'CommitStatus' , foreign_key : :gl_project_id
2016-06-16 23:09:34 +05:30
has_many :pipelines , dependent : :destroy , class_name : 'Ci::Pipeline' , foreign_key : :gl_project_id
2015-12-23 02:04:40 +05:30
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-16 23:09:34 +05:30
has_many :environments , dependent : :destroy
has_many :deployments , dependent : :destroy
2015-12-23 02:04:40 +05:30
accepts_nested_attributes_for :variables , allow_destroy : true
2016-09-29 09:46:39 +05:30
accepts_nested_attributes_for :project_feature
2015-04-26 12:48:37 +05:30
2014-09-02 18:07:02 +05:30
delegate :name , to : :owner , allow_nil : true , prefix : true
delegate :members , to : :team , prefix : true
# Validations
validates :creator , presence : true , on : :create
validates :description , length : { maximum : 2000 } , allow_blank : true
2015-04-26 12:48:37 +05:30
validates :name ,
presence : true ,
length : { within : 0 .. 255 } ,
format : { with : Gitlab :: Regex . project_name_regex ,
message : Gitlab :: Regex . project_name_regex_message }
validates :path ,
presence : true ,
length : { within : 0 .. 255 } ,
format : { with : Gitlab :: Regex . project_path_regex ,
message : Gitlab :: Regex . project_path_regex_message }
2014-09-02 18:07:02 +05:30
validates :namespace , presence : true
validates_uniqueness_of :name , scope : :namespace_id
validates_uniqueness_of :path , scope : :namespace_id
2016-08-24 12:49:21 +05:30
validates :import_url , addressable_url : true , if : :external_import?
2014-09-02 18:07:02 +05:30
validates :star_count , numericality : { greater_than_or_equal_to : 0 }
validate :check_limit , on : :create
2015-04-26 12:48:37 +05:30
validate :avatar_type ,
2015-09-11 14:41:01 +05:30
if : - > ( project ) { project . avatar . present? && project . avatar_changed? }
2015-04-26 12:48:37 +05:30
validates :avatar , file_size : { maximum : 200 . kilobytes . to_i }
2016-06-02 11:05:42 +05:30
validate :visibility_level_allowed_by_group
validate :visibility_level_allowed_as_fork
2016-08-24 12:49:21 +05:30
validate :check_wiki_path_conflict
validates :repository_storage ,
presence : true ,
inclusion : { in : - > ( _object ) { Gitlab . config . repositories . storages . keys } }
2015-04-26 12:48:37 +05:30
2016-01-14 18:37:52 +05:30
add_authentication_token_field :runners_token
before_save :ensure_runners_token
2015-12-23 02:04:40 +05:30
2015-04-26 12:48:37 +05:30
mount_uploader :avatar , AvatarUploader
2014-09-02 18:07:02 +05:30
# Scopes
2016-06-02 11:05:42 +05:30
default_scope { where ( pending_delete : false ) }
2015-04-26 12:48:37 +05:30
scope :sorted_by_activity , - > { reorder ( last_activity_at : :desc ) }
scope :sorted_by_stars , - > { reorder ( 'projects.star_count DESC' ) }
2015-09-11 14:41:01 +05:30
scope :in_namespace , - > ( namespace_ids ) { where ( namespace_id : namespace_ids ) }
2014-09-02 18:07:02 +05:30
scope :personal , - > ( user ) { where ( namespace_id : user . namespace_id ) }
2015-04-26 12:48:37 +05:30
scope :joined , - > ( user ) { where ( 'namespace_id != ?' , user . namespace_id ) }
2016-06-02 11:05:42 +05:30
scope :visible_to_user , - > ( user ) { where ( id : user . authorized_projects . select ( :id ) . reorder ( nil ) ) }
2014-09-02 18:07:02 +05:30
scope :non_archived , - > { where ( archived : false ) }
2016-06-02 11:05:42 +05:30
scope :for_milestones , - > ( ids ) { joins ( :milestones ) . where ( 'milestones.id' = > ids ) . distinct }
scope :with_push , - > { joins ( :events ) . where ( 'events.action = ?' , Event :: PUSHED ) }
2016-09-29 09:46:39 +05:30
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-06-02 11:05:42 +05:30
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 ) }
2014-09-02 18:07:02 +05:30
2016-09-13 17:45:13 +05:30
scope :excluding_project , - > ( project ) { where . not ( id : project ) }
2014-09-02 18:07:02 +05:30
state_machine :import_status , initial : :none do
event :import_start do
2015-04-26 12:48:37 +05:30
transition [ :none , :finished ] = > :started
2014-09-02 18:07:02 +05:30
end
event :import_finish do
2015-04-26 12:48:37 +05:30
transition started : :finished
2014-09-02 18:07:02 +05:30
end
event :import_fail do
2015-04-26 12:48:37 +05:30
transition started : :failed
2014-09-02 18:07:02 +05:30
end
event :import_retry do
2015-04-26 12:48:37 +05:30
transition failed : :started
2014-09-02 18:07:02 +05:30
end
state :started
state :finished
state :failed
2016-06-02 11:05:42 +05:30
after_transition any = > :finished , do : :reset_cache_and_import_attrs
2014-09-02 18:07:02 +05:30
end
class << self
2016-06-02 11:05:42 +05:30
# 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.
def search ( query )
ptable = arel_table
ntable = Namespace . arel_table
pattern = " % #{ query } % "
projects = select ( :id ) . where (
ptable [ :path ] . matches ( pattern ) .
or ( ptable [ :name ] . matches ( pattern ) ) .
or ( ptable [ :description ] . matches ( pattern ) )
)
# 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.
namespaces = select ( :id ) .
except ( :includes ) .
joins ( :namespace ) .
where ( ntable [ :name ] . matches ( pattern ) )
2014-09-02 18:07:02 +05:30
2016-06-02 11:05:42 +05:30
union = Gitlab :: SQL :: Union . new ( [ projects , namespaces ] )
2014-09-02 18:07:02 +05:30
2016-06-02 11:05:42 +05:30
where ( " projects.id IN ( #{ union . to_sql } ) " )
2014-09-02 18:07:02 +05:30
end
2016-04-02 18:10:28 +05:30
def search_by_visibility ( level )
where ( visibility_level : Gitlab :: VisibilityLevel . const_get ( level . upcase ) )
end
2015-04-26 12:48:37 +05:30
def search_by_title ( query )
2016-06-02 11:05:42 +05:30
pattern = " % #{ query } % "
table = Project . arel_table
non_archived . where ( table [ :name ] . matches ( pattern ) )
2014-09-02 18:07:02 +05:30
end
2016-06-16 23:09:34 +05:30
# 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-22 15:30:34 +05:30
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-16 23:09:34 +05:30
end
2015-10-24 18:46:33 +05:30
2016-06-16 23:09:34 +05:30
# 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?
paths . each do | path |
namespace_path , project_path = path . split ( '/' , 2 )
next unless namespace_path && project_path
namespace_path = connection . quote ( namespace_path )
project_path = connection . quote ( project_path )
where = " (namespaces.path = #{ namespace_path }
AND projects . path = #{project_path})"
if cast_lower
where = " (
#{where}
OR (
LOWER ( namespaces . path ) = LOWER ( #{namespace_path})
AND LOWER ( projects . path ) = LOWER ( #{project_path})
)
) "
end
2015-10-24 18:46:33 +05:30
2016-06-16 23:09:34 +05:30
wheres << where
end
2015-11-26 14:37:03 +05:30
2016-06-16 23:09:34 +05:30
if wheres . empty?
none
else
joins ( :namespace ) . where ( wheres . join ( ' OR ' ) )
end
2014-09-02 18:07:02 +05:30
end
def visibility_levels
Gitlab :: VisibilityLevel . options
end
def sort ( method )
2015-04-26 12:48:37 +05:30
if method == 'repository_size_desc'
reorder ( repository_size : :desc , id : :desc )
else
order_by ( method )
2014-09-02 18:07:02 +05:30
end
end
2015-09-11 14:41:01 +05:30
def reference_pattern
name_pattern = Gitlab :: Regex :: NAMESPACE_REGEX_STR
%r{ (?<project> #{ name_pattern } / #{ name_pattern } ) }
end
2015-10-24 18:46:33 +05:30
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
2016-06-22 15:30:34 +05:30
2016-08-24 12:49:21 +05:30
def cached_count
Rails . cache . fetch ( 'total_project_count' , expires_in : 5 . minutes ) do
Project . count
end
2016-06-22 15:30:34 +05:30
end
2014-09-02 18:07:02 +05:30
end
2016-09-29 09:46:39 +05:30
def lfs_enabled?
return namespace . lfs_enabled? if self [ :lfs_enabled ] . nil?
self [ :lfs_enabled ] && Gitlab . config . lfs . enabled
end
2016-08-24 12:49:21 +05:30
def repository_storage_path
Gitlab . config . repositories . storages [ repository_storage ]
end
2014-09-02 18:07:02 +05:30
def team
@team || = ProjectTeam . new ( self )
end
def repository
2016-04-02 18:10:28 +05:30
@repository || = Repository . new ( path_with_namespace , self )
2015-09-11 14:41:01 +05:30
end
2016-06-16 23:09:34 +05:30
def container_registry_path_with_namespace
path_with_namespace . downcase
end
2016-06-02 11:05:42 +05:30
def container_registry_repository
return unless Gitlab . config . registry . enabled
@container_registry_repository || = begin
2016-06-16 23:09:34 +05:30
token = Auth :: ContainerRegistryAuthenticationService . full_access_token ( container_registry_path_with_namespace )
2016-06-02 11:05:42 +05:30
url = Gitlab . config . registry . api_url
host_port = Gitlab . config . registry . host_port
registry = ContainerRegistry :: Registry . new ( url , token : token , path : host_port )
2016-06-16 23:09:34 +05:30
registry . repository ( container_registry_path_with_namespace )
2016-06-02 11:05:42 +05:30
end
end
def container_registry_repository_url
if Gitlab . config . registry . enabled
2016-06-16 23:09:34 +05:30
" #{ Gitlab . config . registry . host_port } / #{ container_registry_path_with_namespace } "
2016-06-02 11:05:42 +05:30
end
end
def has_container_registry_tags?
return unless container_registry_repository
container_registry_repository . tags . any?
end
2016-08-24 12:49:21 +05:30
def commit ( ref = 'HEAD' )
repository . commit ( ref )
end
# ref can't be HEAD, can only be branch/tag name or SHA
def latest_successful_builds_for ( ref = default_branch )
2016-09-29 09:46:39 +05:30
latest_pipeline = pipelines . latest_successful_for ( ref )
2016-08-24 12:49:21 +05:30
if latest_pipeline
latest_pipeline . builds . latest . with_artifacts
else
builds . none
end
2014-09-02 18:07:02 +05:30
end
2016-02-05 20:25:01 +05:30
def merge_base_commit ( first_commit_id , second_commit_id )
sha = repository . merge_base ( first_commit_id , second_commit_id )
repository . commit ( sha ) if sha
end
2014-09-02 18:07:02 +05:30
def saved?
id && persisted?
end
def add_import_job
2015-09-25 12:07:36 +05:30
if forked?
2016-08-24 12:49:21 +05:30
job_id = RepositoryForkWorker . perform_async ( id , forked_from_project . repository_storage_path ,
forked_from_project . path_with_namespace ,
self . namespace . path )
2016-06-02 11:05:42 +05:30
else
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 } "
2015-09-25 12:07:36 +05:30
else
2016-06-02 11:05:42 +05:30
Rails . logger . error " Import job failed to start for #{ path_with_namespace } "
2015-09-25 12:07:36 +05:30
end
2014-09-02 18:07:02 +05:30
end
2016-06-02 11:05:42 +05:30
def reset_cache_and_import_attrs
2015-11-26 14:37:03 +05:30
ProjectCacheWorker . perform_async ( self . id )
2015-04-26 12:48:37 +05:30
self . import_data . destroy if self . import_data
end
2016-06-02 11:05:42 +05:30
def import_url = ( value )
2016-08-24 12:49:21 +05:30
return super ( value ) unless Gitlab :: UrlSanitizer . valid? ( value )
2016-06-02 11:05:42 +05:30
import_url = Gitlab :: UrlSanitizer . new ( value )
super ( import_url . sanitized_url )
2016-08-24 12:49:21 +05:30
create_or_update_import_data ( credentials : import_url . credentials )
2016-06-02 11:05:42 +05:30
end
def import_url
if import_data && super
import_url = Gitlab :: UrlSanitizer . new ( super , credentials : import_data . credentials )
import_url . full_url
else
super
end
end
2016-08-24 12:49:21 +05:30
def valid_import_url?
valid? || errors . messages [ :import_url ] . nil?
end
2016-06-02 11:05:42 +05:30
def create_or_update_import_data ( data : nil , credentials : nil )
2016-08-24 12:49:21 +05:30
return unless import_url . present? && valid_import_url?
2016-06-02 11:05:42 +05:30
project_import_data = import_data || build_import_data
if data
project_import_data . data || = { }
project_import_data . data = project_import_data . data . merge ( data )
end
if credentials
project_import_data . credentials || = { }
project_import_data . credentials = project_import_data . credentials . merge ( credentials )
end
project_import_data . save
end
2014-09-02 18:07:02 +05:30
def import?
2016-06-22 15:30:34 +05:30
external_import? || forked? || gitlab_project_import?
2015-09-25 12:07:36 +05:30
end
2016-04-02 18:10:28 +05:30
def no_import?
import_status == 'none'
end
2015-09-25 12:07:36 +05:30
def external_import?
2014-09-02 18:07:02 +05:30
import_url . present?
end
def imported?
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'
end
2015-11-26 14:37:03 +05:30
def safe_import_url
2016-06-02 11:05:42 +05:30
Gitlab :: UrlSanitizer . new ( import_url ) . masked_url
2015-11-26 14:37:03 +05:30
end
2016-06-22 15:30:34 +05:30
def gitlab_project_import?
import_type == 'gitlab_project'
end
2014-09-02 18:07:02 +05:30
def check_limit
unless creator . can_create_project? or namespace . kind == 'group'
2016-06-16 23:09:34 +05:30
projects_limit = creator . projects_limit
if projects_limit == 0
self . errors . add ( :limit_reached , " Personal project creation is not allowed. Please contact your administrator with questions " )
else
self . errors . add ( :limit_reached , " Your project limit is #{ projects_limit } projects! Please contact your administrator to increase it " )
end
2014-09-02 18:07:02 +05:30
end
rescue
2016-06-02 11:05:42 +05:30
self . errors . add ( :base , " Can't check your ability to create project " )
end
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. " )
2014-09-02 18:07:02 +05:30
end
2016-08-24 12:49:21 +05:30
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
2014-09-02 18:07:02 +05:30
def to_param
2016-09-13 17:45:13 +05:30
if persisted? && errors . include? ( :path )
path_was
else
path
end
2014-09-02 18:07:02 +05:30
end
2015-09-11 14:41:01 +05:30
def to_reference ( _from_project = nil )
path_with_namespace
end
2014-09-02 18:07:02 +05:30
def web_url
2016-06-02 11:05:42 +05:30
Gitlab :: Routing . url_helpers . namespace_project_url ( self . namespace , self )
2014-09-02 18:07:02 +05:30
end
def web_url_without_protocol
2015-04-26 12:48:37 +05:30
web_url . split ( '://' ) [ 1 ]
2014-09-02 18:07:02 +05:30
end
2016-09-13 17:45:13 +05:30
def new_issue_address ( author )
# This feature is disabled for the time being.
return nil
if Gitlab :: IncomingEmail . enabled? && author # rubocop:disable Lint/UnreachableCode
Gitlab :: IncomingEmail . reply_address (
" #{ path_with_namespace } + #{ author . authentication_token } " )
end
end
2014-09-02 18:07:02 +05:30
def build_commit_note ( commit )
2015-04-26 12:48:37 +05:30
notes . new ( commit_id : commit . id , noteable_type : 'Commit' )
2014-09-02 18:07:02 +05:30
end
def last_activity
last_event
end
def last_activity_date
last_activity_at || updated_at
end
def project_id
self . id
end
2015-09-11 14:41:01 +05:30
def get_issue ( issue_id )
2015-04-26 12:48:37 +05:30
if default_issues_tracker?
2015-09-11 14:41:01 +05:30
issues . find_by ( iid : issue_id )
2014-09-02 18:07:02 +05:30
else
2015-09-11 14:41:01 +05:30
ExternalIssue . new ( issue_id , self )
2014-09-02 18:07:02 +05:30
end
end
2015-09-11 14:41:01 +05:30
def issue_exists? ( issue_id )
get_issue ( issue_id )
end
2015-04-26 12:48:37 +05:30
def default_issue_tracker
gitlab_issue_tracker_service || create_gitlab_issue_tracker_service
end
def issues_tracker
if external_issue_tracker
external_issue_tracker
else
default_issue_tracker
end
end
def default_issues_tracker?
2015-09-11 14:41:01 +05:30
! external_issue_tracker
2015-04-26 12:48:37 +05:30
end
def external_issue_tracker
2016-06-16 23:09:34 +05:30
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
2014-09-02 18:07:02 +05:30
end
2016-06-16 23:09:34 +05:30
def cache_has_external_issue_tracker
update_column ( :has_external_issue_tracker , services . external_issue_trackers . any? )
2014-09-02 18:07:02 +05:30
end
2016-09-29 09:46:39 +05:30
def has_wiki?
wiki_enabled? || has_external_wiki?
end
2016-08-24 12:49:21 +05:30
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
2014-09-02 18:07:02 +05:30
def build_missing_services
2015-04-26 12:48:37 +05:30
services_templates = Service . where ( template : true )
Service . available_services_names . each do | service_name |
service = find_service ( services , service_name )
2014-09-02 18:07:02 +05:30
# If service is available but missing in db
2015-04-26 12:48:37 +05:30
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-24 18:46:33 +05:30
self . send :" create_ #{ service_name } _service "
2015-04-26 12:48:37 +05:30
else
Service . create_from_template ( self . id , template )
end
end
2014-09-02 18:07:02 +05:30
end
end
2015-09-25 12:07:36 +05:30
def create_labels
Label . templates . each do | label |
label = label . dup
label . template = nil
label . project_id = self . id
label . save
end
end
2015-04-26 12:48:37 +05:30
def find_service ( list , name )
list . find { | service | service . to_param == name }
2014-09-02 18:07:02 +05:30
end
def ci_services
2016-06-02 11:05:42 +05:30
services . where ( category : :ci )
2014-09-02 18:07:02 +05:30
end
def ci_service
2016-06-02 11:05:42 +05:30
@ci_service || = ci_services . reorder ( nil ) . find_by ( active : true )
2015-12-23 02:04:40 +05:30
end
def jira_tracker?
issues_tracker . to_param == 'jira'
2014-09-02 18:07:02 +05:30
end
2015-04-26 12:48:37 +05:30
def avatar_type
unless self . avatar . image?
self . errors . add :avatar , 'only images allowed'
end
end
def avatar_in_git
2016-06-02 11:05:42 +05:30
repository . avatar
2015-04-26 12:48:37 +05:30
end
def avatar_url
2016-08-24 12:49:21 +05:30
if self [ :avatar ] . present?
2015-04-26 12:48:37 +05:30
[ gitlab_config . url , avatar . url ] . join
elsif avatar_in_git
2016-06-02 11:05:42 +05:30
Gitlab :: Routing . url_helpers . namespace_project_avatar_url ( namespace , self )
2015-04-26 12:48:37 +05:30
end
end
2014-09-02 18:07:02 +05:30
# For compatibility with old code
def code
path
end
2015-04-26 12:48:37 +05:30
def items_for ( entity )
2014-09-02 18:07:02 +05:30
case entity
when 'issue' then
issues
when 'merge_request' then
merge_requests
end
end
2015-10-24 18:46:33 +05:30
def send_move_instructions ( old_path_with_namespace )
2016-01-14 18:37:52 +05:30
# 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 ) }
2014-09-02 18:07:02 +05:30
end
def owner
if group
group
else
namespace . try ( :owner )
end
end
def name_with_namespace
@name_with_namespace || = begin
if namespace
2015-04-26 12:48:37 +05:30
namespace . human_name + ' / ' + name
2014-09-02 18:07:02 +05:30
else
name
end
end
end
2016-06-16 23:09:34 +05:30
alias_method :human_name , :name_with_namespace
2014-09-02 18:07:02 +05:30
def path_with_namespace
if namespace
namespace . path + '/' + path
else
path
end
end
def execute_hooks ( data , hooks_scope = :push_hooks )
hooks . send ( hooks_scope ) . each do | hook |
2015-09-11 14:41:01 +05:30
hook . async_execute ( data , hooks_scope . to_s )
2014-09-02 18:07:02 +05:30
end
end
2015-04-26 12:48:37 +05:30
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 |
service . async_execute ( data )
2014-09-02 18:07:02 +05:30
end
end
def update_merge_requests ( oldrev , newrev , ref , user )
2015-04-26 12:48:37 +05:30
MergeRequests :: RefreshService . new ( self , user ) .
execute ( oldrev , newrev , ref )
2014-09-02 18:07:02 +05:30
end
def valid_repo?
repository . exists?
rescue
2015-04-26 12:48:37 +05:30
errors . add ( :path , 'Invalid repository path' )
2014-09-02 18:07:02 +05:30
false
end
def empty_repo?
2015-11-26 14:37:03 +05:30
! repository . exists? || ! repository . has_visible_content?
2014-09-02 18:07:02 +05:30
end
def repo
repository . raw
end
def url_to_repo
gitlab_shell . url_to_repo ( path_with_namespace )
end
def namespace_dir
namespace . try ( :path ) || ''
end
def repo_exists?
@repo_exists || = repository . exists?
rescue
@repo_exists = false
end
2016-08-24 12:49:21 +05:30
# Branches that are not _exactly_ matched by a protected branch.
2014-09-02 18:07:02 +05:30
def open_branches
2016-08-24 12:49:21 +05:30
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 }
2014-09-02 18:07:02 +05:30
end
def root_ref? ( branch )
repository . root_ref == branch
end
def ssh_url_to_repo
url_to_repo
end
def http_url_to_repo
2015-09-11 14:41:01 +05:30
" #{ web_url } .git "
2014-09-02 18:07:02 +05:30
end
# Check if current branch name is marked as protected in the system
2015-04-26 12:48:37 +05:30
def protected_branch? ( branch_name )
2016-09-13 17:45:13 +05:30
return true if empty_repo? && default_branch_protected?
2016-08-24 12:49:21 +05:30
@protected_branches || = self . protected_branches . to_a
ProtectedBranch . matching ( branch_name , protected_branches : @protected_branches ) . present?
2014-09-02 18:07:02 +05:30
end
2016-09-13 17:45:13 +05:30
def user_can_push_to_empty_repo? ( user )
! default_branch_protected? || team . max_member_access ( user . id ) > Gitlab :: Access :: DEVELOPER
2015-04-26 12:48:37 +05:30
end
2014-09-02 18:07:02 +05:30
def forked?
! ( forked_project_link . nil? || forked_project_link . forked_from_project . nil? )
end
def personal?
! group
end
def rename_repo
path_was = previous_changes [ 'path' ] . first
old_path_with_namespace = File . join ( namespace_dir , path_was )
new_path_with_namespace = File . join ( namespace_dir , path )
2016-09-13 17:45:13 +05:30
Rails . logger . error " Attempting to rename #{ old_path_with_namespace } -> #{ new_path_with_namespace } "
2016-04-02 18:10:28 +05:30
expire_caches_before_rename ( old_path_with_namespace )
2016-06-02 11:05:42 +05:30
if has_container_registry_tags?
2016-09-13 17:45:13 +05:30
Rails . logger . error " Project #{ old_path_with_namespace } cannot be renamed because container registry tags are present "
2016-06-02 11:05:42 +05:30
# we currently doesn't support renaming repository if it contains tags in container registry
raise Exception . new ( 'Project cannot be renamed, because tags are present in its container registry' )
end
2016-08-24 12:49:21 +05:30
if gitlab_shell . mv_repository ( repository_storage_path , old_path_with_namespace , new_path_with_namespace )
2015-09-25 12:07:36 +05:30
# If repository moved successfully we need to send update instructions to users.
2014-09-02 18:07:02 +05:30
# However we cannot allow rollback since we moved repository
# So we basically we mute exceptions in next actions
begin
2016-08-24 12:49:21 +05:30
gitlab_shell . mv_repository ( repository_storage_path , " #{ old_path_with_namespace } .wiki " , " #{ new_path_with_namespace } .wiki " )
2015-10-24 18:46:33 +05:30
send_move_instructions ( old_path_with_namespace )
2014-09-02 18:07:02 +05:30
reset_events_cache
2016-01-14 18:37:52 +05:30
@old_path_with_namespace = old_path_with_namespace
SystemHooksService . new . execute_hooks_for ( self , :rename )
2015-12-23 02:04:40 +05:30
@repository = nil
2016-09-13 17:45:13 +05:30
rescue = > e
Rails . logger . error " Exception renaming #{ old_path_with_namespace } -> #{ new_path_with_namespace } : #{ e } "
2014-09-02 18:07:02 +05:30
# Returning false does not rollback after_* transaction but gives
# us information about failing some of tasks
false
end
else
2016-09-13 17:45:13 +05:30
Rails . logger . error " Repository could not be renamed: #{ old_path_with_namespace } -> #{ new_path_with_namespace } "
2014-09-02 18:07:02 +05:30
# 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-24 18:46:33 +05:30
2016-09-13 17:45:13 +05:30
Gitlab :: AppLogger . info " Project was renamed: #{ old_path_with_namespace } -> #{ new_path_with_namespace } "
2015-10-24 18:46:33 +05:30
Gitlab :: UploadsTransfer . new . rename_project ( path_was , path , namespace . path )
2014-09-02 18:07:02 +05:30
end
2016-04-02 18:10:28 +05:30
# 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-06-02 11:05:42 +05:30
repo . before_delete
2016-04-02 18:10:28 +05:30
end
if wiki . exists?
2016-06-02 11:05:42 +05:30
wiki . before_delete
2016-04-02 18:10:28 +05:30
end
end
2016-06-02 11:05:42 +05:30
def hook_attrs ( backward : true )
attrs = {
2015-04-26 12:48:37 +05:30
name : name ,
2016-04-02 18:10:28 +05:30
description : description ,
2015-09-25 12:07:36 +05:30
web_url : web_url ,
2016-04-02 18:10:28 +05:30
avatar_url : avatar_url ,
git_ssh_url : ssh_url_to_repo ,
git_http_url : http_url_to_repo ,
2015-04-26 12:48:37 +05:30
namespace : namespace . name ,
2016-04-02 18:10:28 +05:30
visibility_level : visibility_level ,
path_with_namespace : path_with_namespace ,
default_branch : default_branch ,
2015-04-26 12:48:37 +05:30
}
2016-06-02 11:05:42 +05:30
# 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
2015-04-26 12:48:37 +05:30
end
2014-09-02 18:07:02 +05:30
# 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
2015-04-26 12:48:37 +05:30
# * when the project avatar changes
2014-09-02 18:07:02 +05:30
# 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
def project_member ( user )
2015-12-23 02:04:40 +05:30
project_members . find_by ( user_id : user )
2014-09-02 18:07:02 +05:30
end
2016-09-13 17:45:13 +05:30
def add_user ( user , access_level , current_user : nil , expires_at : nil )
team . add_user ( user , access_level , current_user : current_user , expires_at : expires_at )
end
2014-09-02 18:07:02 +05:30
def default_branch
@default_branch || = repository . root_ref if repository . exists?
end
def reload_default_branch
@default_branch = nil
default_branch
end
def visibility_level_field
visibility_level
end
def archive!
update_attribute ( :archived , true )
end
def unarchive!
update_attribute ( :archived , false )
end
def change_head ( branch )
2016-06-02 11:05:42 +05:30
repository . before_change_head
repository . rugged . references . create ( 'HEAD' ,
" refs/heads/ #{ branch } " ,
force : true )
repository . copy_gitattributes ( branch )
2016-09-29 09:46:39 +05:30
repository . expire_avatar_cache ( branch )
2014-09-02 18:07:02 +05:30
reload_default_branch
end
def forked_from? ( project )
forked? && project == forked_from_project
end
def update_repository_size
update_attribute ( :repository_size , repository . size )
end
2015-09-11 14:41:01 +05:30
def update_commit_count
update_attribute ( :commit_count , repository . commit_count )
end
2014-09-02 18:07:02 +05:30
def forks_count
2016-01-14 18:37:52 +05:30
forks . count
2014-09-02 18:07:02 +05:30
end
def find_label ( name )
labels . find_by ( name : name )
end
2015-04-26 12:48:37 +05:30
def origin_merge_requests
merge_requests . where ( source_project_id : self . id )
end
def create_repository
2015-09-25 12:07:36 +05:30
# Forked import is handled asynchronously
unless forked?
2016-08-24 12:49:21 +05:30
if gitlab_shell . add_repository ( repository_storage_path , path_with_namespace )
2016-06-02 11:05:42 +05:30
repository . after_create
2015-09-11 14:41:01 +05:30
true
else
errors . add ( :base , 'Failed to create repository via gitlab-shell' )
false
end
2015-04-26 12:48:37 +05:30
end
end
def repository_exists?
! ! repository . exists?
end
def create_wiki
ProjectWiki . new ( self , self . owner ) . wiki
true
2015-10-24 18:46:33 +05:30
rescue ProjectWiki :: CouldNotCreateWikiError
2015-04-26 12:48:37 +05:30
errors . add ( :base , 'Failed create wiki' )
false
end
2015-10-24 18:46:33 +05:30
2015-12-23 02:04:40 +05:30
def jira_tracker_active?
jira_tracker? && jira_service . active
end
2016-06-02 11:05:42 +05:30
def allowed_to_share_with_group?
! namespace . share_with_group_lock
2015-10-24 18:46:33 +05:30
end
2016-09-29 09:46:39 +05:30
def pipeline_for ( ref , sha = nil )
sha || = commit ( ref ) . try ( :sha )
return unless sha
2016-06-16 23:09:34 +05:30
pipelines . order ( id : :desc ) . find_by ( sha : sha , ref : ref )
2015-10-24 18:46:33 +05:30
end
2016-09-29 09:46:39 +05:30
def ensure_pipeline ( ref , sha , current_user = nil )
pipeline_for ( ref , sha ) ||
pipelines . create ( sha : sha , ref : ref , user : current_user )
2015-11-26 14:37:03 +05:30
end
2016-06-02 11:05:42 +05:30
def enable_ci
2016-09-29 09:46:39 +05:30
project_feature . update_attribute ( :builds_access_level , ProjectFeature :: ENABLED )
2015-11-26 14:37:03 +05:30
end
2015-12-23 02:04:40 +05:30
def any_runners? ( & block )
if runners . active . any? ( & block )
return true
end
shared_runners_enabled? && Ci :: Runner . shared . active . any? ( & block )
end
2016-06-16 23:09:34 +05:30
def valid_runners_token? ( token )
2016-06-02 11:05:42 +05:30
self . runners_token && ActiveSupport :: SecurityUtils . variable_size_secure_compare ( token , self . runners_token )
2015-12-23 02:04:40 +05:30
end
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
def open_issues_count
issues . opened . count
end
2016-01-14 18:37:52 +05:30
2016-06-02 11:05:42 +05:30
def visibility_level_allowed_as_fork? ( level = self . visibility_level )
2016-01-14 18:37:52 +05:30
return true unless forked?
2016-06-02 11:05:42 +05:30
# 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
end
def visibility_level_allowed_by_group? ( level = self . visibility_level )
return true unless group
level < = group . visibility_level
end
def visibility_level_allowed? ( level = self . visibility_level )
visibility_level_allowed_as_fork? ( level ) && visibility_level_allowed_by_group? ( level )
2016-01-14 18:37:52 +05:30
end
def runners_token
ensure_runners_token!
end
2016-04-02 18:10:28 +05:30
def wiki
@wiki || = ProjectWiki . new ( self , self . owner )
end
2016-06-02 11:05:42 +05:30
2016-06-16 23:09:34 +05:30
def running_or_pending_build_count ( force : false )
Rails . cache . fetch ( [ 'projects' , id , 'running_or_pending_build_count' ] , force : force ) do
builds . running_or_pending . count ( :all )
end
end
def mark_import_as_failed ( error_message )
original_errors = errors . dup
sanitized_message = Gitlab :: UrlSanitizer . sanitize ( error_message )
import_fail
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
end
2016-06-22 15:30:34 +05:30
def add_export_job ( current_user : )
job_id = ProjectExportWorker . perform_async ( current_user . id , self . id )
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
def export_path
File . join ( Gitlab :: ImportExport . storage_path , path_with_namespace )
end
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-08-24 12:49:21 +05:30
def ensure_dir_exist
gitlab_shell . add_namespace ( repository_storage_path , namespace . path )
end
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 }
]
if container_registry_enabled?
variables << { key : 'CI_REGISTRY_IMAGE' , value : container_registry_repository_url , public : true }
end
variables
end
def secret_variables
variables . map do | variable |
{ key : variable . key , value : variable . value , public : false }
end
end
# 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
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-29 09:46:39 +05:30
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
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
environments . where ( id : environment_ids ) . select do | environment |
environment . includes_commit? ( commit )
end
end
2016-08-24 12:49:21 +05:30
private
2016-09-29 09:46:39 +05:30
def pushes_since_gc_redis_key
" projects/ #{ id } /pushes_since_gc "
end
# Prevents the creation of project_feature record for every project
def setup_project_feature
build_project_feature unless project_feature
end
2016-09-13 17:45:13 +05:30
def default_branch_protected?
current_application_settings . default_branch_protection == Gitlab :: Access :: PROTECTION_FULL ||
current_application_settings . default_branch_protection == Gitlab :: Access :: PROTECTION_DEV_CAN_MERGE
end
2016-08-24 12:49:21 +05:30
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
2014-09-02 18:07:02 +05:30
end