1# frozen_string_literal: true
2
3require 'carrierwave/orm/activerecord'
4
5class Project < ApplicationRecord
6  include Gitlab::ConfigHelper
7  include Gitlab::VisibilityLevel
8  include AccessRequestable
9  include Avatarable
10  include CacheMarkdownField
11  include Sortable
12  include AfterCommitQueue
13  include CaseSensitivity
14  include TokenAuthenticatable
15  include ValidAttribute
16  include ProjectAPICompatibility
17  include ProjectFeaturesCompatibility
18  include SelectForProjectAuthorization
19  include Presentable
20  include HasRepository
21  include HasWiki
22  include CanMoveRepositoryStorage
23  include Routable
24  include GroupDescendant
25  include Gitlab::SQL::Pattern
26  include DeploymentPlatform
27  include ::Gitlab::Utils::StrongMemoize
28  include ChronicDurationAttribute
29  include FastDestroyAll::Helpers
30  include WithUploads
31  include BatchDestroyDependentAssociations
32  include FeatureGate
33  include OptionallySearch
34  include FromUnion
35  include IgnorableColumns
36  include Repositories::CanHousekeepRepository
37  include EachBatch
38  include GitlabRoutingHelper
39  include BulkMemberAccessLoad
40
41  extend Gitlab::Cache::RequestCache
42  extend Gitlab::Utils::Override
43
44  extend Gitlab::ConfigHelper
45
46  ignore_columns :container_registry_enabled, remove_after: '2021-09-22', remove_with: '14.4'
47
48  BoardLimitExceeded = Class.new(StandardError)
49
50  ignore_columns :mirror_last_update_at, :mirror_last_successful_update_at, remove_after: '2021-09-22', remove_with: '14.4'
51  ignore_columns :pull_mirror_branch_prefix, remove_after: '2021-09-22', remove_with: '14.4'
52
53  STATISTICS_ATTRIBUTE = 'repositories_count'
54  UNKNOWN_IMPORT_URL = 'http://unknown.git'
55  # Hashed Storage versions handle rolling out new storage to project and dependents models:
56  # nil: legacy
57  # 1: repository
58  # 2: attachments
59  LATEST_STORAGE_VERSION = 2
60  HASHED_STORAGE_FEATURES = {
61    repository: 1,
62    attachments: 2
63  }.freeze
64
65  VALID_IMPORT_PORTS = [80, 443].freeze
66  VALID_IMPORT_PROTOCOLS = %w(http https git).freeze
67
68  VALID_MIRROR_PORTS = [22, 80, 443].freeze
69  VALID_MIRROR_PROTOCOLS = %w(http https ssh git).freeze
70
71  SORTING_PREFERENCE_FIELD = :projects_sort
72  MAX_BUILD_TIMEOUT = 1.month
73
74  GL_REPOSITORY_TYPES = [Gitlab::GlRepository::PROJECT, Gitlab::GlRepository::WIKI, Gitlab::GlRepository::DESIGN].freeze
75
76  cache_markdown_field :description, pipeline: :description
77
78  default_value_for :packages_enabled, true
79  default_value_for :archived, false
80  default_value_for :resolve_outdated_diff_discussions, false
81  default_value_for(:repository_storage) do
82    Repository.pick_storage_shard
83  end
84
85  default_value_for(:shared_runners_enabled) { Gitlab::CurrentSettings.shared_runners_enabled }
86  default_value_for :issues_enabled, gitlab_config_features.issues
87  default_value_for :merge_requests_enabled, gitlab_config_features.merge_requests
88  default_value_for :builds_enabled, gitlab_config_features.builds
89  default_value_for :wiki_enabled, gitlab_config_features.wiki
90  default_value_for :snippets_enabled, gitlab_config_features.snippets
91  default_value_for :only_allow_merge_if_all_discussions_are_resolved, false
92  default_value_for :remove_source_branch_after_merge, true
93  default_value_for :autoclose_referenced_issues, true
94  default_value_for(:ci_config_path) { Gitlab::CurrentSettings.default_ci_config_path }
95
96  add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:projects_tokens_optional_encryption, default_enabled: true) ? :optional : :required }
97
98  before_validation :mark_remote_mirrors_for_removal, if: -> { RemoteMirror.table_exists? }
99
100  before_save :ensure_runners_token
101  before_validation :ensure_project_namespace_in_sync
102
103  after_save :update_project_statistics, if: :saved_change_to_namespace_id?
104
105  after_save :schedule_sync_event_worker, if: -> { saved_change_to_id? || saved_change_to_namespace_id? }
106
107  after_save :create_import_state, if: ->(project) { project.import? && project.import_state.nil? }
108
109  after_save :save_topics
110
111  after_create -> { create_or_load_association(:project_feature) }
112
113  after_create -> { create_or_load_association(:ci_cd_settings) }
114
115  after_create -> { create_or_load_association(:container_expiration_policy) }
116
117  after_create -> { create_or_load_association(:pages_metadatum) }
118
119  after_create :set_timestamps_for_create
120  after_update :update_forks_visibility_level
121
122  before_destroy :remove_private_deploy_keys
123
124  use_fast_destroy :build_trace_chunks
125
126  after_destroy :remove_exports
127
128  after_validation :check_pending_delete
129
130  # Storage specific hooks
131  after_initialize :use_hashed_storage
132  after_create :check_repository_absence!
133
134  has_many :project_topics, -> { order(:id) }, class_name: 'Projects::ProjectTopic'
135  has_many :topics, through: :project_topics, class_name: 'Projects::Topic'
136
137  attr_accessor :old_path_with_namespace
138  attr_accessor :template_name
139  attr_writer :pipeline_status
140  attr_accessor :skip_disk_validation
141  attr_writer :topic_list
142
143  alias_attribute :title, :name
144
145  # Relations
146  belongs_to :pool_repository
147  belongs_to :creator, class_name: 'User'
148  belongs_to :group, -> { where(type: Group.sti_name) }, foreign_key: 'namespace_id'
149  belongs_to :namespace
150  # Sync deletion via DB Trigger to ensure we do not have
151  # a project without a project_namespace (or vice-versa)
152  belongs_to :project_namespace, autosave: true, class_name: 'Namespaces::ProjectNamespace', foreign_key: 'project_namespace_id'
153  alias_method :parent, :namespace
154  alias_attribute :parent_id, :namespace_id
155
156  has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event'
157  has_many :boards
158
159  def self.integration_association_name(name)
160    "#{name}_integration"
161  end
162
163  # Project integrations
164  has_one :asana_integration, class_name: 'Integrations::Asana'
165  has_one :assembla_integration, class_name: 'Integrations::Assembla'
166  has_one :bamboo_integration, class_name: 'Integrations::Bamboo'
167  has_one :bugzilla_integration, class_name: 'Integrations::Bugzilla'
168  has_one :buildkite_integration, class_name: 'Integrations::Buildkite'
169  has_one :campfire_integration, class_name: 'Integrations::Campfire'
170  has_one :confluence_integration, class_name: 'Integrations::Confluence'
171  has_one :custom_issue_tracker_integration, class_name: 'Integrations::CustomIssueTracker'
172  has_one :datadog_integration, class_name: 'Integrations::Datadog'
173  has_one :discord_integration, class_name: 'Integrations::Discord'
174  has_one :drone_ci_integration, class_name: 'Integrations::DroneCi'
175  has_one :emails_on_push_integration, class_name: 'Integrations::EmailsOnPush'
176  has_one :ewm_integration, class_name: 'Integrations::Ewm'
177  has_one :external_wiki_integration, class_name: 'Integrations::ExternalWiki'
178  has_one :flowdock_integration, class_name: 'Integrations::Flowdock'
179  has_one :hangouts_chat_integration, class_name: 'Integrations::HangoutsChat'
180  has_one :irker_integration, class_name: 'Integrations::Irker'
181  has_one :jenkins_integration, class_name: 'Integrations::Jenkins'
182  has_one :jira_integration, class_name: 'Integrations::Jira'
183  has_one :mattermost_integration, class_name: 'Integrations::Mattermost'
184  has_one :mattermost_slash_commands_integration, class_name: 'Integrations::MattermostSlashCommands'
185  has_one :microsoft_teams_integration, class_name: 'Integrations::MicrosoftTeams'
186  has_one :mock_ci_integration, class_name: 'Integrations::MockCi'
187  has_one :mock_monitoring_integration, class_name: 'Integrations::MockMonitoring'
188  has_one :packagist_integration, class_name: 'Integrations::Packagist'
189  has_one :pipelines_email_integration, class_name: 'Integrations::PipelinesEmail'
190  has_one :pivotaltracker_integration, class_name: 'Integrations::Pivotaltracker'
191  has_one :prometheus_integration, class_name: 'Integrations::Prometheus', inverse_of: :project
192  has_one :pushover_integration, class_name: 'Integrations::Pushover'
193  has_one :redmine_integration, class_name: 'Integrations::Redmine'
194  has_one :shimo_integration, class_name: 'Integrations::Shimo'
195  has_one :slack_integration, class_name: 'Integrations::Slack'
196  has_one :slack_slash_commands_integration, class_name: 'Integrations::SlackSlashCommands'
197  has_one :teamcity_integration, class_name: 'Integrations::Teamcity'
198  has_one :unify_circuit_integration, class_name: 'Integrations::UnifyCircuit'
199  has_one :webex_teams_integration, class_name: 'Integrations::WebexTeams'
200  has_one :youtrack_integration, class_name: 'Integrations::Youtrack'
201  has_one :zentao_integration, class_name: 'Integrations::Zentao'
202
203  has_one :root_of_fork_network,
204          foreign_key: 'root_project_id',
205          inverse_of: :root_project,
206          class_name: 'ForkNetwork'
207  has_one :fork_network_member
208  has_one :fork_network, through: :fork_network_member
209  has_one :forked_from_project, through: :fork_network_member
210  has_many :forked_to_members, class_name: 'ForkNetworkMember', foreign_key: 'forked_from_project_id'
211  has_many :forks, through: :forked_to_members, source: :project, inverse_of: :forked_from_project
212  has_many :fork_network_projects, through: :fork_network, source: :projects
213
214  # Packages
215  has_many :packages, class_name: 'Packages::Package'
216  has_many :package_files, through: :packages, class_name: 'Packages::PackageFile'
217  # debian_distributions and associated component_files must be destroyed by ruby code in order to properly remove carrierwave uploads
218  has_many :debian_distributions, class_name: 'Packages::Debian::ProjectDistribution', dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
219
220  has_one :import_state, autosave: true, class_name: 'ProjectImportState', inverse_of: :project
221  has_one :import_export_upload, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
222  has_many :export_jobs, class_name: 'ProjectExportJob'
223  has_many :bulk_import_exports, class_name: 'BulkImports::Export', inverse_of: :project
224  has_one :project_repository, inverse_of: :project
225  has_one :tracing_setting, class_name: 'ProjectTracingSetting'
226  has_one :incident_management_setting, inverse_of: :project, class_name: 'IncidentManagement::ProjectIncidentManagementSetting'
227  has_one :error_tracking_setting, inverse_of: :project, class_name: 'ErrorTracking::ProjectErrorTrackingSetting'
228  has_one :metrics_setting, inverse_of: :project, class_name: 'ProjectMetricsSetting'
229  has_one :grafana_integration, inverse_of: :project
230  has_one :project_setting, inverse_of: :project, autosave: true
231  has_one :alerting_setting, inverse_of: :project, class_name: 'Alerting::ProjectAlertingSetting'
232  has_one :service_desk_setting, class_name: 'ServiceDeskSetting'
233
234  # Merge requests for target project should be removed with it
235  has_many :merge_requests, foreign_key: 'target_project_id', inverse_of: :target_project
236  has_many :merge_request_metrics, foreign_key: 'target_project', class_name: 'MergeRequest::Metrics', inverse_of: :target_project
237  has_many :source_of_merge_requests, foreign_key: 'source_project_id', class_name: 'MergeRequest'
238  has_many :issues
239  has_many :labels, class_name: 'ProjectLabel'
240  has_many :integrations
241  has_many :events
242  has_many :milestones
243  has_many :iterations
244
245  # Projects with a very large number of notes may time out destroying them
246  # through the foreign key. Additionally, the deprecated attachment uploader
247  # for notes requires us to use dependent: :destroy to avoid orphaning uploaded
248  # files.
249  #
250  # https://gitlab.com/gitlab-org/gitlab/-/issues/207222
251  has_many :notes, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
252
253  has_many :snippets, class_name: 'ProjectSnippet'
254  has_many :hooks, class_name: 'ProjectHook'
255  has_many :protected_branches
256  has_many :exported_protected_branches
257  has_many :protected_tags
258  has_many :repository_languages, -> { order "share DESC" }
259  has_many :designs, inverse_of: :project, class_name: 'DesignManagement::Design'
260
261  has_many :project_authorizations
262  has_many :authorized_users, through: :project_authorizations, source: :user, class_name: 'User'
263  has_many :project_members, -> { where(requested_at: nil) },
264    as: :source, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
265
266  alias_method :members, :project_members
267  has_many :users, through: :project_members
268
269  has_many :requesters, -> { where.not(requested_at: nil) },
270    as: :source, class_name: 'ProjectMember', dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
271  has_many :members_and_requesters, as: :source, class_name: 'ProjectMember'
272
273  has_many :deploy_keys_projects, inverse_of: :project
274  has_many :deploy_keys, through: :deploy_keys_projects
275  has_many :users_star_projects
276  has_many :starrers, through: :users_star_projects, source: :user
277  has_many :releases
278  has_many :lfs_objects_projects
279  has_many :lfs_objects, -> { distinct }, through: :lfs_objects_projects
280  has_many :lfs_file_locks
281  has_many :project_group_links
282  has_many :invited_groups, through: :project_group_links, source: :group
283  has_many :todos
284  has_many :notification_settings, as: :source, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
285
286  has_many :internal_ids
287
288  has_one :import_data, class_name: 'ProjectImportData', inverse_of: :project, autosave: true
289  has_one :project_feature, inverse_of: :project
290  has_one :statistics, class_name: 'ProjectStatistics'
291  has_one :feature_usage, class_name: 'ProjectFeatureUsage'
292
293  has_one :cluster_project, class_name: 'Clusters::Project'
294  has_many :clusters, through: :cluster_project, class_name: 'Clusters::Cluster'
295  has_many :kubernetes_namespaces, class_name: 'Clusters::KubernetesNamespace'
296  has_many :management_clusters, class_name: 'Clusters::Cluster', foreign_key: :management_project_id, inverse_of: :management_project
297  has_many :cluster_agents, class_name: 'Clusters::Agent'
298
299  has_many :prometheus_metrics
300  has_many :prometheus_alerts, inverse_of: :project
301  has_many :prometheus_alert_events, inverse_of: :project
302  has_many :self_managed_prometheus_alert_events, inverse_of: :project
303  has_many :metrics_users_starred_dashboards, class_name: 'Metrics::UsersStarredDashboard', inverse_of: :project
304
305  has_many :alert_management_alerts, class_name: 'AlertManagement::Alert', inverse_of: :project
306  has_many :alert_management_http_integrations, class_name: 'AlertManagement::HttpIntegration', inverse_of: :project
307
308  # Container repositories need to remove data from the container registry,
309  # which is not managed by the DB. Hence we're still using dependent: :destroy
310  # here.
311  has_many :container_repositories, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
312  has_one :container_expiration_policy, inverse_of: :project
313
314  has_many :commit_statuses
315  # The relation :all_pipelines is intended to be used when we want to get the
316  # whole list of pipelines associated to the project
317  has_many :all_pipelines, class_name: 'Ci::Pipeline', inverse_of: :project
318  # The relation :ci_pipelines includes all those that directly contribute to the
319  # latest status of a ref. This does not include dangling pipelines such as those
320  # from webide, child pipelines, etc.
321  has_many :ci_pipelines,
322          -> { ci_sources },
323          class_name: 'Ci::Pipeline',
324          inverse_of: :project
325  has_many :stages, class_name: 'Ci::Stage', inverse_of: :project
326  has_many :ci_refs, class_name: 'Ci::Ref', inverse_of: :project
327
328  # Ci::Build objects store data on the file system such as artifact files and
329  # build traces. Currently there's no efficient way of removing this data in
330  # bulk that doesn't involve loading the rows into memory. As a result we're
331  # still using `dependent: :destroy` here.
332  has_many :pending_builds, class_name: 'Ci::PendingBuild'
333  has_many :builds, class_name: 'Ci::Build', inverse_of: :project, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
334  has_many :processables, class_name: 'Ci::Processable', inverse_of: :project
335  has_many :build_trace_chunks, class_name: 'Ci::BuildTraceChunk', through: :builds, source: :trace_chunks
336  has_many :build_report_results, class_name: 'Ci::BuildReportResult', inverse_of: :project
337  has_many :job_artifacts, class_name: 'Ci::JobArtifact'
338  has_many :pipeline_artifacts, class_name: 'Ci::PipelineArtifact', inverse_of: :project
339  has_many :runner_projects, class_name: 'Ci::RunnerProject', inverse_of: :project
340  has_many :runners, through: :runner_projects, source: :runner, class_name: 'Ci::Runner'
341  has_many :variables, class_name: 'Ci::Variable'
342  has_many :triggers, class_name: 'Ci::Trigger'
343  has_many :environments
344  has_many :environments_for_dashboard, -> { from(with_rank.unfoldered.available, :environments).where('rank <= 3') }, class_name: 'Environment'
345  has_many :deployments
346  has_many :pipeline_schedules, class_name: 'Ci::PipelineSchedule'
347  has_many :project_deploy_tokens
348  has_many :deploy_tokens, through: :project_deploy_tokens
349  has_many :resource_groups, class_name: 'Ci::ResourceGroup', inverse_of: :project
350  has_many :freeze_periods, class_name: 'Ci::FreezePeriod', inverse_of: :project
351
352  has_one :auto_devops, class_name: 'ProjectAutoDevops', inverse_of: :project, autosave: true
353  has_many :custom_attributes, class_name: 'ProjectCustomAttribute'
354
355  has_many :project_badges, class_name: 'ProjectBadge'
356  has_one :ci_cd_settings, class_name: 'ProjectCiCdSetting', inverse_of: :project, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
357
358  has_many :remote_mirrors, inverse_of: :project
359  has_many :cycle_analytics_stages, class_name: 'Analytics::CycleAnalytics::ProjectStage', inverse_of: :project
360  has_many :value_streams, class_name: 'Analytics::CycleAnalytics::ProjectValueStream', inverse_of: :project
361
362  has_many :external_pull_requests, inverse_of: :project
363
364  has_many :sourced_pipelines, class_name: 'Ci::Sources::Pipeline', foreign_key: :source_project_id
365  has_many :source_pipelines, class_name: 'Ci::Sources::Pipeline', foreign_key: :project_id
366
367  has_many :import_failures, inverse_of: :project
368  has_many :jira_imports, -> { order 'jira_imports.created_at' }, class_name: 'JiraImportState', inverse_of: :project
369
370  has_many :daily_build_group_report_results, class_name: 'Ci::DailyBuildGroupReportResult'
371  has_many :ci_feature_usages, class_name: 'Projects::CiFeatureUsage'
372
373  has_many :repository_storage_moves, class_name: 'Projects::RepositoryStorageMove', inverse_of: :container
374
375  has_many :webide_pipelines, -> { webide_source }, class_name: 'Ci::Pipeline', inverse_of: :project
376  has_many :reviews, inverse_of: :project
377
378  has_many :terraform_states, class_name: 'Terraform::State', inverse_of: :project
379
380  # GitLab Pages
381  has_many :pages_domains
382  has_one  :pages_metadatum, class_name: 'ProjectPagesMetadatum', inverse_of: :project
383  # we need to clean up files, not only remove records
384  has_many :pages_deployments, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
385
386  # Can be too many records. We need to implement delete_all in batches.
387  # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/228637
388  has_many :product_analytics_events, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
389
390  has_many :operations_feature_flags, class_name: 'Operations::FeatureFlag'
391  has_one :operations_feature_flags_client, class_name: 'Operations::FeatureFlagsClient'
392  has_many :operations_feature_flags_user_lists, class_name: 'Operations::FeatureFlags::UserList'
393
394  has_many :error_tracking_errors, inverse_of: :project, class_name: 'ErrorTracking::Error'
395  has_many :error_tracking_client_keys, inverse_of: :project, class_name: 'ErrorTracking::ClientKey'
396
397  has_many :timelogs
398
399  has_one :ci_project_mirror, class_name: 'Ci::ProjectMirror'
400  has_many :sync_events, class_name: 'Projects::SyncEvent'
401
402  accepts_nested_attributes_for :variables, allow_destroy: true
403  accepts_nested_attributes_for :project_feature, update_only: true
404  accepts_nested_attributes_for :project_setting, update_only: true
405  accepts_nested_attributes_for :import_data
406  accepts_nested_attributes_for :auto_devops, update_only: true
407  accepts_nested_attributes_for :ci_cd_settings, update_only: true
408  accepts_nested_attributes_for :container_expiration_policy, update_only: true
409
410  accepts_nested_attributes_for :remote_mirrors,
411                                allow_destroy: true,
412                                reject_if: ->(attrs) { attrs[:id].blank? && attrs[:url].blank? }
413
414  accepts_nested_attributes_for :tracing_setting, update_only: true, allow_destroy: true
415  accepts_nested_attributes_for :incident_management_setting, update_only: true
416  accepts_nested_attributes_for :error_tracking_setting, update_only: true
417  accepts_nested_attributes_for :metrics_setting, update_only: true, allow_destroy: true
418  accepts_nested_attributes_for :grafana_integration, update_only: true, allow_destroy: true
419  accepts_nested_attributes_for :prometheus_integration, update_only: true
420  accepts_nested_attributes_for :alerting_setting, update_only: true
421
422  delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
423    :merge_requests_enabled?, :forking_enabled?, :issues_enabled?,
424    :pages_enabled?, :analytics_enabled?, :snippets_enabled?, :public_pages?, :private_pages?,
425    :merge_requests_access_level, :forking_access_level, :issues_access_level,
426    :wiki_access_level, :snippets_access_level, :builds_access_level,
427    :repository_access_level, :pages_access_level, :metrics_dashboard_access_level, :analytics_access_level,
428    :operations_enabled?, :operations_access_level, :security_and_compliance_access_level,
429    :container_registry_access_level, :container_registry_enabled?,
430    to: :project_feature, allow_nil: true
431  alias_method :container_registry_enabled, :container_registry_enabled?
432  delegate :show_default_award_emojis, :show_default_award_emojis=, :show_default_award_emojis?,
433    :warn_about_potentially_unwanted_characters, :warn_about_potentially_unwanted_characters=, :warn_about_potentially_unwanted_characters?,
434    to: :project_setting, allow_nil: true
435  delegate :scheduled?, :started?, :in_progress?, :failed?, :finished?,
436    prefix: :import, to: :import_state, allow_nil: true
437  delegate :squash_always?, :squash_never?, :squash_enabled_by_default?, :squash_readonly?, to: :project_setting
438  delegate :squash_option, :squash_option=, to: :project_setting
439  delegate :previous_default_branch, :previous_default_branch=, to: :project_setting
440  delegate :no_import?, to: :import_state, allow_nil: true
441  delegate :name, to: :owner, allow_nil: true, prefix: true
442  delegate :members, to: :team, prefix: true
443  delegate :add_user, :add_users, to: :team
444  delegate :add_guest, :add_reporter, :add_developer, :add_maintainer, :add_role, to: :team
445  delegate :group_runners_enabled, :group_runners_enabled=, to: :ci_cd_settings, allow_nil: true
446  delegate :root_ancestor, to: :namespace, allow_nil: true
447  delegate :last_pipeline, to: :commit, allow_nil: true
448  delegate :external_dashboard_url, to: :metrics_setting, allow_nil: true, prefix: true
449  delegate :dashboard_timezone, to: :metrics_setting, allow_nil: true, prefix: true
450  delegate :default_git_depth, :default_git_depth=, to: :ci_cd_settings, prefix: :ci, allow_nil: true
451  delegate :forward_deployment_enabled, :forward_deployment_enabled=, to: :ci_cd_settings, prefix: :ci, allow_nil: true
452  delegate :job_token_scope_enabled, :job_token_scope_enabled=, to: :ci_cd_settings, prefix: :ci, allow_nil: true
453  delegate :keep_latest_artifact, :keep_latest_artifact=, to: :ci_cd_settings, allow_nil: true
454  delegate :restrict_user_defined_variables, :restrict_user_defined_variables=, to: :ci_cd_settings, allow_nil: true
455  delegate :actual_limits, :actual_plan_name, to: :namespace, allow_nil: true
456  delegate :allow_merge_on_skipped_pipeline, :allow_merge_on_skipped_pipeline?,
457    :allow_merge_on_skipped_pipeline=, :has_confluence?, :has_shimo?,
458    to: :project_setting
459  delegate :active?, to: :prometheus_integration, allow_nil: true, prefix: true
460  delegate :merge_commit_template, :merge_commit_template=, to: :project_setting, allow_nil: true
461  delegate :squash_commit_template, :squash_commit_template=, to: :project_setting, allow_nil: true
462
463  delegate :log_jira_dvcs_integration_usage, :jira_dvcs_server_last_sync_at, :jira_dvcs_cloud_last_sync_at, to: :feature_usage
464
465  # Validations
466  validates :creator, presence: true, on: :create
467  validates :description, length: { maximum: 2000 }, allow_blank: true
468  validates :ci_config_path,
469    format: { without: %r{(\.{2}|\A/)},
470              message: _('cannot include leading slash or directory traversal.') },
471    length: { maximum: 255 },
472    allow_blank: true
473  validates :name,
474    presence: true,
475    length: { maximum: 255 },
476    format: { with: Gitlab::Regex.project_name_regex,
477              message: Gitlab::Regex.project_name_regex_message }
478  validates :path,
479    presence: true,
480    project_path: true,
481    length: { maximum: 255 }
482
483  validates :project_feature, presence: true
484
485  validates :namespace, presence: true
486  validates :project_namespace, presence: true, on: :create, if: -> { self.namespace && self.root_namespace.project_namespace_creation_enabled? }
487  validates :project_namespace, presence: true, on: :update, if: -> { self.project_namespace_id_changed?(to: nil) }
488  validates :name, uniqueness: { scope: :namespace_id }
489  validates :import_url, public_url: { schemes: ->(project) { project.persisted? ? VALID_MIRROR_PROTOCOLS : VALID_IMPORT_PROTOCOLS },
490                                       ports: ->(project) { project.persisted? ? VALID_MIRROR_PORTS : VALID_IMPORT_PORTS },
491                                       enforce_user: true }, if: [:external_import?, :import_url_changed?]
492  validates :star_count, numericality: { greater_than_or_equal_to: 0 }
493  validate :check_personal_projects_limit, on: :create
494  validate :check_repository_path_availability, on: :update, if: ->(project) { project.renamed? }
495  validate :visibility_level_allowed_by_group, if: :should_validate_visibility_level?
496  validate :visibility_level_allowed_as_fork, if: :should_validate_visibility_level?
497  validate :validate_pages_https_only, if: -> { changes.has_key?(:pages_https_only) }
498  validate :changing_shared_runners_enabled_is_allowed
499  validates :repository_storage,
500    presence: true,
501    inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
502  validates :variables, nested_attributes_duplicates: { scope: :environment_scope }
503  validates :bfg_object_map, file_size: { maximum: :max_attachment_size }
504  validates :max_artifacts_size, numericality: { only_integer: true, greater_than: 0, allow_nil: true }
505  validates :suggestion_commit_message, length: { maximum: 255 }
506
507  # Scopes
508  scope :pending_delete, -> { where(pending_delete: true) }
509  scope :without_deleted, -> { where(pending_delete: false) }
510
511  scope :with_storage_feature, ->(feature) do
512    where(arel_table[:storage_version].gteq(HASHED_STORAGE_FEATURES[feature]))
513  end
514  scope :without_storage_feature, ->(feature) do
515    where(arel_table[:storage_version].lt(HASHED_STORAGE_FEATURES[feature])
516        .or(arel_table[:storage_version].eq(nil)))
517  end
518  scope :with_unmigrated_storage, -> do
519    where(arel_table[:storage_version].lt(LATEST_STORAGE_VERSION)
520        .or(arel_table[:storage_version].eq(nil)))
521  end
522
523  # last_activity_at is throttled every minute, but last_repository_updated_at is updated with every push
524  scope :sorted_by_activity, -> { reorder(Arel.sql("GREATEST(COALESCE(last_activity_at, '1970-01-01'), COALESCE(last_repository_updated_at, '1970-01-01')) DESC")) }
525  scope :sorted_by_stars_desc, -> { reorder(self.arel_table['star_count'].desc) }
526  scope :sorted_by_stars_asc, -> { reorder(self.arel_table['star_count'].asc) }
527  # Sometimes queries (e.g. using CTEs) require explicit disambiguation with table name
528  scope :projects_order_id_asc, -> { reorder(self.arel_table['id'].asc) }
529  scope :projects_order_id_desc, -> { reorder(self.arel_table['id'].desc) }
530
531  scope :sorted_by_similarity_desc, -> (search, include_in_select: false) do
532    order_expression = Gitlab::Database::SimilarityScore.build_expression(search: search, rules: [
533      { column: arel_table["path"], multiplier: 1 },
534      { column: arel_table["name"], multiplier: 0.7 },
535      { column: arel_table["description"], multiplier: 0.2 }
536    ])
537
538    order = Gitlab::Pagination::Keyset::Order.build([
539      Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
540        attribute_name: 'similarity',
541        column_expression: order_expression,
542        order_expression: order_expression.desc,
543        order_direction: :desc,
544        distinct: false,
545        add_to_projections: true
546      ),
547      Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
548        attribute_name: 'id',
549        order_expression: Project.arel_table[:id].desc
550      )
551    ])
552
553    order.apply_cursor_conditions(reorder(order))
554  end
555
556  scope :with_packages, -> { joins(:packages) }
557  scope :in_namespace, ->(namespace_ids) { where(namespace_id: namespace_ids) }
558  scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
559  scope :joined, ->(user) { where.not(namespace_id: user.namespace_id) }
560  scope :starred_by, ->(user) { joins(:users_star_projects).where('users_star_projects.user_id': user.id) }
561  scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) }
562  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)) }
563  scope :archived, -> { where(archived: true) }
564  scope :non_archived, -> { where(archived: false) }
565  scope :with_push, -> { joins(:events).merge(Event.pushed_action) }
566  scope :with_project_feature, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id') }
567  scope :with_jira_dvcs_cloud, -> { joins(:feature_usage).merge(ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: true)) }
568  scope :with_jira_dvcs_server, -> { joins(:feature_usage).merge(ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: false)) }
569  scope :inc_routes, -> { includes(:route, namespace: :route) }
570  scope :with_statistics, -> { includes(:statistics) }
571  scope :with_namespace, -> { includes(:namespace) }
572  scope :with_import_state, -> { includes(:import_state) }
573  scope :include_project_feature, -> { includes(:project_feature) }
574  scope :include_integration, -> (integration_association_name) { includes(integration_association_name) }
575  scope :with_integration, -> (integration_class) { joins(:integrations).merge(integration_class.all) }
576  scope :with_active_integration, -> (integration_class) { with_integration(integration_class).merge(integration_class.active) }
577  scope :with_shared_runners, -> { where(shared_runners_enabled: true) }
578  scope :inside_path, ->(path) do
579    # We need routes alias rs for JOIN so it does not conflict with
580    # includes(:route) which we use in ProjectsFinder.
581    joins("INNER JOIN routes rs ON rs.source_id = projects.id AND rs.source_type = 'Project'")
582      .where('rs.path LIKE ?', "#{sanitize_sql_like(path)}/%")
583  end
584
585  scope :with_feature_enabled, ->(feature) {
586    with_project_feature.merge(ProjectFeature.with_feature_enabled(feature))
587  }
588
589  scope :with_feature_access_level, ->(feature, level) {
590    with_project_feature.merge(ProjectFeature.with_feature_access_level(feature, level))
591  }
592
593  # Picks projects which use the given programming language
594  scope :with_programming_language, ->(language_name) do
595    lang_id_query = ProgrammingLanguage
596        .with_name_case_insensitive(language_name)
597        .select(:id)
598
599    joins(:repository_languages)
600        .where(repository_languages: { programming_language_id: lang_id_query })
601  end
602
603  scope :service_desk_enabled, -> { where(service_desk_enabled: true) }
604  scope :with_builds_enabled, -> { with_feature_enabled(:builds) }
605  scope :with_issues_enabled, -> { with_feature_enabled(:issues) }
606  scope :with_issues_available_for_user, ->(current_user) { with_feature_available_for_user(:issues, current_user) }
607  scope :with_merge_requests_available_for_user, ->(current_user) { with_feature_available_for_user(:merge_requests, current_user) }
608  scope :with_issues_or_mrs_available_for_user, -> (user) do
609    with_issues_available_for_user(user).or(with_merge_requests_available_for_user(user))
610  end
611  scope :with_merge_requests_enabled, -> { with_feature_enabled(:merge_requests) }
612  scope :with_remote_mirrors, -> { joins(:remote_mirrors).where(remote_mirrors: { enabled: true }) }
613  scope :with_limit, -> (maximum) { limit(maximum) }
614
615  scope :with_group_runners_enabled, -> do
616    joins(:ci_cd_settings)
617    .where(project_ci_cd_settings: { group_runners_enabled: true })
618  end
619
620  scope :with_pages_deployed, -> do
621    joins(:pages_metadatum).merge(ProjectPagesMetadatum.deployed)
622  end
623
624  scope :pages_metadata_not_migrated, -> do
625    left_outer_joins(:pages_metadatum)
626      .where(project_pages_metadata: { project_id: nil })
627  end
628
629  scope :with_api_commit_entity_associations, -> {
630    preload(:project_feature, :route, namespace: [:route, :owner])
631  }
632
633  scope :imported_from, -> (type) { where(import_type: type) }
634  scope :with_tracing_enabled, -> { joins(:tracing_setting) }
635  scope :with_enabled_error_tracking, -> { joins(:error_tracking_setting).where(project_error_tracking_settings: { enabled: true }) }
636
637  scope :with_service_desk_key, -> (key) do
638    # project_key is not indexed for now
639    # see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/24063#note_282435524 for details
640    joins(:service_desk_setting).where('service_desk_settings.project_key' => key)
641  end
642
643  scope :with_topic, ->(topic_name) do
644    topic = Projects::Topic.find_by_name(topic_name)
645
646    topic ? where(id: topic.project_topics.select(:project_id)) : none
647  end
648
649  enum auto_cancel_pending_pipelines: { disabled: 0, enabled: 1 }
650
651  chronic_duration_attr :build_timeout_human_readable, :build_timeout,
652    default: 3600, error_message: _('Maximum job timeout has a value which could not be accepted')
653
654  validates :build_timeout, allow_nil: true,
655                            numericality: { greater_than_or_equal_to: 10.minutes,
656                                            less_than: MAX_BUILD_TIMEOUT,
657                                            only_integer: true,
658                                            message: _('needs to be between 10 minutes and 1 month') }
659
660  # Used by Projects::CleanupService to hold a map of rewritten object IDs
661  mount_uploader :bfg_object_map, AttachmentUploader
662
663  def self.with_api_entity_associations
664    preload(:project_feature, :route, :topics, :group, :timelogs, namespace: [:route, :owner])
665  end
666
667  def self.with_web_entity_associations
668    preload(:project_feature, :route, :creator, group: :parent, namespace: [:route, :owner])
669  end
670
671  def self.eager_load_namespace_and_owner
672    includes(namespace: :owner)
673  end
674
675  # Returns a collection of projects that is either public or visible to the
676  # logged in user.
677  def self.public_or_visible_to_user(user = nil, min_access_level = nil)
678    min_access_level = nil if user&.can_read_all_resources?
679
680    return public_to_user unless user
681
682    if user.is_a?(DeployToken)
683      user.accessible_projects
684    else
685      where('EXISTS (?) OR projects.visibility_level IN (?)',
686            user.authorizations_for_projects(min_access_level: min_access_level),
687            Gitlab::VisibilityLevel.levels_for_user(user))
688    end
689  end
690
691  def self.with_feature_available_for_user(feature, user)
692    with_project_feature.merge(ProjectFeature.with_feature_available_for_user(feature, user))
693  end
694
695  def self.projects_user_can(projects, user, action)
696    projects = where(id: projects)
697
698    DeclarativePolicy.user_scope do
699      projects.select { |project| Ability.allowed?(user, action, project) }
700    end
701  end
702
703  # This scope returns projects where user has access to both the project and the feature.
704  def self.filter_by_feature_visibility(feature, user)
705    with_feature_available_for_user(feature, user)
706      .public_or_visible_to_user(
707        user,
708        ProjectFeature.required_minimum_access_level_for_private_project(feature)
709      )
710  end
711
712  def self.wrap_with_cte(collection)
713    cte = Gitlab::SQL::CTE.new(:projects_cte, collection)
714    Project.with(cte.to_arel).from(cte.alias_to(Project.arel_table))
715  end
716
717  scope :active, -> { joins(:issues, :notes, :merge_requests).order('issues.created_at, notes.created_at, merge_requests.created_at DESC') }
718  scope :abandoned, -> { where('projects.last_activity_at < ?', 6.months.ago) }
719
720  scope :excluding_project, ->(project) { where.not(id: project) }
721
722  # We require an alias to the project_mirror_data_table in order to use import_state in our queries
723  scope :joins_import_state, -> { joins("INNER JOIN project_mirror_data import_state ON import_state.project_id = projects.id") }
724  scope :for_group, -> (group) { where(group: group) }
725  scope :for_group_and_its_subgroups, ->(group) { where(namespace_id: group.self_and_descendants.select(:id)) }
726
727  class << self
728    # Searches for a list of projects based on the query given in `query`.
729    #
730    # On PostgreSQL this method uses "ILIKE" to perform a case-insensitive
731    # search.
732    #
733    # query - The search query as a String.
734    def search(query, include_namespace: false)
735      if include_namespace
736        joins(:route).fuzzy_search(query, [Route.arel_table[:path], Route.arel_table[:name], :description])
737      else
738        fuzzy_search(query, [:path, :name, :description])
739      end
740    end
741
742    def search_by_title(query)
743      non_archived.fuzzy_search(query, [:name])
744    end
745
746    def visibility_levels
747      Gitlab::VisibilityLevel.options
748    end
749
750    def sort_by_attribute(method)
751      case method.to_s
752      when 'storage_size_desc'
753        # storage_size is a joined column so we need to
754        # pass a string to avoid AR adding the table name
755        reorder('project_statistics.storage_size DESC, projects.id DESC')
756      when 'latest_activity_desc'
757        reorder(self.arel_table['last_activity_at'].desc)
758      when 'latest_activity_asc'
759        reorder(self.arel_table['last_activity_at'].asc)
760      when 'stars_desc'
761        sorted_by_stars_desc
762      when 'stars_asc'
763        sorted_by_stars_asc
764      else
765        order_by(method)
766      end
767    end
768
769    def reference_pattern
770      %r{
771        (?<!#{Gitlab::PathRegex::PATH_START_CHAR})
772        ((?<namespace>#{Gitlab::PathRegex::FULL_NAMESPACE_FORMAT_REGEX})\/)?
773        (?<project>#{Gitlab::PathRegex::PROJECT_PATH_FORMAT_REGEX})
774      }x
775    end
776
777    def reference_postfix
778      '>'
779    end
780
781    def reference_postfix_escaped
782      '&gt;'
783    end
784
785    # Pattern used to extract `namespace/project>` project references from text.
786    # '>' or its escaped form ('&gt;') are checked for because '>' is sometimes escaped
787    # when the reference comes from an external source.
788    def markdown_reference_pattern
789      @markdown_reference_pattern ||=
790        %r{
791          #{reference_pattern}
792          (#{reference_postfix}|#{reference_postfix_escaped})
793        }x
794    end
795
796    def trending
797      joins('INNER JOIN trending_projects ON projects.id = trending_projects.project_id')
798        .reorder('trending_projects.id ASC')
799    end
800
801    def cached_count
802      Rails.cache.fetch('total_project_count', expires_in: 5.minutes) do
803        Project.count
804      end
805    end
806
807    def group_ids
808      joins(:namespace).where(namespaces: { type: Group.sti_name }).select(:namespace_id)
809    end
810
811    # Returns ids of projects with issuables available for given user
812    #
813    # Used on queries to find milestones or labels which user can see
814    # For example: Milestone.where(project_id: ids_with_issuables_available_for(user))
815    def ids_with_issuables_available_for(user)
816      with_issues_enabled = with_issues_available_for_user(user).select(:id)
817      with_merge_requests_enabled = with_merge_requests_available_for_user(user).select(:id)
818
819      from_union([with_issues_enabled, with_merge_requests_enabled]).select(:id)
820    end
821
822    def find_by_url(url)
823      uri = URI(url)
824
825      return unless uri.host == Gitlab.config.gitlab.host
826
827      match = Rails.application.routes.recognize_path(url)
828
829      return if match[:unmatched_route].present?
830      return if match[:namespace_id].blank? || match[:id].blank?
831
832      find_by_full_path(match.values_at(:namespace_id, :id).join("/"))
833    rescue ActionController::RoutingError, URI::InvalidURIError
834      nil
835    end
836
837    def without_integration(integration)
838      integrations = Integration
839        .select('1')
840        .where("#{Integration.table_name}.project_id = projects.id")
841        .where(type: integration.type)
842
843      Project
844        .where('NOT EXISTS (?)', integrations)
845        .where(pending_delete: false)
846        .where(archived: false)
847    end
848  end
849
850  def initialize(attributes = nil)
851    # We can't use default_value_for because the database has a default
852    # value of 0 for visibility_level. If someone attempts to create a
853    # private project, default_value_for will assume that the
854    # visibility_level hasn't changed and will use the application
855    # setting default, which could be internal or public. For projects
856    # inside a private group, those levels are invalid.
857    #
858    # To fix the problem, we assign the actual default in the application if
859    # no explicit visibility has been initialized.
860    attributes ||= {}
861
862    unless visibility_attribute_present?(attributes)
863      attributes[:visibility_level] = Gitlab::CurrentSettings.default_project_visibility
864    end
865
866    super
867  end
868
869  def parent_loaded?
870    association(:namespace).loaded?
871  end
872
873  def project_setting
874    super.presence || build_project_setting
875  end
876
877  def all_pipelines
878    if builds_enabled?
879      super
880    else
881      super.external
882    end
883  end
884
885  def ci_pipelines
886    if builds_enabled?
887      super
888    else
889      super.external
890    end
891  end
892
893  def active_webide_pipelines(user:)
894    webide_pipelines.running_or_pending.for_user(user)
895  end
896
897  def default_pipeline_lock
898    if keep_latest_artifacts_available?
899      return :artifacts_locked
900    end
901
902    :unlocked
903  end
904
905  def autoclose_referenced_issues
906    return true if super.nil?
907
908    super
909  end
910
911  def preload_protected_branches
912    preloader = ActiveRecord::Associations::Preloader.new
913    preloader.preload(self, protected_branches: [:push_access_levels, :merge_access_levels])
914  end
915
916  # returns all ancestor-groups upto but excluding the given namespace
917  # when no namespace is given, all ancestors upto the top are returned
918  def ancestors_upto(top = nil, hierarchy_order: nil)
919    Gitlab::ObjectHierarchy.new(Group.where(id: namespace_id))
920      .base_and_ancestors(upto: top, hierarchy_order: hierarchy_order)
921  end
922
923  def ancestors(hierarchy_order: nil)
924    if Feature.enabled?(:linear_project_ancestors, self, default_enabled: :yaml)
925      group&.self_and_ancestors(hierarchy_order: hierarchy_order) || Group.none
926    else
927      ancestors_upto(hierarchy_order: hierarchy_order)
928    end
929  end
930
931  def ancestors_upto_ids(...)
932    ancestors_upto(...).pluck(:id)
933  end
934
935  def emails_disabled?
936    strong_memoize(:emails_disabled) do
937      # disabling in the namespace overrides the project setting
938      super || namespace.emails_disabled?
939    end
940  end
941
942  override :lfs_enabled?
943  def lfs_enabled?
944    return namespace.lfs_enabled? if self[:lfs_enabled].nil?
945
946    self[:lfs_enabled] && Gitlab.config.lfs.enabled
947  end
948
949  alias_method :lfs_enabled, :lfs_enabled?
950
951  def auto_devops_enabled?
952    if auto_devops&.enabled.nil?
953      has_auto_devops_implicitly_enabled?
954    else
955      auto_devops.enabled?
956    end
957  end
958
959  def has_auto_devops_implicitly_enabled?
960    auto_devops_config = first_auto_devops_config
961
962    auto_devops_config[:scope] != :project && auto_devops_config[:status]
963  end
964
965  def has_auto_devops_implicitly_disabled?
966    auto_devops_config = first_auto_devops_config
967
968    auto_devops_config[:scope] != :project && !auto_devops_config[:status]
969  end
970
971  def has_packages?(package_type)
972    packages.where(package_type: package_type).exists?
973  end
974
975  def first_auto_devops_config
976    return namespace.first_auto_devops_config if auto_devops&.enabled.nil?
977
978    { scope: :project, status: auto_devops&.enabled || Feature.enabled?(:force_autodevops_on_by_default, self) }
979  end
980
981  def unlink_forks_upon_visibility_decrease_enabled?
982    Feature.enabled?(:unlink_fork_network_upon_visibility_decrease, self, default_enabled: true)
983  end
984
985  def context_commits_enabled?
986    Feature.enabled?(:context_commits, default_enabled: true)
987  end
988
989  # LFS and hashed repository storage are required for using Design Management.
990  def design_management_enabled?
991    lfs_enabled? && hashed_storage?(:repository)
992  end
993
994  def team
995    @team ||= ProjectTeam.new(self)
996  end
997
998  def repository
999    @repository ||= Gitlab::GlRepository::PROJECT.repository_for(self)
1000  end
1001
1002  def design_repository
1003    strong_memoize(:design_repository) do
1004      Gitlab::GlRepository::DESIGN.repository_for(self)
1005    end
1006  end
1007
1008  # Because we use default_value_for we need to be sure
1009  # packages_enabled= method does exist even if we rollback migration.
1010  # Otherwise many tests from spec/migrations will fail.
1011  def packages_enabled=(value)
1012    if has_attribute?(:packages_enabled)
1013      write_attribute(:packages_enabled, value)
1014    end
1015  end
1016
1017  def cleanup
1018    @repository = nil
1019  end
1020
1021  alias_method :reload_repository!, :cleanup
1022
1023  def container_registry_url
1024    if Gitlab.config.registry.enabled
1025      "#{Gitlab.config.registry.host_port}/#{full_path.downcase}"
1026    end
1027  end
1028
1029  def has_container_registry_tags?
1030    return @images if defined?(@images)
1031
1032    @images = container_repositories.to_a.any?(&:has_tags?) ||
1033      has_root_container_repository_tags?
1034  end
1035
1036  # ref can't be HEAD, can only be branch/tag name
1037  def latest_successful_build_for_ref(job_name, ref = default_branch)
1038    return unless ref
1039
1040    latest_pipeline = ci_pipelines.latest_successful_for_ref(ref)
1041    return unless latest_pipeline
1042
1043    latest_pipeline.build_with_artifacts_in_self_and_descendants(job_name)
1044  end
1045
1046  def latest_successful_build_for_sha(job_name, sha)
1047    return unless sha
1048
1049    latest_pipeline = ci_pipelines.latest_successful_for_sha(sha)
1050    return unless latest_pipeline
1051
1052    latest_pipeline.build_with_artifacts_in_self_and_descendants(job_name)
1053  end
1054
1055  def latest_successful_build_for_ref!(job_name, ref = default_branch)
1056    latest_successful_build_for_ref(job_name, ref) || raise(ActiveRecord::RecordNotFound, "Couldn't find job #{job_name}")
1057  end
1058
1059  def latest_pipeline(ref = default_branch, sha = nil)
1060    ref = ref.presence || default_branch
1061    sha ||= commit(ref)&.sha
1062    return unless sha
1063
1064    ci_pipelines.newest_first(ref: ref, sha: sha).take
1065  end
1066
1067  def merge_base_commit(first_commit_id, second_commit_id)
1068    sha = repository.merge_base(first_commit_id, second_commit_id)
1069    commit_by(oid: sha) if sha
1070  end
1071
1072  def saved?
1073    id && persisted?
1074  end
1075
1076  def import_status
1077    import_state&.status || 'none'
1078  end
1079
1080  def jira_import_status
1081    latest_jira_import&.status || 'initial'
1082  end
1083
1084  def human_import_status_name
1085    import_state&.human_status_name || 'none'
1086  end
1087
1088  def add_import_job
1089    job_id =
1090      if forked?
1091        RepositoryForkWorker.perform_async(id)
1092      else
1093        RepositoryImportWorker.perform_async(self.id)
1094      end
1095
1096    log_import_activity(job_id)
1097
1098    job_id
1099  end
1100
1101  def log_import_activity(job_id, type: :import)
1102    job_type = type.to_s.capitalize
1103
1104    if job_id
1105      Gitlab::AppLogger.info("#{job_type} job scheduled for #{full_path} with job ID #{job_id}.")
1106    else
1107      Gitlab::AppLogger.error("#{job_type} job failed to create for #{full_path}.")
1108    end
1109  end
1110
1111  def reset_cache_and_import_attrs
1112    run_after_commit do
1113      ProjectCacheWorker.perform_async(self.id)
1114    end
1115
1116    import_state.update(last_error: nil)
1117    remove_import_data
1118  end
1119
1120  # This method is overridden in EE::Project model
1121  def remove_import_data
1122    import_data&.destroy
1123  end
1124
1125  def ci_config_path=(value)
1126    # Strip all leading slashes so that //foo -> foo
1127    super(value&.delete("\0"))
1128  end
1129
1130  def import_url=(value)
1131    if Gitlab::UrlSanitizer.valid?(value)
1132      import_url = Gitlab::UrlSanitizer.new(value)
1133      super(import_url.sanitized_url)
1134
1135      credentials = import_url.credentials.to_h.transform_values { |value| CGI.unescape(value.to_s) }
1136      create_or_update_import_data(credentials: credentials)
1137    else
1138      super(value)
1139    end
1140  end
1141
1142  def import_url
1143    if import_data && super.present?
1144      import_url = Gitlab::UrlSanitizer.new(super, credentials: import_data.credentials)
1145      import_url.full_url
1146    else
1147      super
1148    end
1149  rescue StandardError
1150    super
1151  end
1152
1153  def valid_import_url?
1154    valid?(:import_url) || errors.messages[:import_url].nil?
1155  end
1156
1157  def create_or_update_import_data(data: nil, credentials: nil)
1158    return if data.nil? && credentials.nil?
1159
1160    project_import_data = import_data || build_import_data
1161
1162    project_import_data.merge_data(data.to_h)
1163    project_import_data.merge_credentials(credentials.to_h)
1164
1165    project_import_data
1166  end
1167
1168  def import?
1169    external_import? || forked? || gitlab_project_import? || jira_import? || bare_repository_import? || gitlab_project_migration?
1170  end
1171
1172  def external_import?
1173    import_url.present?
1174  end
1175
1176  def safe_import_url
1177    Gitlab::UrlSanitizer.new(import_url).masked_url
1178  end
1179
1180  def bare_repository_import?
1181    import_type == 'bare_repository'
1182  end
1183
1184  def jira_import?
1185    import_type == 'jira' && latest_jira_import.present?
1186  end
1187
1188  def gitlab_project_import?
1189    import_type == 'gitlab_project'
1190  end
1191
1192  def gitlab_project_migration?
1193    import_type == 'gitlab_project_migration'
1194  end
1195
1196  def gitea_import?
1197    import_type == 'gitea'
1198  end
1199
1200  def github_import?
1201    import_type == 'github'
1202  end
1203
1204  def github_enterprise_import?
1205    github_import? &&
1206      URI.parse(import_url).host != URI.parse(Octokit::Default::API_ENDPOINT).host
1207  end
1208
1209  def has_remote_mirror?
1210    remote_mirror_available? && remote_mirrors.enabled.exists?
1211  end
1212
1213  def updating_remote_mirror?
1214    remote_mirrors.enabled.started.exists?
1215  end
1216
1217  def update_remote_mirrors
1218    return unless remote_mirror_available?
1219
1220    remote_mirrors.enabled.each(&:sync)
1221  end
1222
1223  def mark_stuck_remote_mirrors_as_failed!
1224    remote_mirrors.stuck.update_all(
1225      update_status: :failed,
1226      last_error: _('The remote mirror took to long to complete.'),
1227      last_update_at: Time.current
1228    )
1229  end
1230
1231  def mark_remote_mirrors_for_removal
1232    remote_mirrors.each(&:mark_for_delete_if_blank_url)
1233  end
1234
1235  def remote_mirror_available?
1236    remote_mirror_available_overridden ||
1237      ::Gitlab::CurrentSettings.mirror_available
1238  end
1239
1240  def check_personal_projects_limit
1241    # Since this method is called as validation hook, `creator` might not be
1242    # present. Since the validation for that will fail, we can just return
1243    # early.
1244    return if !creator || creator.can_create_project? ||
1245        namespace.kind == 'group'
1246
1247    limit = creator.projects_limit
1248    error =
1249      if limit == 0
1250        _('Personal project creation is not allowed. Please contact your administrator with questions')
1251      else
1252        _('Your project limit is %{limit} projects! Please contact your administrator to increase it')
1253      end
1254
1255    self.errors.add(:limit_reached, error % { limit: limit })
1256  end
1257
1258  def should_validate_visibility_level?
1259    new_record? || changes.has_key?(:visibility_level)
1260  end
1261
1262  def visibility_level_allowed_by_group
1263    return if visibility_level_allowed_by_group?
1264
1265    level_name = Gitlab::VisibilityLevel.level_name(self.visibility_level).downcase
1266    group_level_name = Gitlab::VisibilityLevel.level_name(self.group.visibility_level).downcase
1267    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 })
1268  end
1269
1270  def visibility_level_allowed_as_fork
1271    return if visibility_level_allowed_as_fork?
1272
1273    level_name = Gitlab::VisibilityLevel.level_name(self.visibility_level).downcase
1274    self.errors.add(:visibility_level, _("%{level_name} is not allowed since the fork source project has lower visibility.") % { level_name: level_name })
1275  end
1276
1277  def pages_https_only
1278    return false unless Gitlab.config.pages.external_https
1279
1280    super
1281  end
1282
1283  def pages_https_only?
1284    return false unless Gitlab.config.pages.external_https
1285
1286    super
1287  end
1288
1289  def validate_pages_https_only
1290    return unless pages_https_only?
1291
1292    unless pages_domains.all?(&:https?)
1293      errors.add(:pages_https_only, _("cannot be enabled unless all domains have TLS certificates"))
1294    end
1295  end
1296
1297  def changing_shared_runners_enabled_is_allowed
1298    return unless new_record? || changes.has_key?(:shared_runners_enabled)
1299
1300    if shared_runners_setting_conflicting_with_group?
1301      errors.add(:shared_runners_enabled, _('cannot be enabled because parent group does not allow it'))
1302    end
1303  end
1304
1305  def shared_runners_setting_conflicting_with_group?
1306    shared_runners_enabled && group&.shared_runners_setting == Namespace::SR_DISABLED_AND_UNOVERRIDABLE
1307  end
1308
1309  def reconcile_shared_runners_setting!
1310    if shared_runners_setting_conflicting_with_group?
1311      self.shared_runners_enabled = false
1312    end
1313  end
1314
1315  def to_param
1316    if persisted? && errors.include?(:path)
1317      path_was
1318    else
1319      path
1320    end
1321  end
1322
1323  # Produce a valid reference (see Referable#to_reference)
1324  #
1325  # NB: For projects, all references are 'full' - i.e. they all include the
1326  # full_path, rather than just the project name. For this reason, we ignore
1327  # the value of `full:` passed to this method, which is part of the Referable
1328  # interface.
1329  def to_reference(from = nil, full: false)
1330    base = to_reference_base(from, full: true)
1331    "#{base}#{self.class.reference_postfix}"
1332  end
1333
1334  # `from` argument can be a Namespace or Project.
1335  def to_reference_base(from = nil, full: false)
1336    if full || cross_namespace_reference?(from)
1337      full_path
1338    elsif cross_project_reference?(from)
1339      path
1340    end
1341  end
1342
1343  def to_human_reference(from = nil)
1344    if cross_namespace_reference?(from)
1345      name_with_namespace
1346    elsif cross_project_reference?(from)
1347      name
1348    end
1349  end
1350
1351  def readme_url
1352    readme_path = repository.readme_path
1353    if readme_path
1354      Gitlab::Routing.url_helpers.project_blob_url(self, File.join(default_branch, readme_path))
1355    end
1356  end
1357
1358  def new_issuable_address(author, address_type)
1359    return unless Gitlab::IncomingEmail.supports_issue_creation? && author
1360
1361    # check since this can come from a request parameter
1362    return unless %w(issue merge_request).include?(address_type)
1363
1364    author.ensure_incoming_email_token!
1365
1366    suffix = address_type.dasherize
1367
1368    # example: incoming+h5bp-html5-boilerplate-8-1234567890abcdef123456789-issue@localhost.com
1369    # example: incoming+h5bp-html5-boilerplate-8-1234567890abcdef123456789-merge-request@localhost.com
1370    Gitlab::IncomingEmail.reply_address("#{full_path_slug}-#{project_id}-#{author.incoming_email_token}-#{suffix}")
1371  end
1372
1373  def build_commit_note(commit)
1374    notes.new(commit_id: commit.id, noteable_type: 'Commit')
1375  end
1376
1377  def last_activity
1378    last_event
1379  end
1380
1381  def last_activity_date
1382    [last_activity_at, last_repository_updated_at, updated_at].compact.max
1383  end
1384
1385  def project_id
1386    self.id
1387  end
1388
1389  def get_issue(issue_id, current_user)
1390    issue = IssuesFinder.new(current_user, project_id: id).find_by(iid: issue_id) if issues_enabled?
1391
1392    if issue
1393      issue
1394    elsif external_issue_tracker
1395      ExternalIssue.new(issue_id, self)
1396    end
1397  end
1398
1399  def issue_exists?(issue_id)
1400    get_issue(issue_id)
1401  end
1402
1403  def external_issue_reference_pattern
1404    external_issue_tracker.class.reference_pattern(only_long: issues_enabled?)
1405  end
1406
1407  def default_issues_tracker?
1408    !external_issue_tracker
1409  end
1410
1411  def external_issue_tracker
1412    cache_has_external_issue_tracker if has_external_issue_tracker.nil?
1413
1414    return unless has_external_issue_tracker?
1415
1416    @external_issue_tracker ||= integrations.external_issue_trackers.first
1417  end
1418
1419  def external_references_supported?
1420    external_issue_tracker&.support_cross_reference?
1421  end
1422
1423  def has_wiki?
1424    wiki_enabled? || has_external_wiki?
1425  end
1426
1427  def external_wiki
1428    cache_has_external_wiki if has_external_wiki.nil?
1429
1430    return unless has_external_wiki?
1431
1432    @external_wiki ||= integrations.external_wikis.first
1433  end
1434
1435  def find_or_initialize_integrations
1436    Integration
1437      .available_integration_names
1438      .difference(disabled_integrations)
1439      .map { find_or_initialize_integration(_1) }
1440      .sort_by(&:title)
1441  end
1442
1443  def disabled_integrations
1444    disabled_integrations = []
1445    disabled_integrations << 'shimo' unless Feature.enabled?(:shimo_integration, self)
1446    disabled_integrations
1447  end
1448
1449  def find_or_initialize_integration(name)
1450    return if disabled_integrations.include?(name)
1451
1452    find_integration(integrations, name) || build_from_instance(name) || build_integration(name)
1453  end
1454
1455  # rubocop: disable CodeReuse/ServiceClass
1456  def create_labels
1457    Label.templates.each do |label|
1458      params = label.attributes.except('id', 'template', 'created_at', 'updated_at', 'type')
1459      Labels::FindOrCreateService.new(nil, self, params).execute(skip_authorization: true)
1460    end
1461  end
1462  # rubocop: enable CodeReuse/ServiceClass
1463
1464  def ci_integrations
1465    integrations.where(category: :ci)
1466  end
1467
1468  def ci_integration
1469    @ci_integration ||= ci_integrations.reorder(nil).find_by(active: true)
1470  end
1471
1472  def avatar_in_git
1473    repository.avatar
1474  end
1475
1476  def avatar_url(**args)
1477    Gitlab::Routing.url_helpers.project_avatar_url(self) if avatar_in_git
1478  end
1479
1480  # For compatibility with old code
1481  def code
1482    path
1483  end
1484
1485  def all_clusters
1486    group_clusters = Clusters::Cluster.joins(:groups).where(cluster_groups: { group_id: ancestors_upto } )
1487    instance_clusters = Clusters::Cluster.instance_type
1488
1489    Clusters::Cluster.from_union([clusters, group_clusters, instance_clusters])
1490  end
1491
1492  def items_for(entity)
1493    case entity
1494    when 'issue' then
1495      issues
1496    when 'merge_request' then
1497      merge_requests
1498    end
1499  end
1500
1501  # rubocop: disable CodeReuse/ServiceClass
1502  def send_move_instructions(old_path_with_namespace)
1503    # New project path needs to be committed to the DB or notification will
1504    # retrieve stale information
1505    run_after_commit do
1506      NotificationService.new.project_was_moved(self, old_path_with_namespace)
1507    end
1508  end
1509  # rubocop: enable CodeReuse/ServiceClass
1510
1511  def owner
1512    group || namespace.try(:owner)
1513  end
1514
1515  def default_owner
1516    obj = owner
1517
1518    if obj.respond_to?(:default_owner)
1519      obj.default_owner
1520    else
1521      obj
1522    end
1523  end
1524
1525  # rubocop: disable CodeReuse/ServiceClass
1526  def execute_hooks(data, hooks_scope = :push_hooks)
1527    run_after_commit_or_now do
1528      hooks.hooks_for(hooks_scope).select_active(hooks_scope, data).each do |hook|
1529        hook.async_execute(data, hooks_scope.to_s)
1530      end
1531      SystemHooksService.new.execute_hooks(data, hooks_scope)
1532    end
1533  end
1534  # rubocop: enable CodeReuse/ServiceClass
1535
1536  def execute_integrations(data, hooks_scope = :push_hooks)
1537    # Call only service hooks that are active for this scope
1538    run_after_commit_or_now do
1539      integrations.public_send(hooks_scope).each do |integration| # rubocop:disable GitlabSecurity/PublicSend
1540        integration.async_execute(data)
1541      end
1542    end
1543  end
1544
1545  def has_active_hooks?(hooks_scope = :push_hooks)
1546    hooks.hooks_for(hooks_scope).any? || SystemHook.hooks_for(hooks_scope).any? || Gitlab::FileHook.any?
1547  end
1548
1549  def has_active_integrations?(hooks_scope = :push_hooks)
1550    integrations.public_send(hooks_scope).any? # rubocop:disable GitlabSecurity/PublicSend
1551  end
1552
1553  def feature_usage
1554    super.presence || build_feature_usage
1555  end
1556
1557  def forked?
1558    fork_network && fork_network.root_project != self
1559  end
1560
1561  def fork_source
1562    return unless forked?
1563
1564    forked_from_project || fork_network&.root_project
1565  end
1566
1567  def lfs_objects_for_repository_types(*types)
1568    LfsObject
1569      .joins(:lfs_objects_projects)
1570      .where(lfs_objects_projects: { project: self, repository_type: types })
1571  end
1572
1573  def lfs_objects_oids(oids: [])
1574    oids(lfs_objects, oids: oids)
1575  end
1576
1577  def lfs_objects_oids_from_fork_source(oids: [])
1578    return [] unless forked?
1579
1580    oids(fork_source.lfs_objects, oids: oids)
1581  end
1582
1583  def personal?
1584    !group
1585  end
1586
1587  # Expires various caches before a project is renamed.
1588  def expire_caches_before_rename(old_path)
1589    project_repo = Repository.new(old_path, self, shard: repository_storage)
1590    wiki_repo = Repository.new("#{old_path}#{Gitlab::GlRepository::WIKI.path_suffix}", self, shard: repository_storage, repo_type: Gitlab::GlRepository::WIKI)
1591    design_repo = Repository.new("#{old_path}#{Gitlab::GlRepository::DESIGN.path_suffix}", self, shard: repository_storage, repo_type: Gitlab::GlRepository::DESIGN)
1592
1593    [project_repo, wiki_repo, design_repo].each do |repo|
1594      repo.before_delete if repo.exists?
1595    end
1596  end
1597
1598  # Check if repository already exists on disk
1599  def check_repository_path_availability
1600    return true if skip_disk_validation
1601    return false unless repository_storage
1602
1603    # Check if repository with same path already exists on disk we can
1604    # skip this for the hashed storage because the path does not change
1605    if legacy_storage? && repository_with_same_path_already_exists?
1606      errors.add(:base, _('There is already a repository with that name on disk'))
1607      return false
1608    end
1609
1610    true
1611  rescue GRPC::Internal # if the path is too long
1612    false
1613  end
1614
1615  def track_project_repository
1616    repository = project_repository || build_project_repository
1617    repository.update!(shard_name: repository_storage, disk_path: disk_path)
1618  end
1619
1620  def create_repository(force: false)
1621    # Forked import is handled asynchronously
1622    return if forked? && !force
1623
1624    repository.create_repository
1625    repository.after_create
1626
1627    true
1628  rescue StandardError => err
1629    Gitlab::ErrorTracking.track_exception(err, project: { id: id, full_path: full_path, disk_path: disk_path })
1630    errors.add(:base, _('Failed to create repository'))
1631    false
1632  end
1633
1634  def hook_attrs(backward: true)
1635    attrs = {
1636      id: id,
1637      name: name,
1638      description: description,
1639      web_url: web_url,
1640      avatar_url: avatar_url(only_path: false),
1641      git_ssh_url: ssh_url_to_repo,
1642      git_http_url: http_url_to_repo,
1643      namespace: namespace.name,
1644      visibility_level: visibility_level,
1645      path_with_namespace: full_path,
1646      default_branch: default_branch,
1647      ci_config_path: ci_config_path
1648    }
1649
1650    # Backward compatibility
1651    if backward
1652      attrs.merge!({
1653                    homepage: web_url,
1654                    url: url_to_repo,
1655                    ssh_url: ssh_url_to_repo,
1656                    http_url: http_url_to_repo
1657                  })
1658    end
1659
1660    attrs
1661  end
1662
1663  def project_member(user)
1664    if project_members.loaded?
1665      project_members.find { |member| member.user_id == user.id }
1666    else
1667      project_members.find_by(user_id: user)
1668    end
1669  end
1670
1671  def membership_locked?
1672    false
1673  end
1674
1675  def bots
1676    users.project_bot
1677  end
1678
1679  # Filters `users` to return only authorized users of the project
1680  def members_among(users)
1681    if users.is_a?(ActiveRecord::Relation) && !users.loaded?
1682      authorized_users.merge(users)
1683    else
1684      return [] if users.empty?
1685
1686      user_ids = authorized_users.where(users: { id: users.map(&:id) }).pluck(:id)
1687      users.select { |user| user_ids.include?(user.id) }
1688    end
1689  end
1690
1691  def visibility_level_field
1692    :visibility_level
1693  end
1694
1695  override :after_repository_change_head
1696  def after_repository_change_head
1697    ProjectCacheWorker.perform_async(self.id, [], [:commit_count])
1698
1699    super
1700  end
1701
1702  def forked_from?(other_project)
1703    forked? && forked_from_project == other_project
1704  end
1705
1706  def in_fork_network_of?(other_project)
1707    return false if fork_network.nil? || other_project.fork_network.nil?
1708
1709    fork_network == other_project.fork_network
1710  end
1711
1712  def origin_merge_requests
1713    merge_requests.where(source_project_id: self.id)
1714  end
1715
1716  def ensure_repository
1717    create_repository(force: true) unless repository_exists?
1718  end
1719
1720  # update visibility_level of forks
1721  def update_forks_visibility_level
1722    return if unlink_forks_upon_visibility_decrease_enabled?
1723    return unless visibility_level < visibility_level_before_last_save
1724
1725    forks.each do |forked_project|
1726      if forked_project.visibility_level > visibility_level
1727        forked_project.visibility_level = visibility_level
1728        forked_project.save!
1729      end
1730    end
1731  end
1732
1733  def allowed_to_share_with_group?
1734    !namespace.share_with_group_lock
1735  end
1736
1737  def latest_successful_pipeline_for_default_branch
1738    if defined?(@latest_successful_pipeline_for_default_branch)
1739      return @latest_successful_pipeline_for_default_branch
1740    end
1741
1742    @latest_successful_pipeline_for_default_branch =
1743      ci_pipelines.latest_successful_for_ref(default_branch)
1744  end
1745
1746  def latest_successful_pipeline_for(ref = nil)
1747    if ref && ref != default_branch
1748      ci_pipelines.latest_successful_for_ref(ref)
1749    else
1750      latest_successful_pipeline_for_default_branch
1751    end
1752  end
1753
1754  def enable_ci
1755    project_feature.update_attribute(:builds_access_level, ProjectFeature::ENABLED)
1756  end
1757
1758  def shared_runners_available?
1759    shared_runners_enabled?
1760  end
1761
1762  def shared_runners
1763    @shared_runners ||= shared_runners_enabled? ? Ci::Runner.instance_type : Ci::Runner.none
1764  end
1765
1766  def available_shared_runners
1767    @available_shared_runners ||= shared_runners_available? ? shared_runners : Ci::Runner.none
1768  end
1769
1770  def group_runners
1771    @group_runners ||= group_runners_enabled? ? Ci::Runner.belonging_to_parent_group_of_project(self.id) : Ci::Runner.none
1772  end
1773
1774  def all_runners
1775    Ci::Runner.from_union([runners, group_runners, shared_runners])
1776      .allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/339937')
1777  end
1778
1779  def all_available_runners
1780    Ci::Runner.from_union([runners, group_runners, available_shared_runners])
1781      .allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/339937')
1782  end
1783
1784  # Once issue 339937 is fixed, please search for all mentioned of
1785  # https://gitlab.com/gitlab-org/gitlab/-/issues/339937,
1786  # and remove the allow_cross_joins_across_databases.
1787  def active_runners
1788    strong_memoize(:active_runners) do
1789      all_available_runners.active
1790    end
1791  end
1792
1793  def any_online_runners?(&block)
1794    ::Gitlab::Database.allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/339937') do
1795      online_runners_with_tags.any?(&block)
1796    end
1797  end
1798
1799  def valid_runners_token?(token)
1800    self.runners_token && ActiveSupport::SecurityUtils.secure_compare(token, self.runners_token)
1801  end
1802
1803  # rubocop: disable CodeReuse/ServiceClass
1804  def open_issues_count(current_user = nil)
1805    return Projects::OpenIssuesCountService.new(self, current_user).count unless current_user.nil?
1806
1807    BatchLoader.for(self).batch do |projects, loader|
1808      issues_count_per_project = ::Projects::BatchOpenIssuesCountService.new(projects).refresh_cache_and_retrieve_data
1809
1810      issues_count_per_project.each do |project, count|
1811        loader.call(project, count)
1812      end
1813    end
1814  end
1815  # rubocop: enable CodeReuse/ServiceClass
1816
1817  # rubocop: disable CodeReuse/ServiceClass
1818  def open_merge_requests_count(_current_user = nil)
1819    Projects::OpenMergeRequestsCountService.new(self).count
1820  end
1821  # rubocop: enable CodeReuse/ServiceClass
1822
1823  def visibility_level_allowed_as_fork?(level = self.visibility_level)
1824    return true unless forked?
1825
1826    original_project = fork_source
1827    return true unless original_project
1828
1829    level <= original_project.visibility_level
1830  end
1831
1832  def visibility_level_allowed_by_group?(level = self.visibility_level)
1833    return true unless group
1834
1835    level <= group.visibility_level
1836  end
1837
1838  def visibility_level_allowed?(level = self.visibility_level)
1839    visibility_level_allowed_as_fork?(level) && visibility_level_allowed_by_group?(level)
1840  end
1841
1842  def runners_token
1843    ensure_runners_token!
1844  end
1845
1846  def pages_deployed?
1847    pages_metadatum&.deployed?
1848  end
1849
1850  def pages_group_url
1851    # The host in URL always needs to be downcased
1852    Gitlab.config.pages.url.sub(%r{^https?://}) do |prefix|
1853      "#{prefix}#{pages_subdomain}."
1854    end.downcase
1855  end
1856
1857  def pages_url
1858    url = pages_group_url
1859    url_path = full_path.partition('/').last
1860
1861    # If the project path is the same as host, we serve it as group page
1862    return url if url == "#{Settings.pages.protocol}://#{url_path}".downcase
1863
1864    "#{url}/#{url_path}"
1865  end
1866
1867  def pages_group_root?
1868    pages_group_url == pages_url
1869  end
1870
1871  def pages_subdomain
1872    full_path.partition('/').first
1873  end
1874
1875  def pages_path
1876    # TODO: when we migrate Pages to work with new storage types, change here to use disk_path
1877    File.join(Settings.pages.path, full_path)
1878  end
1879
1880  def pages_available?
1881    Gitlab.config.pages.enabled
1882  end
1883
1884  def remove_private_deploy_keys
1885    exclude_keys_linked_to_other_projects = <<-SQL
1886      NOT EXISTS (
1887        SELECT 1
1888        FROM deploy_keys_projects dkp2
1889        WHERE dkp2.deploy_key_id = deploy_keys_projects.deploy_key_id
1890        AND dkp2.project_id != deploy_keys_projects.project_id
1891      )
1892    SQL
1893
1894    deploy_keys.where(public: false)
1895               .where(exclude_keys_linked_to_other_projects)
1896               .delete_all
1897  end
1898
1899  def mark_pages_as_deployed(artifacts_archive: nil)
1900    ensure_pages_metadatum.update!(deployed: true, artifacts_archive: artifacts_archive)
1901  end
1902
1903  def mark_pages_as_not_deployed
1904    ensure_pages_metadatum.update!(deployed: false, artifacts_archive: nil, pages_deployment: nil)
1905  end
1906
1907  def update_pages_deployment!(deployment)
1908    ensure_pages_metadatum.update!(pages_deployment: deployment)
1909  end
1910
1911  def set_first_pages_deployment!(deployment)
1912    ensure_pages_metadatum
1913
1914    # where().update_all to perform update in the single transaction with check for null
1915    ProjectPagesMetadatum
1916      .where(project_id: id, pages_deployment_id: nil)
1917      .update_all(deployed: deployment.present?, pages_deployment_id: deployment&.id)
1918  end
1919
1920  def set_full_path(gl_full_path: full_path)
1921    # We'd need to keep track of project full path otherwise directory tree
1922    # created with hashed storage enabled cannot be usefully imported using
1923    # the import rake task.
1924    repository.raw_repository.set_full_path(full_path: gl_full_path)
1925  rescue Gitlab::Git::Repository::NoRepository => e
1926    Gitlab::AppLogger.error("Error writing to .git/config for project #{full_path} (#{id}): #{e.message}.")
1927    nil
1928  end
1929
1930  def after_import
1931    repository.expire_content_cache
1932    wiki.repository.expire_content_cache
1933
1934    DetectRepositoryLanguagesWorker.perform_async(id)
1935    ProjectCacheWorker.perform_async(self.id, [], [:repository_size])
1936    AuthorizedProjectUpdate::ProjectRecalculateWorker.perform_async(id)
1937
1938    # The import assigns iid values on its own, e.g. by re-using GitHub ids.
1939    # Flush existing InternalId records for this project for consistency reasons.
1940    # Those records are going to be recreated with the next normal creation
1941    # of a model instance (e.g. an Issue).
1942    InternalId.flush_records!(project: self)
1943
1944    import_state.finish
1945    update_project_counter_caches
1946    after_create_default_branch
1947    join_pool_repository
1948    refresh_markdown_cache!
1949    set_full_path
1950  end
1951
1952  def update_project_counter_caches
1953    classes = [
1954      Projects::OpenIssuesCountService,
1955      Projects::OpenMergeRequestsCountService
1956    ]
1957
1958    classes.each do |klass|
1959      klass.new(self).refresh_cache
1960    end
1961  end
1962
1963  # rubocop: disable CodeReuse/ServiceClass
1964  def after_create_default_branch
1965    Projects::ProtectDefaultBranchService.new(self).execute
1966  end
1967  # rubocop: enable CodeReuse/ServiceClass
1968
1969  # Lazy loading of the `pipeline_status` attribute
1970  def pipeline_status
1971    @pipeline_status ||= Gitlab::Cache::Ci::ProjectPipelineStatus.load_for_project(self)
1972  end
1973
1974  def add_export_job(current_user:, after_export_strategy: nil, params: {})
1975    job_id = ProjectExportWorker.perform_async(current_user.id, self.id, after_export_strategy, params)
1976
1977    if job_id
1978      Gitlab::AppLogger.info "Export job started for project ID #{self.id} with job ID #{job_id}"
1979    else
1980      Gitlab::AppLogger.error "Export job failed to start for project ID #{self.id}"
1981    end
1982  end
1983
1984  def import_export_shared
1985    @import_export_shared ||= Gitlab::ImportExport::Shared.new(self)
1986  end
1987
1988  def export_path
1989    return unless namespace.present? || hashed_storage?(:repository)
1990
1991    import_export_shared.archive_path
1992  end
1993
1994  def export_status
1995    if regeneration_in_progress?
1996      :regeneration_in_progress
1997    elsif export_enqueued?
1998      :queued
1999    elsif export_in_progress?
2000      :started
2001    elsif export_file_exists?
2002      :finished
2003    else
2004      :none
2005    end
2006  end
2007
2008  def export_in_progress?
2009    strong_memoize(:export_in_progress) do
2010      ::Projects::ExportJobFinder.new(self, { status: :started }).execute.present?
2011    end
2012  end
2013
2014  def export_enqueued?
2015    strong_memoize(:export_enqueued) do
2016      ::Projects::ExportJobFinder.new(self, { status: :queued }).execute.present?
2017    end
2018  end
2019
2020  def regeneration_in_progress?
2021    (export_enqueued? || export_in_progress?) && export_file_exists?
2022  end
2023
2024  def remove_exports
2025    return unless export_file_exists?
2026
2027    import_export_upload.remove_export_file!
2028    import_export_upload.save unless import_export_upload.destroyed?
2029  end
2030
2031  def export_file_exists?
2032    import_export_upload&.export_file_exists?
2033  end
2034
2035  def export_archive_exists?
2036    import_export_upload&.export_archive_exists?
2037  end
2038
2039  def export_file
2040    import_export_upload&.export_file
2041  end
2042
2043  def full_path_slug
2044    Gitlab::Utils.slugify(full_path.to_s)
2045  end
2046
2047  def has_ci?
2048    repository.gitlab_ci_yml || auto_devops_enabled?
2049  end
2050
2051  def predefined_variables
2052    strong_memoize(:predefined_variables) do
2053      Gitlab::Ci::Variables::Collection.new
2054        .concat(predefined_ci_server_variables)
2055        .concat(predefined_project_variables)
2056        .concat(pages_variables)
2057        .concat(container_registry_variables)
2058        .concat(dependency_proxy_variables)
2059        .concat(auto_devops_variables)
2060        .concat(api_variables)
2061    end
2062  end
2063
2064  def predefined_project_variables
2065    Gitlab::Ci::Variables::Collection.new
2066      .append(key: 'GITLAB_FEATURES', value: licensed_features.join(','))
2067      .append(key: 'CI_PROJECT_ID', value: id.to_s)
2068      .append(key: 'CI_PROJECT_NAME', value: path)
2069      .append(key: 'CI_PROJECT_TITLE', value: title)
2070      .append(key: 'CI_PROJECT_PATH', value: full_path)
2071      .append(key: 'CI_PROJECT_PATH_SLUG', value: full_path_slug)
2072      .append(key: 'CI_PROJECT_NAMESPACE', value: namespace.full_path)
2073      .append(key: 'CI_PROJECT_ROOT_NAMESPACE', value: namespace.root_ancestor.path)
2074      .append(key: 'CI_PROJECT_URL', value: web_url)
2075      .append(key: 'CI_PROJECT_VISIBILITY', value: Gitlab::VisibilityLevel.string_level(visibility_level))
2076      .append(key: 'CI_PROJECT_REPOSITORY_LANGUAGES', value: repository_languages.map(&:name).join(',').downcase)
2077      .append(key: 'CI_PROJECT_CLASSIFICATION_LABEL', value: external_authorization_classification_label)
2078      .append(key: 'CI_DEFAULT_BRANCH', value: default_branch)
2079      .append(key: 'CI_CONFIG_PATH', value: ci_config_path_or_default)
2080  end
2081
2082  def predefined_ci_server_variables
2083    Gitlab::Ci::Variables::Collection.new
2084      .append(key: 'CI', value: 'true')
2085      .append(key: 'GITLAB_CI', value: 'true')
2086      .append(key: 'CI_SERVER_URL', value: Gitlab.config.gitlab.url)
2087      .append(key: 'CI_SERVER_HOST', value: Gitlab.config.gitlab.host)
2088      .append(key: 'CI_SERVER_PORT', value: Gitlab.config.gitlab.port.to_s)
2089      .append(key: 'CI_SERVER_PROTOCOL', value: Gitlab.config.gitlab.protocol)
2090      .append(key: 'CI_SERVER_NAME', value: 'GitLab')
2091      .append(key: 'CI_SERVER_VERSION', value: Gitlab::VERSION)
2092      .append(key: 'CI_SERVER_VERSION_MAJOR', value: Gitlab.version_info.major.to_s)
2093      .append(key: 'CI_SERVER_VERSION_MINOR', value: Gitlab.version_info.minor.to_s)
2094      .append(key: 'CI_SERVER_VERSION_PATCH', value: Gitlab.version_info.patch.to_s)
2095      .append(key: 'CI_SERVER_REVISION', value: Gitlab.revision)
2096  end
2097
2098  def pages_variables
2099    Gitlab::Ci::Variables::Collection.new.tap do |variables|
2100      break unless pages_enabled?
2101
2102      variables.append(key: 'CI_PAGES_DOMAIN', value: Gitlab.config.pages.host)
2103      variables.append(key: 'CI_PAGES_URL', value: pages_url)
2104    end
2105  end
2106
2107  def api_variables
2108    Gitlab::Ci::Variables::Collection.new.tap do |variables|
2109      variables.append(key: 'CI_API_V4_URL', value: API::Helpers::Version.new('v4').root_url)
2110    end
2111  end
2112
2113  def dependency_proxy_variables
2114    Gitlab::Ci::Variables::Collection.new.tap do |variables|
2115      break variables unless Gitlab.config.dependency_proxy.enabled
2116
2117      variables.append(key: 'CI_DEPENDENCY_PROXY_SERVER', value: Gitlab.host_with_port)
2118      variables.append(
2119        key: 'CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX',
2120        # The namespace path can include uppercase letters, which
2121        # Docker doesn't allow. The proxy expects it to be downcased.
2122        value: "#{Gitlab.host_with_port}/#{namespace.root_ancestor.path.downcase}#{DependencyProxy::URL_SUFFIX}"
2123      )
2124      variables.append(
2125        key: 'CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX',
2126        value: "#{Gitlab.host_with_port}/#{namespace.full_path.downcase}#{DependencyProxy::URL_SUFFIX}"
2127      )
2128    end
2129  end
2130
2131  def container_registry_variables
2132    Gitlab::Ci::Variables::Collection.new.tap do |variables|
2133      break variables unless Gitlab.config.registry.enabled
2134
2135      variables.append(key: 'CI_REGISTRY', value: Gitlab.config.registry.host_port)
2136
2137      if container_registry_enabled?
2138        variables.append(key: 'CI_REGISTRY_IMAGE', value: container_registry_url)
2139      end
2140    end
2141  end
2142
2143  def default_environment
2144    production_first = Arel.sql("(CASE WHEN name = 'production' THEN 0 ELSE 1 END), id ASC")
2145
2146    environments
2147      .with_state(:available)
2148      .reorder(production_first)
2149      .first
2150  end
2151
2152  def ci_variables_for(ref:, environment: nil)
2153    cache_key = "ci_variables_for:project:#{self&.id}:ref:#{ref}:environment:#{environment}"
2154
2155    ::Gitlab::SafeRequestStore.fetch(cache_key) do
2156      uncached_ci_variables_for(ref: ref, environment: environment)
2157    end
2158  end
2159
2160  def uncached_ci_variables_for(ref:, environment: nil)
2161    result = if protected_for?(ref)
2162               variables
2163             else
2164               variables.unprotected
2165             end
2166
2167    if environment
2168      result.on_environment(environment)
2169    else
2170      result.where(environment_scope: '*')
2171    end
2172  end
2173
2174  def ci_instance_variables_for(ref:)
2175    if protected_for?(ref)
2176      Ci::InstanceVariable.all_cached
2177    else
2178      Ci::InstanceVariable.unprotected_cached
2179    end
2180  end
2181
2182  def protected_for?(ref)
2183    raise Repository::AmbiguousRefError if repository.ambiguous_ref?(ref)
2184
2185    resolved_ref = repository.expand_ref(ref) || ref
2186    return false unless Gitlab::Git.tag_ref?(resolved_ref) || Gitlab::Git.branch_ref?(resolved_ref)
2187
2188    ref_name = if resolved_ref == ref
2189                 Gitlab::Git.ref_name(resolved_ref)
2190               else
2191                 ref
2192               end
2193
2194    if Gitlab::Git.branch_ref?(resolved_ref)
2195      ProtectedBranch.protected?(self, ref_name)
2196    elsif Gitlab::Git.tag_ref?(resolved_ref)
2197      ProtectedTag.protected?(self, ref_name)
2198    end
2199  end
2200
2201  def deployment_variables(environment:, kubernetes_namespace: nil)
2202    platform = deployment_platform(environment: environment)
2203
2204    return [] unless platform.present?
2205
2206    platform.predefined_variables(
2207      project: self,
2208      environment_name: environment,
2209      kubernetes_namespace: kubernetes_namespace
2210    )
2211  end
2212
2213  def auto_devops_variables
2214    return [] unless auto_devops_enabled?
2215
2216    (auto_devops || build_auto_devops)&.predefined_variables
2217  end
2218
2219  def route_map_for(commit_sha)
2220    @route_maps_by_commit ||= Hash.new do |h, sha|
2221      h[sha] = begin
2222        data = repository.route_map_for(sha)
2223
2224        Gitlab::RouteMap.new(data) if data
2225      rescue Gitlab::RouteMap::FormatError
2226        nil
2227      end
2228    end
2229
2230    @route_maps_by_commit[commit_sha]
2231  end
2232
2233  def public_path_for_source_path(path, commit_sha)
2234    map = route_map_for(commit_sha)
2235    return unless map
2236
2237    map.public_path_for_source_path(path)
2238  end
2239
2240  def parent_changed?
2241    namespace_id_changed?
2242  end
2243
2244  def default_merge_request_target
2245    return self if project_setting.mr_default_target_self
2246    return self unless mr_can_target_upstream?
2247
2248    forked_from_project
2249  end
2250
2251  def mr_can_target_upstream?
2252    # When our current visibility is more restrictive than the upstream project,
2253    # (e.g., the fork is `private` but the parent is `public`), don't allow target upstream
2254    forked_from_project &&
2255      forked_from_project.merge_requests_enabled? &&
2256      forked_from_project.visibility_level_value <= visibility_level_value
2257  end
2258
2259  def multiple_issue_boards_available?
2260    true
2261  end
2262
2263  def full_path_before_last_save
2264    File.join(namespace.full_path, path_before_last_save)
2265  end
2266
2267  alias_method :name_with_namespace, :full_name
2268  alias_method :human_name, :full_name
2269  # @deprecated cannot remove yet because it has an index with its name in elasticsearch
2270  alias_method :path_with_namespace, :full_path
2271
2272  # rubocop: disable CodeReuse/ServiceClass
2273  def forks_count
2274    BatchLoader.for(self).batch do |projects, loader|
2275      fork_count_per_project = ::Projects::BatchForksCountService.new(projects).refresh_cache_and_retrieve_data
2276
2277      fork_count_per_project.each do |project, count|
2278        loader.call(project, count)
2279      end
2280    end
2281  end
2282  # rubocop: enable CodeReuse/ServiceClass
2283
2284  def legacy_storage?
2285    [nil, 0].include?(self.storage_version)
2286  end
2287
2288  # Check if Hashed Storage is enabled for the project with at least informed feature rolled out
2289  #
2290  # @param [Symbol] feature that needs to be rolled out for the project (:repository, :attachments)
2291  def hashed_storage?(feature)
2292    raise ArgumentError, _("Invalid feature") unless HASHED_STORAGE_FEATURES.include?(feature)
2293
2294    self.storage_version && self.storage_version >= HASHED_STORAGE_FEATURES[feature]
2295  end
2296
2297  def renamed?
2298    persisted? && path_changed?
2299  end
2300
2301  def human_merge_method
2302    if merge_method == :ff
2303      'Fast-forward'
2304    else
2305      merge_method.to_s.humanize
2306    end
2307  end
2308
2309  def merge_method
2310    if self.merge_requests_ff_only_enabled
2311      :ff
2312    elsif self.merge_requests_rebase_enabled
2313      :rebase_merge
2314    else
2315      :merge
2316    end
2317  end
2318
2319  def merge_method=(method)
2320    case method.to_s
2321    when "ff"
2322      self.merge_requests_ff_only_enabled = true
2323      self.merge_requests_rebase_enabled = true
2324    when "rebase_merge"
2325      self.merge_requests_ff_only_enabled = false
2326      self.merge_requests_rebase_enabled = true
2327    when "merge"
2328      self.merge_requests_ff_only_enabled = false
2329      self.merge_requests_rebase_enabled = false
2330    end
2331  end
2332
2333  def ff_merge_must_be_possible?
2334    self.merge_requests_ff_only_enabled || self.merge_requests_rebase_enabled
2335  end
2336
2337  def migrate_to_hashed_storage!
2338    return unless storage_upgradable?
2339
2340    if git_transfer_in_progress?
2341      HashedStorage::ProjectMigrateWorker.perform_in(Gitlab::ReferenceCounter::REFERENCE_EXPIRE_TIME, id)
2342    else
2343      HashedStorage::ProjectMigrateWorker.perform_async(id)
2344    end
2345  end
2346
2347  def rollback_to_legacy_storage!
2348    return if legacy_storage?
2349
2350    if git_transfer_in_progress?
2351      HashedStorage::ProjectRollbackWorker.perform_in(Gitlab::ReferenceCounter::REFERENCE_EXPIRE_TIME, id)
2352    else
2353      HashedStorage::ProjectRollbackWorker.perform_async(id)
2354    end
2355  end
2356
2357  override :git_transfer_in_progress?
2358  def git_transfer_in_progress?
2359    GL_REPOSITORY_TYPES.any? do |type|
2360      reference_counter(type: type).value > 0
2361    end
2362  end
2363
2364  def storage_version=(value)
2365    super
2366
2367    @storage = nil if storage_version_changed?
2368  end
2369
2370  def badges
2371    return project_badges unless group
2372
2373    Badge.from_union([
2374      project_badges,
2375      GroupBadge.where(group: group.self_and_ancestors)
2376    ])
2377  end
2378
2379  def merge_requests_allowing_push_to_user(user)
2380    return MergeRequest.none unless user
2381
2382    developer_access_exists = user.project_authorizations
2383                                .where('access_level >= ? ', Gitlab::Access::DEVELOPER)
2384                                .where('project_authorizations.project_id = merge_requests.target_project_id')
2385                                .limit(1)
2386                                .select(1)
2387    merge_requests_allowing_collaboration.where('EXISTS (?)', developer_access_exists)
2388  end
2389
2390  def any_branch_allows_collaboration?(user)
2391    fetch_branch_allows_collaboration(user)
2392  end
2393
2394  def branch_allows_collaboration?(user, branch_name)
2395    fetch_branch_allows_collaboration(user, branch_name)
2396  end
2397
2398  def external_authorization_classification_label
2399    super || ::Gitlab::CurrentSettings.current_application_settings
2400               .external_authorization_service_default_label
2401  end
2402
2403  # Overridden in EE::Project
2404  def licensed_feature_available?(_feature)
2405    false
2406  end
2407
2408  def licensed_features
2409    []
2410  end
2411
2412  def mark_primary_write_location
2413    self.class.sticking.mark_primary_write_location(:project, self.id)
2414  end
2415
2416  def toggle_ci_cd_settings!(settings_attribute)
2417    ci_cd_settings.toggle!(settings_attribute)
2418  end
2419
2420  def gitlab_deploy_token
2421    @gitlab_deploy_token ||= deploy_tokens.gitlab_deploy_token
2422  end
2423
2424  def any_lfs_file_locks?
2425    lfs_file_locks.any?
2426  end
2427  request_cache(:any_lfs_file_locks?) { self.id }
2428
2429  def auto_cancel_pending_pipelines?
2430    auto_cancel_pending_pipelines == 'enabled'
2431  end
2432
2433  def storage
2434    @storage ||=
2435      if hashed_storage?(:repository)
2436        Storage::Hashed.new(self)
2437      else
2438        Storage::LegacyProject.new(self)
2439      end
2440  end
2441
2442  def storage_upgradable?
2443    storage_version != LATEST_STORAGE_VERSION
2444  end
2445
2446  def snippets_visible?(user = nil)
2447    Ability.allowed?(user, :read_snippet, self)
2448  end
2449
2450  def max_attachment_size
2451    Gitlab::CurrentSettings.max_attachment_size.megabytes.to_i
2452  end
2453
2454  def object_pool_params
2455    return {} unless !forked? && git_objects_poolable?
2456
2457    {
2458      repository_storage: repository_storage,
2459      pool_repository:    pool_repository || create_new_pool_repository
2460    }
2461  end
2462
2463  # Git objects are only poolable when the project is or has:
2464  # - Hashed storage -> The object pool will have a remote to its members, using relative paths.
2465  #                     If the repository path changes we would have to update the remote.
2466  # - not private    -> The visibility level or repository access level has to be greater than private
2467  #                     to prevent fetching objects that might not exist
2468  # - Repository     -> Else the disk path will be empty, and there's nothing to pool
2469  def git_objects_poolable?
2470    hashed_storage?(:repository) &&
2471      visibility_level > Gitlab::VisibilityLevel::PRIVATE &&
2472      repository_access_level > ProjectFeature::PRIVATE &&
2473      repository_exists? &&
2474      Gitlab::CurrentSettings.hashed_storage_enabled
2475  end
2476
2477  def leave_pool_repository
2478    pool_repository&.mark_obsolete_if_last(repository) && update_column(:pool_repository_id, nil)
2479  end
2480
2481  def link_pool_repository
2482    pool_repository&.link_repository(repository)
2483  end
2484
2485  def has_pool_repository?
2486    pool_repository.present?
2487  end
2488
2489  def access_request_approvers_to_be_notified
2490    members.maintainers.connected_to_user.order_recent_sign_in.limit(Member::ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT)
2491  end
2492
2493  def pages_lookup_path(trim_prefix: nil, domain: nil)
2494    Pages::LookupPath.new(self, trim_prefix: trim_prefix, domain: domain)
2495  end
2496
2497  def closest_setting(name)
2498    setting = read_attribute(name)
2499    setting = closest_namespace_setting(name) if setting.nil?
2500    setting = app_settings_for(name) if setting.nil?
2501    setting
2502  end
2503
2504  def drop_visibility_level!
2505    if group && group.visibility_level < visibility_level
2506      self.visibility_level = group.visibility_level
2507    end
2508
2509    if Gitlab::CurrentSettings.restricted_visibility_levels.include?(visibility_level)
2510      self.visibility_level = Gitlab::VisibilityLevel::PRIVATE
2511    end
2512  end
2513
2514  def template_source?
2515    false
2516  end
2517
2518  def jira_subscription_exists?
2519    JiraConnectSubscription.for_project(self).exists?
2520  end
2521
2522  def uses_default_ci_config?
2523    ci_config_path.blank? || ci_config_path == Gitlab::FileDetector::PATTERNS[:gitlab_ci]
2524  end
2525
2526  def uses_external_project_ci_config?
2527    !!(ci_config_path =~ %r{@.+/.+})
2528  end
2529
2530  def limited_protected_branches(limit)
2531    protected_branches.limit(limit)
2532  end
2533
2534  def self_monitoring?
2535    Gitlab::CurrentSettings.self_monitoring_project_id == id
2536  end
2537
2538  def deploy_token_create_url(opts = {})
2539    Gitlab::Routing.url_helpers.create_deploy_token_project_settings_repository_path(self, opts)
2540  end
2541
2542  def deploy_token_revoke_url_for(token)
2543    Gitlab::Routing.url_helpers.revoke_project_deploy_token_path(self, token)
2544  end
2545
2546  def default_branch_protected?
2547    branch_protection = Gitlab::Access::BranchProtection.new(self.namespace.default_branch_protection)
2548
2549    branch_protection.fully_protected? || branch_protection.developer_can_merge?
2550  end
2551
2552  def environments_for_scope(scope)
2553    quoted_scope = ::Gitlab::SQL::Glob.q(scope)
2554
2555    environments.where("name LIKE (#{::Gitlab::SQL::Glob.to_like(quoted_scope)})") # rubocop:disable GitlabSecurity/SqlInjection
2556  end
2557
2558  def latest_jira_import
2559    jira_imports.last
2560  end
2561
2562  def metrics_setting
2563    super || build_metrics_setting
2564  end
2565
2566  def service_desk_enabled
2567    Gitlab::ServiceDesk.enabled?(project: self)
2568  end
2569
2570  alias_method :service_desk_enabled?, :service_desk_enabled
2571
2572  def service_desk_address
2573    service_desk_custom_address || service_desk_incoming_address
2574  end
2575
2576  def service_desk_incoming_address
2577    return unless service_desk_enabled?
2578
2579    config = Gitlab.config.incoming_email
2580    wildcard = Gitlab::IncomingEmail::WILDCARD_PLACEHOLDER
2581
2582    config.address&.gsub(wildcard, "#{full_path_slug}-#{default_service_desk_suffix}")
2583  end
2584
2585  def service_desk_custom_address
2586    return unless Gitlab::ServiceDeskEmail.enabled?
2587
2588    key = service_desk_setting&.project_key || default_service_desk_suffix
2589
2590    Gitlab::ServiceDeskEmail.address_for_key("#{full_path_slug}-#{key}")
2591  end
2592
2593  def default_service_desk_suffix
2594    "#{id}-issue-"
2595  end
2596
2597  def root_namespace
2598    if namespace.has_parent?
2599      namespace.root_ancestor
2600    else
2601      namespace
2602    end
2603  end
2604
2605  # for projects that are part of user namespace, return project.
2606  def self_or_root_group_ids
2607    if group
2608      root_group = root_namespace
2609    else
2610      project = self
2611    end
2612
2613    [project&.id, root_group&.id]
2614  end
2615
2616  def package_already_taken?(package_name, package_version, package_type:)
2617    Packages::Package.with_name(package_name)
2618      .with_version(package_version)
2619      .with_package_type(package_type)
2620      .for_projects(
2621        root_ancestor.all_projects
2622          .id_not_in(id)
2623          .select(:id)
2624      ).exists?
2625  end
2626
2627  def default_branch_or_main
2628    return default_branch if default_branch
2629
2630    Gitlab::DefaultBranch.value(object: self)
2631  end
2632
2633  def ci_config_path_or_default
2634    ci_config_path.presence || Ci::Pipeline::DEFAULT_CONFIG_PATH
2635  end
2636
2637  def ci_config_for(sha)
2638    repository.gitlab_ci_yml_for(sha, ci_config_path_or_default)
2639  end
2640
2641  def ci_config_external_project
2642    Project.find_by_full_path(ci_config_path.split('@', 2).last)
2643  end
2644
2645  def enabled_group_deploy_keys
2646    return GroupDeployKey.none unless group
2647
2648    GroupDeployKey.for_groups(group.self_and_ancestors_ids)
2649  end
2650
2651  def feature_flags_client_token
2652    instance = operations_feature_flags_client || create_operations_feature_flags_client!
2653    instance.token
2654  end
2655
2656  def tracing_external_url
2657    tracing_setting&.external_url
2658  end
2659
2660  override :git_garbage_collect_worker_klass
2661  def git_garbage_collect_worker_klass
2662    Projects::GitGarbageCollectWorker
2663  end
2664
2665  def activity_path
2666    Gitlab::Routing.url_helpers.activity_project_path(self)
2667  end
2668
2669  def increment_statistic_value(statistic, delta)
2670    return if pending_delete?
2671
2672    ProjectStatistics.increment_statistic(self, statistic, delta)
2673  end
2674
2675  def ci_forward_deployment_enabled?
2676    return false unless ci_cd_settings
2677
2678    ci_cd_settings.forward_deployment_enabled?
2679  end
2680
2681  def ci_job_token_scope_enabled?
2682    return false unless ci_cd_settings
2683
2684    ci_cd_settings.job_token_scope_enabled?
2685  end
2686
2687  def restrict_user_defined_variables?
2688    return false unless ci_cd_settings
2689
2690    ci_cd_settings.restrict_user_defined_variables?
2691  end
2692
2693  def keep_latest_artifacts_available?
2694    return false unless ci_cd_settings
2695
2696    ci_cd_settings.keep_latest_artifacts_available?
2697  end
2698
2699  def keep_latest_artifact?
2700    return false unless ci_cd_settings
2701
2702    ci_cd_settings.keep_latest_artifact?
2703  end
2704
2705  def group_runners_enabled?
2706    return false unless ci_cd_settings
2707
2708    ci_cd_settings.group_runners_enabled?
2709  end
2710
2711  def topic_list
2712    self.topics.map(&:name)
2713  end
2714
2715  override :after_change_head_branch_does_not_exist
2716  def after_change_head_branch_does_not_exist(branch)
2717    self.errors.add(:base, _("Could not change HEAD: branch '%{branch}' does not exist") % { branch: branch })
2718  end
2719
2720  def visible_group_links(for_user:)
2721    user = for_user
2722    links = project_group_links_with_preload
2723    user.max_member_access_for_group_ids(links.map(&:group_id)) if user && links.any?
2724
2725    DeclarativePolicy.user_scope do
2726      links.select { Ability.allowed?(user, :read_group, _1.group) }
2727    end
2728  end
2729
2730  def remove_project_authorizations(user_ids, per_batch = 1000)
2731    user_ids.each_slice(per_batch) do |user_ids_batch|
2732      project_authorizations.where(user_id: user_ids_batch).delete_all
2733    end
2734  end
2735
2736  private
2737
2738  # overridden in EE
2739  def project_group_links_with_preload
2740    project_group_links
2741  end
2742
2743  def save_topics
2744    return if @topic_list.nil?
2745
2746    @topic_list = @topic_list.split(',') if @topic_list.instance_of?(String)
2747    @topic_list = @topic_list.map(&:strip).uniq.reject(&:empty?)
2748
2749    if @topic_list != self.topic_list
2750      self.topics.delete_all
2751      self.topics = @topic_list.map { |topic| Projects::Topic.find_or_create_by(name: topic) }
2752    end
2753
2754    @topic_list = nil
2755  end
2756
2757  def find_integration(integrations, name)
2758    integrations.find { _1.to_param == name }
2759  end
2760
2761  def build_from_instance(name)
2762    instance = find_integration(integration_instances, name)
2763
2764    return unless instance
2765
2766    Integration.build_from_integration(instance, project_id: id)
2767  end
2768
2769  def build_integration(name)
2770    Integration.integration_name_to_model(name).new(project_id: id)
2771  end
2772
2773  def integration_instances
2774    @integration_instances ||= Integration.for_instance
2775  end
2776
2777  def closest_namespace_setting(name)
2778    namespace.closest_setting(name)
2779  end
2780
2781  def app_settings_for(name)
2782    Gitlab::CurrentSettings.send(name) # rubocop:disable GitlabSecurity/PublicSend
2783  end
2784
2785  def merge_requests_allowing_collaboration(source_branch = nil)
2786    relation = source_of_merge_requests.opened.where(allow_collaboration: true)
2787    relation = relation.where(source_branch: source_branch) if source_branch
2788    relation
2789  end
2790
2791  def create_new_pool_repository
2792    pool = PoolRepository.safe_find_or_create_by!(shard: Shard.by_name(repository_storage), source_project: self)
2793    update!(pool_repository: pool)
2794
2795    pool.schedule unless pool.scheduled?
2796
2797    pool
2798  end
2799
2800  def join_pool_repository
2801    return unless pool_repository
2802
2803    ObjectPool::JoinWorker.perform_async(pool_repository.id, self.id)
2804  end
2805
2806  def use_hashed_storage
2807    if self.new_record? && Gitlab::CurrentSettings.hashed_storage_enabled
2808      self.storage_version = LATEST_STORAGE_VERSION
2809    end
2810  end
2811
2812  def check_repository_absence!
2813    return if skip_disk_validation
2814
2815    if repository_storage.blank? || repository_with_same_path_already_exists?
2816      errors.add(:base, _('There is already a repository with that name on disk'))
2817      throw :abort # rubocop:disable Cop/BanCatchThrow
2818    end
2819  end
2820
2821  def repository_with_same_path_already_exists?
2822    gitlab_shell.repository_exists?(repository_storage, "#{disk_path}.git")
2823  end
2824
2825  def set_timestamps_for_create
2826    update_columns(last_activity_at: self.created_at, last_repository_updated_at: self.created_at)
2827  end
2828
2829  def cross_namespace_reference?(from)
2830    case from
2831    when Project
2832      namespace_id != from.namespace_id
2833    when Namespace
2834      namespace != from
2835    when User
2836      true
2837    end
2838  end
2839
2840  # Check if a reference is being done cross-project
2841  def cross_project_reference?(from)
2842    return true if from.is_a?(Namespace)
2843
2844    from && self != from
2845  end
2846
2847  def update_project_statistics
2848    stats = statistics || build_statistics
2849    stats.update(namespace_id: namespace_id)
2850  end
2851
2852  def check_pending_delete
2853    return if valid_attribute?(:name) && valid_attribute?(:path)
2854    return unless pending_delete_twin
2855
2856    %i[route route.path name path].each do |error|
2857      errors.delete(error)
2858    end
2859
2860    errors.add(:base, _("The project is still being deleted. Please try again later."))
2861  end
2862
2863  def pending_delete_twin
2864    return false unless path
2865
2866    Project.pending_delete.find_by_full_path(full_path)
2867  end
2868
2869  ##
2870  # This method is here because of support for legacy container repository
2871  # which has exactly the same path like project does, but which might not be
2872  # persisted in `container_repositories` table.
2873  #
2874  def has_root_container_repository_tags?
2875    return false unless Gitlab.config.registry.enabled
2876
2877    ContainerRepository.build_root_repository(self).has_tags?
2878  end
2879
2880  def fetch_branch_allows_collaboration(user, branch_name = nil)
2881    return false unless user
2882
2883    Gitlab::SafeRequestStore.fetch("project-#{id}:branch-#{branch_name}:user-#{user.id}:branch_allows_collaboration") do
2884      next false if empty_repo?
2885
2886      # Issue for N+1: https://gitlab.com/gitlab-org/gitlab-foss/issues/49322
2887      Gitlab::GitalyClient.allow_n_plus_1_calls do
2888        merge_requests_allowing_collaboration(branch_name).any? do |merge_request|
2889          merge_request.can_be_merged_by?(user, skip_collaboration_check: true)
2890        end
2891      end
2892    end
2893  end
2894
2895  def ensure_pages_metadatum
2896    pages_metadatum || create_pages_metadatum!
2897  rescue ActiveRecord::RecordNotUnique
2898    reset
2899    retry
2900  end
2901
2902  def oids(objects, oids: [])
2903    objects = objects.where(oid: oids) if oids.any?
2904
2905    [].tap do |out|
2906      objects.each_batch { |relation| out.concat(relation.pluck(:oid)) }
2907    end
2908  end
2909
2910  def cache_has_external_wiki
2911    update_column(:has_external_wiki, integrations.external_wikis.any?) if Gitlab::Database.read_write?
2912  end
2913
2914  def cache_has_external_issue_tracker
2915    update_column(:has_external_issue_tracker, integrations.external_issue_trackers.any?) if Gitlab::Database.read_write?
2916  end
2917
2918  def online_runners_with_tags
2919    @online_runners_with_tags ||= active_runners.with_tags.online
2920  end
2921
2922  def ensure_project_namespace_in_sync
2923    # create project_namespace when project is created if create_project_namespace_on_project_create FF is enabled
2924    build_project_namespace if project_namespace_creation_enabled?
2925
2926    # regardless of create_project_namespace_on_project_create FF we need
2927    # to keep project and project namespace in sync if there is one
2928    sync_attributes(project_namespace) if sync_project_namespace?
2929  end
2930
2931  def project_namespace_creation_enabled?
2932    new_record? && !project_namespace && self.namespace && self.root_namespace.project_namespace_creation_enabled?
2933  end
2934
2935  def sync_project_namespace?
2936    (changes.keys & %w(name path namespace_id namespace visibility_level shared_runners_enabled)).any? && project_namespace.present?
2937  end
2938
2939  def sync_attributes(project_namespace)
2940    project_namespace.name = name
2941    project_namespace.path = path
2942    project_namespace.parent = namespace
2943    project_namespace.shared_runners_enabled = shared_runners_enabled
2944    project_namespace.visibility_level = visibility_level
2945  end
2946
2947  # SyncEvents are created by PG triggers (with the function `insert_projects_sync_event`)
2948  def schedule_sync_event_worker
2949    run_after_commit do
2950      Projects::SyncEvent.enqueue_worker
2951    end
2952  end
2953end
2954
2955Project.prepend_mod_with('Project')
2956