summaryrefslogtreecommitdiff
path: root/app/models/project.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/project.rb')
-rw-r--r--app/models/project.rb143
1 files changed, 96 insertions, 47 deletions
diff --git a/app/models/project.rb b/app/models/project.rb
index f03e5293b58..9d572b7e2f8 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -19,6 +19,7 @@ class Project < ApplicationRecord
include Presentable
include HasRepository
include HasWiki
+ include HasIntegrations
include CanMoveRepositoryStorage
include Routable
include GroupDescendant
@@ -33,7 +34,6 @@ class Project < ApplicationRecord
include OptionallySearch
include FromUnion
include IgnorableColumns
- include Integration
include Repositories::CanHousekeepRepository
include EachBatch
include GitlabRoutingHelper
@@ -104,16 +104,13 @@ class Project < ApplicationRecord
after_save :create_import_state, if: ->(project) { project.import? && project.import_state.nil? }
- after_create :create_project_feature, unless: :project_feature
+ after_create -> { create_or_load_association(:project_feature) }
- after_create :create_ci_cd_settings,
- unless: :ci_cd_settings
+ after_create -> { create_or_load_association(:ci_cd_settings) }
- after_create :create_container_expiration_policy,
- unless: :container_expiration_policy
+ after_create -> { create_or_load_association(:container_expiration_policy) }
- after_create :create_pages_metadatum,
- unless: :pages_metadatum
+ after_create -> { create_or_load_association(:pages_metadatum) }
after_create :set_timestamps_for_create
after_update :update_forks_visibility_level
@@ -131,7 +128,41 @@ class Project < ApplicationRecord
after_initialize :use_hashed_storage
after_create :check_repository_absence!
- acts_as_ordered_taggable
+ acts_as_ordered_taggable_on :topics
+ # The 'tag_list' alias and the 'has_many' associations are required during the 'tags -> topics' migration
+ # TODO: eliminate 'tag_list', 'topic_taggings' and 'tags' in the further process of the migration
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/331081
+ alias_attribute :tag_list, :topic_list
+ has_many :topic_taggings, -> { includes(:tag).order("#{ActsAsTaggableOn::Tagging.table_name}.id") },
+ as: :taggable,
+ class_name: 'ActsAsTaggableOn::Tagging',
+ after_add: :dirtify_tag_list,
+ after_remove: :dirtify_tag_list
+ has_many :topics, -> { order("#{ActsAsTaggableOn::Tagging.table_name}.id") },
+ class_name: 'ActsAsTaggableOn::Tag',
+ through: :topic_taggings,
+ source: :tag
+ has_many :tags, -> { order("#{ActsAsTaggableOn::Tagging.table_name}.id") },
+ class_name: 'ActsAsTaggableOn::Tag',
+ through: :topic_taggings,
+ source: :tag
+
+ # Overwriting 'topic_list' and 'topic_list=' is necessary to ensure functionality during the background migration [1].
+ # [1] https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61237
+ # TODO: remove 'topic_list' and 'topic_list=' once the background migration is complete
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/331081
+ def topic_list
+ # Return both old topics (context 'tags') and new topics (context 'topics')
+ tag_list_on('tags') + tag_list_on('topics')
+ end
+
+ def topic_list=(new_tags)
+ # Old topics with context 'tags' are added as new topics with context 'topics'
+ super(new_tags)
+
+ # Remove old topics with context 'tags'
+ set_tag_list_on('tags', '')
+ end
attr_accessor :old_path_with_namespace
attr_accessor :template_name
@@ -151,26 +182,26 @@ class Project < ApplicationRecord
has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event'
has_many :boards
- # Project services
- has_one :campfire_service
- has_one :datadog_service
+ # Project integrations
+ has_one :asana_service, class_name: 'Integrations::Asana'
+ has_one :assembla_service, class_name: 'Integrations::Assembla'
+ has_one :bamboo_service, class_name: 'Integrations::Bamboo'
+ has_one :campfire_service, class_name: 'Integrations::Campfire'
+ has_one :confluence_service, class_name: 'Integrations::Confluence'
+ has_one :datadog_service, class_name: 'Integrations::Datadog'
+ has_one :emails_on_push_service, class_name: 'Integrations::EmailsOnPush'
has_one :discord_service
has_one :drone_ci_service
- has_one :emails_on_push_service
has_one :ewm_service
has_one :pipelines_email_service
has_one :irker_service
has_one :pivotaltracker_service
- has_one :hipchat_service
has_one :flowdock_service
- has_one :assembla_service
- has_one :asana_service
has_one :mattermost_slash_commands_service
has_one :mattermost_service
has_one :slack_slash_commands_service
has_one :slack_service
has_one :buildkite_service
- has_one :bamboo_service
has_one :teamcity_service
has_one :pushover_service
has_one :jenkins_service
@@ -179,7 +210,6 @@ class Project < ApplicationRecord
has_one :youtrack_service
has_one :custom_issue_tracker_service
has_one :bugzilla_service
- has_one :confluence_service
has_one :external_wiki_service
has_one :prometheus_service, inverse_of: :project
has_one :mock_ci_service
@@ -227,7 +257,7 @@ class Project < ApplicationRecord
has_many :source_of_merge_requests, foreign_key: 'source_project_id', class_name: 'MergeRequest'
has_many :issues
has_many :labels, class_name: 'ProjectLabel'
- has_many :services
+ has_many :integrations
has_many :events
has_many :milestones
has_many :iterations
@@ -338,7 +368,8 @@ class Project < ApplicationRecord
has_one :ci_cd_settings, class_name: 'ProjectCiCdSetting', inverse_of: :project, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :remote_mirrors, inverse_of: :project
- has_many :cycle_analytics_stages, class_name: 'Analytics::CycleAnalytics::ProjectStage'
+ has_many :cycle_analytics_stages, class_name: 'Analytics::CycleAnalytics::ProjectStage', inverse_of: :project
+ has_many :value_streams, class_name: 'Analytics::CycleAnalytics::ProjectValueStream', inverse_of: :project
has_many :external_pull_requests, inverse_of: :project
@@ -371,6 +402,8 @@ class Project < ApplicationRecord
has_one :operations_feature_flags_client, class_name: 'Operations::FeatureFlagsClient'
has_many :operations_feature_flags_user_lists, class_name: 'Operations::FeatureFlags::UserList'
+ has_many :timelogs
+
accepts_nested_attributes_for :variables, allow_destroy: true
accepts_nested_attributes_for :project_feature, update_only: true
accepts_nested_attributes_for :project_setting, update_only: true
@@ -528,7 +561,7 @@ class Project < ApplicationRecord
scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct }
scope :with_push, -> { joins(:events).merge(Event.pushed_action) }
scope :with_project_feature, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id') }
- scope :with_active_jira_services, -> { joins(:services).merge(::JiraService.active) } # rubocop:disable CodeReuse/ServiceClass
+ scope :with_active_jira_services, -> { joins(:integrations).merge(::JiraService.active) } # rubocop:disable CodeReuse/ServiceClass
scope :with_jira_dvcs_cloud, -> { joins(:feature_usage).merge(ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: true)) }
scope :with_jira_dvcs_server, -> { joins(:feature_usage).merge(ProjectFeatureUsage.with_jira_dvcs_integration_enabled(cloud: false)) }
scope :inc_routes, -> { includes(:route, namespace: :route) }
@@ -619,7 +652,7 @@ class Project < ApplicationRecord
mount_uploader :bfg_object_map, AttachmentUploader
def self.with_api_entity_associations
- preload(:project_feature, :route, :tags, :group, namespace: [:route, :owner])
+ preload(:project_feature, :route, :tags, :group, :timelogs, namespace: [:route, :owner])
end
def self.with_web_entity_associations
@@ -832,6 +865,10 @@ class Project < ApplicationRecord
super
end
+ def parent_loaded?
+ association(:namespace).loaded?
+ end
+
def project_setting
super.presence || build_project_setting
end
@@ -1005,7 +1042,7 @@ class Project < ApplicationRecord
end
def latest_successful_build_for_ref!(job_name, ref = default_branch)
- latest_successful_build_for_ref(job_name, ref) || raise(ActiveRecord::RecordNotFound.new("Couldn't find job #{job_name}"))
+ latest_successful_build_for_ref(job_name, ref) || raise(ActiveRecord::RecordNotFound, "Couldn't find job #{job_name}")
end
def latest_pipeline(ref = default_branch, sha = nil)
@@ -1098,7 +1135,7 @@ class Project < ApplicationRecord
else
super
end
- rescue
+ rescue StandardError
super
end
@@ -1342,7 +1379,7 @@ class Project < ApplicationRecord
return unless has_external_issue_tracker?
- @external_issue_tracker ||= services.external_issue_trackers.first
+ @external_issue_tracker ||= integrations.external_issue_trackers.first
end
def external_references_supported?
@@ -1358,11 +1395,11 @@ class Project < ApplicationRecord
return unless has_external_wiki?
- @external_wiki ||= services.external_wikis.first
+ @external_wiki ||= integrations.external_wikis.first
end
def find_or_initialize_services
- available_services_names = Service.available_services_names - disabled_services
+ available_services_names = Integration.available_services_names - disabled_services
available_services_names.map do |service_name|
find_or_initialize_service(service_name)
@@ -1378,7 +1415,7 @@ class Project < ApplicationRecord
def find_or_initialize_service(name)
return if disabled_services.include?(name)
- find_service(services, name) || build_from_instance_or_template(name) || build_service(name)
+ find_service(integrations, name) || build_from_instance_or_template(name) || build_service(name)
end
# rubocop: disable CodeReuse/ServiceClass
@@ -1391,7 +1428,7 @@ class Project < ApplicationRecord
# rubocop: enable CodeReuse/ServiceClass
def ci_services
- services.where(category: :ci)
+ integrations.where(category: :ci)
end
def ci_service
@@ -1399,7 +1436,7 @@ class Project < ApplicationRecord
end
def monitoring_services
- services.where(category: :monitoring)
+ integrations.where(category: :monitoring)
end
def monitoring_service
@@ -1477,8 +1514,8 @@ class Project < ApplicationRecord
def execute_services(data, hooks_scope = :push_hooks)
# Call only service hooks that are active for this scope
run_after_commit_or_now do
- services.public_send(hooks_scope).each do |service| # rubocop:disable GitlabSecurity/PublicSend
- service.async_execute(data)
+ integrations.public_send(hooks_scope).each do |integration| # rubocop:disable GitlabSecurity/PublicSend
+ integration.async_execute(data)
end
end
end
@@ -1488,7 +1525,7 @@ class Project < ApplicationRecord
end
def has_active_services?(hooks_scope = :push_hooks)
- services.public_send(hooks_scope).any? # rubocop:disable GitlabSecurity/PublicSend
+ integrations.public_send(hooks_scope).any? # rubocop:disable GitlabSecurity/PublicSend
end
def feature_usage
@@ -1560,7 +1597,7 @@ class Project < ApplicationRecord
repository.after_create
true
- rescue => err
+ rescue StandardError => err
Gitlab::ErrorTracking.track_exception(err, project: { id: id, full_path: full_path, disk_path: disk_path })
errors.add(:base, _('Failed to create repository'))
false
@@ -2417,7 +2454,7 @@ class Project < ApplicationRecord
end
def access_request_approvers_to_be_notified
- members.maintainers.order_recent_sign_in.limit(ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT)
+ members.maintainers.connected_to_user.order_recent_sign_in.limit(ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT)
end
def pages_lookup_path(trim_prefix: nil, domain: nil)
@@ -2529,12 +2566,14 @@ class Project < ApplicationRecord
namespace.root_ancestor.all_projects
.joins(:packages)
.where.not(id: id)
- .merge(Packages::Package.with_name(package_name))
+ .merge(Packages::Package.default_scoped.with_name(package_name))
.exists?
end
- def default_branch_or_master
- default_branch || 'master'
+ def default_branch_or_main
+ return default_branch if default_branch
+
+ Gitlab::DefaultBranch.value(object: self)
end
def ci_config_path_or_default
@@ -2569,6 +2608,16 @@ class Project < ApplicationRecord
Feature.enabled?(:inherited_issuable_templates, self, default_enabled: :yaml)
end
+ def activity_path
+ Gitlab::Routing.url_helpers.activity_project_path(self)
+ end
+
+ def increment_statistic_value(statistic, delta)
+ return if pending_delete?
+
+ ProjectStatistics.increment_statistic(self, statistic, delta)
+ end
+
private
def set_container_registry_access_level
@@ -2591,22 +2640,22 @@ class Project < ApplicationRecord
def build_from_instance_or_template(name)
instance = find_service(services_instances, name)
- return Service.build_from_integration(instance, project_id: id) if instance
+ return Integration.build_from_integration(instance, project_id: id) if instance
template = find_service(services_templates, name)
- return Service.build_from_integration(template, project_id: id) if template
+ return Integration.build_from_integration(template, project_id: id) if template
end
def build_service(name)
- "#{name}_service".classify.constantize.new(project_id: id)
+ Integration.service_name_to_model(name).new(project_id: id)
end
def services_templates
- @services_templates ||= Service.for_template
+ @services_templates ||= Integration.for_template
end
def services_instances
- @services_instances ||= Service.for_instance
+ @services_instances ||= Integration.for_instance
end
def closest_namespace_setting(name)
@@ -2664,7 +2713,7 @@ class Project < ApplicationRecord
def cross_namespace_reference?(from)
case from
when Project
- namespace != from.namespace
+ namespace_id != from.namespace_id
when Namespace
namespace != from
when User
@@ -2743,11 +2792,11 @@ class Project < ApplicationRecord
end
def cache_has_external_wiki
- update_column(:has_external_wiki, services.external_wikis.any?) if Gitlab::Database.read_write?
+ update_column(:has_external_wiki, integrations.external_wikis.any?) if Gitlab::Database.read_write?
end
def cache_has_external_issue_tracker
- update_column(:has_external_issue_tracker, services.external_issue_trackers.any?) if Gitlab::Database.read_write?
+ update_column(:has_external_issue_tracker, integrations.external_issue_trackers.any?) if Gitlab::Database.read_write?
end
def active_runners_with_tags
@@ -2759,4 +2808,4 @@ class Project < ApplicationRecord
end
end
-Project.prepend_if_ee('EE::Project')
+Project.prepend_mod_with('Project')