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.rb168
1 files changed, 103 insertions, 65 deletions
diff --git a/app/models/project.rb b/app/models/project.rb
index cf740de9405..7735f23cb9e 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'carrierwave/orm/activerecord'
class Project < ActiveRecord::Base
@@ -31,6 +33,7 @@ class Project < ActiveRecord::Base
BoardLimitExceeded = Class.new(StandardError)
+ STATISTICS_ATTRIBUTE = 'repositories_count'.freeze
NUMBER_OF_PERMITTED_BOARDS = 1
UNKNOWN_IMPORT_URL = 'http://unknown.git'.freeze
# Hashed Storage versions handle rolling out new storage to project and dependents models:
@@ -79,6 +82,10 @@ class Project < ActiveRecord::Base
after_create :create_project_feature, unless: :project_feature
+ after_create -> { SiteStatistic.track(STATISTICS_ATTRIBUTE) }
+ before_destroy ->(project) { project.project_feature.untrack_statistics_for_deletion! }
+ after_destroy -> { SiteStatistic.untrack(STATISTICS_ATTRIBUTE) }
+
after_create :create_ci_cd_settings,
unless: :ci_cd_settings,
if: proc { ProjectCiCdSetting.available? }
@@ -154,6 +161,7 @@ class Project < ActiveRecord::Base
has_one :mock_monitoring_service
has_one :microsoft_teams_service
has_one :packagist_service
+ has_one :hangouts_chat_service
# TODO: replace these relations with the fork network versions
has_one :forked_project_link, foreign_key: "forked_to_project_id"
@@ -171,6 +179,7 @@ class Project < ActiveRecord::Base
has_one :fork_network, through: :fork_network_member
has_one :import_state, autosave: true, class_name: 'ProjectImportState', inverse_of: :project
+ has_one :import_export_upload, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
# Merge Requests for target project should be removed with it
has_many :merge_requests, foreign_key: 'target_project_id'
@@ -185,6 +194,7 @@ class Project < ActiveRecord::Base
has_many :hooks, class_name: 'ProjectHook'
has_many :protected_branches
has_many :protected_tags
+ has_many :repository_languages, -> { order "share DESC" }
has_many :project_authorizations
has_many :authorized_users, through: :project_authorizations, source: :user, class_name: 'User'
@@ -268,7 +278,8 @@ class Project < ActiveRecord::Base
delegate :name, to: :owner, allow_nil: true, prefix: true
delegate :members, to: :team, prefix: true
delegate :add_user, :add_users, to: :team
- delegate :add_guest, :add_reporter, :add_developer, :add_master, :add_role, to: :team
+ delegate :add_guest, :add_reporter, :add_developer, :add_maintainer, :add_role, to: :team
+ delegate :add_master, to: :team # @deprecated
delegate :group_runners_enabled, :group_runners_enabled=, :group_runners_enabled?, to: :ci_cd_settings
# Validations
@@ -324,6 +335,7 @@ class Project < ActiveRecord::Base
scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
scope :starred_by, ->(user) { joins(:users_star_projects).where('users_star_projects.user_id': user.id) }
scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) }
+ 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)) }
scope :archived, -> { where(archived: true) }
scope :non_archived, -> { where(archived: false) }
scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct }
@@ -366,8 +378,10 @@ class Project < ActiveRecord::Base
chronic_duration_attr :build_timeout_human_readable, :build_timeout, default: 3600
validates :build_timeout, allow_nil: true,
- numericality: { greater_than_or_equal_to: 600,
- message: 'needs to be at least 10 minutes' }
+ numericality: { greater_than_or_equal_to: 10.minutes,
+ less_than: 1.month,
+ only_integer: true,
+ message: 'needs to be beetween 10 minutes and 1 month' }
# Returns a collection of projects that is either public or visible to the
# logged in user.
@@ -511,6 +525,10 @@ class Project < ActiveRecord::Base
end
end
+ def has_auto_devops_implicitly_enabled?
+ auto_devops&.enabled.nil? && Gitlab::CurrentSettings.auto_devops_enabled?
+ end
+
def has_auto_devops_implicitly_disabled?
auto_devops&.enabled.nil? && !Gitlab::CurrentSettings.auto_devops_enabled?
end
@@ -555,6 +573,10 @@ class Project < ActiveRecord::Base
repository.commit_by(oid: oid)
end
+ def commits_by(oids:)
+ repository.commits_by(oids: oids)
+ end
+
# ref can't be HEAD, can only be branch/tag name or SHA
def latest_successful_builds_for(ref = default_branch)
latest_pipeline = pipelines.latest_successful_for(ref)
@@ -654,6 +676,8 @@ class Project < ActiveRecord::Base
project_import_data.credentials ||= {}
project_import_data.credentials = project_import_data.credentials.merge(credentials)
end
+
+ project_import_data
end
def import?
@@ -1249,8 +1273,6 @@ class Project < ActiveRecord::Base
return true if skip_disk_validation
return false unless repository_storage
- expires_full_path_cache # we need to clear cache to validate renames correctly
-
# Check if repository with same path already exists on disk we can
# skip this for the hashed storage because the path does not change
if legacy_storage? && repository_with_same_path_already_exists?
@@ -1326,14 +1348,6 @@ class Project < ActiveRecord::Base
:visibility_level
end
- def archive!
- update_attribute(:archived, true)
- end
-
- def unarchive!
- update_attribute(:archived, false)
- end
-
def change_head(branch)
if repository.branch_exists?(branch)
repository.before_change_head
@@ -1444,7 +1458,7 @@ class Project < ActiveRecord::Base
end
def shared_runners
- @shared_runners ||= shared_runners_available? ? Ci::Runner.shared : Ci::Runner.none
+ @shared_runners ||= shared_runners_available? ? Ci::Runner.instance_type : Ci::Runner.none
end
def group_runners
@@ -1574,47 +1588,33 @@ class Project < ActiveRecord::Base
end
def rename_repo
- new_full_path = build_full_path
+ path_before = previous_changes['path'].first
+ full_path_before = full_path_was
+ full_path_after = build_full_path
- Rails.logger.error "Attempting to rename #{full_path_was} -> #{new_full_path}"
+ Gitlab::AppLogger.info("Attempting to rename #{full_path_was} -> #{full_path_after}")
if has_container_registry_tags?
- Rails.logger.error "Project #{full_path_was} cannot be renamed because container registry tags are present!"
+ Gitlab::AppLogger.info("Project #{full_path_was} cannot be renamed because container registry tags are present!")
- # we currently doesn't support renaming repository if it contains images in container registry
+ # we currently don't support renaming repository if it contains images in container registry
raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
end
- expire_caches_before_rename(full_path_was)
+ expire_caches_before_rename(full_path_before)
- if storage.rename_repo
- Gitlab::AppLogger.info "Project was renamed: #{full_path_was} -> #{new_full_path}"
- rename_repo_notify!
- after_rename_repo
+ if rename_or_migrate_repository!
+ Gitlab::AppLogger.info("Project was renamed: #{full_path_before} -> #{full_path_after}")
+ after_rename_repository(full_path_before, path_before)
else
- Rails.logger.error "Repository could not be renamed: #{full_path_was} -> #{new_full_path}"
+ Gitlab::AppLogger.info("Repository could not be renamed: #{full_path_before} -> #{full_path_after}")
# if we cannot move namespace directory we should rollback
# db changes in order to prevent out of sync between db and fs
- raise StandardError.new('repository cannot be renamed')
+ raise StandardError.new('Repository cannot be renamed')
end
end
- def after_rename_repo
- write_repository_config
-
- path_before_change = previous_changes['path'].first
-
- # We need to check if project had been rolled out to move resource to hashed storage or not and decide
- # if we need execute any take action or no-op.
-
- unless hashed_storage?(:attachments)
- Gitlab::UploadsTransfer.new.rename_project(path_before_change, self.path, namespace.full_path)
- end
-
- Gitlab::PagesTransfer.new.rename_project(path_before_change, self.path, namespace.full_path)
- end
-
def write_repository_config(gl_full_path: full_path)
# We'd need to keep track of project full path otherwise directory tree
# created with hashed storage enabled cannot be usefully imported using
@@ -1625,18 +1625,6 @@ class Project < ActiveRecord::Base
nil
end
- def rename_repo_notify!
- # When we import a project overwriting the original project, there
- # is a move operation. In that case we don't want to send the instructions.
- send_move_instructions(full_path_was) unless import_started?
- expires_full_path_cache
-
- self.old_path_with_namespace = full_path_was
- SystemHooksService.new.execute_hooks_for(self, :rename)
-
- reload_repository!
- end
-
def after_import
repository.after_import
wiki.repository.after_import
@@ -1668,10 +1656,10 @@ class Project < ActiveRecord::Base
params = {
name: default_branch,
push_access_levels_attributes: [{
- access_level: Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_PUSH ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER
+ access_level: Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_PUSH ? Gitlab::Access::DEVELOPER : Gitlab::Access::MAINTAINER
}],
merge_access_levels_attributes: [{
- access_level: Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE ? Gitlab::Access::DEVELOPER : Gitlab::Access::MASTER
+ access_level: Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE ? Gitlab::Access::DEVELOPER : Gitlab::Access::MAINTAINER
}]
}
@@ -1734,7 +1722,7 @@ class Project < ActiveRecord::Base
:started
elsif after_export_in_progress?
:after_export_action
- elsif export_project_path
+ elsif export_project_path || export_project_object_exists?
:finished
else
:none
@@ -1749,16 +1737,21 @@ class Project < ActiveRecord::Base
import_export_shared.after_export_in_progress?
end
- def remove_exports
- return nil unless export_path.present?
-
- FileUtils.rm_rf(export_path)
+ def remove_exports(path = export_path)
+ if path.present?
+ FileUtils.rm_rf(path)
+ elsif export_project_object_exists?
+ import_export_upload.remove_export_file!
+ import_export_upload.save
+ end
end
def remove_exported_project_file
- return unless export_project_path.present?
+ remove_exports(export_project_path)
+ end
- FileUtils.rm_f(export_project_path)
+ def export_project_object_exists?
+ Gitlab::ImportExport.object_storage? && import_export_upload&.export_file&.file
end
def full_path_slug
@@ -1796,6 +1789,15 @@ class Project < ActiveRecord::Base
end
end
+ def default_environment
+ production_first = "(CASE WHEN name = 'production' THEN 0 ELSE 1 END), id ASC"
+
+ environments
+ .with_state(:available)
+ .reorder(production_first)
+ .first
+ end
+
def secret_variables_for(ref:, environment: nil)
# EE would use the environment
if protected_for?(ref)
@@ -2047,6 +2049,39 @@ class Project < ActiveRecord::Base
private
+ def rename_or_migrate_repository!
+ if Gitlab::CurrentSettings.hashed_storage_enabled? && storage_version != LATEST_STORAGE_VERSION
+ ::Projects::HashedStorageMigrationService.new(self, full_path_was).execute
+ else
+ storage.rename_repo
+ end
+ end
+
+ def after_rename_repository(full_path_before, path_before)
+ execute_rename_repository_hooks!(full_path_before)
+
+ write_repository_config
+
+ # We need to check if project had been rolled out to move resource to hashed storage or not and decide
+ # if we need execute any take action or no-op.
+ unless hashed_storage?(:attachments)
+ Gitlab::UploadsTransfer.new.rename_project(path_before, self.path, namespace.full_path)
+ end
+
+ Gitlab::PagesTransfer.new.rename_project(path_before, self.path, namespace.full_path)
+ end
+
+ def execute_rename_repository_hooks!(full_path_before)
+ # When we import a project overwriting the original project, there
+ # is a move operation. In that case we don't want to send the instructions.
+ send_move_instructions(full_path_before) unless import_started?
+
+ self.old_path_with_namespace = full_path_before
+ SystemHooksService.new.execute_hooks_for(self, :rename)
+
+ reload_repository!
+ end
+
def storage
@storage ||=
if hashed_storage?(:repository)
@@ -2175,10 +2210,13 @@ class Project < ActiveRecord::Base
merge_requests = source_of_merge_requests.opened
.where(allow_collaboration: true)
- if branch_name
- merge_requests.find_by(source_branch: branch_name)&.can_be_merged_by?(user)
- else
- merge_requests.any? { |merge_request| merge_request.can_be_merged_by?(user) }
+ # Issue for N+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/49322
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ if branch_name
+ merge_requests.find_by(source_branch: branch_name)&.can_be_merged_by?(user)
+ else
+ merge_requests.any? { |merge_request| merge_request.can_be_merged_by?(user) }
+ end
end
end