2018-11-18 11:00:15 +05:30
# frozen_string_literal: true
2015-04-26 12:48:37 +05:30
require 'carrierwave/orm/activerecord'
2019-07-07 11:18:12 +05:30
class Project < ApplicationRecord
2015-09-11 14:41:01 +05:30
include Gitlab :: ConfigHelper
2014-09-02 18:07:02 +05:30
include Gitlab :: VisibilityLevel
2016-06-16 23:09:34 +05:30
include AccessRequestable
2017-09-10 17:25:29 +05:30
include Avatarable
2016-11-03 12:29:30 +05:30
include CacheMarkdownField
2015-09-11 14:41:01 +05:30
include Sortable
2015-10-24 18:46:33 +05:30
include AfterCommitQueue
include CaseSensitivity
2016-01-14 18:37:52 +05:30
include TokenAuthenticatable
2017-08-17 22:00:37 +05:30
include ValidAttribute
2019-09-30 21:07:59 +05:30
include ProjectAPICompatibility
2016-09-29 09:46:39 +05:30
include ProjectFeaturesCompatibility
2017-08-17 22:00:37 +05:30
include SelectForProjectAuthorization
2018-03-27 19:54:05 +05:30
include Presentable
2020-03-13 15:44:24 +05:30
include HasRepository
2020-05-24 23:13:21 +05:30
include HasWiki
2021-02-22 17:27:13 +05:30
include CanMoveRepositoryStorage
2017-08-17 22:00:37 +05:30
include Routable
2018-03-17 18:26:18 +05:30
include GroupDescendant
include Gitlab :: SQL :: Pattern
include DeploymentPlatform
include :: Gitlab :: Utils :: StrongMemoize
2018-05-09 12:01:36 +05:30
include ChronicDurationAttribute
2018-10-15 14:42:47 +05:30
include FastDestroyAll :: Helpers
2018-11-08 19:23:39 +05:30
include WithUploads
include BatchDestroyDependentAssociations
2018-11-20 20:47:30 +05:30
include FeatureGate
include OptionallySearch
2018-12-05 23:21:45 +05:30
include FromUnion
2020-01-01 13:55:28 +05:30
include IgnorableColumns
2021-03-08 18:12:59 +05:30
include Repositories :: CanHousekeepRepository
2021-01-03 14:25:43 +05:30
include EachBatch
2021-04-29 21:17:54 +05:30
include GitlabRoutingHelper
2021-12-07 22:27:20 +05:30
include BulkMemberAccessLoad
2022-06-21 17:19:12 +05:30
include BulkUsersByEmailLoad
2022-03-02 08:16:31 +05:30
include RunnerTokenExpirationInterval
2022-05-07 20:08:51 +05:30
include BlocksUnsafeSerialization
2021-04-29 21:17:54 +05:30
2018-11-08 19:23:39 +05:30
extend Gitlab :: Cache :: RequestCache
2021-01-03 14:25:43 +05:30
extend Gitlab :: Utils :: Override
2015-12-23 02:04:40 +05:30
2014-09-02 18:07:02 +05:30
extend Gitlab :: ConfigHelper
2021-10-27 15:23:28 +05:30
ignore_columns :container_registry_enabled , remove_after : '2021-09-22' , remove_with : '14.4'
2017-08-17 22:00:37 +05:30
BoardLimitExceeded = Class . new ( StandardError )
2022-07-16 23:28:13 +05:30
ExportLimitExceeded = Class . new ( StandardError )
2016-11-03 12:29:30 +05:30
2021-10-27 15:23:28 +05:30
ignore_columns :mirror_last_update_at , :mirror_last_successful_update_at , remove_after : '2021-09-22' , remove_with : '14.4'
ignore_columns :pull_mirror_branch_prefix , remove_after : '2021-09-22' , remove_with : '14.4'
2019-12-04 20:38:33 +05:30
STATISTICS_ATTRIBUTE = 'repositories_count'
UNKNOWN_IMPORT_URL = 'http://unknown.git'
2018-03-17 18:26:18 +05:30
# Hashed Storage versions handle rolling out new storage to project and dependents models:
# nil: legacy
# 1: repository
# 2: attachments
LATEST_STORAGE_VERSION = 2
HASHED_STORAGE_FEATURES = {
repository : 1 ,
attachments : 2
} . freeze
2018-03-26 14:24:53 +05:30
2018-12-13 13:39:08 +05:30
VALID_IMPORT_PORTS = [ 80 , 443 ] . freeze
VALID_IMPORT_PROTOCOLS = %w( http https git ) . freeze
VALID_MIRROR_PORTS = [ 22 , 80 , 443 ] . freeze
VALID_MIRROR_PROTOCOLS = %w( http https ssh git ) . freeze
2015-09-25 12:07:36 +05:30
2019-12-04 20:38:33 +05:30
SORTING_PREFERENCE_FIELD = :projects_sort
MAX_BUILD_TIMEOUT = 1 . month
2021-02-22 17:27:13 +05:30
GL_REPOSITORY_TYPES = [ Gitlab :: GlRepository :: PROJECT , Gitlab :: GlRepository :: WIKI , Gitlab :: GlRepository :: DESIGN ] . freeze
2022-04-04 11:22:00 +05:30
MAX_SUGGESTIONS_TEMPLATE_LENGTH = 255
MAX_COMMIT_TEMPLATE_LENGTH = 500
DEFAULT_MERGE_COMMIT_TEMPLATE = << ~ MSG . rstrip . freeze
Merge branch '%{source_branch}' into '%{target_branch}'
%{ title }
%{ issues }
See merge request %{ reference }
MSG
DEFAULT_SQUASH_COMMIT_TEMPLATE = '%{title}'
2016-11-03 12:29:30 +05:30
cache_markdown_field :description , pipeline : :description
2020-07-28 23:09:34 +05:30
default_value_for :packages_enabled , true
2014-09-02 18:07:02 +05:30
default_value_for :archived , false
2018-03-17 18:26:18 +05:30
default_value_for :resolve_outdated_diff_discussions , false
2020-04-08 14:13:33 +05:30
default_value_for ( :repository_storage ) do
2021-03-11 19:13:27 +05:30
Repository . pick_storage_shard
2020-04-08 14:13:33 +05:30
end
2018-03-17 18:26:18 +05:30
default_value_for ( :shared_runners_enabled ) { Gitlab :: CurrentSettings . shared_runners_enabled }
2016-11-24 13:41:30 +05:30
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
2017-08-17 22:00:37 +05:30
default_value_for :only_allow_merge_if_all_discussions_are_resolved , false
2019-12-26 22:10:19 +05:30
default_value_for :remove_source_branch_after_merge , true
2020-03-13 15:44:24 +05:30
default_value_for :autoclose_referenced_issues , true
2019-12-26 22:10:19 +05:30
default_value_for ( :ci_config_path ) { Gitlab :: CurrentSettings . default_ci_config_path }
2014-09-02 18:07:02 +05:30
2022-02-27 12:50:16 +05:30
add_authentication_token_field :runners_token ,
2022-07-16 23:28:13 +05:30
encrypted : - > { Feature . enabled? ( :projects_tokens_optional_encryption ) ? :optional : :required } ,
2022-04-01 21:47:47 +05:30
prefix : RunnersTokenPrefixable :: RUNNERS_TOKEN_PREFIX
2018-10-15 14:42:47 +05:30
2018-11-08 19:23:39 +05:30
before_validation :mark_remote_mirrors_for_removal , if : - > { RemoteMirror . table_exists? }
2018-10-15 14:42:47 +05:30
2018-03-17 18:26:18 +05:30
before_save :ensure_runners_token
2021-12-11 22:18:48 +05:30
before_validation :ensure_project_namespace_in_sync
2016-08-24 12:49:21 +05:30
2022-07-23 23:45:48 +05:30
before_validation :set_package_registry_access_level , if : :packages_enabled_changed?
2019-07-31 22:56:46 +05:30
after_save :update_project_statistics , if : :saved_change_to_namespace_id?
2018-10-15 14:42:47 +05:30
2022-01-26 12:08:38 +05:30
after_save :schedule_sync_event_worker , if : - > { saved_change_to_id? || saved_change_to_namespace_id? }
2018-10-15 14:42:47 +05:30
after_save :create_import_state , if : - > ( project ) { project . import? && project . import_state . nil? }
2021-11-11 11:23:49 +05:30
after_save :save_topics
2021-06-08 01:23:25 +05:30
after_create - > { create_or_load_association ( :project_feature ) }
2018-10-15 14:42:47 +05:30
2021-06-08 01:23:25 +05:30
after_create - > { create_or_load_association ( :ci_cd_settings ) }
2018-10-15 14:42:47 +05:30
2021-06-08 01:23:25 +05:30
after_create - > { create_or_load_association ( :container_expiration_policy ) }
2020-01-01 13:55:28 +05:30
2021-06-08 01:23:25 +05:30
after_create - > { create_or_load_association ( :pages_metadatum ) }
2019-12-21 20:55:43 +05:30
2018-12-13 13:39:08 +05:30
after_create :set_timestamps_for_create
2018-03-17 18:26:18 +05:30
after_update :update_forks_visibility_level
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
before_destroy :remove_private_deploy_keys
2018-10-15 14:42:47 +05:30
use_fast_destroy :build_trace_chunks
2018-03-17 18:26:18 +05:30
after_destroy :remove_exports
2016-01-14 18:37:52 +05:30
2017-08-17 22:00:37 +05:30
after_validation :check_pending_delete
2018-03-17 18:26:18 +05:30
# Storage specific hooks
after_initialize :use_hashed_storage
after_create :check_repository_absence!
2017-09-10 17:25:29 +05:30
2021-11-11 11:23:49 +05:30
has_many :project_topics , - > { order ( :id ) } , class_name : 'Projects::ProjectTopic'
has_many :topics , through : :project_topics , class_name : 'Projects::Topic'
2016-01-14 18:37:52 +05:30
attr_accessor :old_path_with_namespace
2017-09-10 17:25:29 +05:30
attr_accessor :template_name
2017-08-17 22:00:37 +05:30
attr_writer :pipeline_status
2018-03-17 18:26:18 +05:30
attr_accessor :skip_disk_validation
2021-11-11 11:23:49 +05:30
attr_writer :topic_list
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
2018-12-13 13:39:08 +05:30
belongs_to :pool_repository
2017-08-17 22:00:37 +05:30
belongs_to :creator , class_name : 'User'
2021-11-18 22:05:49 +05:30
belongs_to :group , - > { where ( type : Group . sti_name ) } , foreign_key : 'namespace_id'
2014-09-02 18:07:02 +05:30
belongs_to :namespace
2021-11-11 11:23:49 +05:30
# Sync deletion via DB Trigger to ensure we do not have
# a project without a project_namespace (or vice-versa)
2021-12-11 22:18:48 +05:30
belongs_to :project_namespace , autosave : true , class_name : 'Namespaces::ProjectNamespace' , foreign_key : 'project_namespace_id'
2018-03-17 18:26:18 +05:30
alias_method :parent , :namespace
alias_attribute :parent_id , :namespace_id
2014-09-02 18:07:02 +05:30
2017-08-17 22:00:37 +05:30
has_one :last_event , - > { order 'events.created_at DESC' } , class_name : 'Event'
2019-07-07 11:18:12 +05:30
has_many :boards
2016-09-29 09:46:39 +05:30
2021-09-04 01:27:46 +05:30
def self . integration_association_name ( name )
2021-09-30 23:02:18 +05:30
" #{ name } _integration "
2021-09-04 01:27:46 +05:30
end
2021-06-08 01:23:25 +05:30
# Project integrations
2021-09-04 01:27:46 +05:30
has_one :asana_integration , class_name : 'Integrations::Asana'
has_one :assembla_integration , class_name : 'Integrations::Assembla'
has_one :bamboo_integration , class_name : 'Integrations::Bamboo'
has_one :bugzilla_integration , class_name : 'Integrations::Bugzilla'
has_one :buildkite_integration , class_name : 'Integrations::Buildkite'
has_one :campfire_integration , class_name : 'Integrations::Campfire'
has_one :confluence_integration , class_name : 'Integrations::Confluence'
has_one :custom_issue_tracker_integration , class_name : 'Integrations::CustomIssueTracker'
has_one :datadog_integration , class_name : 'Integrations::Datadog'
has_one :discord_integration , class_name : 'Integrations::Discord'
has_one :drone_ci_integration , class_name : 'Integrations::DroneCi'
has_one :emails_on_push_integration , class_name : 'Integrations::EmailsOnPush'
has_one :ewm_integration , class_name : 'Integrations::Ewm'
has_one :external_wiki_integration , class_name : 'Integrations::ExternalWiki'
has_one :flowdock_integration , class_name : 'Integrations::Flowdock'
has_one :hangouts_chat_integration , class_name : 'Integrations::HangoutsChat'
2022-05-07 20:08:51 +05:30
has_one :harbor_integration , class_name : 'Integrations::Harbor'
2021-09-04 01:27:46 +05:30
has_one :irker_integration , class_name : 'Integrations::Irker'
2021-09-30 23:02:18 +05:30
has_one :jenkins_integration , class_name : 'Integrations::Jenkins'
has_one :jira_integration , class_name : 'Integrations::Jira'
has_one :mattermost_integration , class_name : 'Integrations::Mattermost'
has_one :mattermost_slash_commands_integration , class_name : 'Integrations::MattermostSlashCommands'
has_one :microsoft_teams_integration , class_name : 'Integrations::MicrosoftTeams'
has_one :mock_ci_integration , class_name : 'Integrations::MockCi'
has_one :mock_monitoring_integration , class_name : 'Integrations::MockMonitoring'
has_one :packagist_integration , class_name : 'Integrations::Packagist'
has_one :pipelines_email_integration , class_name : 'Integrations::PipelinesEmail'
has_one :pivotaltracker_integration , class_name : 'Integrations::Pivotaltracker'
has_one :prometheus_integration , class_name : 'Integrations::Prometheus' , inverse_of : :project
has_one :pushover_integration , class_name : 'Integrations::Pushover'
has_one :redmine_integration , class_name : 'Integrations::Redmine'
2021-12-11 22:18:48 +05:30
has_one :shimo_integration , class_name : 'Integrations::Shimo'
2021-09-30 23:02:18 +05:30
has_one :slack_integration , class_name : 'Integrations::Slack'
has_one :slack_slash_commands_integration , class_name : 'Integrations::SlackSlashCommands'
has_one :teamcity_integration , class_name : 'Integrations::Teamcity'
has_one :unify_circuit_integration , class_name : 'Integrations::UnifyCircuit'
has_one :webex_teams_integration , class_name : 'Integrations::WebexTeams'
has_one :youtrack_integration , class_name : 'Integrations::Youtrack'
2021-11-11 11:23:49 +05:30
has_one :zentao_integration , class_name : 'Integrations::Zentao'
2017-09-10 17:25:29 +05:30
2018-03-17 18:26:18 +05:30
has_one :root_of_fork_network ,
foreign_key : 'root_project_id' ,
inverse_of : :root_project ,
class_name : 'ForkNetwork'
has_one :fork_network_member
has_one :fork_network , through : :fork_network_member
2018-12-13 13:39:08 +05:30
has_one :forked_from_project , through : :fork_network_member
has_many :forked_to_members , class_name : 'ForkNetworkMember' , foreign_key : 'forked_from_project_id'
has_many :forks , through : :forked_to_members , source : :project , inverse_of : :forked_from_project
2020-01-01 13:55:28 +05:30
has_many :fork_network_projects , through : :fork_network , source : :projects
2015-04-26 12:48:37 +05:30
2020-07-28 23:09:34 +05:30
# Packages
has_many :packages , class_name : 'Packages::Package'
has_many :package_files , through : :packages , class_name : 'Packages::PackageFile'
2021-03-11 19:13:27 +05:30
# debian_distributions and associated component_files must be destroyed by ruby code in order to properly remove carrierwave uploads
2021-03-08 18:12:59 +05:30
has_many :debian_distributions , class_name : 'Packages::Debian::ProjectDistribution' , dependent : :destroy # rubocop:disable Cop/ActiveRecordDependent
2022-07-16 23:28:13 +05:30
has_one :packages_cleanup_policy , class_name : 'Packages::Cleanup::Policy' , inverse_of : :project
2020-07-28 23:09:34 +05:30
2018-10-15 14:42:47 +05:30
has_one :import_state , autosave : true , class_name : 'ProjectImportState' , inverse_of : :project
2018-11-08 19:23:39 +05:30
has_one :import_export_upload , dependent : :destroy # rubocop:disable Cop/ActiveRecordDependent
2020-04-08 14:13:33 +05:30
has_many :export_jobs , class_name : 'ProjectExportJob'
2021-11-18 22:05:49 +05:30
has_many :bulk_import_exports , class_name : 'BulkImports::Export' , inverse_of : :project
2019-02-15 15:39:39 +05:30
has_one :project_repository , inverse_of : :project
2021-01-03 14:25:43 +05:30
has_one :tracing_setting , class_name : 'ProjectTracingSetting'
2020-03-13 15:44:24 +05:30
has_one :incident_management_setting , inverse_of : :project , class_name : 'IncidentManagement::ProjectIncidentManagementSetting'
2019-02-15 15:39:39 +05:30
has_one :error_tracking_setting , inverse_of : :project , class_name : 'ErrorTracking::ProjectErrorTrackingSetting'
2019-07-31 22:56:46 +05:30
has_one :metrics_setting , inverse_of : :project , class_name : 'ProjectMetricsSetting'
2019-12-21 20:55:43 +05:30
has_one :grafana_integration , inverse_of : :project
2020-06-23 00:09:42 +05:30
has_one :project_setting , inverse_of : :project , autosave : true
2020-04-22 19:07:51 +05:30
has_one :alerting_setting , inverse_of : :project , class_name : 'Alerting::ProjectAlertingSetting'
2020-07-28 23:09:34 +05:30
has_one :service_desk_setting , class_name : 'ServiceDeskSetting'
2018-10-15 14:42:47 +05:30
2021-04-29 21:17:54 +05:30
# Merge requests for target project should be removed with it
2018-12-13 13:39:08 +05:30
has_many :merge_requests , foreign_key : 'target_project_id' , inverse_of : :target_project
2021-03-11 19:13:27 +05:30
has_many :merge_request_metrics , foreign_key : 'target_project' , class_name : 'MergeRequest::Metrics' , inverse_of : :target_project
2018-03-27 19:54:05 +05:30
has_many :source_of_merge_requests , foreign_key : 'source_project_id' , class_name : 'MergeRequest'
2017-09-10 17:25:29 +05:30
has_many :issues
has_many :labels , class_name : 'ProjectLabel'
2021-06-08 01:23:25 +05:30
has_many :integrations
2017-09-10 17:25:29 +05:30
has_many :events
has_many :milestones
2020-05-24 23:13:21 +05:30
has_many :iterations
2021-09-04 01:27:46 +05:30
# Projects with a very large number of notes may time out destroying them
# through the foreign key. Additionally, the deprecated attachment uploader
# for notes requires us to use dependent: :destroy to avoid orphaning uploaded
# files.
#
# https://gitlab.com/gitlab-org/gitlab/-/issues/207222
has_many :notes , dependent : :destroy # rubocop:disable Cop/ActiveRecordDependent
2017-09-10 17:25:29 +05:30
has_many :snippets , class_name : 'ProjectSnippet'
has_many :hooks , class_name : 'ProjectHook'
has_many :protected_branches
2021-02-22 17:27:13 +05:30
has_many :exported_protected_branches
2017-09-10 17:25:29 +05:30
has_many :protected_tags
2018-11-18 11:00:15 +05:30
has_many :repository_languages , - > { order " share DESC " }
2020-05-24 23:13:21 +05:30
has_many :designs , inverse_of : :project , class_name : 'DesignManagement::Design'
2016-08-24 12:49:21 +05:30
2017-08-17 22:00:37 +05:30
has_many :project_authorizations
has_many :authorized_users , through : :project_authorizations , source : :user , class_name : 'User'
2017-09-10 17:25:29 +05:30
has_many :project_members , - > { where ( requested_at : nil ) } ,
as : :source , dependent : :delete_all # rubocop:disable Cop/ActiveRecordDependent
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
2017-09-10 17:25:29 +05:30
has_many :requesters , - > { where . not ( requested_at : nil ) } ,
as : :source , class_name : 'ProjectMember' , dependent : :delete_all # rubocop:disable Cop/ActiveRecordDependent
2018-03-17 18:26:18 +05:30
has_many :members_and_requesters , as : :source , class_name : 'ProjectMember'
2016-08-24 12:49:21 +05:30
2019-10-12 21:52:04 +05:30
has_many :deploy_keys_projects , inverse_of : :project
2014-09-02 18:07:02 +05:30
has_many :deploy_keys , through : :deploy_keys_projects
2017-09-10 17:25:29 +05:30
has_many :users_star_projects
2014-09-02 18:07:02 +05:30
has_many :starrers , through : :users_star_projects , source : :user
2017-09-10 17:25:29 +05:30
has_many :releases
2021-09-04 01:27:46 +05:30
has_many :lfs_objects_projects
2019-09-04 21:01:54 +05:30
has_many :lfs_objects , - > { distinct } , through : :lfs_objects_projects
2018-03-17 18:26:18 +05:30
has_many :lfs_file_locks
2017-09-10 17:25:29 +05:30
has_many :project_group_links
2016-06-02 11:05:42 +05:30
has_many :invited_groups , through : :project_group_links , source : :group
2017-09-10 17:25:29 +05:30
has_many :todos
has_many :notification_settings , as : :source , dependent : :delete_all # rubocop:disable Cop/ActiveRecordDependent
2018-05-09 12:01:36 +05:30
has_many :internal_ids
2017-09-10 17:25:29 +05:30
has_one :import_data , class_name : 'ProjectImportData' , inverse_of : :project , autosave : true
2018-03-17 18:26:18 +05:30
has_one :project_feature , inverse_of : :project
2017-09-10 17:25:29 +05:30
has_one :statistics , class_name : 'ProjectStatistics'
2020-11-24 15:15:51 +05:30
has_one :feature_usage , class_name : 'ProjectFeatureUsage'
2017-09-10 17:25:29 +05:30
2018-03-17 18:26:18 +05:30
has_one :cluster_project , class_name : 'Clusters::Project'
has_many :clusters , through : :cluster_project , class_name : 'Clusters::Cluster'
2019-02-15 15:39:39 +05:30
has_many :kubernetes_namespaces , class_name : 'Clusters::KubernetesNamespace'
2019-12-21 20:55:43 +05:30
has_many :management_clusters , class_name : 'Clusters::Cluster' , foreign_key : :management_project_id , inverse_of : :management_project
2020-10-24 23:57:45 +05:30
has_many :cluster_agents , class_name : 'Clusters::Agent'
2018-03-17 18:26:18 +05:30
2018-11-20 20:47:30 +05:30
has_many :prometheus_metrics
2020-03-13 15:44:24 +05:30
has_many :prometheus_alerts , inverse_of : :project
2020-04-22 19:07:51 +05:30
has_many :prometheus_alert_events , inverse_of : :project
has_many :self_managed_prometheus_alert_events , inverse_of : :project
2020-05-24 23:13:21 +05:30
has_many :metrics_users_starred_dashboards , class_name : 'Metrics::UsersStarredDashboard' , inverse_of : :project
has_many :alert_management_alerts , class_name : 'AlertManagement::Alert' , inverse_of : :project
2021-01-03 14:25:43 +05:30
has_many :alert_management_http_integrations , class_name : 'AlertManagement::HttpIntegration' , inverse_of : :project
2018-11-20 20:47:30 +05:30
2017-09-10 17:25:29 +05:30
# Container repositories need to remove data from the container registry,
# which is not managed by the DB. Hence we're still using dependent: :destroy
# here.
has_many :container_repositories , dependent : :destroy # rubocop:disable Cop/ActiveRecordDependent
2020-01-01 13:55:28 +05:30
has_one :container_expiration_policy , inverse_of : :project
2017-09-10 17:25:29 +05:30
has_many :commit_statuses
2019-07-07 11:18:12 +05:30
# The relation :all_pipelines is intended to be used when we want to get the
2019-02-15 15:39:39 +05:30
# whole list of pipelines associated to the project
has_many :all_pipelines , class_name : 'Ci::Pipeline' , inverse_of : :project
2020-11-24 15:15:51 +05:30
# The relation :ci_pipelines includes all those that directly contribute to the
# latest status of a ref. This does not include dangling pipelines such as those
# from webide, child pipelines, etc.
2019-02-15 15:39:39 +05:30
has_many :ci_pipelines ,
- > { ci_sources } ,
class_name : 'Ci::Pipeline' ,
inverse_of : :project
2018-11-08 19:23:39 +05:30
has_many :stages , class_name : 'Ci::Stage' , inverse_of : :project
2020-06-23 00:09:42 +05:30
has_many :ci_refs , class_name : 'Ci::Ref' , inverse_of : :project
2017-09-10 17:25:29 +05:30
2021-11-11 11:23:49 +05:30
has_many :pending_builds , class_name : 'Ci::PendingBuild'
2022-05-07 20:08:51 +05:30
has_many :builds , class_name : 'Ci::Build' , inverse_of : :project
2021-01-03 14:25:43 +05:30
has_many :processables , class_name : 'Ci::Processable' , inverse_of : :project
2022-05-07 20:08:51 +05:30
has_many :build_trace_chunks , class_name : 'Ci::BuildTraceChunk' , through : :builds , source : :trace_chunks , dependent : :restrict_with_error
2020-06-23 00:09:42 +05:30
has_many :build_report_results , class_name : 'Ci::BuildReportResult' , inverse_of : :project
2022-05-07 20:08:51 +05:30
has_many :job_artifacts , class_name : 'Ci::JobArtifact' , dependent : :restrict_with_error
has_many :pipeline_artifacts , class_name : 'Ci::PipelineArtifact' , inverse_of : :project , dependent : :restrict_with_error
2018-11-08 19:23:39 +05:30
has_many :runner_projects , class_name : 'Ci::RunnerProject' , inverse_of : :project
2015-12-23 02:04:40 +05:30
has_many :runners , through : :runner_projects , source : :runner , class_name : 'Ci::Runner'
2017-09-10 17:25:29 +05:30
has_many :variables , class_name : 'Ci::Variable'
has_many :triggers , class_name : 'Ci::Trigger'
2022-05-07 20:08:51 +05:30
has_many :secure_files , class_name : 'Ci::SecureFile' , dependent : :restrict_with_error
2017-09-10 17:25:29 +05:30
has_many :environments
2019-12-26 22:10:19 +05:30
has_many :environments_for_dashboard , - > { from ( with_rank . unfoldered . available , :environments ) . where ( 'rank <= 3' ) } , class_name : 'Environment'
2019-12-21 20:55:43 +05:30
has_many :deployments
2017-09-10 17:25:29 +05:30
has_many :pipeline_schedules , class_name : 'Ci::PipelineSchedule'
2018-05-09 12:01:36 +05:30
has_many :project_deploy_tokens
has_many :deploy_tokens , through : :project_deploy_tokens
2020-03-13 15:44:24 +05:30
has_many :resource_groups , class_name : 'Ci::ResourceGroup' , inverse_of : :project
2020-05-24 23:13:21 +05:30
has_many :freeze_periods , class_name : 'Ci::FreezePeriod' , inverse_of : :project
2017-08-17 22:00:37 +05:30
2019-09-30 21:07:59 +05:30
has_one :auto_devops , class_name : 'ProjectAutoDevops' , inverse_of : :project , autosave : true
2018-03-17 18:26:18 +05:30
has_many :custom_attributes , class_name : 'ProjectCustomAttribute'
2018-03-27 19:54:05 +05:30
has_many :project_badges , class_name : 'ProjectBadge'
2018-10-15 14:42:47 +05:30
has_one :ci_cd_settings , class_name : 'ProjectCiCdSetting' , inverse_of : :project , autosave : true , dependent : :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :remote_mirrors , inverse_of : :project
2021-06-08 01:23:25 +05:30
has_many :cycle_analytics_stages , class_name : 'Analytics::CycleAnalytics::ProjectStage' , inverse_of : :project
has_many :value_streams , class_name : 'Analytics::CycleAnalytics::ProjectValueStream' , inverse_of : :project
2018-03-27 19:54:05 +05:30
2019-12-04 20:38:33 +05:30
has_many :external_pull_requests , inverse_of : :project
2019-12-21 20:55:43 +05:30
has_many :sourced_pipelines , class_name : 'Ci::Sources::Pipeline' , foreign_key : :source_project_id
has_many :source_pipelines , class_name : 'Ci::Sources::Pipeline' , foreign_key : :project_id
2020-01-01 13:55:28 +05:30
has_many :import_failures , inverse_of : :project
2022-06-21 17:19:12 +05:30
has_many :jira_imports , - > { order ( JiraImportState . arel_table [ :created_at ] . asc ) } , class_name : 'JiraImportState' , inverse_of : :project
2020-04-22 19:07:51 +05:30
2020-05-24 23:13:21 +05:30
has_many :daily_build_group_report_results , class_name : 'Ci::DailyBuildGroupReportResult'
2021-11-11 11:23:49 +05:30
has_many :ci_feature_usages , class_name : 'Projects::CiFeatureUsage'
2020-05-24 23:13:21 +05:30
2021-04-17 20:07:23 +05:30
has_many :repository_storage_moves , class_name : 'Projects::RepositoryStorageMove' , inverse_of : :container
2020-01-01 13:55:28 +05:30
2020-06-23 00:09:42 +05:30
has_many :webide_pipelines , - > { webide_source } , class_name : 'Ci::Pipeline' , inverse_of : :project
has_many :reviews , inverse_of : :project
2021-01-03 14:25:43 +05:30
has_many :terraform_states , class_name : 'Terraform::State' , inverse_of : :project
2020-11-24 15:15:51 +05:30
# GitLab Pages
has_many :pages_domains
has_one :pages_metadatum , class_name : 'ProjectPagesMetadatum' , inverse_of : :project
2021-01-29 00:20:46 +05:30
# we need to clean up files, not only remove records
has_many :pages_deployments , dependent : :destroy # rubocop:disable Cop/ActiveRecordDependent
2020-11-24 15:15:51 +05:30
2020-10-24 23:57:45 +05:30
# Can be too many records. We need to implement delete_all in batches.
# Issue https://gitlab.com/gitlab-org/gitlab/-/issues/228637
has_many :product_analytics_events , dependent : :destroy # rubocop:disable Cop/ActiveRecordDependent
2020-11-24 15:15:51 +05:30
has_many :operations_feature_flags , class_name : 'Operations::FeatureFlag'
has_one :operations_feature_flags_client , class_name : 'Operations::FeatureFlagsClient'
has_many :operations_feature_flags_user_lists , class_name : 'Operations::FeatureFlags::UserList'
2021-10-27 15:23:28 +05:30
has_many :error_tracking_client_keys , inverse_of : :project , class_name : 'ErrorTracking::ClientKey'
2021-09-30 23:02:18 +05:30
2021-06-08 01:23:25 +05:30
has_many :timelogs
2022-01-26 12:08:38 +05:30
has_one :ci_project_mirror , class_name : 'Ci::ProjectMirror'
has_many :sync_events , class_name : 'Projects::SyncEvent'
2022-07-23 23:45:48 +05:30
has_one :build_artifacts_size_refresh , class_name : 'Projects::BuildArtifactsSizeRefresh'
2015-12-23 02:04:40 +05:30
accepts_nested_attributes_for :variables , allow_destroy : true
2018-03-17 18:26:18 +05:30
accepts_nested_attributes_for :project_feature , update_only : true
2020-05-24 23:13:21 +05:30
accepts_nested_attributes_for :project_setting , update_only : true
2017-09-10 17:25:29 +05:30
accepts_nested_attributes_for :import_data
2018-03-17 18:26:18 +05:30
accepts_nested_attributes_for :auto_devops , update_only : true
2019-09-04 21:01:54 +05:30
accepts_nested_attributes_for :ci_cd_settings , update_only : true
2020-01-01 13:55:28 +05:30
accepts_nested_attributes_for :container_expiration_policy , update_only : true
2015-04-26 12:48:37 +05:30
2018-10-15 14:42:47 +05:30
accepts_nested_attributes_for :remote_mirrors ,
allow_destroy : true ,
reject_if : - > ( attrs ) { attrs [ :id ] . blank? && attrs [ :url ] . blank? }
2021-01-03 14:25:43 +05:30
accepts_nested_attributes_for :tracing_setting , update_only : true , allow_destroy : true
2020-03-13 15:44:24 +05:30
accepts_nested_attributes_for :incident_management_setting , update_only : true
2019-02-15 15:39:39 +05:30
accepts_nested_attributes_for :error_tracking_setting , update_only : true
2019-07-31 22:56:46 +05:30
accepts_nested_attributes_for :metrics_setting , update_only : true , allow_destroy : true
2019-12-21 20:55:43 +05:30
accepts_nested_attributes_for :grafana_integration , update_only : true , allow_destroy : true
2021-09-30 23:02:18 +05:30
accepts_nested_attributes_for :prometheus_integration , update_only : true
2020-04-22 19:07:51 +05:30
accepts_nested_attributes_for :alerting_setting , update_only : true
2020-03-13 15:44:24 +05:30
delegate :feature_available? , :builds_enabled? , :wiki_enabled? ,
:merge_requests_enabled? , :forking_enabled? , :issues_enabled? ,
2021-02-22 17:27:13 +05:30
:pages_enabled? , :analytics_enabled? , :snippets_enabled? , :public_pages? , :private_pages? ,
2020-03-13 15:44:24 +05:30
:merge_requests_access_level , :forking_access_level , :issues_access_level ,
:wiki_access_level , :snippets_access_level , :builds_access_level ,
2022-07-23 23:45:48 +05:30
:repository_access_level , :package_registry_access_level , :pages_access_level , :metrics_dashboard_access_level , :analytics_access_level ,
2021-04-17 20:07:23 +05:30
:operations_enabled? , :operations_access_level , :security_and_compliance_access_level ,
2021-09-30 23:02:18 +05:30
:container_registry_access_level , :container_registry_enabled? ,
2021-04-17 20:07:23 +05:30
to : :project_feature , allow_nil : true
2021-09-30 23:02:18 +05:30
alias_method :container_registry_enabled , :container_registry_enabled?
2021-10-29 20:43:33 +05:30
delegate :show_default_award_emojis , :show_default_award_emojis = , :show_default_award_emojis? ,
2022-07-16 23:28:13 +05:30
:enforce_auth_checks_on_uploads , :enforce_auth_checks_on_uploads = , :enforce_auth_checks_on_uploads? ,
2021-10-29 20:43:33 +05:30
:warn_about_potentially_unwanted_characters , :warn_about_potentially_unwanted_characters = , :warn_about_potentially_unwanted_characters? ,
2020-05-24 23:13:21 +05:30
to : :project_setting , allow_nil : true
2020-01-01 13:55:28 +05:30
delegate :scheduled? , :started? , :in_progress? , :failed? , :finished? ,
prefix : :import , to : :import_state , allow_nil : true
2020-07-28 23:09:34 +05:30
delegate :squash_always? , :squash_never? , :squash_enabled_by_default? , :squash_readonly? , to : :project_setting
2021-09-30 23:02:18 +05:30
delegate :squash_option , :squash_option = , to : :project_setting
2022-03-02 08:16:31 +05:30
delegate :mr_default_target_self , :mr_default_target_self = , to : :project_setting
2021-09-30 23:02:18 +05:30
delegate :previous_default_branch , :previous_default_branch = , to : :project_setting
2020-01-01 13:55:28 +05:30
delegate :no_import? , to : :import_state , allow_nil : true
2014-09-02 18:07:02 +05:30
delegate :name , to : :owner , allow_nil : true , prefix : true
delegate :members , to : :team , prefix : true
2017-08-17 22:00:37 +05:30
delegate :add_user , :add_users , to : :team
2022-05-07 20:08:51 +05:30
delegate :add_guest , :add_reporter , :add_developer , :add_maintainer , :add_owner , :add_role , to : :team
2021-09-30 23:02:18 +05:30
delegate :group_runners_enabled , :group_runners_enabled = , to : :ci_cd_settings , allow_nil : true
2022-07-16 23:28:13 +05:30
delegate :root_ancestor , :certificate_based_clusters_enabled? , to : :namespace , allow_nil : true
2019-07-07 11:18:12 +05:30
delegate :last_pipeline , to : :commit , allow_nil : true
2019-09-04 21:01:54 +05:30
delegate :external_dashboard_url , to : :metrics_setting , allow_nil : true , prefix : true
2020-06-23 00:09:42 +05:30
delegate :dashboard_timezone , to : :metrics_setting , allow_nil : true , prefix : true
2021-09-04 01:27:46 +05:30
delegate :default_git_depth , :default_git_depth = , to : :ci_cd_settings , prefix : :ci , allow_nil : true
2021-09-30 23:02:18 +05:30
delegate :forward_deployment_enabled , :forward_deployment_enabled = , to : :ci_cd_settings , prefix : :ci , allow_nil : true
delegate :job_token_scope_enabled , :job_token_scope_enabled = , to : :ci_cd_settings , prefix : :ci , allow_nil : true
delegate :keep_latest_artifact , :keep_latest_artifact = , to : :ci_cd_settings , allow_nil : true
delegate :restrict_user_defined_variables , :restrict_user_defined_variables = , to : :ci_cd_settings , allow_nil : true
2022-06-21 17:19:12 +05:30
delegate :separated_caches , :separated_caches = , to : :ci_cd_settings , prefix : :ci , allow_nil : true
2022-03-02 08:16:31 +05:30
delegate :runner_token_expiration_interval , :runner_token_expiration_interval = , :runner_token_expiration_interval_human_readable , :runner_token_expiration_interval_human_readable = , to : :ci_cd_settings , allow_nil : true
delegate :actual_limits , :actual_plan_name , :actual_plan , to : :namespace , allow_nil : true
2020-07-28 23:09:34 +05:30
delegate :allow_merge_on_skipped_pipeline , :allow_merge_on_skipped_pipeline? ,
2022-01-26 12:08:38 +05:30
:allow_merge_on_skipped_pipeline = , :has_confluence? , :has_shimo? ,
2020-07-28 23:09:34 +05:30
to : :project_setting
2021-09-30 23:02:18 +05:30
delegate :active? , to : :prometheus_integration , allow_nil : true , prefix : true
2021-12-11 22:18:48 +05:30
delegate :merge_commit_template , :merge_commit_template = , to : :project_setting , allow_nil : true
2022-01-26 12:08:38 +05:30
delegate :squash_commit_template , :squash_commit_template = , to : :project_setting , allow_nil : true
2014-09-02 18:07:02 +05:30
2020-11-24 15:15:51 +05:30
delegate :log_jira_dvcs_integration_usage , :jira_dvcs_server_last_sync_at , :jira_dvcs_cloud_last_sync_at , to : :feature_usage
2014-09-02 18:07:02 +05:30
# Validations
validates :creator , presence : true , on : :create
validates :description , length : { maximum : 2000 } , allow_blank : true
2017-09-10 17:25:29 +05:30
validates :ci_config_path ,
2018-03-17 18:26:18 +05:30
format : { without : %r{ ( \ . { 2 } | \ A/) } ,
2019-07-31 22:56:46 +05:30
message : _ ( 'cannot include leading slash or directory traversal.' ) } ,
2017-09-10 17:25:29 +05:30
length : { maximum : 255 } ,
allow_blank : true
2015-04-26 12:48:37 +05:30
validates :name ,
presence : true ,
2017-08-17 22:00:37 +05:30
length : { maximum : 255 } ,
2015-04-26 12:48:37 +05:30
format : { with : Gitlab :: Regex . project_name_regex ,
message : Gitlab :: Regex . project_name_regex_message }
validates :path ,
presence : true ,
2018-03-17 18:26:18 +05:30
project_path : true ,
length : { maximum : 255 }
2022-05-07 20:08:51 +05:30
validates :path ,
format : { with : Gitlab :: Regex . oci_repository_path_regex ,
message : Gitlab :: Regex . oci_repository_path_regex_message } ,
if : :path_changed?
2017-08-17 22:00:37 +05:30
2020-03-13 15:44:24 +05:30
validates :project_feature , presence : true
2014-09-02 18:07:02 +05:30
validates :namespace , presence : true
2022-05-07 20:08:51 +05:30
validates :project_namespace , presence : true , on : :create , if : - > { self . namespace }
2022-01-26 12:08:38 +05:30
validates :project_namespace , presence : true , on : :update , if : - > { self . project_namespace_id_changed? ( to : nil ) }
2017-08-17 22:00:37 +05:30
validates :name , uniqueness : { scope : :namespace_id }
2019-07-31 22:56:46 +05:30
validates :import_url , public_url : { schemes : - > ( project ) { project . persisted? ? VALID_MIRROR_PROTOCOLS : VALID_IMPORT_PROTOCOLS } ,
2019-01-03 12:48:30 +05:30
ports : - > ( project ) { project . persisted? ? VALID_MIRROR_PORTS : VALID_IMPORT_PORTS } ,
enforce_user : true } , if : [ :external_import? , :import_url_changed? ]
2014-09-02 18:07:02 +05:30
validates :star_count , numericality : { greater_than_or_equal_to : 0 }
2019-03-02 22:35:43 +05:30
validate :check_personal_projects_limit , on : :create
2018-03-17 18:26:18 +05:30
validate :check_repository_path_availability , on : :update , if : - > ( project ) { project . renamed? }
2019-06-05 12:25:43 +05:30
validate :visibility_level_allowed_by_group , if : :should_validate_visibility_level?
validate :visibility_level_allowed_as_fork , if : :should_validate_visibility_level?
2018-05-09 12:01:36 +05:30
validate :validate_pages_https_only , if : - > { changes . has_key? ( :pages_https_only ) }
2021-01-03 14:25:43 +05:30
validate :changing_shared_runners_enabled_is_allowed
2016-08-24 12:49:21 +05:30
validates :repository_storage ,
presence : true ,
inclusion : { in : - > ( _object ) { Gitlab . config . repositories . storages . keys } }
2021-03-11 19:13:27 +05:30
validates :variables , nested_attributes_duplicates : { scope : :environment_scope }
2019-02-15 15:39:39 +05:30
validates :bfg_object_map , file_size : { maximum : :max_attachment_size }
2020-03-13 15:44:24 +05:30
validates :max_artifacts_size , numericality : { only_integer : true , greater_than : 0 , allow_nil : true }
2022-04-04 11:22:00 +05:30
validates :suggestion_commit_message , length : { maximum : MAX_SUGGESTIONS_TEMPLATE_LENGTH }
2015-04-26 12:48:37 +05:30
2014-09-02 18:07:02 +05:30
# Scopes
2017-09-10 17:25:29 +05:30
scope :pending_delete , - > { where ( pending_delete : true ) }
scope :without_deleted , - > { where ( pending_delete : false ) }
2022-05-07 20:08:51 +05:30
scope :not_hidden , - > { where ( hidden : false ) }
2022-04-04 11:22:00 +05:30
scope :not_aimed_for_deletion , - > { where ( marked_for_deletion_at : nil ) . without_deleted }
2017-08-17 22:00:37 +05:30
2020-01-01 13:55:28 +05:30
scope :with_storage_feature , - > ( feature ) do
where ( arel_table [ :storage_version ] . gteq ( HASHED_STORAGE_FEATURES [ feature ] ) )
end
scope :without_storage_feature , - > ( feature ) do
where ( arel_table [ :storage_version ] . lt ( HASHED_STORAGE_FEATURES [ feature ] )
. or ( arel_table [ :storage_version ] . eq ( nil ) ) )
end
scope :with_unmigrated_storage , - > do
where ( arel_table [ :storage_version ] . lt ( LATEST_STORAGE_VERSION )
. or ( arel_table [ :storage_version ] . eq ( nil ) ) )
end
2018-03-17 18:26:18 +05:30
2022-06-21 17:19:12 +05:30
scope :sorted_by_updated_asc , - > { reorder ( self . arel_table [ 'updated_at' ] . asc ) }
scope :sorted_by_updated_desc , - > { reorder ( self . arel_table [ 'updated_at' ] . desc ) }
2020-03-13 15:44:24 +05:30
scope :sorted_by_stars_desc , - > { reorder ( self . arel_table [ 'star_count' ] . desc ) }
scope :sorted_by_stars_asc , - > { reorder ( self . arel_table [ 'star_count' ] . asc ) }
# Sometimes queries (e.g. using CTEs) require explicit disambiguation with table name
2021-11-11 11:23:49 +05:30
scope :projects_order_id_asc , - > { reorder ( self . arel_table [ 'id' ] . asc ) }
2020-03-13 15:44:24 +05:30
scope :projects_order_id_desc , - > { reorder ( self . arel_table [ 'id' ] . desc ) }
2015-04-26 12:48:37 +05:30
2020-11-24 15:15:51 +05:30
scope :sorted_by_similarity_desc , - > ( search , include_in_select : false ) do
2020-10-24 23:57:45 +05:30
order_expression = Gitlab :: Database :: SimilarityScore . build_expression ( search : search , rules : [
{ column : arel_table [ " path " ] , multiplier : 1 } ,
{ column : arel_table [ " name " ] , multiplier : 0 . 7 } ,
{ column : arel_table [ " description " ] , multiplier : 0 . 2 }
] )
2021-04-17 20:07:23 +05:30
order = Gitlab :: Pagination :: Keyset :: Order . build ( [
Gitlab :: Pagination :: Keyset :: ColumnOrderDefinition . new (
attribute_name : 'similarity' ,
column_expression : order_expression ,
order_expression : order_expression . desc ,
order_direction : :desc ,
distinct : false ,
add_to_projections : true
) ,
Gitlab :: Pagination :: Keyset :: ColumnOrderDefinition . new (
attribute_name : 'id' ,
order_expression : Project . arel_table [ :id ] . desc
)
] )
2020-11-24 15:15:51 +05:30
2021-04-17 20:07:23 +05:30
order . apply_cursor_conditions ( reorder ( order ) )
2020-10-24 23:57:45 +05:30
end
2020-07-28 23:09:34 +05:30
scope :with_packages , - > { joins ( :packages ) }
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 ) }
2021-04-29 21:17:54 +05:30
scope :joined , - > ( user ) { where . not ( namespace_id : user . namespace_id ) }
2017-09-10 17:25:29 +05:30
scope :starred_by , - > ( user ) { joins ( :users_star_projects ) . where ( 'users_star_projects.user_id' : user . id ) }
2016-06-02 11:05:42 +05:30
scope :visible_to_user , - > ( user ) { where ( id : user . authorized_projects . select ( :id ) . reorder ( nil ) ) }
2018-11-18 11:00:15 +05:30
scope :visible_to_user_and_access_level , - > ( user , access_level ) { where ( id : user . authorized_projects . where ( 'project_authorizations.access_level >= ?' , access_level ) . select ( :id ) . reorder ( nil ) ) }
2018-03-17 18:26:18 +05:30
scope :archived , - > { where ( archived : true ) }
2014-09-02 18:07:02 +05:30
scope :non_archived , - > { where ( archived : false ) }
2020-06-23 00:09:42 +05:30
scope :with_push , - > { joins ( :events ) . merge ( Event . pushed_action ) }
2016-11-24 13:41:30 +05:30
scope :with_project_feature , - > { joins ( 'LEFT JOIN project_features ON projects.id = project_features.project_id' ) }
2020-11-24 15:15:51 +05:30
scope :with_jira_dvcs_cloud , - > { joins ( :feature_usage ) . merge ( ProjectFeatureUsage . with_jira_dvcs_integration_enabled ( cloud : true ) ) }
scope :with_jira_dvcs_server , - > { joins ( :feature_usage ) . merge ( ProjectFeatureUsage . with_jira_dvcs_integration_enabled ( cloud : false ) ) }
2020-01-01 13:55:28 +05:30
scope :inc_routes , - > { includes ( :route , namespace : :route ) }
2017-08-17 22:00:37 +05:30
scope :with_statistics , - > { includes ( :statistics ) }
2020-03-13 15:44:24 +05:30
scope :with_namespace , - > { includes ( :namespace ) }
2022-07-23 23:45:48 +05:30
scope :with_group , - > { includes ( :group ) }
2020-03-13 15:44:24 +05:30
scope :with_import_state , - > { includes ( :import_state ) }
2020-07-28 23:09:34 +05:30
scope :include_project_feature , - > { includes ( :project_feature ) }
2021-10-27 15:23:28 +05:30
scope :include_integration , - > ( integration_association_name ) { includes ( integration_association_name ) }
scope :with_integration , - > ( integration_class ) { joins ( :integrations ) . merge ( integration_class . all ) }
scope :with_active_integration , - > ( integration_class ) { with_integration ( integration_class ) . merge ( integration_class . active ) }
2017-08-17 22:00:37 +05:30
scope :with_shared_runners , - > { where ( shared_runners_enabled : true ) }
scope :inside_path , - > ( path ) do
# We need routes alias rs for JOIN so it does not conflict with
# includes(:route) which we use in ProjectsFinder.
2017-09-10 17:25:29 +05:30
joins ( " INNER JOIN routes rs ON rs.source_id = projects.id AND rs.source_type = 'Project' " )
. where ( 'rs.path LIKE ?' , " #{ sanitize_sql_like ( path ) } /% " )
2017-08-17 22:00:37 +05:30
end
2016-11-24 13:41:30 +05:30
scope :with_feature_enabled , - > ( feature ) {
2022-01-26 12:08:38 +05:30
with_project_feature . merge ( ProjectFeature . with_feature_enabled ( feature ) )
2016-11-24 13:41:30 +05:30
}
scope :with_feature_access_level , - > ( feature , level ) {
2022-01-26 12:08:38 +05:30
with_project_feature . merge ( ProjectFeature . with_feature_access_level ( feature , level ) )
2016-11-24 13:41:30 +05:30
}
2019-03-02 22:35:43 +05:30
# Picks projects which use the given programming language
scope :with_programming_language , - > ( language_name ) do
lang_id_query = ProgrammingLanguage
. with_name_case_insensitive ( language_name )
. select ( :id )
joins ( :repository_languages )
. where ( repository_languages : { programming_language_id : lang_id_query } )
end
2020-07-28 23:09:34 +05:30
scope :service_desk_enabled , - > { where ( service_desk_enabled : true ) }
2016-11-24 13:41:30 +05:30
scope :with_builds_enabled , - > { with_feature_enabled ( :builds ) }
scope :with_issues_enabled , - > { with_feature_enabled ( :issues ) }
2018-03-17 18:26:18 +05:30
scope :with_issues_available_for_user , - > ( current_user ) { with_feature_available_for_user ( :issues , current_user ) }
2019-06-05 12:25:43 +05:30
scope :with_merge_requests_available_for_user , - > ( current_user ) { with_feature_available_for_user ( :merge_requests , current_user ) }
2020-03-13 15:44:24 +05:30
scope :with_issues_or_mrs_available_for_user , - > ( user ) do
with_issues_available_for_user ( user ) . or ( with_merge_requests_available_for_user ( user ) )
end
2017-09-10 17:25:29 +05:30
scope :with_merge_requests_enabled , - > { with_feature_enabled ( :merge_requests ) }
2021-04-29 21:17:54 +05:30
scope :with_remote_mirrors , - > { joins ( :remote_mirrors ) . where ( remote_mirrors : { enabled : true } ) }
2020-01-01 13:55:28 +05:30
scope :with_limit , - > ( maximum ) { limit ( maximum ) }
2018-10-15 14:42:47 +05:30
scope :with_group_runners_enabled , - > do
joins ( :ci_cd_settings )
. where ( project_ci_cd_settings : { group_runners_enabled : true } )
end
2016-11-24 13:41:30 +05:30
2019-12-21 20:55:43 +05:30
scope :with_pages_deployed , - > do
joins ( :pages_metadatum ) . merge ( ProjectPagesMetadatum . deployed )
end
scope :pages_metadata_not_migrated , - > do
left_outer_joins ( :pages_metadatum )
. where ( project_pages_metadata : { project_id : nil } )
end
2020-07-28 23:09:34 +05:30
scope :with_api_commit_entity_associations , - > {
preload ( :project_feature , :route , namespace : [ :route , :owner ] )
2020-06-23 00:09:42 +05:30
}
2022-06-21 17:19:12 +05:30
scope :created_by , - > ( user ) { where ( creator : user ) }
2020-11-24 15:15:51 +05:30
scope :imported_from , - > ( type ) { where ( import_type : type ) }
2022-06-21 17:19:12 +05:30
scope :imported , - > { where . not ( import_type : nil ) }
2021-01-03 14:25:43 +05:30
scope :with_tracing_enabled , - > { joins ( :tracing_setting ) }
2021-01-29 00:20:46 +05:30
scope :with_enabled_error_tracking , - > { joins ( :error_tracking_setting ) . where ( project_error_tracking_settings : { enabled : true } ) }
2020-11-24 15:15:51 +05:30
2021-09-04 01:27:46 +05:30
scope :with_service_desk_key , - > ( key ) do
# project_key is not indexed for now
# see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24063#note_282435524 for details
joins ( :service_desk_setting ) . where ( 'service_desk_settings.project_key' = > key )
end
2021-11-11 11:23:49 +05:30
scope :with_topic , - > ( topic_name ) do
topic = Projects :: Topic . find_by_name ( topic_name )
2021-11-18 22:05:49 +05:30
topic ? where ( id : topic . project_topics . select ( :project_id ) ) : none
2021-11-11 11:23:49 +05:30
end
2017-08-17 22:00:37 +05:30
enum auto_cancel_pending_pipelines : { disabled : 0 , enabled : 1 }
2019-02-15 15:39:39 +05:30
chronic_duration_attr :build_timeout_human_readable , :build_timeout ,
2019-07-31 22:56:46 +05:30
default : 3600 , error_message : _ ( 'Maximum job timeout has a value which could not be accepted' )
2018-05-09 12:01:36 +05:30
validates :build_timeout , allow_nil : true ,
2018-11-18 11:00:15 +05:30
numericality : { greater_than_or_equal_to : 10 . minutes ,
2019-12-04 20:38:33 +05:30
less_than : MAX_BUILD_TIMEOUT ,
2018-11-18 11:00:15 +05:30
only_integer : true ,
2019-09-30 21:07:59 +05:30
message : _ ( 'needs to be between 10 minutes and 1 month' ) }
2018-05-09 12:01:36 +05:30
2019-02-15 15:39:39 +05:30
# Used by Projects::CleanupService to hold a map of rewritten object IDs
mount_uploader :bfg_object_map , AttachmentUploader
2020-07-28 23:09:34 +05:30
def self . with_api_entity_associations
2021-11-18 22:05:49 +05:30
preload ( :project_feature , :route , :topics , :group , :timelogs , namespace : [ :route , :owner ] )
2020-07-28 23:09:34 +05:30
end
2020-06-23 00:09:42 +05:30
def self . with_web_entity_associations
2021-04-29 21:17:54 +05:30
preload ( :project_feature , :route , :creator , group : :parent , namespace : [ :route , :owner ] )
2020-06-23 00:09:42 +05:30
end
2018-11-20 20:47:30 +05:30
def self . eager_load_namespace_and_owner
includes ( namespace : :owner )
end
2017-09-10 17:25:29 +05:30
# Returns a collection of projects that is either public or visible to the
# logged in user.
2019-07-07 11:18:12 +05:30
def self . public_or_visible_to_user ( user = nil , min_access_level = nil )
2021-01-29 00:20:46 +05:30
min_access_level = nil if user & . can_read_all_resources?
2019-07-07 11:18:12 +05:30
2020-05-24 23:13:21 +05:30
return public_to_user unless user
if user . is_a? ( DeployToken )
2021-01-03 14:25:43 +05:30
user . accessible_projects
2020-05-24 23:13:21 +05:30
else
2018-03-17 18:26:18 +05:30
where ( 'EXISTS (?) OR projects.visibility_level IN (?)' ,
2019-07-07 11:18:12 +05:30
user . authorizations_for_projects ( min_access_level : min_access_level ) ,
2018-03-17 18:26:18 +05:30
Gitlab :: VisibilityLevel . levels_for_user ( user ) )
2017-09-10 17:25:29 +05:30
end
end
2016-11-24 13:41:30 +05:30
def self . with_feature_available_for_user ( feature , user )
2022-01-26 12:08:38 +05:30
with_project_feature . merge ( ProjectFeature . with_feature_available_for_user ( feature , user ) )
2016-11-24 13:41:30 +05:30
end
2016-09-29 09:46:39 +05:30
2020-07-28 23:09:34 +05:30
def self . projects_user_can ( projects , user , action )
projects = where ( id : projects )
DeclarativePolicy . user_scope do
projects . select { | project | Ability . allowed? ( user , action , project ) }
end
end
2019-10-03 12:08:05 +05:30
# This scope returns projects where user has access to both the project and the feature.
def self . filter_by_feature_visibility ( feature , user )
2019-12-04 20:38:33 +05:30
with_feature_available_for_user ( feature , user )
. public_or_visible_to_user (
user ,
ProjectFeature . required_minimum_access_level_for_private_project ( feature )
)
2019-10-03 12:08:05 +05:30
end
2020-03-13 15:44:24 +05:30
def self . wrap_with_cte ( collection )
cte = Gitlab :: SQL :: CTE . new ( :projects_cte , collection )
Project . with ( cte . to_arel ) . from ( cte . alias_to ( Project . arel_table ) )
end
2022-07-16 23:28:13 +05:30
def self . inactive
project_statistics = :: ProjectStatistics . arel_table
minimum_size_mb = :: Gitlab :: CurrentSettings . inactive_projects_min_size_mb . megabytes
last_activity_cutoff = :: Gitlab :: CurrentSettings . inactive_projects_send_warning_email_after_months . months . ago
joins ( :statistics )
. where ( ( project_statistics [ :storage_size ] ) . gt ( minimum_size_mb ) )
. where ( 'last_activity_at < ?' , last_activity_cutoff )
end
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 ) }
2017-09-10 17:25:29 +05:30
2019-02-15 15:39:39 +05:30
# We require an alias to the project_mirror_data_table in order to use import_state in our queries
scope :joins_import_state , - > { joins ( " INNER JOIN project_mirror_data import_state ON import_state.project_id = projects.id " ) }
2018-12-13 13:39:08 +05:30
scope :for_group , - > ( group ) { where ( group : group ) }
2019-12-04 20:38:33 +05:30
scope :for_group_and_its_subgroups , - > ( group ) { where ( namespace_id : group . self_and_descendants . select ( :id ) ) }
2022-04-04 11:22:00 +05:30
scope :for_group_and_its_ancestor_groups , - > ( group ) { where ( namespace_id : group . self_and_ancestors . select ( :id ) ) }
2014-09-02 18:07:02 +05:30
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
2020-06-23 00:09:42 +05:30
# search.
2016-06-02 11:05:42 +05:30
#
# query - The search query as a String.
2020-04-22 19:07:51 +05:30
def search ( query , include_namespace : false )
if include_namespace
joins ( :route ) . fuzzy_search ( query , [ Route . arel_table [ :path ] , Route . arel_table [ :name ] , :description ] )
2020-01-01 13:55:28 +05:30
else
fuzzy_search ( query , [ :path , :name , :description ] )
end
2014-09-02 18:07:02 +05:30
end
2015-04-26 12:48:37 +05:30
def search_by_title ( query )
2018-03-17 18:26:18 +05:30
non_archived . fuzzy_search ( query , [ :name ] )
2014-09-02 18:07:02 +05:30
end
def visibility_levels
Gitlab :: VisibilityLevel . options
end
2018-05-09 12:01:36 +05:30
def sort_by_attribute ( method )
2017-08-17 22:00:37 +05:30
case method . to_s
when 'storage_size_desc'
# storage_size is a joined column so we need to
# pass a string to avoid AR adding the table name
reorder ( 'project_statistics.storage_size DESC, projects.id DESC' )
when 'latest_activity_desc'
2022-06-21 17:19:12 +05:30
sorted_by_updated_desc
2017-08-17 22:00:37 +05:30
when 'latest_activity_asc'
2022-06-21 17:19:12 +05:30
sorted_by_updated_asc
2018-12-05 23:21:45 +05:30
when 'stars_desc'
2019-07-31 22:56:46 +05:30
sorted_by_stars_desc
when 'stars_asc'
sorted_by_stars_asc
2015-04-26 12:48:37 +05:30
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
2017-08-17 22:00:37 +05:30
%r{
2019-02-02 18:00:53 +05:30
( ?< ! #{Gitlab::PathRegex::PATH_START_CHAR})
2017-09-10 17:25:29 +05:30
( ( ? < namespace > #{Gitlab::PathRegex::FULL_NAMESPACE_FORMAT_REGEX})\/)?
( ? < project > #{Gitlab::PathRegex::PROJECT_PATH_FORMAT_REGEX})
2017-08-17 22:00:37 +05:30
} x
2015-09-11 14:41:01 +05:30
end
2015-10-24 18:46:33 +05:30
2018-11-20 20:47:30 +05:30
def reference_postfix
'>'
end
def reference_postfix_escaped
'>'
end
# Pattern used to extract `namespace/project>` project references from text.
# '>' or its escaped form ('>') are checked for because '>' is sometimes escaped
# when the reference comes from an external source.
def markdown_reference_pattern
2020-07-28 23:09:34 +05:30
@markdown_reference_pattern || =
%r{
#{reference_pattern}
( #{reference_postfix}|#{reference_postfix_escaped})
} x
2018-11-20 20:47:30 +05:30
end
2016-11-03 12:29:30 +05:30
def trending
2017-09-10 17:25:29 +05:30
joins ( 'INNER JOIN trending_projects ON projects.id = trending_projects.project_id' )
. reorder ( 'trending_projects.id ASC' )
2015-10-24 18:46:33 +05:30
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
2016-11-03 12:29:30 +05:30
def group_ids
2021-11-18 22:05:49 +05:30
joins ( :namespace ) . where ( namespaces : { type : Group . sti_name } ) . select ( :namespace_id )
2016-11-03 12:29:30 +05:30
end
2019-06-05 12:25:43 +05:30
2019-10-31 01:37:42 +05:30
# Returns ids of projects with issuables available for given user
2019-06-05 12:25:43 +05:30
#
2019-10-31 01:37:42 +05:30
# Used on queries to find milestones or labels which user can see
# For example: Milestone.where(project_id: ids_with_issuables_available_for(user))
def ids_with_issuables_available_for ( user )
2019-06-05 12:25:43 +05:30
with_issues_enabled = with_issues_available_for_user ( user ) . select ( :id )
with_merge_requests_enabled = with_merge_requests_available_for_user ( user ) . select ( :id )
from_union ( [ with_issues_enabled , with_merge_requests_enabled ] ) . select ( :id )
end
2020-07-28 23:09:34 +05:30
2021-09-04 01:27:46 +05:30
def find_by_url ( url )
uri = URI ( url )
return unless uri . host == Gitlab . config . gitlab . host
match = Rails . application . routes . recognize_path ( url )
return if match [ :unmatched_route ] . present?
return if match [ :namespace_id ] . blank? || match [ :id ] . blank?
find_by_full_path ( match . values_at ( :namespace_id , :id ) . join ( " / " ) )
rescue ActionController :: RoutingError , URI :: InvalidURIError
nil
2020-07-28 23:09:34 +05:30
end
2021-12-11 22:18:48 +05:30
def without_integration ( integration )
integrations = Integration
. select ( '1' )
. where ( " #{ Integration . table_name } .project_id = projects.id " )
. where ( type : integration . type )
Project
. where ( 'NOT EXISTS (?)' , integrations )
. where ( pending_delete : false )
. where ( archived : false )
end
2014-09-02 18:07:02 +05:30
end
2019-09-30 21:07:59 +05:30
def initialize ( attributes = nil )
2019-09-04 21:01:54 +05:30
# We can't use default_value_for because the database has a default
# value of 0 for visibility_level. If someone attempts to create a
# private project, default_value_for will assume that the
# visibility_level hasn't changed and will use the application
# setting default, which could be internal or public. For projects
# inside a private group, those levels are invalid.
#
# To fix the problem, we assign the actual default in the application if
# no explicit visibility has been initialized.
2019-09-30 21:07:59 +05:30
attributes || = { }
2019-09-04 21:01:54 +05:30
unless visibility_attribute_present? ( attributes )
attributes [ :visibility_level ] = Gitlab :: CurrentSettings . default_project_visibility
end
super
end
2021-06-08 01:23:25 +05:30
def parent_loaded?
association ( :namespace ) . loaded?
end
2022-06-21 17:19:12 +05:30
def personal_namespace_holder? ( user )
return false unless personal?
return false unless user
# We do not want to use a check like `project.team.owner?(user)`
# here because that would depend upon the state of the `project_authorizations` cache,
# and also perform the check across multiple `owners` of the project, but our intention
# is to check if the user is the "holder" of the personal namespace, so need to make this
# check against only a single user (ie, namespace.owner).
namespace . owner == user
end
2020-06-23 00:09:42 +05:30
def project_setting
super . presence || build_project_setting
end
2019-02-15 15:39:39 +05:30
def all_pipelines
2019-02-02 18:00:53 +05:30
if builds_enabled?
super
else
super . external
end
end
2019-07-07 11:18:12 +05:30
def ci_pipelines
if builds_enabled?
super
else
super . external
end
end
2020-06-23 00:09:42 +05:30
def active_webide_pipelines ( user : )
webide_pipelines . running_or_pending . for_user ( user )
end
2021-03-11 19:13:27 +05:30
def default_pipeline_lock
if keep_latest_artifacts_available?
return :artifacts_locked
end
:unlocked
2021-03-08 18:12:59 +05:30
end
2020-03-13 15:44:24 +05:30
def autoclose_referenced_issues
return true if super . nil?
super
end
2019-12-26 22:10:19 +05:30
def preload_protected_branches
preloader = ActiveRecord :: Associations :: Preloader . new
preloader . preload ( self , protected_branches : [ :push_access_levels , :merge_access_levels ] )
end
2018-03-17 18:26:18 +05:30
# returns all ancestor-groups upto but excluding the given namespace
# when no namespace is given, all ancestors upto the top are returned
2019-02-15 15:39:39 +05:30
def ancestors_upto ( top = nil , hierarchy_order : nil )
Gitlab :: ObjectHierarchy . new ( Group . where ( id : namespace_id ) )
. base_and_ancestors ( upto : top , hierarchy_order : hierarchy_order )
2018-03-17 18:26:18 +05:30
end
2021-10-27 15:23:28 +05:30
def ancestors ( hierarchy_order : nil )
2022-07-16 23:28:13 +05:30
if Feature . enabled? ( :linear_project_ancestors , self )
2021-10-27 15:23:28 +05:30
group & . self_and_ancestors ( hierarchy_order : hierarchy_order ) || Group . none
else
ancestors_upto ( hierarchy_order : hierarchy_order )
end
end
2019-02-15 15:39:39 +05:30
2021-09-04 01:27:46 +05:30
def ancestors_upto_ids ( ... )
ancestors_upto ( ... ) . pluck ( :id )
end
2019-10-12 21:52:04 +05:30
def emails_disabled?
strong_memoize ( :emails_disabled ) do
# disabling in the namespace overrides the project setting
2019-12-21 20:55:43 +05:30
super || namespace . emails_disabled?
2019-10-12 21:52:04 +05:30
end
end
2021-01-03 14:25:43 +05:30
override :lfs_enabled?
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
2018-12-13 13:39:08 +05:30
alias_method :lfs_enabled , :lfs_enabled?
2018-03-17 18:26:18 +05:30
def auto_devops_enabled?
if auto_devops & . enabled . nil?
2018-11-20 20:47:30 +05:30
has_auto_devops_implicitly_enabled?
2018-03-17 18:26:18 +05:30
else
auto_devops . enabled?
end
end
2018-11-18 11:00:15 +05:30
def has_auto_devops_implicitly_enabled?
2019-07-07 11:18:12 +05:30
auto_devops_config = first_auto_devops_config
auto_devops_config [ :scope ] != :project && auto_devops_config [ :status ]
2018-11-18 11:00:15 +05:30
end
2018-03-17 18:26:18 +05:30
def has_auto_devops_implicitly_disabled?
2019-07-07 11:18:12 +05:30
auto_devops_config = first_auto_devops_config
auto_devops_config [ :scope ] != :project && ! auto_devops_config [ :status ]
end
2020-10-24 23:57:45 +05:30
def has_packages? ( package_type )
packages . where ( package_type : package_type ) . exists?
end
2022-07-16 23:28:13 +05:30
def packages_cleanup_policy
super || build_packages_cleanup_policy
end
2019-07-07 11:18:12 +05:30
def first_auto_devops_config
return namespace . first_auto_devops_config if auto_devops & . enabled . nil?
{ scope : :project , status : auto_devops & . enabled || Feature . enabled? ( :force_autodevops_on_by_default , self ) }
end
2020-01-01 13:55:28 +05:30
def unlink_forks_upon_visibility_decrease_enabled?
2022-07-16 23:28:13 +05:30
Feature . enabled? ( :unlink_fork_network_upon_visibility_decrease , self )
2020-01-01 13:55:28 +05:30
end
2020-05-24 23:13:21 +05:30
# LFS and hashed repository storage are required for using Design Management.
def design_management_enabled?
lfs_enabled? && hashed_storage? ( :repository )
end
2014-09-02 18:07:02 +05:30
def team
@team || = ProjectTeam . new ( self )
end
def repository
2020-11-24 15:15:51 +05:30
@repository || = Gitlab :: GlRepository :: PROJECT . repository_for ( self )
2015-09-11 14:41:01 +05:30
end
2020-05-24 23:13:21 +05:30
def design_repository
strong_memoize ( :design_repository ) do
2020-11-24 15:15:51 +05:30
Gitlab :: GlRepository :: DESIGN . repository_for ( self )
2020-05-24 23:13:21 +05:30
end
end
2020-07-28 23:09:34 +05:30
# Because we use default_value_for we need to be sure
# packages_enabled= method does exist even if we rollback migration.
# Otherwise many tests from spec/migrations will fail.
def packages_enabled = ( value )
if has_attribute? ( :packages_enabled )
write_attribute ( :packages_enabled , value )
end
end
2018-03-17 18:26:18 +05:30
def cleanup
@repository = nil
end
alias_method :reload_repository! , :cleanup
2017-08-17 22:00:37 +05:30
def container_registry_url
2016-06-02 11:05:42 +05:30
if Gitlab . config . registry . enabled
2017-09-10 17:25:29 +05:30
" #{ Gitlab . config . registry . host_port } / #{ full_path . downcase } "
2016-06-02 11:05:42 +05:30
end
end
2022-06-21 17:19:12 +05:30
def container_repositories_size
strong_memoize ( :container_repositories_size ) do
next unless Gitlab . com?
next 0 if container_repositories . empty?
next unless container_repositories . all_migrated?
next unless ContainerRegistry :: GitlabApiClient . supports_gitlab_api?
ContainerRegistry :: GitlabApiClient . deduplicated_size ( full_path )
end
end
2016-06-02 11:05:42 +05:30
def has_container_registry_tags?
2017-09-10 17:25:29 +05:30
return @images if defined? ( @images )
@images = container_repositories . to_a . any? ( & :has_tags? ) ||
2017-08-17 22:00:37 +05:30
has_root_container_repository_tags?
2016-06-02 11:05:42 +05:30
end
2019-10-12 21:52:04 +05:30
# ref can't be HEAD, can only be branch/tag name
def latest_successful_build_for_ref ( job_name , ref = default_branch )
return unless ref
latest_pipeline = ci_pipelines . latest_successful_for_ref ( ref )
2019-03-02 22:35:43 +05:30
return unless latest_pipeline
2016-08-24 12:49:21 +05:30
2021-01-03 14:25:43 +05:30
latest_pipeline . build_with_artifacts_in_self_and_descendants ( job_name )
2014-09-02 18:07:02 +05:30
end
2019-10-12 21:52:04 +05:30
def latest_successful_build_for_sha ( job_name , sha )
return unless sha
latest_pipeline = ci_pipelines . latest_successful_for_sha ( sha )
return unless latest_pipeline
2021-01-03 14:25:43 +05:30
latest_pipeline . build_with_artifacts_in_self_and_descendants ( job_name )
2019-10-12 21:52:04 +05:30
end
def latest_successful_build_for_ref! ( job_name , ref = default_branch )
2021-06-08 01:23:25 +05:30
latest_successful_build_for_ref ( job_name , ref ) || raise ( ActiveRecord :: RecordNotFound , " Couldn't find job #{ job_name } " )
2019-02-15 15:39:39 +05:30
end
2020-11-24 15:15:51 +05:30
def latest_pipeline ( ref = default_branch , sha = nil )
2019-12-04 20:38:33 +05:30
ref = ref . presence || default_branch
2020-11-24 15:15:51 +05:30
sha || = commit ( ref ) & . sha
2019-12-04 20:38:33 +05:30
return unless sha
2020-11-24 15:15:51 +05:30
ci_pipelines . newest_first ( ref : ref , sha : sha ) . take
2019-12-04 20:38:33 +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 )
2018-03-17 18:26:18 +05:30
commit_by ( oid : sha ) if sha
2016-02-05 20:25:01 +05:30
end
2014-09-02 18:07:02 +05:30
def saved?
id && persisted?
end
2019-02-15 15:39:39 +05:30
def import_status
import_state & . status || 'none'
end
2020-04-22 19:07:51 +05:30
def jira_import_status
latest_jira_import & . status || 'initial'
end
2019-02-15 15:39:39 +05:30
def human_import_status_name
import_state & . human_status_name || 'none'
end
2014-09-02 18:07:02 +05:30
def add_import_job
2017-08-17 22:00:37 +05:30
job_id =
if forked?
2018-05-09 12:01:36 +05:30
RepositoryForkWorker . perform_async ( id )
2017-08-17 22:00:37 +05:30
else
RepositoryImportWorker . perform_async ( self . id )
end
2016-06-02 11:05:42 +05:30
2018-03-17 18:26:18 +05:30
log_import_activity ( job_id )
job_id
end
def log_import_activity ( job_id , type : :import )
job_type = type . to_s . capitalize
2016-06-02 11:05:42 +05:30
if job_id
2022-07-23 23:45:48 +05:30
Gitlab :: AppLogger . info ( " #{ job_type } job scheduled for #{ full_path } with job ID #{ job_id } (primary: #{ :: Gitlab :: Database :: LoadBalancing :: Session . current . use_primary? } ). " )
2015-09-25 12:07:36 +05:30
else
2020-06-23 00:09:42 +05:30
Gitlab :: AppLogger . error ( " #{ job_type } job failed to create for #{ full_path } . " )
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
2017-09-10 17:25:29 +05:30
run_after_commit do
ProjectCacheWorker . perform_async ( self . id )
end
2019-02-15 15:39:39 +05:30
import_state . update ( last_error : nil )
2017-09-10 17:25:29 +05:30
remove_import_data
end
2015-11-26 14:37:03 +05:30
2018-12-13 13:39:08 +05:30
# This method is overridden in EE::Project model
2017-09-10 17:25:29 +05:30
def remove_import_data
import_data & . destroy
end
def ci_config_path = ( value )
# Strip all leading slashes so that //foo -> foo
2018-03-17 18:26:18 +05:30
super ( value & . delete ( " \0 " ) )
2015-04-26 12:48:37 +05:30
end
2016-06-02 11:05:42 +05:30
def import_url = ( value )
2019-03-02 22:35:43 +05:30
if Gitlab :: UrlSanitizer . valid? ( value )
import_url = Gitlab :: UrlSanitizer . new ( value )
super ( import_url . sanitized_url )
2020-03-13 15:44:24 +05:30
credentials = import_url . credentials . to_h . transform_values { | value | CGI . unescape ( value . to_s ) }
create_or_update_import_data ( credentials : credentials )
2019-03-02 22:35:43 +05:30
else
super ( value )
end
2016-06-02 11:05:42 +05:30
end
def import_url
2016-11-03 12:29:30 +05:30
if import_data && super . present?
2016-06-02 11:05:42 +05:30
import_url = Gitlab :: UrlSanitizer . new ( super , credentials : import_data . credentials )
import_url . full_url
else
super
end
2021-06-08 01:23:25 +05:30
rescue StandardError
2018-12-13 13:39:08 +05:30
super
2016-06-02 11:05:42 +05:30
end
2016-08-24 12:49:21 +05:30
def valid_import_url?
2017-09-10 17:25:29 +05:30
valid? ( :import_url ) || errors . messages [ :import_url ] . nil?
2016-08-24 12:49:21 +05:30
end
2016-06-02 11:05:42 +05:30
def create_or_update_import_data ( data : nil , credentials : nil )
2018-05-09 12:01:36 +05:30
return if data . nil? && credentials . nil?
2016-08-24 12:49:21 +05:30
2016-06-02 11:05:42 +05:30
project_import_data = import_data || build_import_data
2018-03-17 18:26:18 +05:30
2019-02-15 15:39:39 +05:30
project_import_data . merge_data ( data . to_h )
project_import_data . merge_credentials ( credentials . to_h )
2018-11-18 11:00:15 +05:30
project_import_data
2016-06-02 11:05:42 +05:30
end
2014-09-02 18:07:02 +05:30
def import?
2021-11-18 22:05:49 +05:30
external_import? || forked? || gitlab_project_import? || jira_import? || bare_repository_import? || gitlab_project_migration?
2015-09-25 12:07:36 +05:30
end
def external_import?
2014-09-02 18:07:02 +05:30
import_url . present?
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
2018-03-17 18:26:18 +05:30
def bare_repository_import?
import_type == 'bare_repository'
end
2020-04-22 19:07:51 +05:30
def jira_import?
2020-06-23 00:09:42 +05:30
import_type == 'jira' && latest_jira_import . present?
2020-04-22 19:07:51 +05:30
end
2016-06-22 15:30:34 +05:30
def gitlab_project_import?
import_type == 'gitlab_project'
end
2021-11-18 22:05:49 +05:30
def gitlab_project_migration?
import_type == 'gitlab_project_migration'
end
2017-08-17 22:00:37 +05:30
def gitea_import?
import_type == 'gitea'
end
2021-10-27 15:23:28 +05:30
def github_import?
import_type == 'github'
end
def github_enterprise_import?
github_import? &&
URI . parse ( import_url ) . host != URI . parse ( Octokit :: Default :: API_ENDPOINT ) . host
end
2018-10-15 14:42:47 +05:30
def has_remote_mirror?
remote_mirror_available? && remote_mirrors . enabled . exists?
end
def updating_remote_mirror?
remote_mirrors . enabled . started . exists?
end
def update_remote_mirrors
return unless remote_mirror_available?
remote_mirrors . enabled . each ( & :sync )
end
def mark_stuck_remote_mirrors_as_failed!
remote_mirrors . stuck . update_all (
update_status : :failed ,
2019-07-31 22:56:46 +05:30
last_error : _ ( 'The remote mirror took to long to complete.' ) ,
2020-06-23 00:09:42 +05:30
last_update_at : Time . current
2018-10-15 14:42:47 +05:30
)
end
def mark_remote_mirrors_for_removal
remote_mirrors . each ( & :mark_for_delete_if_blank_url )
end
def remote_mirror_available?
remote_mirror_available_overridden ||
:: Gitlab :: CurrentSettings . mirror_available
end
2019-03-02 22:35:43 +05:30
def check_personal_projects_limit
# Since this method is called as validation hook, `creator` might not be
# present. Since the validation for that will fail, we can just return
# early.
return if ! creator || creator . can_create_project? ||
namespace . kind == 'group'
2016-06-16 23:09:34 +05:30
2019-03-02 22:35:43 +05:30
limit = creator . projects_limit
error =
2020-10-24 23:57:45 +05:30
if limit == 0
2019-03-02 22:35:43 +05:30
_ ( 'Personal project creation is not allowed. Please contact your administrator with questions' )
2016-06-16 23:09:34 +05:30
else
2019-03-02 22:35:43 +05:30
_ ( 'Your project limit is %{limit} projects! Please contact your administrator to increase it' )
2016-06-16 23:09:34 +05:30
end
2019-03-02 22:35:43 +05:30
self . errors . add ( :limit_reached , error % { limit : limit } )
2016-06-02 11:05:42 +05:30
end
2019-06-05 12:25:43 +05:30
def should_validate_visibility_level?
new_record? || changes . has_key? ( :visibility_level )
end
2016-06-02 11:05:42 +05:30
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
2019-07-31 22:56:46 +05:30
self . errors . add ( :visibility_level , _ ( " %{level_name} is not allowed in a %{group_level_name} group. " ) % { level_name : level_name , group_level_name : group_level_name } )
2016-06-02 11:05:42 +05:30
end
def visibility_level_allowed_as_fork
return if visibility_level_allowed_as_fork?
level_name = Gitlab :: VisibilityLevel . level_name ( self . visibility_level ) . downcase
2019-07-31 22:56:46 +05:30
self . errors . add ( :visibility_level , _ ( " %{level_name} is not allowed since the fork source project has lower visibility. " ) % { level_name : level_name } )
2014-09-02 18:07:02 +05:30
end
2018-05-09 12:01:36 +05:30
def pages_https_only
return false unless Gitlab . config . pages . external_https
super
end
def pages_https_only?
return false unless Gitlab . config . pages . external_https
super
end
def validate_pages_https_only
return unless pages_https_only?
unless pages_domains . all? ( & :https? )
2019-07-31 22:56:46 +05:30
errors . add ( :pages_https_only , _ ( " cannot be enabled unless all domains have TLS certificates " ) )
2018-05-09 12:01:36 +05:30
end
end
2021-01-03 14:25:43 +05:30
def changing_shared_runners_enabled_is_allowed
return unless new_record? || changes . has_key? ( :shared_runners_enabled )
2021-11-18 22:05:49 +05:30
if shared_runners_setting_conflicting_with_group?
2021-01-03 14:25:43 +05:30
errors . add ( :shared_runners_enabled , _ ( 'cannot be enabled because parent group does not allow it' ) )
end
end
2021-11-18 22:05:49 +05:30
def shared_runners_setting_conflicting_with_group?
shared_runners_enabled && group & . shared_runners_setting == Namespace :: SR_DISABLED_AND_UNOVERRIDABLE
end
def reconcile_shared_runners_setting!
if shared_runners_setting_conflicting_with_group?
self . shared_runners_enabled = false
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
2020-03-13 15:44:24 +05:30
# Produce a valid reference (see Referable#to_reference)
#
# NB: For projects, all references are 'full' - i.e. they all include the
# full_path, rather than just the project name. For this reason, we ignore
# the value of `full:` passed to this method, which is part of the Referable
# interface.
def to_reference ( from = nil , full : false )
base = to_reference_base ( from , full : true )
" #{ base } #{ self . class . reference_postfix } "
2018-11-20 20:47:30 +05:30
end
2017-08-17 22:00:37 +05:30
# `from` argument can be a Namespace or Project.
2020-03-13 15:44:24 +05:30
def to_reference_base ( from = nil , full : false )
2017-08-17 22:00:37 +05:30
if full || cross_namespace_reference? ( from )
2017-09-10 17:25:29 +05:30
full_path
2017-08-17 22:00:37 +05:30
elsif cross_project_reference? ( from )
path
end
2015-09-11 14:41:01 +05:30
end
2018-03-17 18:26:18 +05:30
def to_human_reference ( from = nil )
if cross_namespace_reference? ( from )
2017-08-17 22:00:37 +05:30
name_with_namespace
2018-03-17 18:26:18 +05:30
elsif cross_project_reference? ( from )
2017-08-17 22:00:37 +05:30
name
end
2014-09-02 18:07:02 +05:30
end
2018-11-08 19:23:39 +05:30
def readme_url
2019-02-15 15:39:39 +05:30
readme_path = repository . readme_path
if readme_path
Gitlab :: Routing . url_helpers . project_blob_url ( self , File . join ( default_branch , readme_path ) )
2018-11-08 19:23:39 +05:30
end
end
2018-03-17 18:26:18 +05:30
def new_issuable_address ( author , address_type )
2017-08-17 22:00:37 +05:30
return unless Gitlab :: IncomingEmail . supports_issue_creation? && author
2016-09-13 17:45:13 +05:30
2019-02-15 15:39:39 +05:30
# check since this can come from a request parameter
return unless %w( issue merge_request ) . include? ( address_type )
2017-08-17 22:00:37 +05:30
author . ensure_incoming_email_token!
2019-02-15 15:39:39 +05:30
suffix = address_type . dasherize
# example: incoming+h5bp-html5-boilerplate-8-1234567890abcdef123456789-issue@localhost.com
# example: incoming+h5bp-html5-boilerplate-8-1234567890abcdef123456789-merge-request@localhost.com
Gitlab :: IncomingEmail . reply_address ( " #{ full_path_slug } - #{ project_id } - #{ author . incoming_email_token } - #{ suffix } " )
2016-09-13 17:45:13 +05:30
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
2022-06-21 17:19:12 +05:30
updated_at
2014-09-02 18:07:02 +05:30
end
def project_id
self . id
end
2017-01-15 13:20:01 +05:30
def get_issue ( issue_id , current_user )
2017-09-10 17:25:29 +05:30
issue = IssuesFinder . new ( current_user , project_id : id ) . find_by ( iid : issue_id ) if issues_enabled?
if issue
issue
elsif external_issue_tracker
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
2017-08-17 22:00:37 +05:30
def external_issue_reference_pattern
2017-09-10 17:25:29 +05:30
external_issue_tracker . class . reference_pattern ( only_long : issues_enabled? )
2016-11-03 12:29:30 +05:30
end
2015-04-26 12:48:37 +05:30
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
2021-03-11 19:13:27 +05:30
cache_has_external_issue_tracker if has_external_issue_tracker . nil?
2016-06-16 23:09:34 +05:30
2021-03-11 19:13:27 +05:30
return unless has_external_issue_tracker?
2014-09-02 18:07:02 +05:30
2021-06-08 01:23:25 +05:30
@external_issue_tracker || = integrations . external_issue_trackers . first
2014-09-02 18:07:02 +05:30
end
2020-04-08 14:13:33 +05:30
def external_references_supported?
external_issue_tracker & . support_cross_reference?
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
2021-03-08 18:12:59 +05:30
cache_has_external_wiki if has_external_wiki . nil?
2016-08-24 12:49:21 +05:30
2021-03-08 18:12:59 +05:30
return unless has_external_wiki?
2016-08-24 12:49:21 +05:30
2021-06-08 01:23:25 +05:30
@external_wiki || = integrations . external_wikis . first
2016-08-24 12:49:21 +05:30
end
2021-09-30 23:02:18 +05:30
def find_or_initialize_integrations
Integration
. available_integration_names
. difference ( disabled_integrations )
. map { find_or_initialize_integration ( _1 ) }
. sort_by ( & :title )
2018-11-08 19:23:39 +05:30
end
2021-09-30 23:02:18 +05:30
def disabled_integrations
2022-01-26 12:08:38 +05:30
disabled_integrations = [ ]
disabled_integrations << 'shimo' unless Feature . enabled? ( :shimo_integration , self )
disabled_integrations
2014-09-02 18:07:02 +05:30
end
2021-09-30 23:02:18 +05:30
def find_or_initialize_integration ( name )
2022-06-21 17:19:12 +05:30
return if disabled_integrations . include? ( name ) || Integration . available_integration_names . exclude? ( name )
2018-12-05 23:21:45 +05:30
2021-10-27 15:23:28 +05:30
find_integration ( integrations , name ) || build_from_instance ( name ) || build_integration ( name )
2017-08-17 22:00:37 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ServiceClass
2015-09-25 12:07:36 +05:30
def create_labels
Label . templates . each do | label |
2021-10-27 15:23:28 +05:30
params = label . attributes . except ( 'id' , 'template' , 'created_at' , 'updated_at' , 'type' )
2016-11-03 12:29:30 +05:30
Labels :: FindOrCreateService . new ( nil , self , params ) . execute ( skip_authorization : true )
2015-09-25 12:07:36 +05:30
end
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ServiceClass
2015-09-25 12:07:36 +05:30
2021-09-30 23:02:18 +05:30
def ci_integrations
2021-06-08 01:23:25 +05:30
integrations . where ( category : :ci )
2014-09-02 18:07:02 +05:30
end
2021-09-30 23:02:18 +05:30
def ci_integration
@ci_integration || = ci_integrations . reorder ( nil ) . find_by ( active : true )
2017-08-17 22:00:37 +05:30
end
2015-04-26 12:48:37 +05:30
def avatar_in_git
2016-06-02 11:05:42 +05:30
repository . avatar
2015-04-26 12:48:37 +05:30
end
2017-09-10 17:25:29 +05:30
def avatar_url ( ** args )
2018-03-17 18:26:18 +05:30
Gitlab :: Routing . url_helpers . project_avatar_url ( self ) if avatar_in_git
2015-04-26 12:48:37 +05:30
end
2014-09-02 18:07:02 +05:30
# For compatibility with old code
def code
path
end
2019-02-15 15:39:39 +05:30
def all_clusters
group_clusters = Clusters :: Cluster . joins ( :groups ) . where ( cluster_groups : { group_id : ancestors_upto } )
2020-01-01 13:55:28 +05:30
instance_clusters = Clusters :: Cluster . instance_type
2019-02-15 15:39:39 +05:30
2020-01-01 13:55:28 +05:30
Clusters :: Cluster . from_union ( [ clusters , group_clusters , instance_clusters ] )
2019-02-15 15:39:39 +05:30
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
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ServiceClass
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
2018-03-17 18:26:18 +05:30
run_after_commit do
NotificationService . new . project_was_moved ( self , old_path_with_namespace )
end
2014-09-02 18:07:02 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ServiceClass
2014-09-02 18:07:02 +05:30
def owner
2022-04-04 11:22:00 +05:30
# This will be phased out and replaced with `owners` relationship
# backed by memberships with direct/inherited Owner access roles
# See https://gitlab.com/groups/gitlab-org/-/epics/7405
2020-05-24 23:13:21 +05:30
group || namespace . try ( :owner )
2014-09-02 18:07:02 +05:30
end
2022-04-04 11:22:00 +05:30
def deprecated_owner
# Kept in order to maintain webhook structures until we remove owner_name and owner_email
# See https://gitlab.com/gitlab-org/gitlab/-/issues/350603
group || namespace . try ( :owner )
end
def owners
# This will be phased out and replaced with `owners` relationship
# backed by memberships with direct/inherited Owner access roles
# See https://gitlab.com/groups/gitlab-org/-/epics/7405
team . owners
end
2022-03-02 08:16:31 +05:30
def first_owner
2020-10-24 23:57:45 +05:30
obj = owner
2022-03-02 08:16:31 +05:30
if obj . respond_to? ( :first_owner )
obj . first_owner
2020-10-24 23:57:45 +05:30
else
obj
end
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ServiceClass
2014-09-02 18:07:02 +05:30
def execute_hooks ( data , hooks_scope = :push_hooks )
2018-03-17 18:26:18 +05:30
run_after_commit_or_now do
2022-05-07 20:08:51 +05:30
triggered_hooks ( hooks_scope , data ) . execute
2018-03-17 18:26:18 +05:30
SystemHooksService . new . execute_hooks ( data , hooks_scope )
2014-09-02 18:07:02 +05:30
end
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ServiceClass
2014-09-02 18:07:02 +05:30
2022-05-07 20:08:51 +05:30
def triggered_hooks ( hooks_scope , data )
triggered = :: Projects :: TriggeredHooks . new ( hooks_scope , data )
triggered . add_hooks ( hooks )
end
2021-09-30 23:02:18 +05:30
def execute_integrations ( data , hooks_scope = :push_hooks )
2015-04-26 12:48:37 +05:30
# Call only service hooks that are active for this scope
2018-03-17 18:26:18 +05:30
run_after_commit_or_now do
2021-06-08 01:23:25 +05:30
integrations . public_send ( hooks_scope ) . each do | integration | # rubocop:disable GitlabSecurity/PublicSend
integration . async_execute ( data )
2018-03-17 18:26:18 +05:30
end
2014-09-02 18:07:02 +05:30
end
end
2019-10-12 21:52:04 +05:30
def has_active_hooks? ( hooks_scope = :push_hooks )
2020-03-13 15:44:24 +05:30
hooks . hooks_for ( hooks_scope ) . any? || SystemHook . hooks_for ( hooks_scope ) . any? || Gitlab :: FileHook . any?
2019-10-12 21:52:04 +05:30
end
2021-09-30 23:02:18 +05:30
def has_active_integrations? ( hooks_scope = :push_hooks )
2021-06-08 01:23:25 +05:30
integrations . public_send ( hooks_scope ) . any? # rubocop:disable GitlabSecurity/PublicSend
2019-10-12 21:52:04 +05:30
end
2020-11-24 15:15:51 +05:30
def feature_usage
super . presence || build_feature_usage
end
2014-09-02 18:07:02 +05:30
def forked?
2018-12-13 13:39:08 +05:30
fork_network && fork_network . root_project != self
2014-09-02 18:07:02 +05:30
end
2018-03-17 18:26:18 +05:30
def fork_source
2019-07-07 11:18:12 +05:30
return unless forked?
2018-03-17 18:26:18 +05:30
forked_from_project || fork_network & . root_project
end
2020-11-24 15:15:51 +05:30
def lfs_objects_for_repository_types ( * types )
2020-03-13 15:44:24 +05:30
LfsObject
. joins ( :lfs_objects_projects )
2020-11-24 15:15:51 +05:30
. where ( lfs_objects_projects : { project : self , repository_type : types } )
2020-04-08 14:13:33 +05:30
end
def lfs_objects_oids ( oids : [ ] )
oids ( lfs_objects , oids : oids )
2018-05-09 12:01:36 +05:30
end
2022-01-26 12:08:38 +05:30
def lfs_objects_oids_from_fork_source ( oids : [ ] )
return [ ] unless forked?
oids ( fork_source . lfs_objects , oids : oids )
end
2014-09-02 18:07:02 +05:30
def personal?
! group
end
2016-04-02 18:10:28 +05:30
# Expires various caches before a project is renamed.
def expire_caches_before_rename ( old_path )
2020-05-24 23:13:21 +05:30
project_repo = Repository . new ( old_path , self , shard : repository_storage )
wiki_repo = Repository . new ( " #{ old_path } #{ Gitlab :: GlRepository :: WIKI . path_suffix } " , self , shard : repository_storage , repo_type : Gitlab :: GlRepository :: WIKI )
design_repo = Repository . new ( " #{ old_path } #{ Gitlab :: GlRepository :: DESIGN . path_suffix } " , self , shard : repository_storage , repo_type : Gitlab :: GlRepository :: DESIGN )
2016-04-02 18:10:28 +05:30
2020-05-24 23:13:21 +05:30
[ project_repo , wiki_repo , design_repo ] . each do | repo |
repo . before_delete if repo . exists?
2016-04-02 18:10:28 +05:30
end
end
2017-09-10 17:25:29 +05:30
# Check if repository already exists on disk
2018-03-17 18:26:18 +05:30
def check_repository_path_availability
return true if skip_disk_validation
2018-10-15 14:42:47 +05:30
return false unless repository_storage
2017-09-10 17:25:29 +05:30
2018-03-17 18:26:18 +05:30
# Check if repository with same path already exists on disk we can
# skip this for the hashed storage because the path does not change
if legacy_storage? && repository_with_same_path_already_exists?
2019-07-31 22:56:46 +05:30
errors . add ( :base , _ ( 'There is already a repository with that name on disk' ) )
2017-09-10 17:25:29 +05:30
return false
end
true
2018-03-17 18:26:18 +05:30
rescue GRPC :: Internal # if the path is too long
false
2017-09-10 17:25:29 +05:30
end
2019-02-15 15:39:39 +05:30
def track_project_repository
repository = project_repository || build_project_repository
repository . update! ( shard_name : repository_storage , disk_path : disk_path )
end
2018-03-17 18:26:18 +05:30
def create_repository ( force : false )
# Forked import is handled asynchronously
return if forked? && ! force
2020-04-08 14:13:33 +05:30
repository . create_repository
repository . after_create
true
2021-06-08 01:23:25 +05:30
rescue StandardError = > err
2020-04-08 14:13:33 +05:30
Gitlab :: ErrorTracking . track_exception ( err , project : { id : id , full_path : full_path , disk_path : disk_path } )
errors . add ( :base , _ ( 'Failed to create repository' ) )
false
2017-09-10 17:25:29 +05:30
end
2016-06-02 11:05:42 +05:30
def hook_attrs ( backward : true )
attrs = {
2018-03-17 18:26:18 +05:30
id : id ,
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 ,
2018-03-17 18:26:18 +05:30
avatar_url : avatar_url ( only_path : false ) ,
2016-04-02 18:10:28 +05:30
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 ,
2017-09-10 17:25:29 +05:30
path_with_namespace : full_path ,
2016-04-02 18:10:28 +05:30
default_branch : default_branch ,
2017-09-10 17:25:29 +05:30
ci_config_path : ci_config_path
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
2022-03-02 08:16:31 +05:30
def member ( user )
2018-03-17 18:26:18 +05:30
if project_members . loaded?
project_members . find { | member | member . user_id == user . id }
else
project_members . find_by ( user_id : user )
end
2014-09-02 18:07:02 +05:30
end
2021-11-11 11:23:49 +05:30
def membership_locked?
false
end
2020-05-24 23:13:21 +05:30
def bots
users . project_bot
end
2018-12-05 23:21:45 +05:30
# Filters `users` to return only authorized users of the project
def members_among ( users )
if users . is_a? ( ActiveRecord :: Relation ) && ! users . loaded?
authorized_users . merge ( users )
else
return [ ] if users . empty?
user_ids = authorized_users . where ( users : { id : users . map ( & :id ) } ) . pluck ( :id )
users . select { | user | user_ids . include? ( user . id ) }
end
end
2014-09-02 18:07:02 +05:30
def visibility_level_field
2017-08-17 22:00:37 +05:30
:visibility_level
2014-09-02 18:07:02 +05:30
end
2021-09-04 01:27:46 +05:30
override :after_repository_change_head
def after_repository_change_head
ProjectCacheWorker . perform_async ( self . id , [ ] , [ :commit_count ] )
super
2014-09-02 18:07:02 +05:30
end
2018-03-17 18:26:18 +05:30
def forked_from? ( other_project )
forked? && forked_from_project == other_project
end
def in_fork_network_of? ( other_project )
return false if fork_network . nil? || other_project . fork_network . nil?
fork_network == other_project . fork_network
2014-09-02 18:07:02 +05:30
end
2015-04-26 12:48:37 +05:30
def origin_merge_requests
merge_requests . where ( source_project_id : self . id )
end
2017-09-10 17:25:29 +05:30
def ensure_repository
create_repository ( force : true ) unless repository_exists?
2015-04-26 12:48:37 +05:30
end
2018-03-17 18:26:18 +05:30
# update visibility_level of forks
2017-09-10 17:25:29 +05:30
def update_forks_visibility_level
2020-01-01 13:55:28 +05:30
return if unlink_forks_upon_visibility_decrease_enabled?
2019-07-31 22:56:46 +05:30
return unless visibility_level < visibility_level_before_last_save
2017-09-10 17:25:29 +05:30
forks . each do | forked_project |
if forked_project . visibility_level > visibility_level
forked_project . visibility_level = visibility_level
forked_project . save!
end
end
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
2018-03-17 18:26:18 +05:30
def latest_successful_pipeline_for_default_branch
if defined? ( @latest_successful_pipeline_for_default_branch )
return @latest_successful_pipeline_for_default_branch
end
@latest_successful_pipeline_for_default_branch =
2019-10-12 21:52:04 +05:30
ci_pipelines . latest_successful_for_ref ( default_branch )
2018-03-17 18:26:18 +05:30
end
def latest_successful_pipeline_for ( ref = nil )
if ref && ref != default_branch
2019-10-12 21:52:04 +05:30
ci_pipelines . latest_successful_for_ref ( ref )
2018-03-17 18:26:18 +05:30
else
latest_successful_pipeline_for_default_branch
end
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
2017-08-17 22:00:37 +05:30
def shared_runners_available?
shared_runners_enabled?
end
2015-12-23 02:04:40 +05:30
2017-08-17 22:00:37 +05:30
def shared_runners
2021-09-04 01:27:46 +05:30
@shared_runners || = shared_runners_enabled? ? Ci :: Runner . instance_type : Ci :: Runner . none
end
def available_shared_runners
@available_shared_runners || = shared_runners_available? ? shared_runners : Ci :: Runner . none
2015-12-23 02:04:40 +05:30
end
2018-10-15 14:42:47 +05:30
def group_runners
@group_runners || = group_runners_enabled? ? Ci :: Runner . belonging_to_parent_group_of_project ( self . id ) : Ci :: Runner . none
end
def all_runners
2018-12-05 23:21:45 +05:30
Ci :: Runner . from_union ( [ runners , group_runners , shared_runners ] )
2015-12-23 02:04:40 +05:30
end
2021-09-04 01:27:46 +05:30
def all_available_runners
Ci :: Runner . from_union ( [ runners , group_runners , available_shared_runners ] )
end
2018-11-08 19:23:39 +05:30
def active_runners
strong_memoize ( :active_runners ) do
2021-09-04 01:27:46 +05:30
all_available_runners . active
2018-11-08 19:23:39 +05:30
end
end
2021-04-29 21:17:54 +05:30
def any_online_runners? ( & block )
2022-03-02 08:16:31 +05:30
online_runners_with_tags . any? ( & block )
2021-04-29 21:17:54 +05:30
end
2017-08-17 22:00:37 +05:30
def valid_runners_token? ( token )
2019-09-30 21:07:59 +05:30
self . runners_token && ActiveSupport :: SecurityUtils . secure_compare ( token , self . runners_token )
2015-12-23 02:04:40 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ServiceClass
2018-11-08 19:23:39 +05:30
def open_issues_count ( current_user = nil )
2021-11-11 11:23:49 +05:30
return Projects :: OpenIssuesCountService . new ( self , current_user ) . count unless current_user . nil?
2021-11-18 22:05:49 +05:30
BatchLoader . for ( self ) . batch do | projects , loader |
2021-11-11 11:23:49 +05:30
issues_count_per_project = :: Projects :: BatchOpenIssuesCountService . new ( projects ) . refresh_cache_and_retrieve_data
issues_count_per_project . each do | project , count |
loader . call ( project , count )
end
end
2018-03-17 18:26:18 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ServiceClass
2018-03-17 18:26:18 +05:30
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ServiceClass
2021-09-04 01:27:46 +05:30
def open_merge_requests_count ( _current_user = nil )
2018-03-17 18:26:18 +05:30
Projects :: OpenMergeRequestsCountService . new ( self ) . count
2015-12-23 02:04:40 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ServiceClass
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
2018-12-13 13:39:08 +05:30
original_project = fork_source
2016-06-02 11:05:42 +05:30
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
2022-02-27 12:50:16 +05:30
override :format_runners_token
def format_runners_token ( token )
2022-05-07 20:08:51 +05:30
" #{ RunnersTokenPrefixable :: RUNNERS_TOKEN_PREFIX } #{ token } "
2022-02-27 12:50:16 +05:30
end
2017-08-17 22:00:37 +05:30
def pages_deployed?
2020-10-24 23:57:45 +05:30
pages_metadatum & . deployed?
2017-08-17 22:00:37 +05:30
end
2018-05-09 12:01:36 +05:30
def pages_group_url
2017-08-17 22:00:37 +05:30
# The host in URL always needs to be downcased
2018-05-09 12:01:36 +05:30
Gitlab . config . pages . url . sub ( %r{ ^https?:// } ) do | prefix |
" #{ prefix } #{ pages_subdomain } . "
2017-08-17 22:00:37 +05:30
end . downcase
2018-05-09 12:01:36 +05:30
end
def pages_url
url = pages_group_url
url_path = full_path . partition ( '/' ) . last
2017-08-17 22:00:37 +05:30
# If the project path is the same as host, we serve it as group page
2020-07-28 23:09:34 +05:30
return url if url == " #{ Settings . pages . protocol } :// #{ url_path } " . downcase
2017-08-17 22:00:37 +05:30
" #{ url } / #{ url_path } "
end
2019-12-21 20:55:43 +05:30
def pages_group_root?
pages_group_url == pages_url
end
2017-08-17 22:00:37 +05:30
def pages_subdomain
full_path . partition ( '/' ) . first
end
def pages_path
2018-03-17 18:26:18 +05:30
# TODO: when we migrate Pages to work with new storage types, change here to use disk_path
File . join ( Settings . pages . path , full_path )
2017-08-17 22:00:37 +05:30
end
2018-03-17 18:26:18 +05:30
def pages_available?
2019-03-02 22:35:43 +05:30
Gitlab . config . pages . enabled
2018-03-17 18:26:18 +05:30
end
2022-06-21 17:19:12 +05:30
def pages_show_onboarding?
! ( pages_metadatum & . onboarding_complete || pages_metadatum & . deployed )
end
2017-09-10 17:25:29 +05:30
def remove_private_deploy_keys
exclude_keys_linked_to_other_projects = <<-SQL
NOT EXISTS (
SELECT 1
FROM deploy_keys_projects dkp2
WHERE dkp2 . deploy_key_id = deploy_keys_projects . deploy_key_id
AND dkp2 . project_id != deploy_keys_projects . project_id
)
SQL
deploy_keys . where ( public : false )
. where ( exclude_keys_linked_to_other_projects )
. delete_all
end
2022-06-21 17:19:12 +05:30
def mark_pages_onboarding_complete
ensure_pages_metadatum . update! ( onboarding_complete : true )
end
2022-05-07 20:08:51 +05:30
def mark_pages_as_deployed
ensure_pages_metadatum . update! ( deployed : true )
2019-12-21 20:55:43 +05:30
end
def mark_pages_as_not_deployed
2022-05-07 20:08:51 +05:30
ensure_pages_metadatum . update! ( deployed : false )
2021-01-29 00:20:46 +05:30
end
def update_pages_deployment! ( deployment )
ensure_pages_metadatum . update! ( pages_deployment : deployment )
2019-12-21 20:55:43 +05:30
end
2021-03-08 18:12:59 +05:30
def set_first_pages_deployment! ( deployment )
ensure_pages_metadatum
# where().update_all to perform update in the single transaction with check for null
ProjectPagesMetadatum
. where ( project_id : id , pages_deployment_id : nil )
2021-04-29 21:17:54 +05:30
. update_all ( deployed : deployment . present? , pages_deployment_id : deployment & . id )
2021-03-08 18:12:59 +05:30
end
2021-10-27 15:23:28 +05:30
def set_full_path ( gl_full_path : full_path )
2018-03-17 18:26:18 +05:30
# We'd need to keep track of project full path otherwise directory tree
# created with hashed storage enabled cannot be usefully imported using
# the import rake task.
2021-10-27 15:23:28 +05:30
repository . raw_repository . set_full_path ( full_path : gl_full_path )
2018-03-17 18:26:18 +05:30
rescue Gitlab :: Git :: Repository :: NoRepository = > e
2020-06-23 00:09:42 +05:30
Gitlab :: AppLogger . error ( " Error writing to .git/config for project #{ full_path } ( #{ id } ): #{ e . message } . " )
2018-03-17 18:26:18 +05:30
nil
end
def after_import
2022-07-29 17:44:30 +05:30
repository . remove_prohibited_branches
2020-04-08 14:13:33 +05:30
repository . expire_content_cache
wiki . repository . expire_content_cache
DetectRepositoryLanguagesWorker . perform_async ( id )
2021-02-22 17:27:13 +05:30
ProjectCacheWorker . perform_async ( self . id , [ ] , [ :repository_size ] )
2021-10-27 15:23:28 +05:30
AuthorizedProjectUpdate :: ProjectRecalculateWorker . perform_async ( id )
2019-03-02 22:35:43 +05:30
2022-06-21 17:19:12 +05:30
enqueue_record_project_target_platforms
2019-03-02 22:35:43 +05:30
# The import assigns iid values on its own, e.g. by re-using GitHub ids.
# Flush existing InternalId records for this project for consistency reasons.
# Those records are going to be recreated with the next normal creation
# of a model instance (e.g. an Issue).
InternalId . flush_records! ( project : self )
2022-06-21 17:19:12 +05:30
import_state & . finish
2018-03-17 18:26:18 +05:30
update_project_counter_caches
after_create_default_branch
2019-02-15 15:39:39 +05:30
join_pool_repository
2018-05-09 12:01:36 +05:30
refresh_markdown_cache!
2021-10-27 15:23:28 +05:30
set_full_path
2018-03-17 18:26:18 +05:30
end
def update_project_counter_caches
classes = [
Projects :: OpenIssuesCountService ,
Projects :: OpenMergeRequestsCountService
]
classes . each do | klass |
klass . new ( self ) . refresh_cache
end
end
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ServiceClass
2018-03-17 18:26:18 +05:30
def after_create_default_branch
2019-03-02 22:35:43 +05:30
Projects :: ProtectDefaultBranchService . new ( self ) . execute
2018-03-17 18:26:18 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ServiceClass
2018-03-17 18:26:18 +05:30
2017-08-17 22:00:37 +05:30
# Lazy loading of the `pipeline_status` attribute
def pipeline_status
@pipeline_status || = Gitlab :: Cache :: Ci :: ProjectPipelineStatus . load_for_project ( self )
end
2018-05-09 12:01:36 +05:30
def add_export_job ( current_user : , after_export_strategy : nil , params : { } )
2022-07-16 23:28:13 +05:30
check_project_export_limit!
2018-05-09 12:01:36 +05:30
job_id = ProjectExportWorker . perform_async ( current_user . id , self . id , after_export_strategy , params )
2016-06-22 15:30:34 +05:30
if job_id
2020-06-23 00:09:42 +05:30
Gitlab :: AppLogger . info " Export job started for project ID #{ self . id } with job ID #{ job_id } "
2016-06-22 15:30:34 +05:30
else
2020-06-23 00:09:42 +05:30
Gitlab :: AppLogger . error " Export job failed to start for project ID #{ self . id } "
2016-06-22 15:30:34 +05:30
end
end
2018-03-27 19:54:05 +05:30
def import_export_shared
@import_export_shared || = Gitlab :: ImportExport :: Shared . new ( self )
end
2016-06-22 15:30:34 +05:30
def export_path
2019-07-07 11:18:12 +05:30
return unless namespace . present? || hashed_storage? ( :repository )
2018-03-17 18:26:18 +05:30
2018-03-27 19:54:05 +05:30
import_export_shared . archive_path
2016-06-22 15:30:34 +05:30
end
2018-03-27 19:54:05 +05:30
def export_status
2020-04-08 14:13:33 +05:30
if regeneration_in_progress?
:regeneration_in_progress
elsif export_enqueued?
:queued
elsif export_in_progress?
2018-03-27 19:54:05 +05:30
:started
2018-11-20 20:47:30 +05:30
elsif export_file_exists?
2018-03-27 19:54:05 +05:30
:finished
else
:none
end
end
def export_in_progress?
2020-04-08 14:13:33 +05:30
strong_memoize ( :export_in_progress ) do
:: Projects :: ExportJobFinder . new ( self , { status : :started } ) . execute . present?
end
end
def export_enqueued?
strong_memoize ( :export_enqueued ) do
:: Projects :: ExportJobFinder . new ( self , { status : :queued } ) . execute . present?
end
2018-03-27 19:54:05 +05:30
end
2020-04-08 14:13:33 +05:30
def regeneration_in_progress?
( export_enqueued? || export_in_progress? ) && export_file_exists?
2018-05-09 12:01:36 +05:30
end
2018-11-20 20:47:30 +05:30
def remove_exports
return unless export_file_exists?
import_export_upload . remove_export_file!
import_export_upload . save unless import_export_upload . destroyed?
2018-03-17 18:26:18 +05:30
end
2018-11-20 20:47:30 +05:30
def export_file_exists?
2021-09-04 01:27:46 +05:30
import_export_upload & . export_file_exists?
end
def export_archive_exists?
import_export_upload & . export_archive_exists?
2018-11-08 19:23:39 +05:30
end
2018-05-09 12:01:36 +05:30
2018-11-20 20:47:30 +05:30
def export_file
import_export_upload & . export_file
2018-05-09 12:01:36 +05:30
end
2018-03-17 18:26:18 +05:30
def full_path_slug
Gitlab :: Utils . slugify ( full_path . to_s )
end
def has_ci?
repository . gitlab_ci_yml || auto_devops_enabled?
2016-06-22 15:30:34 +05:30
end
2016-08-24 12:49:21 +05:30
def predefined_variables
2021-12-11 22:18:48 +05:30
strong_memoize ( :predefined_variables ) do
Gitlab :: Ci :: Variables :: Collection . new
. concat ( predefined_ci_server_variables )
. concat ( predefined_project_variables )
. concat ( pages_variables )
. concat ( container_registry_variables )
. concat ( dependency_proxy_variables )
. concat ( auto_devops_variables )
. concat ( api_variables )
end
2020-01-01 13:55:28 +05:30
end
2018-05-09 12:01:36 +05:30
2020-01-01 13:55:28 +05:30
def predefined_project_variables
2018-05-09 12:01:36 +05:30
Gitlab :: Ci :: Variables :: Collection . new
2020-01-01 13:55:28 +05:30
. append ( key : 'GITLAB_FEATURES' , value : licensed_features . join ( ',' ) )
2018-05-09 12:01:36 +05:30
. append ( key : 'CI_PROJECT_ID' , value : id . to_s )
. append ( key : 'CI_PROJECT_NAME' , value : path )
2019-12-21 20:55:43 +05:30
. append ( key : 'CI_PROJECT_TITLE' , value : title )
2022-07-23 23:45:48 +05:30
. append ( key : 'CI_PROJECT_DESCRIPTION' , value : description )
2018-05-09 12:01:36 +05:30
. append ( key : 'CI_PROJECT_PATH' , value : full_path )
. append ( key : 'CI_PROJECT_PATH_SLUG' , value : full_path_slug )
. append ( key : 'CI_PROJECT_NAMESPACE' , value : namespace . full_path )
2020-07-28 23:09:34 +05:30
. append ( key : 'CI_PROJECT_ROOT_NAMESPACE' , value : namespace . root_ancestor . path )
2018-05-09 12:01:36 +05:30
. append ( key : 'CI_PROJECT_URL' , value : web_url )
2020-01-01 13:55:28 +05:30
. append ( key : 'CI_PROJECT_VISIBILITY' , value : Gitlab :: VisibilityLevel . string_level ( visibility_level ) )
2019-12-04 20:38:33 +05:30
. append ( key : 'CI_PROJECT_REPOSITORY_LANGUAGES' , value : repository_languages . map ( & :name ) . join ( ',' ) . downcase )
2021-10-27 15:23:28 +05:30
. append ( key : 'CI_PROJECT_CLASSIFICATION_LABEL' , value : external_authorization_classification_label )
2020-01-01 13:55:28 +05:30
. append ( key : 'CI_DEFAULT_BRANCH' , value : default_branch )
2021-04-17 20:07:23 +05:30
. append ( key : 'CI_CONFIG_PATH' , value : ci_config_path_or_default )
2020-01-01 13:55:28 +05:30
end
def predefined_ci_server_variables
Gitlab :: Ci :: Variables :: Collection . new
. append ( key : 'CI' , value : 'true' )
. append ( key : 'GITLAB_CI' , value : 'true' )
2020-03-13 15:44:24 +05:30
. append ( key : 'CI_SERVER_URL' , value : Gitlab . config . gitlab . url )
2020-01-01 13:55:28 +05:30
. append ( key : 'CI_SERVER_HOST' , value : Gitlab . config . gitlab . host )
2020-03-13 15:44:24 +05:30
. append ( key : 'CI_SERVER_PORT' , value : Gitlab . config . gitlab . port . to_s )
. append ( key : 'CI_SERVER_PROTOCOL' , value : Gitlab . config . gitlab . protocol )
2020-01-01 13:55:28 +05:30
. append ( key : 'CI_SERVER_NAME' , value : 'GitLab' )
. append ( key : 'CI_SERVER_VERSION' , value : Gitlab :: VERSION )
. append ( key : 'CI_SERVER_VERSION_MAJOR' , value : Gitlab . version_info . major . to_s )
. append ( key : 'CI_SERVER_VERSION_MINOR' , value : Gitlab . version_info . minor . to_s )
. append ( key : 'CI_SERVER_VERSION_PATCH' , value : Gitlab . version_info . patch . to_s )
. append ( key : 'CI_SERVER_REVISION' , value : Gitlab . revision )
2019-02-15 15:39:39 +05:30
end
2019-03-02 22:35:43 +05:30
def pages_variables
Gitlab :: Ci :: Variables :: Collection . new . tap do | variables |
2020-01-01 13:55:28 +05:30
break unless pages_enabled?
2019-03-02 22:35:43 +05:30
variables . append ( key : 'CI_PAGES_DOMAIN' , value : Gitlab . config . pages . host )
variables . append ( key : 'CI_PAGES_URL' , value : pages_url )
end
end
2019-02-15 15:39:39 +05:30
def api_variables
Gitlab :: Ci :: Variables :: Collection . new . tap do | variables |
variables . append ( key : 'CI_API_V4_URL' , value : API :: Helpers :: Version . new ( 'v4' ) . root_url )
end
2016-08-24 12:49:21 +05:30
end
2021-02-22 17:27:13 +05:30
def dependency_proxy_variables
Gitlab :: Ci :: Variables :: Collection . new . tap do | variables |
break variables unless Gitlab . config . dependency_proxy . enabled
2021-04-17 20:07:23 +05:30
variables . append ( key : 'CI_DEPENDENCY_PROXY_SERVER' , value : Gitlab . host_with_port )
2021-02-22 17:27:13 +05:30
variables . append (
key : 'CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX' ,
2021-04-17 20:07:23 +05:30
# The namespace path can include uppercase letters, which
# Docker doesn't allow. The proxy expects it to be downcased.
value : " #{ Gitlab . host_with_port } / #{ namespace . root_ancestor . path . downcase } #{ DependencyProxy :: URL_SUFFIX } "
2021-02-22 17:27:13 +05:30
)
2021-11-11 11:23:49 +05:30
variables . append (
key : 'CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX' ,
value : " #{ Gitlab . host_with_port } / #{ namespace . full_path . downcase } #{ DependencyProxy :: URL_SUFFIX } "
)
2021-02-22 17:27:13 +05:30
end
end
2016-08-24 12:49:21 +05:30
def container_registry_variables
2018-05-09 12:01:36 +05:30
Gitlab :: Ci :: Variables :: Collection . new . tap do | variables |
2018-10-15 14:42:47 +05:30
break variables unless Gitlab . config . registry . enabled
2016-08-24 12:49:21 +05:30
2018-05-09 12:01:36 +05:30
variables . append ( key : 'CI_REGISTRY' , value : Gitlab . config . registry . host_port )
2016-08-24 12:49:21 +05:30
2018-05-09 12:01:36 +05:30
if container_registry_enabled?
variables . append ( key : 'CI_REGISTRY_IMAGE' , value : container_registry_url )
end
2016-08-24 12:49:21 +05:30
end
end
2018-11-08 19:23:39 +05:30
def default_environment
2019-12-26 22:10:19 +05:30
production_first = Arel . sql ( " (CASE WHEN name = 'production' THEN 0 ELSE 1 END), id ASC " )
2018-11-08 19:23:39 +05:30
environments
. with_state ( :available )
. reorder ( production_first )
. first
end
2018-12-13 13:39:08 +05:30
def ci_variables_for ( ref : , environment : nil )
2020-04-08 14:13:33 +05:30
cache_key = " ci_variables_for:project: #{ self & . id } :ref: #{ ref } :environment: #{ environment } "
:: Gitlab :: SafeRequestStore . fetch ( cache_key ) do
uncached_ci_variables_for ( ref : ref , environment : environment )
end
end
def uncached_ci_variables_for ( ref : , environment : nil )
2019-10-12 21:52:04 +05:30
result = if protected_for? ( ref )
variables
else
variables . unprotected
end
if environment
result . on_environment ( environment )
2017-09-10 17:25:29 +05:30
else
2019-10-12 21:52:04 +05:30
result . where ( environment_scope : '*' )
2016-08-24 12:49:21 +05:30
end
end
2017-09-10 17:25:29 +05:30
def protected_for? ( ref )
2019-01-03 12:48:30 +05:30
raise Repository :: AmbiguousRefError if repository . ambiguous_ref? ( ref )
resolved_ref = repository . expand_ref ( ref ) || ref
return false unless Gitlab :: Git . tag_ref? ( resolved_ref ) || Gitlab :: Git . branch_ref? ( resolved_ref )
ref_name = if resolved_ref == ref
Gitlab :: Git . ref_name ( resolved_ref )
else
ref
end
if Gitlab :: Git . branch_ref? ( resolved_ref )
ProtectedBranch . protected? ( self , ref_name )
elsif Gitlab :: Git . tag_ref? ( resolved_ref )
ProtectedTag . protected? ( self , ref_name )
2018-03-17 18:26:18 +05:30
end
2017-09-10 17:25:29 +05:30
end
2020-01-01 13:55:28 +05:30
def deployment_variables ( environment : , kubernetes_namespace : nil )
2019-10-12 21:52:04 +05:30
platform = deployment_platform ( environment : environment )
return [ ] unless platform . present?
2020-01-01 13:55:28 +05:30
platform . predefined_variables (
project : self ,
environment_name : environment ,
kubernetes_namespace : kubernetes_namespace
)
2018-03-17 18:26:18 +05:30
end
2016-08-24 12:49:21 +05:30
2018-03-17 18:26:18 +05:30
def auto_devops_variables
return [ ] unless auto_devops_enabled?
2018-05-09 12:01:36 +05:30
( auto_devops || build_auto_devops ) & . predefined_variables
2016-08-24 12:49:21 +05:30
end
2017-08-17 22:00:37 +05:30
def route_map_for ( commit_sha )
@route_maps_by_commit || = Hash . new do | h , sha |
h [ sha ] = begin
data = repository . route_map_for ( sha )
2016-09-29 09:46:39 +05:30
2019-09-30 21:07:59 +05:30
Gitlab :: RouteMap . new ( data ) if data
2021-04-29 21:17:54 +05:30
rescue Gitlab :: RouteMap :: FormatError
nil
2016-09-29 09:46:39 +05:30
end
2017-08-17 22:00:37 +05:30
end
@route_maps_by_commit [ commit_sha ]
end
2016-09-29 09:46:39 +05:30
2017-08-17 22:00:37 +05:30
def public_path_for_source_path ( path , commit_sha )
map = route_map_for ( commit_sha )
return unless map
map . public_path_for_source_path ( path )
end
def parent_changed?
namespace_id_changed?
end
def default_merge_request_target
2021-04-29 21:17:54 +05:30
return self if project_setting . mr_default_target_self
return self unless mr_can_target_upstream?
forked_from_project
end
def mr_can_target_upstream?
# When our current visibility is more restrictive than the upstream project,
# (e.g., the fork is `private` but the parent is `public`), don't allow target upstream
forked_from_project &&
forked_from_project . merge_requests_enabled? &&
forked_from_project . visibility_level_value < = visibility_level_value
2016-09-29 09:46:39 +05:30
end
2018-03-27 19:54:05 +05:30
def multiple_issue_boards_available?
2019-09-30 21:07:59 +05:30
true
2018-03-17 18:26:18 +05:30
end
2019-07-31 22:56:46 +05:30
def full_path_before_last_save
File . join ( namespace . full_path , path_before_last_save )
2018-03-17 18:26:18 +05:30
end
2017-08-17 22:00:37 +05:30
alias_method :name_with_namespace , :full_name
alias_method :human_name , :full_name
2017-09-10 17:25:29 +05:30
# @deprecated cannot remove yet because it has an index with its name in elasticsearch
2017-08-17 22:00:37 +05:30
alias_method :path_with_namespace , :full_path
2018-12-05 23:21:45 +05:30
# rubocop: disable CodeReuse/ServiceClass
2017-09-10 17:25:29 +05:30
def forks_count
2021-11-18 22:05:49 +05:30
BatchLoader . for ( self ) . batch do | projects , loader |
2020-07-28 23:09:34 +05:30
fork_count_per_project = :: Projects :: BatchForksCountService . new ( projects ) . refresh_cache_and_retrieve_data
fork_count_per_project . each do | project , count |
loader . call ( project , count )
end
end
2017-09-10 17:25:29 +05:30
end
2018-12-05 23:21:45 +05:30
# rubocop: enable CodeReuse/ServiceClass
2017-09-10 17:25:29 +05:30
def legacy_storage?
2018-03-17 18:26:18 +05:30
[ nil , 0 ] . include? ( self . storage_version )
end
# Check if Hashed Storage is enabled for the project with at least informed feature rolled out
#
# @param [Symbol] feature that needs to be rolled out for the project (:repository, :attachments)
def hashed_storage? ( feature )
2019-07-31 22:56:46 +05:30
raise ArgumentError , _ ( " Invalid feature " ) unless HASHED_STORAGE_FEATURES . include? ( feature )
2018-03-17 18:26:18 +05:30
self . storage_version && self . storage_version > = HASHED_STORAGE_FEATURES [ feature ]
end
def renamed?
persisted? && path_changed?
end
2019-07-07 11:18:12 +05:30
def human_merge_method
if merge_method == :ff
'Fast-forward'
else
merge_method . to_s . humanize
end
end
2018-03-17 18:26:18 +05:30
def merge_method
if self . merge_requests_ff_only_enabled
:ff
elsif self . merge_requests_rebase_enabled
:rebase_merge
else
:merge
end
end
def merge_method = ( method )
case method . to_s
when " ff "
self . merge_requests_ff_only_enabled = true
self . merge_requests_rebase_enabled = true
when " rebase_merge "
self . merge_requests_ff_only_enabled = false
self . merge_requests_rebase_enabled = true
when " merge "
self . merge_requests_ff_only_enabled = false
self . merge_requests_rebase_enabled = false
end
end
def ff_merge_must_be_possible?
self . merge_requests_ff_only_enabled || self . merge_requests_rebase_enabled
end
def migrate_to_hashed_storage!
2019-02-15 15:39:39 +05:30
return unless storage_upgradable?
2018-03-17 18:26:18 +05:30
2019-02-15 15:39:39 +05:30
if git_transfer_in_progress?
2019-07-07 11:18:12 +05:30
HashedStorage :: ProjectMigrateWorker . perform_in ( Gitlab :: ReferenceCounter :: REFERENCE_EXPIRE_TIME , id )
else
HashedStorage :: ProjectMigrateWorker . perform_async ( id )
end
end
def rollback_to_legacy_storage!
return if legacy_storage?
if git_transfer_in_progress?
HashedStorage :: ProjectRollbackWorker . perform_in ( Gitlab :: ReferenceCounter :: REFERENCE_EXPIRE_TIME , id )
2018-03-17 18:26:18 +05:30
else
2019-07-07 11:18:12 +05:30
HashedStorage :: ProjectRollbackWorker . perform_async ( id )
2018-03-17 18:26:18 +05:30
end
end
2021-02-22 17:27:13 +05:30
override :git_transfer_in_progress?
2019-02-15 15:39:39 +05:30
def git_transfer_in_progress?
2021-02-22 17:27:13 +05:30
GL_REPOSITORY_TYPES . any? do | type |
reference_counter ( type : type ) . value > 0
end
2019-02-15 15:39:39 +05:30
end
2018-03-17 18:26:18 +05:30
def storage_version = ( value )
super
@storage = nil if storage_version_changed?
end
2018-03-27 19:54:05 +05:30
def badges
return project_badges unless group
2018-12-05 23:21:45 +05:30
Badge . from_union ( [
project_badges ,
GroupBadge . where ( group : group . self_and_ancestors )
] )
2018-03-27 19:54:05 +05:30
end
def merge_requests_allowing_push_to_user ( user )
return MergeRequest . none unless user
developer_access_exists = user . project_authorizations
. where ( 'access_level >= ? ' , Gitlab :: Access :: DEVELOPER )
. where ( 'project_authorizations.project_id = merge_requests.target_project_id' )
. limit ( 1 )
. select ( 1 )
2019-02-15 15:39:39 +05:30
merge_requests_allowing_collaboration . where ( 'EXISTS (?)' , developer_access_exists )
2018-03-27 19:54:05 +05:30
end
2019-02-15 15:39:39 +05:30
def any_branch_allows_collaboration? ( user )
fetch_branch_allows_collaboration ( user )
end
2018-03-27 19:54:05 +05:30
2019-02-15 15:39:39 +05:30
def branch_allows_collaboration? ( user , branch_name )
fetch_branch_allows_collaboration ( user , branch_name )
2018-03-27 19:54:05 +05:30
end
2019-07-07 11:18:12 +05:30
def external_authorization_classification_label
super || :: Gitlab :: CurrentSettings . current_application_settings
. external_authorization_service_default_label
end
2021-04-29 21:17:54 +05:30
# Overridden in EE::Project
def licensed_feature_available? ( _feature )
false
end
2018-10-15 14:42:47 +05:30
def licensed_features
[ ]
end
2021-01-03 14:25:43 +05:30
def mark_primary_write_location
2021-11-18 22:05:49 +05:30
self . class . sticking . mark_primary_write_location ( :project , self . id )
2021-01-03 14:25:43 +05:30
end
2018-10-15 14:42:47 +05:30
def toggle_ci_cd_settings! ( settings_attribute )
ci_cd_settings . toggle! ( settings_attribute )
end
def gitlab_deploy_token
2022-07-23 23:45:48 +05:30
strong_memoize ( :gitlab_deploy_token ) do
if Feature . enabled? ( :ci_variable_for_group_gitlab_deploy_token , self )
deploy_tokens . gitlab_deploy_token || group & . gitlab_deploy_token
else
deploy_tokens . gitlab_deploy_token
end
end
2018-10-15 14:42:47 +05:30
end
2018-11-08 19:23:39 +05:30
def any_lfs_file_locks?
lfs_file_locks . any?
end
request_cache ( :any_lfs_file_locks? ) { self . id }
def auto_cancel_pending_pipelines?
auto_cancel_pending_pipelines == 'enabled'
end
2018-03-17 18:26:18 +05:30
def storage
@storage || =
if hashed_storage? ( :repository )
2020-03-13 15:44:24 +05:30
Storage :: Hashed . new ( self )
2018-03-17 18:26:18 +05:30
else
Storage :: LegacyProject . new ( self )
end
end
2018-12-13 13:39:08 +05:30
def storage_upgradable?
storage_version != LATEST_STORAGE_VERSION
end
def snippets_visible? ( user = nil )
2020-03-13 15:44:24 +05:30
Ability . allowed? ( user , :read_snippet , self )
2018-12-13 13:39:08 +05:30
end
2019-02-15 15:39:39 +05:30
def max_attachment_size
Gitlab :: CurrentSettings . max_attachment_size . megabytes . to_i
end
def object_pool_params
return { } unless ! forked? && git_objects_poolable?
{
repository_storage : repository_storage ,
pool_repository : pool_repository || create_new_pool_repository
}
end
# Git objects are only poolable when the project is or has:
# - Hashed storage -> The object pool will have a remote to its members, using relative paths.
# If the repository path changes we would have to update the remote.
2020-01-01 13:55:28 +05:30
# - not private -> The visibility level or repository access level has to be greater than private
# to prevent fetching objects that might not exist
2019-02-15 15:39:39 +05:30
# - Repository -> Else the disk path will be empty, and there's nothing to pool
def git_objects_poolable?
hashed_storage? ( :repository ) &&
2020-01-01 13:55:28 +05:30
visibility_level > Gitlab :: VisibilityLevel :: PRIVATE &&
repository_access_level > ProjectFeature :: PRIVATE &&
2019-02-15 15:39:39 +05:30
repository_exists? &&
2019-12-04 20:38:33 +05:30
Gitlab :: CurrentSettings . hashed_storage_enabled
2019-02-15 15:39:39 +05:30
end
def leave_pool_repository
2022-07-16 23:28:13 +05:30
pool_repository & . unlink_repository ( repository ) && update_column ( :pool_repository_id , nil )
2019-03-02 22:35:43 +05:30
end
def link_pool_repository
pool_repository & . link_repository ( repository )
2019-02-15 15:39:39 +05:30
end
2019-07-07 11:18:12 +05:30
def has_pool_repository?
pool_repository . present?
end
2019-12-04 20:38:33 +05:30
def access_request_approvers_to_be_notified
2022-07-23 23:45:48 +05:30
access_request_approvers = members . owners_and_maintainers
2022-05-07 20:08:51 +05:30
access_request_approvers . connected_to_user . order_recent_sign_in . limit ( Member :: ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT )
2019-12-04 20:38:33 +05:30
end
2019-12-21 20:55:43 +05:30
def pages_lookup_path ( trim_prefix : nil , domain : nil )
Pages :: LookupPath . new ( self , trim_prefix : trim_prefix , domain : domain )
end
def closest_setting ( name )
setting = read_attribute ( name )
setting = closest_namespace_setting ( name ) if setting . nil?
setting = app_settings_for ( name ) if setting . nil?
setting
end
def drop_visibility_level!
if group && group . visibility_level < visibility_level
self . visibility_level = group . visibility_level
end
if Gitlab :: CurrentSettings . restricted_visibility_levels . include? ( visibility_level )
self . visibility_level = Gitlab :: VisibilityLevel :: PRIVATE
end
2019-12-04 20:38:33 +05:30
end
2020-02-01 01:16:34 +05:30
def template_source?
false
end
2020-11-24 15:15:51 +05:30
def jira_subscription_exists?
JiraConnectSubscription . for_project ( self ) . exists?
end
2020-03-13 15:44:24 +05:30
def uses_default_ci_config?
ci_config_path . blank? || ci_config_path == Gitlab :: FileDetector :: PATTERNS [ :gitlab_ci ]
end
2021-11-11 11:23:49 +05:30
def uses_external_project_ci_config?
! ! ( ci_config_path =~ %r{ @.+/.+ } )
end
2020-03-13 15:44:24 +05:30
def limited_protected_branches ( limit )
protected_branches . limit ( limit )
end
def self_monitoring?
Gitlab :: CurrentSettings . self_monitoring_project_id == id
end
2020-04-08 14:13:33 +05:30
def deploy_token_create_url ( opts = { } )
2020-04-25 10:58:03 +05:30
Gitlab :: Routing . url_helpers . create_deploy_token_project_settings_repository_path ( self , opts )
2020-04-08 14:13:33 +05:30
end
def deploy_token_revoke_url_for ( token )
Gitlab :: Routing . url_helpers . revoke_project_deploy_token_path ( self , token )
end
def default_branch_protected?
branch_protection = Gitlab :: Access :: BranchProtection . new ( self . namespace . default_branch_protection )
branch_protection . fully_protected? || branch_protection . developer_can_merge?
end
2020-04-22 19:07:51 +05:30
def environments_for_scope ( scope )
quoted_scope = :: Gitlab :: SQL :: Glob . q ( scope )
environments . where ( " name LIKE ( #{ :: Gitlab :: SQL :: Glob . to_like ( quoted_scope ) } ) " ) # rubocop:disable GitlabSecurity/SqlInjection
end
def latest_jira_import
jira_imports . last
end
2020-06-23 00:09:42 +05:30
def metrics_setting
super || build_metrics_setting
end
2020-07-28 23:09:34 +05:30
def service_desk_enabled
Gitlab :: ServiceDesk . enabled? ( project : self )
end
alias_method :service_desk_enabled? , :service_desk_enabled
def service_desk_address
2020-10-24 23:57:45 +05:30
service_desk_custom_address || service_desk_incoming_address
end
def service_desk_incoming_address
2020-07-28 23:09:34 +05:30
return unless service_desk_enabled?
config = Gitlab . config . incoming_email
wildcard = Gitlab :: IncomingEmail :: WILDCARD_PLACEHOLDER
2021-12-11 22:18:48 +05:30
config . address & . gsub ( wildcard , " #{ full_path_slug } - #{ default_service_desk_suffix } " )
2020-07-28 23:09:34 +05:30
end
2020-10-24 23:57:45 +05:30
def service_desk_custom_address
2021-03-08 18:12:59 +05:30
return unless Gitlab :: ServiceDeskEmail . enabled?
2020-10-24 23:57:45 +05:30
2021-12-11 22:18:48 +05:30
key = service_desk_setting & . project_key || default_service_desk_suffix
2020-10-24 23:57:45 +05:30
2021-03-08 18:12:59 +05:30
Gitlab :: ServiceDeskEmail . address_for_key ( " #{ full_path_slug } - #{ key } " )
2021-02-22 17:27:13 +05:30
end
2021-12-11 22:18:48 +05:30
def default_service_desk_suffix
" #{ id } -issue- "
end
2020-07-28 23:09:34 +05:30
def root_namespace
if namespace . has_parent?
namespace . root_ancestor
else
namespace
end
end
2021-09-04 01:27:46 +05:30
# for projects that are part of user namespace, return project.
def self_or_root_group_ids
if group
root_group = root_namespace
else
project = self
end
[ project & . id , root_group & . id ]
end
2022-04-04 11:22:00 +05:30
def related_group_ids
ids = invited_group_ids
ids += group . self_and_ancestors_ids if group
ids
end
2021-10-27 15:23:28 +05:30
def package_already_taken? ( package_name , package_version , package_type : )
Packages :: Package . with_name ( package_name )
. with_version ( package_version )
. with_package_type ( package_type )
2022-03-02 08:16:31 +05:30
. not_pending_destruction
2021-10-27 15:23:28 +05:30
. for_projects (
root_ancestor . all_projects
. id_not_in ( id )
. select ( :id )
) . exists?
2020-07-28 23:09:34 +05:30
end
2021-06-08 01:23:25 +05:30
def default_branch_or_main
return default_branch if default_branch
Gitlab :: DefaultBranch . value ( object : self )
2020-11-24 15:15:51 +05:30
end
def ci_config_path_or_default
ci_config_path . presence || Ci :: Pipeline :: DEFAULT_CONFIG_PATH
end
2021-01-03 14:25:43 +05:30
def ci_config_for ( sha )
repository . gitlab_ci_yml_for ( sha , ci_config_path_or_default )
end
2021-11-11 11:23:49 +05:30
def ci_config_external_project
Project . find_by_full_path ( ci_config_path . split ( '@' , 2 ) . last )
end
2020-11-24 15:15:51 +05:30
def enabled_group_deploy_keys
return GroupDeployKey . none unless group
GroupDeployKey . for_groups ( group . self_and_ancestors_ids )
end
2021-01-03 14:25:43 +05:30
def feature_flags_client_token
instance = operations_feature_flags_client || create_operations_feature_flags_client!
instance . token
end
def tracing_external_url
tracing_setting & . external_url
end
2021-03-11 19:13:27 +05:30
override :git_garbage_collect_worker_klass
def git_garbage_collect_worker_klass
Projects :: GitGarbageCollectWorker
end
2021-06-08 01:23:25 +05:30
def activity_path
Gitlab :: Routing . url_helpers . activity_project_path ( self )
end
def increment_statistic_value ( statistic , delta )
return if pending_delete?
ProjectStatistics . increment_statistic ( self , statistic , delta )
end
2021-09-30 23:02:18 +05:30
def ci_forward_deployment_enabled?
return false unless ci_cd_settings
ci_cd_settings . forward_deployment_enabled?
end
def ci_job_token_scope_enabled?
return false unless ci_cd_settings
ci_cd_settings . job_token_scope_enabled?
end
def restrict_user_defined_variables?
return false unless ci_cd_settings
ci_cd_settings . restrict_user_defined_variables?
end
def keep_latest_artifacts_available?
return false unless ci_cd_settings
ci_cd_settings . keep_latest_artifacts_available?
end
def keep_latest_artifact?
return false unless ci_cd_settings
ci_cd_settings . keep_latest_artifact?
end
2022-03-02 08:16:31 +05:30
def runner_token_expiration_interval
ci_cd_settings & . runner_token_expiration_interval
end
2021-09-30 23:02:18 +05:30
def group_runners_enabled?
return false unless ci_cd_settings
ci_cd_settings . group_runners_enabled?
2021-09-04 01:27:46 +05:30
end
2021-11-11 11:23:49 +05:30
def topic_list
self . topics . map ( & :name )
end
override :after_change_head_branch_does_not_exist
def after_change_head_branch_does_not_exist ( branch )
self . errors . add ( :base , _ ( " Could not change HEAD: branch '%{branch}' does not exist " ) % { branch : branch } )
end
2021-10-29 20:43:33 +05:30
def visible_group_links ( for_user : )
user = for_user
links = project_group_links_with_preload
user . max_member_access_for_group_ids ( links . map ( & :group_id ) ) if user && links . any?
DeclarativePolicy . user_scope do
links . select { Ability . allowed? ( user , :read_group , _1 . group ) }
end
end
2022-01-26 12:08:38 +05:30
def remove_project_authorizations ( user_ids , per_batch = 1000 )
user_ids . each_slice ( per_batch ) do | user_ids_batch |
project_authorizations . where ( user_id : user_ids_batch ) . delete_all
end
end
2022-03-02 08:16:31 +05:30
def enforced_runner_token_expiration_interval
all_parent_groups = Gitlab :: ObjectHierarchy . new ( Group . where ( id : group ) ) . base_and_ancestors
all_group_settings = NamespaceSetting . where ( namespace_id : all_parent_groups )
group_interval = all_group_settings . where . not ( project_runner_token_expiration_interval : nil ) . minimum ( :project_runner_token_expiration_interval ) & . seconds
[
Gitlab :: CurrentSettings . project_runner_token_expiration_interval & . seconds ,
group_interval
] . compact . min
end
2022-04-04 11:22:00 +05:30
def merge_commit_template_or_default
merge_commit_template . presence || DEFAULT_MERGE_COMMIT_TEMPLATE
end
def merge_commit_template_or_default = ( value )
project_setting . merge_commit_template =
if value . blank? || value . delete ( " \r " ) == DEFAULT_MERGE_COMMIT_TEMPLATE
nil
else
value
end
end
def squash_commit_template_or_default
squash_commit_template . presence || DEFAULT_SQUASH_COMMIT_TEMPLATE
end
def squash_commit_template_or_default = ( value )
project_setting . squash_commit_template =
if value . blank? || value . delete ( " \r " ) == DEFAULT_SQUASH_COMMIT_TEMPLATE
nil
else
value
end
end
2022-05-07 20:08:51 +05:30
def pending_delete_or_hidden?
pending_delete? || hidden?
end
2022-06-21 17:19:12 +05:30
def work_items_feature_flag_enabled?
2022-07-16 23:28:13 +05:30
group & . work_items_feature_flag_enabled? || Feature . enabled? ( :work_items , self )
2022-06-21 17:19:12 +05:30
end
def enqueue_record_project_target_platforms
return unless Gitlab . com?
2022-07-16 23:28:13 +05:30
return unless Feature . enabled? ( :record_projects_target_platforms , self )
2022-06-21 17:19:12 +05:30
Projects :: RecordTargetPlatformsWorker . perform_async ( id )
end
def inactive?
( statistics || build_statistics ) . storage_size > :: Gitlab :: CurrentSettings . inactive_projects_min_size_mb . megabytes &&
last_activity_at < :: Gitlab :: CurrentSettings . inactive_projects_send_warning_email_after_months . months . ago
end
2022-07-23 23:45:48 +05:30
def refreshing_build_artifacts_size?
build_artifacts_size_refresh & . started?
end
def security_training_available?
licensed_feature_available? ( :security_training )
end
2018-12-13 13:39:08 +05:30
private
2021-10-29 20:43:33 +05:30
# overridden in EE
def project_group_links_with_preload
project_group_links
end
2021-11-11 11:23:49 +05:30
def save_topics
2022-04-04 11:22:00 +05:30
topic_ids_before = self . topic_ids
update_topics
Projects :: Topic . update_non_private_projects_counter ( topic_ids_before , self . topic_ids , visibility_level_previously_was , visibility_level )
end
def update_topics
2021-11-11 11:23:49 +05:30
return if @topic_list . nil?
@topic_list = @topic_list . split ( ',' ) if @topic_list . instance_of? ( String )
@topic_list = @topic_list . map ( & :strip ) . uniq . reject ( & :empty? )
2021-11-18 22:05:49 +05:30
if @topic_list != self . topic_list
self . topics . delete_all
2022-05-07 20:08:51 +05:30
self . topics = @topic_list . map do | topic |
2022-07-16 23:28:13 +05:30
Projects :: Topic . where ( 'lower(name) = ?' , topic . downcase ) . order ( total_projects_count : :desc ) . first_or_create ( name : topic , title : topic )
2022-05-07 20:08:51 +05:30
end
2021-11-11 11:23:49 +05:30
end
@topic_list = nil
end
2021-09-30 23:02:18 +05:30
def find_integration ( integrations , name )
integrations . find { _1 . to_param == name }
2020-04-22 19:07:51 +05:30
end
2021-10-27 15:23:28 +05:30
def build_from_instance ( name )
2021-09-30 23:02:18 +05:30
instance = find_integration ( integration_instances , name )
2020-06-23 00:09:42 +05:30
2021-10-27 15:23:28 +05:30
return unless instance
Integration . build_from_integration ( instance , project_id : id )
2020-06-23 00:09:42 +05:30
end
2021-09-30 23:02:18 +05:30
def build_integration ( name )
2021-09-04 01:27:46 +05:30
Integration . integration_name_to_model ( name ) . new ( project_id : id )
2021-04-29 21:17:54 +05:30
end
2021-09-30 23:02:18 +05:30
def integration_instances
@integration_instances || = Integration . for_instance
2020-06-23 00:09:42 +05:30
end
2019-12-21 20:55:43 +05:30
def closest_namespace_setting ( name )
namespace . closest_setting ( name )
end
def app_settings_for ( name )
Gitlab :: CurrentSettings . send ( name ) # rubocop:disable GitlabSecurity/PublicSend
end
2019-02-15 15:39:39 +05:30
def merge_requests_allowing_collaboration ( source_branch = nil )
relation = source_of_merge_requests . opened . where ( allow_collaboration : true )
relation = relation . where ( source_branch : source_branch ) if source_branch
relation
end
def create_new_pool_repository
2019-07-31 22:56:46 +05:30
pool = PoolRepository . safe_find_or_create_by! ( shard : Shard . by_name ( repository_storage ) , source_project : self )
update! ( pool_repository : pool )
2019-02-15 15:39:39 +05:30
pool . schedule unless pool . scheduled?
2019-07-31 22:56:46 +05:30
2019-02-15 15:39:39 +05:30
pool
end
def join_pool_repository
return unless pool_repository
ObjectPool :: JoinWorker . perform_async ( pool_repository . id , self . id )
end
2018-03-17 18:26:18 +05:30
def use_hashed_storage
if self . new_record? && Gitlab :: CurrentSettings . hashed_storage_enabled
self . storage_version = LATEST_STORAGE_VERSION
end
end
def check_repository_absence!
return if skip_disk_validation
2018-10-15 14:42:47 +05:30
if repository_storage . blank? || repository_with_same_path_already_exists?
2019-07-31 22:56:46 +05:30
errors . add ( :base , _ ( 'There is already a repository with that name on disk' ) )
2020-04-08 14:13:33 +05:30
throw :abort # rubocop:disable Cop/BanCatchThrow
2018-03-17 18:26:18 +05:30
end
end
def repository_with_same_path_already_exists?
2019-12-21 20:55:43 +05:30
gitlab_shell . repository_exists? ( repository_storage , " #{ disk_path } .git " )
2018-03-17 18:26:18 +05:30
end
2018-12-13 13:39:08 +05:30
def set_timestamps_for_create
update_columns ( last_activity_at : self . created_at , last_repository_updated_at : self . created_at )
2018-03-17 18:26:18 +05:30
end
2017-08-17 22:00:37 +05:30
def cross_namespace_reference? ( from )
case from
when Project
2021-06-08 01:23:25 +05:30
namespace_id != from . namespace_id
2017-08-17 22:00:37 +05:30
when Namespace
namespace != from
2020-10-24 23:57:45 +05:30
when User
true
2017-08-17 22:00:37 +05:30
end
end
# Check if a reference is being done cross-project
def cross_project_reference? ( from )
return true if from . is_a? ( Namespace )
from && self != from
end
def update_project_statistics
stats = statistics || build_statistics
stats . update ( namespace_id : namespace_id )
2016-08-24 12:49:21 +05:30
end
2017-08-17 22:00:37 +05:30
def check_pending_delete
return if valid_attribute? ( :name ) && valid_attribute? ( :path )
return unless pending_delete_twin
% i [ route route . path name path ] . each do | error |
errors . delete ( error )
end
2016-08-24 12:49:21 +05:30
2019-07-31 22:56:46 +05:30
errors . add ( :base , _ ( " The project is still being deleted. Please try again later. " ) )
2016-08-24 12:49:21 +05:30
end
2017-08-17 22:00:37 +05:30
def pending_delete_twin
return false unless path
2016-08-24 12:49:21 +05:30
2017-09-10 17:25:29 +05:30
Project . pending_delete . find_by_full_path ( full_path )
2017-08-17 22:00:37 +05:30
end
2016-08-24 12:49:21 +05:30
2017-08-17 22:00:37 +05:30
##
# This method is here because of support for legacy container repository
# which has exactly the same path like project does, but which might not be
# persisted in `container_repositories` table.
#
def has_root_container_repository_tags?
return false unless Gitlab . config . registry . enabled
ContainerRepository . build_root_repository ( self ) . has_tags?
2016-08-24 12:49:21 +05:30
end
2016-11-03 12:29:30 +05:30
2019-02-15 15:39:39 +05:30
def fetch_branch_allows_collaboration ( user , branch_name = nil )
return false unless user
2018-05-09 12:01:36 +05:30
2019-02-15 15:39:39 +05:30
Gitlab :: SafeRequestStore . fetch ( " project- #{ id } :branch- #{ branch_name } :user- #{ user . id } :branch_allows_collaboration " ) do
next false if empty_repo?
2018-11-08 19:23:39 +05:30
2019-12-04 20:38:33 +05:30
# Issue for N+1: https://gitlab.com/gitlab-org/gitlab-foss/issues/49322
2018-11-18 11:00:15 +05:30
Gitlab :: GitalyClient . allow_n_plus_1_calls do
2019-02-15 15:39:39 +05:30
merge_requests_allowing_collaboration ( branch_name ) . any? do | merge_request |
2021-04-01 16:36:13 +05:30
merge_request . can_be_merged_by? ( user , skip_collaboration_check : true )
2018-11-18 11:00:15 +05:30
end
2018-11-08 19:23:39 +05:30
end
2018-03-27 19:54:05 +05:30
end
end
2018-12-05 23:21:45 +05:30
2019-12-21 20:55:43 +05:30
def ensure_pages_metadatum
pages_metadatum || create_pages_metadatum!
rescue ActiveRecord :: RecordNotUnique
reset
retry
end
2020-04-08 14:13:33 +05:30
def oids ( objects , oids : [ ] )
2020-11-24 15:15:51 +05:30
objects = objects . where ( oid : oids ) if oids . any?
2020-04-08 14:13:33 +05:30
2020-11-24 15:15:51 +05:30
[ ] . tap do | out |
objects . each_batch { | relation | out . concat ( relation . pluck ( :oid ) ) }
end
2020-04-08 14:13:33 +05:30
end
2021-03-08 18:12:59 +05:30
def cache_has_external_wiki
2021-06-08 01:23:25 +05:30
update_column ( :has_external_wiki , integrations . external_wikis . any? ) if Gitlab :: Database . read_write?
2021-03-08 18:12:59 +05:30
end
2021-03-11 19:13:27 +05:30
def cache_has_external_issue_tracker
2021-06-08 01:23:25 +05:30
update_column ( :has_external_issue_tracker , integrations . external_issue_trackers . any? ) if Gitlab :: Database . read_write?
2021-03-11 19:13:27 +05:30
end
2021-04-17 20:07:23 +05:30
2021-04-29 21:17:54 +05:30
def online_runners_with_tags
2021-11-11 11:23:49 +05:30
@online_runners_with_tags || = active_runners . with_tags . online
2021-04-17 20:07:23 +05:30
end
2021-11-18 22:05:49 +05:30
def ensure_project_namespace_in_sync
2022-05-07 20:08:51 +05:30
# create project_namespace when project is created
2021-12-11 22:18:48 +05:30
build_project_namespace if project_namespace_creation_enabled?
sync_attributes ( project_namespace ) if sync_project_namespace?
end
def project_namespace_creation_enabled?
2022-05-07 20:08:51 +05:30
new_record? && ! project_namespace && self . namespace
2021-12-11 22:18:48 +05:30
end
def sync_project_namespace?
( changes . keys & %w( name path namespace_id namespace visibility_level shared_runners_enabled ) ) . any? && project_namespace . present?
end
def sync_attributes ( project_namespace )
2022-07-23 23:45:48 +05:30
attributes_to_sync = changes . slice ( * %w( name path namespace_id namespace visibility_level shared_runners_enabled ) )
. transform_values { | val | val [ 1 ] }
# if visibility_level is not set explicitly for project, it defaults to 0,
# but for namespace visibility_level defaults to 20,
# so it gets out of sync right away if we do not set it explicitly when creating the project namespace
attributes_to_sync [ 'visibility_level' ] || = visibility_level if new_record?
# when a project is associated with a group while the group is created we need to ensure we associate the new
# group with the project namespace as well.
# E.g.
# project = create(:project) <- project is saved
# create(:group, projects: [project]) <- associate project with a group that is not yet created.
if attributes_to_sync . has_key? ( 'namespace_id' ) && attributes_to_sync [ 'namespace_id' ] . blank? && namespace . present?
attributes_to_sync [ 'parent' ] = namespace
end
project_namespace . assign_attributes ( attributes_to_sync )
2021-11-18 22:05:49 +05:30
end
2022-01-26 12:08:38 +05:30
# SyncEvents are created by PG triggers (with the function `insert_projects_sync_event`)
def schedule_sync_event_worker
run_after_commit do
Projects :: SyncEvent . enqueue_worker
end
end
2022-07-16 23:28:13 +05:30
def check_project_export_limit!
return if Gitlab :: CurrentSettings . current_application_settings . max_export_size == 0
if self . statistics . storage_size > Gitlab :: CurrentSettings . current_application_settings . max_export_size . megabytes
raise ExportLimitExceeded , _ ( 'The project size exceeds the export limit.' )
end
end
2022-07-23 23:45:48 +05:30
def set_package_registry_access_level
return if ! project_feature || project_feature . package_registry_access_level_changed?
self . project_feature . package_registry_access_level = packages_enabled ? enabled_package_registry_access_level_by_project_visibility : ProjectFeature :: DISABLED
end
def enabled_package_registry_access_level_by_project_visibility
case visibility_level
when PUBLIC
ProjectFeature :: PUBLIC
when INTERNAL
ProjectFeature :: ENABLED
else
ProjectFeature :: PRIVATE
end
end
2014-09-02 18:07:02 +05:30
end
2019-12-04 20:38:33 +05:30
2021-06-08 01:23:25 +05:30
Project . prepend_mod_with ( 'Project' )