diff options
Diffstat (limited to 'app/models')
165 files changed, 909 insertions, 565 deletions
diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb index 1b78fd04ebb..a3a1748142f 100644 --- a/app/models/abuse_report.rb +++ b/app/models/abuse_report.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class AbuseReport < ActiveRecord::Base +class AbuseReport < ApplicationRecord include CacheMarkdownField cache_markdown_field :message, pipeline: :single_line diff --git a/app/models/appearance.rb b/app/models/appearance.rb index bdee9b2b73c..2815a117f7f 100644 --- a/app/models/appearance.rb +++ b/app/models/appearance.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Appearance < ActiveRecord::Base +class Appearance < ApplicationRecord include CacheableAttributes include CacheMarkdownField include ObjectStorage::BackgroundMove diff --git a/app/models/application_record.rb b/app/models/application_record.rb index a3d662d8250..6976185264e 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -7,6 +7,14 @@ class ApplicationRecord < ActiveRecord::Base where(id: ids) end + def self.id_not_in(ids) + where.not(id: ids) + end + + def self.pluck_primary_key + where(nil).pluck(self.primary_key) + end + def self.safe_find_or_create_by!(*args) safe_find_or_create_by(*args).tap do |record| record.validate! unless record.persisted? diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index cd36c963ee5..9e91e4ab4b9 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -1,26 +1,20 @@ # frozen_string_literal: true -class ApplicationSetting < ActiveRecord::Base +class ApplicationSetting < ApplicationRecord include CacheableAttributes include CacheMarkdownField include TokenAuthenticatable include IgnorableColumn include ChronicDurationAttribute - add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption) ? :optional : :required } + add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption, default_enabled: true) ? :optional : :required } add_authentication_token_field :health_check_access_token - DOMAIN_LIST_SEPARATOR = %r{\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace - | # or - \s # any whitespace character - | # or - [\r\n] # any number of newline characters - }x - - # Setting a key restriction to `-1` means that all keys of this type are - # forbidden. - FORBIDDEN_KEY_VALUE = KeyRestrictionValidator::FORBIDDEN - SUPPORTED_KEY_TYPES = %i[rsa dsa ecdsa ed25519].freeze + # Include here so it can override methods from + # `add_authentication_token_field` + # We don't prepend for now because otherwise we'll need to + # fix a lot of tests using allow_any_instance_of + include ApplicationSettingImplementation serialize :restricted_visibility_levels # rubocop:disable Cop/ActiveRecordSerialize serialize :import_sources # rubocop:disable Cop/ActiveRecordSerialize @@ -42,8 +36,6 @@ class ApplicationSetting < ActiveRecord::Base cache_markdown_field :shared_runners_text, pipeline: :plain_markdown cache_markdown_field :after_sign_up_text - attr_accessor :domain_whitelist_raw, :domain_blacklist_raw - default_value_for :id, 1 chronic_duration_attr_writer :archive_builds_in_human_readable, :archive_builds_in_seconds @@ -231,266 +223,4 @@ class ApplicationSetting < ActiveRecord::Base reset_memoized_terms end after_commit :expire_performance_bar_allowed_user_ids_cache, if: -> { previous_changes.key?('performance_bar_allowed_group_id') } - - def self.defaults - { - after_sign_up_text: nil, - akismet_enabled: false, - allow_local_requests_from_hooks_and_services: false, - authorized_keys_enabled: true, # TODO default to false if the instance is configured to use AuthorizedKeysCommand - container_registry_token_expire_delay: 5, - default_artifacts_expire_in: '30 days', - default_branch_protection: Settings.gitlab['default_branch_protection'], - default_group_visibility: Settings.gitlab.default_projects_features['visibility_level'], - default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], - default_projects_limit: Settings.gitlab['default_projects_limit'], - default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'], - disabled_oauth_sign_in_sources: [], - domain_whitelist: Settings.gitlab['domain_whitelist'], - dsa_key_restriction: 0, - ecdsa_key_restriction: 0, - ed25519_key_restriction: 0, - first_day_of_week: 0, - gitaly_timeout_default: 55, - gitaly_timeout_fast: 10, - gitaly_timeout_medium: 30, - gravatar_enabled: Settings.gravatar['enabled'], - help_page_hide_commercial_content: false, - help_page_text: nil, - hide_third_party_offers: false, - housekeeping_bitmaps_enabled: true, - housekeeping_enabled: true, - housekeeping_full_repack_period: 50, - housekeeping_gc_period: 200, - housekeeping_incremental_repack_period: 10, - import_sources: Settings.gitlab['import_sources'], - max_artifacts_size: Settings.artifacts['max_size'], - max_attachment_size: Settings.gitlab['max_attachment_size'], - mirror_available: true, - password_authentication_enabled_for_git: true, - password_authentication_enabled_for_web: Settings.gitlab['signin_enabled'], - performance_bar_allowed_group_id: nil, - rsa_key_restriction: 0, - plantuml_enabled: false, - plantuml_url: nil, - polling_interval_multiplier: 1, - project_export_enabled: true, - recaptcha_enabled: false, - repository_checks_enabled: true, - repository_storages: ['default'], - require_two_factor_authentication: false, - restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'], - session_expire_delay: Settings.gitlab['session_expire_delay'], - send_user_confirmation_email: false, - shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], - shared_runners_text: nil, - sign_in_text: nil, - signup_enabled: Settings.gitlab['signup_enabled'], - terminal_max_session_time: 0, - throttle_authenticated_api_enabled: false, - throttle_authenticated_api_period_in_seconds: 3600, - throttle_authenticated_api_requests_per_period: 7200, - throttle_authenticated_web_enabled: false, - throttle_authenticated_web_period_in_seconds: 3600, - throttle_authenticated_web_requests_per_period: 7200, - throttle_unauthenticated_enabled: false, - throttle_unauthenticated_period_in_seconds: 3600, - throttle_unauthenticated_requests_per_period: 3600, - two_factor_grace_period: 48, - unique_ips_limit_enabled: false, - unique_ips_limit_per_user: 10, - unique_ips_limit_time_window: 3600, - usage_ping_enabled: Settings.gitlab['usage_ping_enabled'], - instance_statistics_visibility_private: false, - user_default_external: false, - user_default_internal_regex: nil, - user_show_add_ssh_key_message: true, - usage_stats_set_by_user_id: nil, - diff_max_patch_bytes: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES, - commit_email_hostname: default_commit_email_hostname, - protected_ci_variables: false, - local_markdown_version: 0 - } - end - - def self.default_commit_email_hostname - "users.noreply.#{Gitlab.config.gitlab.host}" - end - - def self.create_from_defaults - build_from_defaults.tap(&:save) - end - - def self.human_attribute_name(attr, _options = {}) - if attr == :default_artifacts_expire_in - 'Default artifacts expiration' - else - super - end - end - - def home_page_url_column_exists? - ::Gitlab::Database.cached_column_exists?(:application_settings, :home_page_url) - end - - def help_page_support_url_column_exists? - ::Gitlab::Database.cached_column_exists?(:application_settings, :help_page_support_url) - end - - def disabled_oauth_sign_in_sources=(sources) - sources = (sources || []).map(&:to_s) & Devise.omniauth_providers.map(&:to_s) - super(sources) - end - - def domain_whitelist_raw - self.domain_whitelist&.join("\n") - end - - def domain_blacklist_raw - self.domain_blacklist&.join("\n") - end - - def domain_whitelist_raw=(values) - self.domain_whitelist = [] - self.domain_whitelist = values.split(DOMAIN_LIST_SEPARATOR) - self.domain_whitelist.reject! { |d| d.empty? } - self.domain_whitelist - end - - def domain_blacklist_raw=(values) - self.domain_blacklist = [] - self.domain_blacklist = values.split(DOMAIN_LIST_SEPARATOR) - self.domain_blacklist.reject! { |d| d.empty? } - self.domain_blacklist - end - - def domain_blacklist_file=(file) - self.domain_blacklist_raw = file.read - end - - def repository_storages - Array(read_attribute(:repository_storages)) - end - - def commit_email_hostname - super.presence || self.class.default_commit_email_hostname - end - - def default_project_visibility=(level) - super(Gitlab::VisibilityLevel.level_value(level)) - end - - def default_snippet_visibility=(level) - super(Gitlab::VisibilityLevel.level_value(level)) - end - - def default_group_visibility=(level) - super(Gitlab::VisibilityLevel.level_value(level)) - end - - def restricted_visibility_levels=(levels) - super(levels&.map { |level| Gitlab::VisibilityLevel.level_value(level) }) - end - - def strip_sentry_values - sentry_dsn.strip! if sentry_dsn.present? - clientside_sentry_dsn.strip! if clientside_sentry_dsn.present? - end - - def performance_bar_allowed_group - Group.find_by_id(performance_bar_allowed_group_id) - end - - # Return true if the Performance Bar is enabled for a given group - def performance_bar_enabled - performance_bar_allowed_group_id.present? - end - - # Choose one of the available repository storage options. Currently all have - # equal weighting. - def pick_repository_storage - repository_storages.sample - end - - def runners_registration_token - ensure_runners_registration_token! - end - - def health_check_access_token - ensure_health_check_access_token! - end - - def usage_ping_can_be_configured? - Settings.gitlab.usage_ping_enabled - end - - def usage_ping_enabled - usage_ping_can_be_configured? && super - end - - def allowed_key_types - SUPPORTED_KEY_TYPES.select do |type| - key_restriction_for(type) != FORBIDDEN_KEY_VALUE - end - end - - def key_restriction_for(type) - attr_name = "#{type}_key_restriction" - - has_attribute?(attr_name) ? public_send(attr_name) : FORBIDDEN_KEY_VALUE # rubocop:disable GitlabSecurity/PublicSend - end - - def allow_signup? - signup_enabled? && password_authentication_enabled_for_web? - end - - def password_authentication_enabled? - password_authentication_enabled_for_web? || password_authentication_enabled_for_git? - end - - def user_default_internal_regex_enabled? - user_default_external? && user_default_internal_regex.present? - end - - def user_default_internal_regex_instance - Regexp.new(user_default_internal_regex, Regexp::IGNORECASE) - end - - delegate :terms, to: :latest_terms, allow_nil: true - def latest_terms - @latest_terms ||= Term.latest - end - - def reset_memoized_terms - @latest_terms = nil - latest_terms - end - - def archive_builds_older_than - archive_builds_in_seconds.seconds.ago if archive_builds_in_seconds - end - - private - - def ensure_uuid! - return if uuid? - - self.uuid = SecureRandom.uuid - end - - def check_repository_storages - invalid = repository_storages - Gitlab.config.repositories.storages.keys - errors.add(:repository_storages, "can't include: #{invalid.join(", ")}") unless - invalid.empty? - end - - def terms_exist - return unless enforce_terms? - - errors.add(:terms, "You need to set terms to be enforced") unless terms.present? - end - - def expire_performance_bar_allowed_user_ids_cache - Gitlab::PerformanceBar.expire_allowed_user_ids_cache - end end diff --git a/app/models/application_setting/term.rb b/app/models/application_setting/term.rb index 498701ba22b..723540c9b91 100644 --- a/app/models/application_setting/term.rb +++ b/app/models/application_setting/term.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class ApplicationSetting - class Term < ActiveRecord::Base + class Term < ApplicationRecord include CacheMarkdownField has_many :term_agreements diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb new file mode 100644 index 00000000000..265aa1d4965 --- /dev/null +++ b/app/models/application_setting_implementation.rb @@ -0,0 +1,281 @@ +# frozen_string_literal: true + +module ApplicationSettingImplementation + extend ActiveSupport::Concern + + DOMAIN_LIST_SEPARATOR = %r{\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace + | # or + \s # any whitespace character + | # or + [\r\n] # any number of newline characters + }x + + # Setting a key restriction to `-1` means that all keys of this type are + # forbidden. + FORBIDDEN_KEY_VALUE = KeyRestrictionValidator::FORBIDDEN + SUPPORTED_KEY_TYPES = %i[rsa dsa ecdsa ed25519].freeze + + class_methods do + def defaults + { + after_sign_up_text: nil, + akismet_enabled: false, + allow_local_requests_from_hooks_and_services: false, + authorized_keys_enabled: true, # TODO default to false if the instance is configured to use AuthorizedKeysCommand + container_registry_token_expire_delay: 5, + default_artifacts_expire_in: '30 days', + default_branch_protection: Settings.gitlab['default_branch_protection'], + default_group_visibility: Settings.gitlab.default_projects_features['visibility_level'], + default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], + default_projects_limit: Settings.gitlab['default_projects_limit'], + default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'], + disabled_oauth_sign_in_sources: [], + domain_whitelist: Settings.gitlab['domain_whitelist'], + dsa_key_restriction: 0, + ecdsa_key_restriction: 0, + ed25519_key_restriction: 0, + first_day_of_week: 0, + gitaly_timeout_default: 55, + gitaly_timeout_fast: 10, + gitaly_timeout_medium: 30, + gravatar_enabled: Settings.gravatar['enabled'], + help_page_hide_commercial_content: false, + help_page_text: nil, + hide_third_party_offers: false, + housekeeping_bitmaps_enabled: true, + housekeeping_enabled: true, + housekeeping_full_repack_period: 50, + housekeeping_gc_period: 200, + housekeeping_incremental_repack_period: 10, + import_sources: Settings.gitlab['import_sources'], + max_artifacts_size: Settings.artifacts['max_size'], + max_attachment_size: Settings.gitlab['max_attachment_size'], + mirror_available: true, + password_authentication_enabled_for_git: true, + password_authentication_enabled_for_web: Settings.gitlab['signin_enabled'], + performance_bar_allowed_group_id: nil, + rsa_key_restriction: 0, + plantuml_enabled: false, + plantuml_url: nil, + polling_interval_multiplier: 1, + project_export_enabled: true, + recaptcha_enabled: false, + repository_checks_enabled: true, + repository_storages: ['default'], + require_two_factor_authentication: false, + restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'], + session_expire_delay: Settings.gitlab['session_expire_delay'], + send_user_confirmation_email: false, + shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], + shared_runners_text: nil, + sign_in_text: nil, + signup_enabled: Settings.gitlab['signup_enabled'], + terminal_max_session_time: 0, + throttle_authenticated_api_enabled: false, + throttle_authenticated_api_period_in_seconds: 3600, + throttle_authenticated_api_requests_per_period: 7200, + throttle_authenticated_web_enabled: false, + throttle_authenticated_web_period_in_seconds: 3600, + throttle_authenticated_web_requests_per_period: 7200, + throttle_unauthenticated_enabled: false, + throttle_unauthenticated_period_in_seconds: 3600, + throttle_unauthenticated_requests_per_period: 3600, + two_factor_grace_period: 48, + unique_ips_limit_enabled: false, + unique_ips_limit_per_user: 10, + unique_ips_limit_time_window: 3600, + usage_ping_enabled: Settings.gitlab['usage_ping_enabled'], + instance_statistics_visibility_private: false, + user_default_external: false, + user_default_internal_regex: nil, + user_show_add_ssh_key_message: true, + usage_stats_set_by_user_id: nil, + diff_max_patch_bytes: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES, + commit_email_hostname: default_commit_email_hostname, + protected_ci_variables: false, + local_markdown_version: 0 + } + end + + def default_commit_email_hostname + "users.noreply.#{Gitlab.config.gitlab.host}" + end + + def create_from_defaults + build_from_defaults.tap(&:save) + end + + def human_attribute_name(attr, _options = {}) + if attr == :default_artifacts_expire_in + 'Default artifacts expiration' + else + super + end + end + end + + def home_page_url_column_exists? + ::Gitlab::Database.cached_column_exists?(:application_settings, :home_page_url) + end + + def help_page_support_url_column_exists? + ::Gitlab::Database.cached_column_exists?(:application_settings, :help_page_support_url) + end + + def disabled_oauth_sign_in_sources=(sources) + sources = (sources || []).map(&:to_s) & Devise.omniauth_providers.map(&:to_s) + super(sources) + end + + def domain_whitelist_raw + self.domain_whitelist&.join("\n") + end + + def domain_blacklist_raw + self.domain_blacklist&.join("\n") + end + + def domain_whitelist_raw=(values) + self.domain_whitelist = [] + self.domain_whitelist = values.split(DOMAIN_LIST_SEPARATOR) + self.domain_whitelist.reject! { |d| d.empty? } + self.domain_whitelist + end + + def domain_blacklist_raw=(values) + self.domain_blacklist = [] + self.domain_blacklist = values.split(DOMAIN_LIST_SEPARATOR) + self.domain_blacklist.reject! { |d| d.empty? } + self.domain_blacklist + end + + def domain_blacklist_file=(file) + self.domain_blacklist_raw = file.read + end + + def repository_storages + Array(read_attribute(:repository_storages)) + end + + def commit_email_hostname + super.presence || self.class.default_commit_email_hostname + end + + def default_project_visibility=(level) + super(Gitlab::VisibilityLevel.level_value(level)) + end + + def default_snippet_visibility=(level) + super(Gitlab::VisibilityLevel.level_value(level)) + end + + def default_group_visibility=(level) + super(Gitlab::VisibilityLevel.level_value(level)) + end + + def restricted_visibility_levels=(levels) + super(levels&.map { |level| Gitlab::VisibilityLevel.level_value(level) }) + end + + def strip_sentry_values + sentry_dsn.strip! if sentry_dsn.present? + clientside_sentry_dsn.strip! if clientside_sentry_dsn.present? + end + + def performance_bar_allowed_group + Group.find_by_id(performance_bar_allowed_group_id) + end + + # Return true if the Performance Bar is enabled for a given group + def performance_bar_enabled + performance_bar_allowed_group_id.present? + end + + # Choose one of the available repository storage options. Currently all have + # equal weighting. + def pick_repository_storage + repository_storages.sample + end + + def runners_registration_token + ensure_runners_registration_token! + end + + def health_check_access_token + ensure_health_check_access_token! + end + + def usage_ping_can_be_configured? + Settings.gitlab.usage_ping_enabled + end + + def usage_ping_enabled + usage_ping_can_be_configured? && super + end + + def allowed_key_types + SUPPORTED_KEY_TYPES.select do |type| + key_restriction_for(type) != FORBIDDEN_KEY_VALUE + end + end + + def key_restriction_for(type) + attr_name = "#{type}_key_restriction" + + has_attribute?(attr_name) ? public_send(attr_name) : FORBIDDEN_KEY_VALUE # rubocop:disable GitlabSecurity/PublicSend + end + + def allow_signup? + signup_enabled? && password_authentication_enabled_for_web? + end + + def password_authentication_enabled? + password_authentication_enabled_for_web? || password_authentication_enabled_for_git? + end + + def user_default_internal_regex_enabled? + user_default_external? && user_default_internal_regex.present? + end + + def user_default_internal_regex_instance + Regexp.new(user_default_internal_regex, Regexp::IGNORECASE) + end + + delegate :terms, to: :latest_terms, allow_nil: true + def latest_terms + @latest_terms ||= ApplicationSetting::Term.latest + end + + def reset_memoized_terms + @latest_terms = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables + latest_terms + end + + def archive_builds_older_than + archive_builds_in_seconds.seconds.ago if archive_builds_in_seconds + end + + private + + def ensure_uuid! + return if uuid? + + self.uuid = SecureRandom.uuid + end + + def check_repository_storages + invalid = repository_storages - Gitlab.config.repositories.storages.keys + errors.add(:repository_storages, "can't include: #{invalid.join(", ")}") unless + invalid.empty? + end + + def terms_exist + return unless enforce_terms? + + errors.add(:terms, "You need to set terms to be enforced") unless terms.present? + end + + def expire_performance_bar_allowed_user_ids_cache + Gitlab::PerformanceBar.expire_allowed_user_ids_cache + end +end diff --git a/app/models/audit_event.rb b/app/models/audit_event.rb index 8508c88d406..6ef2914ac11 100644 --- a/app/models/audit_event.rb +++ b/app/models/audit_event.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class AuditEvent < ActiveRecord::Base +class AuditEvent < ApplicationRecord serialize :details, Hash # rubocop:disable Cop/ActiveRecordSerialize belongs_to :user, foreign_key: :author_id diff --git a/app/models/award_emoji.rb b/app/models/award_emoji.rb index ddc516ccb60..e26162f6151 100644 --- a/app/models/award_emoji.rb +++ b/app/models/award_emoji.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class AwardEmoji < ActiveRecord::Base +class AwardEmoji < ApplicationRecord DOWNVOTE_NAME = "thumbsdown".freeze UPVOTE_NAME = "thumbsup".freeze diff --git a/app/models/badge.rb b/app/models/badge.rb index f016654206b..a244ed473de 100644 --- a/app/models/badge.rb +++ b/app/models/badge.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Badge < ActiveRecord::Base +class Badge < ApplicationRecord include FromUnion # This structure sets the placeholders that the urls diff --git a/app/models/board.rb b/app/models/board.rb index 758a71d6903..e08db764f65 100644 --- a/app/models/board.rb +++ b/app/models/board.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Board < ActiveRecord::Base +class Board < ApplicationRecord belongs_to :group belongs_to :project diff --git a/app/models/board_group_recent_visit.rb b/app/models/board_group_recent_visit.rb index f5b75270595..2f1cd830791 100644 --- a/app/models/board_group_recent_visit.rb +++ b/app/models/board_group_recent_visit.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Tracks which boards in a specific group a user has visited -class BoardGroupRecentVisit < ActiveRecord::Base +class BoardGroupRecentVisit < ApplicationRecord belongs_to :user belongs_to :group belongs_to :board diff --git a/app/models/board_project_recent_visit.rb b/app/models/board_project_recent_visit.rb index 2a1b14b3ae0..236d88e909c 100644 --- a/app/models/board_project_recent_visit.rb +++ b/app/models/board_project_recent_visit.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # Tracks which boards in a specific project a user has visited -class BoardProjectRecentVisit < ActiveRecord::Base +class BoardProjectRecentVisit < ApplicationRecord belongs_to :user belongs_to :project belongs_to :board diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb index 2d237383e60..18fe2a9624f 100644 --- a/app/models/broadcast_message.rb +++ b/app/models/broadcast_message.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -class BroadcastMessage < ActiveRecord::Base +class BroadcastMessage < ApplicationRecord include CacheMarkdownField include Sortable - cache_markdown_field :message, pipeline: :broadcast_message + cache_markdown_field :message, pipeline: :broadcast_message, whitelisted: true validates :message, presence: true validates :starts_at, presence: true diff --git a/app/models/chat_name.rb b/app/models/chat_name.rb index 03b0af53046..0041595baba 100644 --- a/app/models/chat_name.rb +++ b/app/models/chat_name.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ChatName < ActiveRecord::Base +class ChatName < ApplicationRecord LAST_USED_AT_INTERVAL = 1.hour belongs_to :service diff --git a/app/models/chat_team.rb b/app/models/chat_team.rb index 4e724f9adf7..52b5a7b4a91 100644 --- a/app/models/chat_team.rb +++ b/app/models/chat_team.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ChatTeam < ActiveRecord::Base +class ChatTeam < ApplicationRecord validates :team_id, presence: true validates :namespace, uniqueness: true diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index a629db82c19..1bd517641ac 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -26,7 +26,8 @@ module Ci belongs_to :erased_by, class_name: 'User' RUNNER_FEATURES = { - upload_multiple_artifacts: -> (build) { build.publishes_artifacts_reports? } + upload_multiple_artifacts: -> (build) { build.publishes_artifacts_reports? }, + refspecs: -> (build) { build.merge_request_ref? } }.freeze has_one :deployment, as: :deployable, class_name: 'Deployment' @@ -47,7 +48,8 @@ module Ci delegate :terminal_specification, to: :runner_session, allow_nil: true delegate :gitlab_deploy_token, to: :project delegate :trigger_short_token, to: :trigger_request, allow_nil: true - delegate :merge_request_event?, to: :pipeline + delegate :merge_request_event?, :merge_request_ref?, + :legacy_detached_merge_request_pipeline?, to: :pipeline ## # Since Gitlab 11.5, deployments records started being created right after @@ -172,6 +174,10 @@ module Ci end state_machine :status do + event :enqueue do + transition [:created, :skipped, :manual, :scheduled] => :preparing, if: :any_unmet_prerequisites? + end + event :actionize do transition created: :manual end @@ -185,8 +191,12 @@ module Ci end event :enqueue_scheduled do + transition scheduled: :preparing, if: ->(build) do + build.scheduled_at&.past? && build.any_unmet_prerequisites? + end + transition scheduled: :pending, if: ->(build) do - build.scheduled_at && build.scheduled_at < Time.now + build.scheduled_at&.past? && !build.any_unmet_prerequisites? end end @@ -204,6 +214,12 @@ module Ci end end + after_transition any => [:preparing] do |build| + build.run_after_commit do + Ci::BuildPrepareWorker.perform_async(id) + end + end + after_transition any => [:pending] do |build| build.run_after_commit do BuildQueueWorker.perform_async(id) @@ -355,6 +371,16 @@ module Ci !retried? end + def any_unmet_prerequisites? + return false unless Feature.enabled?(:ci_preparing_state, default_enabled: true) + + prerequisites.present? + end + + def prerequisites + Gitlab::Ci::Build::Prerequisite::Factory.new(self).unmet + end + def expanded_environment_name return unless has_environment? diff --git a/app/models/ci/build_metadata.rb b/app/models/ci/build_metadata.rb index cd8eb774cf5..f281cbd1d6f 100644 --- a/app/models/ci/build_metadata.rb +++ b/app/models/ci/build_metadata.rb @@ -3,7 +3,7 @@ module Ci # The purpose of this class is to store Build related data that can be disposed. # Data that should be persisted forever, should be stored with Ci::Build model. - class BuildMetadata < ActiveRecord::Base + class BuildMetadata < ApplicationRecord extend Gitlab::Ci::Model include Presentable include ChronicDurationAttribute diff --git a/app/models/ci/build_runner_session.rb b/app/models/ci/build_runner_session.rb index 457d7eeab6a..061eff090f5 100644 --- a/app/models/ci/build_runner_session.rb +++ b/app/models/ci/build_runner_session.rb @@ -3,7 +3,7 @@ module Ci # The purpose of this class is to store Build related runner session. # Data will be removed after transitioning from running to any state. - class BuildRunnerSession < ActiveRecord::Base + class BuildRunnerSession < ApplicationRecord extend Gitlab::Ci::Model self.table_name = 'ci_builds_runner_session' diff --git a/app/models/ci/build_trace_chunk.rb b/app/models/ci/build_trace_chunk.rb index 75017f224a0..0a7a0e0772b 100644 --- a/app/models/ci/build_trace_chunk.rb +++ b/app/models/ci/build_trace_chunk.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class BuildTraceChunk < ActiveRecord::Base + class BuildTraceChunk < ApplicationRecord include FastDestroyAll include ::Gitlab::ExclusiveLeaseHelpers extend Gitlab::Ci::Model diff --git a/app/models/ci/build_trace_section.rb b/app/models/ci/build_trace_section.rb index a4bee59c83b..8be42eb48d6 100644 --- a/app/models/ci/build_trace_section.rb +++ b/app/models/ci/build_trace_section.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class BuildTraceSection < ActiveRecord::Base + class BuildTraceSection < ApplicationRecord extend Gitlab::Ci::Model belongs_to :build, class_name: 'Ci::Build' diff --git a/app/models/ci/build_trace_section_name.rb b/app/models/ci/build_trace_section_name.rb index cbdf3c4b673..c065cfea14e 100644 --- a/app/models/ci/build_trace_section_name.rb +++ b/app/models/ci/build_trace_section_name.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class BuildTraceSectionName < ActiveRecord::Base + class BuildTraceSectionName < ApplicationRecord extend Gitlab::Ci::Model belongs_to :project diff --git a/app/models/ci/group_variable.rb b/app/models/ci/group_variable.rb index 323ff560564..0e50265c7ba 100644 --- a/app/models/ci/group_variable.rb +++ b/app/models/ci/group_variable.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class GroupVariable < ActiveRecord::Base + class GroupVariable < ApplicationRecord extend Gitlab::Ci::Model include HasVariable include Presentable diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index 789bb293811..99512a7c1dd 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class JobArtifact < ActiveRecord::Base + class JobArtifact < ApplicationRecord include AfterCommitQueue include ObjectStorage::BackgroundMove extend Gitlab::Ci::Model diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index adffdc0355e..abac16a138d 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class Pipeline < ActiveRecord::Base + class Pipeline < ApplicationRecord extend Gitlab::Ci::Model include HasStatus include Importable @@ -82,10 +82,14 @@ module Ci state_machine :status, initial: :created do event :enqueue do - transition [:created, :skipped, :scheduled] => :pending + transition [:created, :preparing, :skipped, :scheduled] => :pending transition [:success, :failed, :canceled] => :running end + event :prepare do + transition any - [:preparing] => :preparing + end + event :run do transition any - [:running] => :running end @@ -118,7 +122,7 @@ module Ci # Do not add any operations to this state_machine # Create a separate worker for each new operation - before_transition [:created, :pending] => :running do |pipeline| + before_transition [:created, :preparing, :pending] => :running do |pipeline| pipeline.started_at = Time.now end @@ -141,7 +145,7 @@ module Ci end end - after_transition [:created, :pending] => :running do |pipeline| + after_transition [:created, :preparing, :pending] => :running do |pipeline| pipeline.run_after_commit { PipelineMetricsWorker.perform_async(pipeline.id) } end @@ -149,7 +153,7 @@ module Ci pipeline.run_after_commit { PipelineMetricsWorker.perform_async(pipeline.id) } end - after_transition [:created, :pending, :running] => :success do |pipeline| + after_transition [:created, :preparing, :pending, :running] => :success do |pipeline| pipeline.run_after_commit { PipelineSuccessWorker.perform_async(pipeline.id) } end @@ -180,7 +184,7 @@ module Ci scope :sort_by_merge_request_pipelines, -> do sql = 'CASE ci_pipelines.source WHEN (?) THEN 0 ELSE 1 END, ci_pipelines.id DESC' - query = ActiveRecord::Base.send(:sanitize_sql_array, [sql, sources[:merge_request_event]]) # rubocop:disable GitlabSecurity/PublicSend + query = ApplicationRecord.send(:sanitize_sql_array, [sql, sources[:merge_request_event]]) # rubocop:disable GitlabSecurity/PublicSend order(query) end @@ -461,9 +465,9 @@ module Ci end def latest? - return false unless ref && commit.present? + return false unless git_ref && commit.present? - project.commit(ref) == commit + project.commit(git_ref) == commit end def retried @@ -597,6 +601,7 @@ module Ci retry_optimistic_lock(self) do case latest_builds_status.to_s when 'created' then nil + when 'preparing' then prepare when 'pending' then enqueue when 'running' then run when 'success' then succeed @@ -733,6 +738,10 @@ module Ci triggered_by_merge_request? && target_sha.nil? end + def legacy_detached_merge_request_pipeline? + detached_merge_request_pipeline? && !merge_request_ref? + end + def merge_request_pipeline? triggered_by_merge_request? && target_sha.present? end @@ -741,6 +750,14 @@ module Ci triggered_by_merge_request? && target_sha == merge_request.target_branch_sha end + def merge_request_ref? + MergeRequest.merge_request_ref?(ref) + end + + def matches_sha_or_source_sha?(sha) + self.sha == sha || self.source_sha == sha + end + private def ci_yaml_from_repo @@ -772,16 +789,18 @@ module Ci end def git_ref - if merge_request_event? - ## - # In the future, we're going to change this ref to - # merge request's merged reference, such as "refs/merge-requests/:iid/merge". - # In order to do that, we have to update GitLab-Runner's source pulling - # logic. - # See https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/1092 - Gitlab::Git::BRANCH_REF_PREFIX + ref.to_s - else - super + strong_memoize(:git_ref) do + if merge_request_event? + ## + # In the future, we're going to change this ref to + # merge request's merged reference, such as "refs/merge-requests/:iid/merge". + # In order to do that, we have to update GitLab-Runner's source pulling + # logic. + # See https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/1092 + Gitlab::Git::BRANCH_REF_PREFIX + ref.to_s + else + super + end end end diff --git a/app/models/ci/pipeline_chat_data.rb b/app/models/ci/pipeline_chat_data.rb index 8d37500fec5..65466a8c6f8 100644 --- a/app/models/ci/pipeline_chat_data.rb +++ b/app/models/ci/pipeline_chat_data.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class PipelineChatData < ActiveRecord::Base + class PipelineChatData < ApplicationRecord self.table_name = 'ci_pipeline_chat_data' belongs_to :chat_name diff --git a/app/models/ci/pipeline_schedule.rb b/app/models/ci/pipeline_schedule.rb index 1c1f203bdb2..1454b2dfb39 100644 --- a/app/models/ci/pipeline_schedule.rb +++ b/app/models/ci/pipeline_schedule.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class PipelineSchedule < ActiveRecord::Base + class PipelineSchedule < ApplicationRecord extend Gitlab::Ci::Model include Importable include IgnorableColumn diff --git a/app/models/ci/pipeline_schedule_variable.rb b/app/models/ci/pipeline_schedule_variable.rb index fbb9987cab2..be6e5e76c31 100644 --- a/app/models/ci/pipeline_schedule_variable.rb +++ b/app/models/ci/pipeline_schedule_variable.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class PipelineScheduleVariable < ActiveRecord::Base + class PipelineScheduleVariable < ApplicationRecord extend Gitlab::Ci::Model include HasVariable diff --git a/app/models/ci/pipeline_variable.rb b/app/models/ci/pipeline_variable.rb index 08514d6af4e..51a6272e1ff 100644 --- a/app/models/ci/pipeline_variable.rb +++ b/app/models/ci/pipeline_variable.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class PipelineVariable < ActiveRecord::Base + class PipelineVariable < ApplicationRecord extend Gitlab::Ci::Model include HasVariable diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index ce26ee168ef..07d00503861 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class Runner < ActiveRecord::Base + class Runner < ApplicationRecord extend Gitlab::Ci::Model include Gitlab::SQL::Pattern include IgnorableColumn @@ -10,7 +10,7 @@ module Ci include FromUnion include TokenAuthenticatable - add_authentication_token_field :token, encrypted: -> { Feature.enabled?(:ci_runners_tokens_optional_encryption) ? :optional : :required } + add_authentication_token_field :token, encrypted: -> { Feature.enabled?(:ci_runners_tokens_optional_encryption, default_enabled: true) ? :optional : :required } enum access_level: { not_protected: 0, diff --git a/app/models/ci/runner_namespace.rb b/app/models/ci/runner_namespace.rb index 22b80b98551..6903e8a21a1 100644 --- a/app/models/ci/runner_namespace.rb +++ b/app/models/ci/runner_namespace.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class RunnerNamespace < ActiveRecord::Base + class RunnerNamespace < ApplicationRecord extend Gitlab::Ci::Model belongs_to :runner, inverse_of: :runner_namespaces, validate: true diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb index 1a718d24141..f5bd50dc5a3 100644 --- a/app/models/ci/runner_project.rb +++ b/app/models/ci/runner_project.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class RunnerProject < ActiveRecord::Base + class RunnerProject < ApplicationRecord extend Gitlab::Ci::Model belongs_to :runner, inverse_of: :runner_projects diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index 0389945191e..b25b0369666 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class Stage < ActiveRecord::Base + class Stage < ApplicationRecord extend Gitlab::Ci::Model include Importable include HasStatus @@ -39,10 +39,14 @@ module Ci state_machine :status, initial: :created do event :enqueue do - transition created: :pending + transition [:created, :preparing] => :pending transition [:success, :failed, :canceled, :skipped] => :running end + event :prepare do + transition any - [:preparing] => :preparing + end + event :run do transition any - [:running] => :running end @@ -76,6 +80,7 @@ module Ci retry_optimistic_lock(self) do case statuses.latest.status when 'created' then nil + when 'preparing' then prepare when 'pending' then enqueue when 'running' then run when 'success' then succeed diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb index 637148c4ce4..8927bb9bc18 100644 --- a/app/models/ci/trigger.rb +++ b/app/models/ci/trigger.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class Trigger < ActiveRecord::Base + class Trigger < ApplicationRecord extend Gitlab::Ci::Model include IgnorableColumn include Presentable diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb index 0b52c690e93..5daf3dd192d 100644 --- a/app/models/ci/trigger_request.rb +++ b/app/models/ci/trigger_request.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class TriggerRequest < ActiveRecord::Base + class TriggerRequest < ApplicationRecord extend Gitlab::Ci::Model belongs_to :trigger diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index 64836ea4fa4..a77bbef0fca 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Ci - class Variable < ActiveRecord::Base + class Variable < ApplicationRecord extend Gitlab::Ci::Model include HasVariable include Presentable diff --git a/app/models/clusters/applications/cert_manager.rb b/app/models/clusters/applications/cert_manager.rb index c758577815a..ac0e7eb03bc 100644 --- a/app/models/clusters/applications/cert_manager.rb +++ b/app/models/clusters/applications/cert_manager.rb @@ -2,7 +2,7 @@ module Clusters module Applications - class CertManager < ActiveRecord::Base + class CertManager < ApplicationRecord VERSION = 'v0.5.2'.freeze self.table_name = 'clusters_applications_cert_managers' diff --git a/app/models/clusters/applications/helm.rb b/app/models/clusters/applications/helm.rb index 423071ec024..71aff00077d 100644 --- a/app/models/clusters/applications/helm.rb +++ b/app/models/clusters/applications/helm.rb @@ -4,7 +4,7 @@ require 'openssl' module Clusters module Applications - class Helm < ActiveRecord::Base + class Helm < ApplicationRecord self.table_name = 'clusters_applications_helm' attr_encrypted :ca_key, diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb index 567f1a2267f..376d54aab2c 100644 --- a/app/models/clusters/applications/ingress.rb +++ b/app/models/clusters/applications/ingress.rb @@ -2,7 +2,7 @@ module Clusters module Applications - class Ingress < ActiveRecord::Base + class Ingress < ApplicationRecord VERSION = '1.1.2'.freeze self.table_name = 'clusters_applications_ingress' diff --git a/app/models/clusters/applications/jupyter.rb b/app/models/clusters/applications/jupyter.rb index 7efcc175f9f..f86ff3551a1 100644 --- a/app/models/clusters/applications/jupyter.rb +++ b/app/models/clusters/applications/jupyter.rb @@ -2,7 +2,7 @@ module Clusters module Applications - class Jupyter < ActiveRecord::Base + class Jupyter < ApplicationRecord VERSION = '0.9-174bbd5'.freeze self.table_name = 'clusters_applications_jupyter' diff --git a/app/models/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb index 347c3c8c37f..f7e54833296 100644 --- a/app/models/clusters/applications/knative.rb +++ b/app/models/clusters/applications/knative.rb @@ -2,8 +2,8 @@ module Clusters module Applications - class Knative < ActiveRecord::Base - VERSION = '0.2.2'.freeze + class Knative < ApplicationRecord + VERSION = '0.3.0'.freeze REPOSITORY = 'https://storage.googleapis.com/triggermesh-charts'.freeze METRICS_CONFIG = 'https://storage.googleapis.com/triggermesh-charts/istio-metrics.yaml'.freeze FETCH_IP_ADDRESS_DELAY = 30.seconds @@ -86,7 +86,7 @@ module Clusters end def ingress_service - cluster.kubeclient.get_service('knative-ingressgateway', 'istio-system') + cluster.kubeclient.get_service('istio-ingressgateway', 'istio-system') end def services_for(ns: namespace) diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb index fa7ce363531..954c29da196 100644 --- a/app/models/clusters/applications/prometheus.rb +++ b/app/models/clusters/applications/prometheus.rb @@ -2,7 +2,7 @@ module Clusters module Applications - class Prometheus < ActiveRecord::Base + class Prometheus < ApplicationRecord include PrometheusAdapter VERSION = '6.7.3' diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb index 941551dadaa..8cb81bfcbe4 100644 --- a/app/models/clusters/applications/runner.rb +++ b/app/models/clusters/applications/runner.rb @@ -2,8 +2,8 @@ module Clusters module Applications - class Runner < ActiveRecord::Base - VERSION = '0.2.0'.freeze + class Runner < ApplicationRecord + VERSION = '0.3.0'.freeze self.table_name = 'clusters_applications_runners' @@ -13,7 +13,7 @@ module Clusters include ::Clusters::Concerns::ApplicationData belongs_to :runner, class_name: 'Ci::Runner', foreign_key: :runner_id - delegate :project, to: :cluster + delegate :project, :group, to: :cluster default_value_for :version, VERSION @@ -55,12 +55,17 @@ module Clusters end def runner_create_params - { + attributes = { name: 'kubernetes-cluster', - runner_type: :project_type, - tag_list: %w(kubernetes cluster), - projects: [project] + runner_type: cluster.cluster_type, + tag_list: %w[kubernetes cluster] } + + if cluster.group_type? + attributes.merge(groups: [group]) + elsif cluster.project_type? + attributes.merge(projects: [project]) + end end def gitlab_url diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index 5156c7d7514..4262c03498d 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -1,22 +1,24 @@ # frozen_string_literal: true module Clusters - class Cluster < ActiveRecord::Base + class Cluster < ApplicationRecord include Presentable include Gitlab::Utils::StrongMemoize include FromUnion self.table_name = 'clusters' + PROJECT_ONLY_APPLICATIONS = { + Applications::Jupyter.application_name => Applications::Jupyter, + Applications::Knative.application_name => Applications::Knative, + Applications::Prometheus.application_name => Applications::Prometheus + }.freeze APPLICATIONS = { Applications::Helm.application_name => Applications::Helm, Applications::Ingress.application_name => Applications::Ingress, Applications::CertManager.application_name => Applications::CertManager, - Applications::Prometheus.application_name => Applications::Prometheus, - Applications::Runner.application_name => Applications::Runner, - Applications::Jupyter.application_name => Applications::Jupyter, - Applications::Knative.application_name => Applications::Knative - }.freeze + Applications::Runner.application_name => Applications::Runner + }.merge(PROJECT_ONLY_APPLICATIONS).freeze DEFAULT_ENVIRONMENT = '*'.freeze KUBE_INGRESS_BASE_DOMAIN = 'KUBE_INGRESS_BASE_DOMAIN'.freeze @@ -70,6 +72,7 @@ module Clusters delegate :external_hostname, to: :application_ingress, prefix: true, allow_nil: true alias_attribute :base_domain, :domain + alias_attribute :provided_by_user?, :user? enum cluster_type: { instance_type: 1, @@ -149,10 +152,6 @@ module Clusters return platform_kubernetes if kubernetes? end - def managed? - !user? - end - def all_projects if project_type? projects diff --git a/app/models/clusters/group.rb b/app/models/clusters/group.rb index 2b08a9e47f0..27f39b53579 100644 --- a/app/models/clusters/group.rb +++ b/app/models/clusters/group.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Clusters - class Group < ActiveRecord::Base + class Group < ApplicationRecord self.table_name = 'cluster_groups' belongs_to :cluster, class_name: 'Clusters::Cluster' diff --git a/app/models/clusters/kubernetes_namespace.rb b/app/models/clusters/kubernetes_namespace.rb index 7fc75e00cd0..b0c4900546e 100644 --- a/app/models/clusters/kubernetes_namespace.rb +++ b/app/models/clusters/kubernetes_namespace.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Clusters - class KubernetesNamespace < ActiveRecord::Base + class KubernetesNamespace < ApplicationRecord include Gitlab::Kubernetes self.table_name = 'clusters_kubernetes_namespaces' diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb index fb2221b601f..2ae141190a8 100644 --- a/app/models/clusters/platforms/kubernetes.rb +++ b/app/models/clusters/platforms/kubernetes.rb @@ -2,7 +2,7 @@ module Clusters module Platforms - class Kubernetes < ActiveRecord::Base + class Kubernetes < ApplicationRecord include Gitlab::Kubernetes include ReactiveCaching include EnumWithNil @@ -54,7 +54,7 @@ module Clusters delegate :project, to: :cluster, allow_nil: true delegate :enabled?, to: :cluster, allow_nil: true - delegate :managed?, to: :cluster, allow_nil: true + delegate :provided_by_user?, to: :cluster, allow_nil: true delegate :allow_user_defined_namespace?, to: :cluster, allow_nil: true delegate :kubernetes_namespace, to: :cluster @@ -110,7 +110,7 @@ module Clusters # short time later def terminals(environment) with_reactive_cache do |data| - pods = filter_by_label(data[:pods], app: environment.slug) + pods = filter_by_project_environment(data[:pods], project.full_path_slug, environment.slug) terminals = pods.flat_map { |pod| terminals_for_pod(api_url, actual_namespace, pod) }.compact terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) } end @@ -219,7 +219,7 @@ module Clusters end def prevent_modification - return unless managed? + return if provided_by_user? if api_url_changed? || token_changed? || ca_pem_changed? errors.add(:base, _('Cannot modify managed Kubernetes cluster')) diff --git a/app/models/clusters/project.rb b/app/models/clusters/project.rb index 15092b1c9d2..d2b68b3f117 100644 --- a/app/models/clusters/project.rb +++ b/app/models/clusters/project.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Clusters - class Project < ActiveRecord::Base + class Project < ApplicationRecord self.table_name = 'cluster_projects' belongs_to :cluster, class_name: 'Clusters::Cluster' diff --git a/app/models/clusters/providers/gcp.rb b/app/models/clusters/providers/gcp.rb index 16b59cd9d14..390748bf252 100644 --- a/app/models/clusters/providers/gcp.rb +++ b/app/models/clusters/providers/gcp.rb @@ -2,7 +2,7 @@ module Clusters module Providers - class Gcp < ActiveRecord::Base + class Gcp < ApplicationRecord self.table_name = 'cluster_providers_gcp' belongs_to :cluster, inverse_of: :provider_gcp, class_name: 'Clusters::Cluster' diff --git a/app/models/commit_collection.rb b/app/models/commit_collection.rb index 42ec5b5e664..e8df46e1cc3 100644 --- a/app/models/commit_collection.rb +++ b/app/models/commit_collection.rb @@ -28,10 +28,43 @@ class CommitCollection def without_merge_commits strong_memoize(:without_merge_commits) do - commits.reject(&:merge_commit?) + # `#enrich!` the collection to ensure all commits contain + # the necessary parent data + enrich!.commits.reject(&:merge_commit?) end end + def unenriched + commits.reject(&:gitaly_commit?) + end + + def fully_enriched? + unenriched.empty? + end + + # Batch load any commits that are not backed by full gitaly data, and + # replace them in the collection. + def enrich! + # A project is needed in order to fetch data from gitaly. Projects + # can be absent from commits in certain rare situations (like when + # viewing a MR of a deleted fork). In these cases, assume that the + # enriched data is not needed. + return self if project.blank? || fully_enriched? + + # Batch load full Commits from the repository + # and map to a Hash of id => Commit + replacements = Hash[unenriched.map do |c| + [c.id, Commit.lazy(project, c.id)] + end.compact] + + # Replace the commits, keeping the same order + @commits = @commits.map do |c| + replacements.fetch(c.id, c) + end + + self + end + # Sets the pipeline status for every commit. # # Setting this status ahead of time removes the need for running a query for diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 7f6562b63e5..f97dc38dab7 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class CommitStatus < ActiveRecord::Base +class CommitStatus < ApplicationRecord include HasStatus include Importable include AfterCommitQueue @@ -66,7 +66,10 @@ class CommitStatus < ActiveRecord::Base end event :enqueue do - transition [:created, :skipped, :manual, :scheduled] => :pending + # A CommitStatus will never have prerequisites, but this event + # is shared by Ci::Build, which cannot progress unless prerequisites + # are satisfied. + transition [:created, :preparing, :skipped, :manual, :scheduled] => :pending, unless: :any_unmet_prerequisites? end event :run do @@ -74,26 +77,26 @@ class CommitStatus < ActiveRecord::Base end event :skip do - transition [:created, :pending] => :skipped + transition [:created, :preparing, :pending] => :skipped end event :drop do - transition [:created, :pending, :running, :scheduled] => :failed + transition [:created, :preparing, :pending, :running, :scheduled] => :failed end event :success do - transition [:created, :pending, :running] => :success + transition [:created, :preparing, :pending, :running] => :success end event :cancel do - transition [:created, :pending, :running, :manual, :scheduled] => :canceled + transition [:created, :preparing, :pending, :running, :manual, :scheduled] => :canceled end - before_transition [:created, :skipped, :manual, :scheduled] => :pending do |commit_status| + before_transition [:created, :preparing, :skipped, :manual, :scheduled] => :pending do |commit_status| commit_status.queued_at = Time.now end - before_transition [:created, :pending] => :running do |commit_status| + before_transition [:created, :preparing, :pending] => :running do |commit_status| commit_status.started_at = Time.now end @@ -180,6 +183,10 @@ class CommitStatus < ActiveRecord::Base false end + def any_unmet_prerequisites? + false + end + def auto_canceled? canceled? && auto_canceled_by_id? end diff --git a/app/models/commit_status_enums.rb b/app/models/commit_status_enums.rb index 152105d9429..45e08fa18fe 100644 --- a/app/models/commit_status_enums.rb +++ b/app/models/commit_status_enums.rb @@ -14,7 +14,8 @@ module CommitStatusEnums runner_unsupported: 6, stale_schedule: 7, job_execution_timeout: 8, - archived_failure: 9 + archived_failure: 9, + unmet_prerequisites: 10 } end end diff --git a/app/models/concerns/atomic_internal_id.rb b/app/models/concerns/atomic_internal_id.rb index 4e15b60ccd1..ab3d9e923c0 100644 --- a/app/models/concerns/atomic_internal_id.rb +++ b/app/models/concerns/atomic_internal_id.rb @@ -7,7 +7,7 @@ # # For example, let's generate internal ids for Issue per Project: # ``` -# class Issue < ActiveRecord::Base +# class Issue < ApplicationRecord # has_internal_id :iid, scope: :project, init: ->(s) { s.project.issues.maximum(:iid) } # end # ``` diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb index 1a8570b80c3..28ea51d6769 100644 --- a/app/models/concerns/cache_markdown_field.rb +++ b/app/models/concerns/cache_markdown_field.rb @@ -7,6 +7,7 @@ # cache_markdown_field :foo # cache_markdown_field :bar # cache_markdown_field :baz, pipeline: :single_line +# cache_markdown_field :baz, whitelisted: true # # Corresponding foo_html, bar_html and baz_html fields should exist. module CacheMarkdownField @@ -14,7 +15,7 @@ module CacheMarkdownField # Increment this number every time the renderer changes its output CACHE_COMMONMARK_VERSION_START = 10 - CACHE_COMMONMARK_VERSION = 14 + CACHE_COMMONMARK_VERSION = 15 # changes to these attributes cause the cache to be invalidates INVALIDATED_BY = %w[author project].freeze @@ -37,7 +38,15 @@ module CacheMarkdownField end def html_fields - markdown_fields.map {|field| html_field(field) } + markdown_fields.map { |field| html_field(field) } + end + + def html_fields_whitelisted + markdown_fields.each_with_object([]) do |field, fields| + if @data[field].fetch(:whitelisted, false) + fields << html_field(field) + end + end end end @@ -149,13 +158,18 @@ module CacheMarkdownField alias_method :attributes_before_markdown_cache, :attributes def attributes attrs = attributes_before_markdown_cache + html_fields = cached_markdown_fields.html_fields + whitelisted = cached_markdown_fields.html_fields_whitelisted + exclude_fields = html_fields - whitelisted - attrs.delete('cached_markdown_version') - - cached_markdown_fields.html_fields.each do |field| + exclude_fields.each do |field| attrs.delete(field) end + if whitelisted.empty? + attrs.delete('cached_markdown_version') + end + attrs end diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb index 0d2be4c61ab..8882f48c281 100644 --- a/app/models/concerns/has_status.rb +++ b/app/models/concerns/has_status.rb @@ -5,14 +5,14 @@ module HasStatus DEFAULT_STATUS = 'created'.freeze BLOCKED_STATUS = %w[manual scheduled].freeze - AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped manual scheduled].freeze + AVAILABLE_STATUSES = %w[created preparing pending running success failed canceled skipped manual scheduled].freeze STARTED_STATUSES = %w[running success failed skipped manual scheduled].freeze - ACTIVE_STATUSES = %w[pending running].freeze + ACTIVE_STATUSES = %w[preparing pending running].freeze COMPLETED_STATUSES = %w[success failed canceled skipped].freeze - ORDERED_STATUSES = %w[failed pending running manual scheduled canceled success skipped created].freeze + ORDERED_STATUSES = %w[failed preparing pending running manual scheduled canceled success skipped created].freeze STATUSES_ENUM = { created: 0, pending: 1, running: 2, success: 3, failed: 4, canceled: 5, skipped: 6, manual: 7, - scheduled: 8 }.freeze + scheduled: 8, preparing: 9 }.freeze UnknownStatusError = Class.new(StandardError) @@ -26,6 +26,7 @@ module HasStatus success = scope_relevant.success.select('count(*)').to_sql manual = scope_relevant.manual.select('count(*)').to_sql scheduled = scope_relevant.scheduled.select('count(*)').to_sql + preparing = scope_relevant.preparing.select('count(*)').to_sql pending = scope_relevant.pending.select('count(*)').to_sql running = scope_relevant.running.select('count(*)').to_sql skipped = scope_relevant.skipped.select('count(*)').to_sql @@ -37,12 +38,14 @@ module HasStatus WHEN (#{builds})=(#{skipped}) THEN 'skipped' WHEN (#{builds})=(#{success}) THEN 'success' WHEN (#{builds})=(#{created}) THEN 'created' + WHEN (#{builds})=(#{preparing}) THEN 'preparing' WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'success' WHEN (#{builds})=(#{success})+(#{skipped})+(#{canceled}) THEN 'canceled' WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending' WHEN (#{running})+(#{pending})>0 THEN 'running' WHEN (#{manual})>0 THEN 'manual' WHEN (#{scheduled})>0 THEN 'scheduled' + WHEN (#{preparing})>0 THEN 'preparing' WHEN (#{created})>0 THEN 'running' ELSE 'failed' END)" @@ -70,6 +73,7 @@ module HasStatus state_machine :status, initial: :created do state :created, value: 'created' + state :preparing, value: 'preparing' state :pending, value: 'pending' state :running, value: 'running' state :failed, value: 'failed' @@ -81,6 +85,7 @@ module HasStatus end scope :created, -> { where(status: 'created') } + scope :preparing, -> { where(status: 'preparing') } scope :relevant, -> { where(status: AVAILABLE_STATUSES - ['created']) } scope :running, -> { where(status: 'running') } scope :pending, -> { where(status: 'pending') } @@ -90,14 +95,14 @@ module HasStatus scope :skipped, -> { where(status: 'skipped') } scope :manual, -> { where(status: 'manual') } scope :scheduled, -> { where(status: 'scheduled') } - scope :alive, -> { where(status: [:created, :pending, :running]) } + scope :alive, -> { where(status: [:created, :preparing, :pending, :running]) } scope :created_or_pending, -> { where(status: [:created, :pending]) } scope :running_or_pending, -> { where(status: [:running, :pending]) } scope :finished, -> { where(status: [:success, :failed, :canceled]) } scope :failed_or_canceled, -> { where(status: [:failed, :canceled]) } scope :cancelable, -> do - where(status: [:running, :pending, :created, :scheduled]) + where(status: [:running, :preparing, :pending, :created, :scheduled]) end end diff --git a/app/models/concerns/ignorable_column.rb b/app/models/concerns/ignorable_column.rb index 5c1f7dfcd2a..3bec44dc79b 100644 --- a/app/models/concerns/ignorable_column.rb +++ b/app/models/concerns/ignorable_column.rb @@ -5,7 +5,7 @@ # # Example: # -# class User < ActiveRecord::Base +# class User < ApplicationRecord # include IgnorableColumn # # ignore_column :updated_at diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb index 614c3242874..b140fca9b83 100644 --- a/app/models/concerns/participable.rb +++ b/app/models/concerns/participable.rb @@ -7,7 +7,7 @@ # # Usage: # -# class Issue < ActiveRecord::Base +# class Issue < ApplicationRecord # include Participable # # # ... diff --git a/app/models/concerns/reactive_caching.rb b/app/models/concerns/reactive_caching.rb index d2ead7130e5..1ab3b3ddc46 100644 --- a/app/models/concerns/reactive_caching.rb +++ b/app/models/concerns/reactive_caching.rb @@ -7,7 +7,7 @@ # # Example of use: # -# class Foo < ActiveRecord::Base +# class Foo < ApplicationRecord # include ReactiveCaching # # self.reactive_cache_key = ->(thing) { ["foo", thing.id] } diff --git a/app/models/concerns/sha_attribute.rb b/app/models/concerns/sha_attribute.rb index a479bef993c..70ac873a030 100644 --- a/app/models/concerns/sha_attribute.rb +++ b/app/models/concerns/sha_attribute.rb @@ -39,7 +39,7 @@ module ShaAttribute end def database_exists? - ActiveRecord::Base.connection + ApplicationRecord.connection true rescue diff --git a/app/models/concerns/strip_attribute.rb b/app/models/concerns/strip_attribute.rb index c9f5ba7793d..8f6a6244dd3 100644 --- a/app/models/concerns/strip_attribute.rb +++ b/app/models/concerns/strip_attribute.rb @@ -6,7 +6,7 @@ # # Usage: # -# class Milestone < ActiveRecord::Base +# class Milestone < ApplicationRecord # strip_attributes :title # end # diff --git a/app/models/concerns/token_authenticatable.rb b/app/models/concerns/token_authenticatable.rb index f5bb559ceda..8c769be0489 100644 --- a/app/models/concerns/token_authenticatable.rb +++ b/app/models/concerns/token_authenticatable.rb @@ -26,34 +26,41 @@ module TokenAuthenticatable end end - define_method(token_field) do + mod = token_authenticatable_module + + mod.define_method(token_field) do strategy.get_token(self) end - define_method("set_#{token_field}") do |token| + mod.define_method("set_#{token_field}") do |token| strategy.set_token(self, token) end - define_method("ensure_#{token_field}") do + mod.define_method("ensure_#{token_field}") do strategy.ensure_token(self) end # Returns a token, but only saves when the database is in read & write mode - define_method("ensure_#{token_field}!") do + mod.define_method("ensure_#{token_field}!") do strategy.ensure_token!(self) end # Resets the token, but only saves when the database is in read & write mode - define_method("reset_#{token_field}!") do + mod.define_method("reset_#{token_field}!") do strategy.reset_token!(self) end - define_method("#{token_field}_matches?") do |other_token| + mod.define_method("#{token_field}_matches?") do |other_token| token = read_attribute(token_field) token.present? && ActiveSupport::SecurityUtils.variable_size_secure_compare(other_token, token) end end + def token_authenticatable_module + @token_authenticatable_module ||= + const_set(:TokenAuthenticatable, Module.new).tap(&method(:include)) + end + def token_authenticatable_fields @token_authenticatable_fields ||= [] end diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb index cf057d774cf..39e12ac2b06 100644 --- a/app/models/container_repository.rb +++ b/app/models/container_repository.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ContainerRepository < ActiveRecord::Base +class ContainerRepository < ApplicationRecord include Gitlab::Utils::StrongMemoize belongs_to :project diff --git a/app/models/conversational_development_index/metric.rb b/app/models/conversational_development_index/metric.rb index c54537572d6..b91123be87e 100644 --- a/app/models/conversational_development_index/metric.rb +++ b/app/models/conversational_development_index/metric.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module ConversationalDevelopmentIndex - class Metric < ActiveRecord::Base + class Metric < ApplicationRecord include Presentable self.table_name = 'conversational_development_index_metrics' diff --git a/app/models/deploy_keys_project.rb b/app/models/deploy_keys_project.rb index 71fd02fac86..15906ed8e06 100644 --- a/app/models/deploy_keys_project.rb +++ b/app/models/deploy_keys_project.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class DeployKeysProject < ActiveRecord::Base +class DeployKeysProject < ApplicationRecord belongs_to :project belongs_to :deploy_key, inverse_of: :deploy_keys_projects diff --git a/app/models/deploy_token.rb b/app/models/deploy_token.rb index e3524305346..b0e570f52ba 100644 --- a/app/models/deploy_token.rb +++ b/app/models/deploy_token.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class DeployToken < ActiveRecord::Base +class DeployToken < ApplicationRecord include Expirable include TokenAuthenticatable include PolicyActor diff --git a/app/models/deployment.rb b/app/models/deployment.rb index 811e623b7f7..d847a0a11e4 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Deployment < ActiveRecord::Base +class Deployment < ApplicationRecord include AtomicInternalId include IidRoutes include AfterCommitQueue @@ -78,6 +78,10 @@ class Deployment < ActiveRecord::Base Commit.truncate_sha(sha) end + def cluster + project.deployment_platform(environment: environment.name)&.cluster + end + def last? self == environment.last_deployment end diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb index 805092e527a..feabea9b8ba 100644 --- a/app/models/diff_note.rb +++ b/app/models/diff_note.rb @@ -77,8 +77,8 @@ class DiffNote < Note def supports_suggestion? return false unless noteable.supports_suggestion? && on_text? # We don't want to trigger side-effects of `diff_file` call. - return false unless file = fetch_diff_file - return false unless line = file.line_for_position(self.original_position) + return false unless file = latest_diff_file + return false unless line = file.line_for_position(self.position) line&.suggestible? end @@ -88,7 +88,7 @@ class DiffNote < Note end def banzai_render_context(field) - super.merge(suggestions_filter_enabled: supports_suggestion?) + super.merge(project: project, suggestions_filter_enabled: supports_suggestion?) end private diff --git a/app/models/email.rb b/app/models/email.rb index 7c33c5c7e64..0ddaa049c3b 100644 --- a/app/models/email.rb +++ b/app/models/email.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Email < ActiveRecord::Base +class Email < ApplicationRecord include Sortable include Gitlab::SQL::Pattern diff --git a/app/models/environment.rb b/app/models/environment.rb index 3d909cc8e5c..25373c7a1f7 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Environment < ActiveRecord::Base +class Environment < ApplicationRecord include Gitlab::Utils::StrongMemoize # Used to generate random suffixes for the slug LETTERS = 'a'..'z' diff --git a/app/models/epic.rb b/app/models/epic.rb index ccd10593434..3693db1de33 100644 --- a/app/models/epic.rb +++ b/app/models/epic.rb @@ -2,7 +2,7 @@ # Placeholder class for model that is implemented in EE # It reserves '&' as a reference prefix, but the table does not exists in CE -class Epic < ActiveRecord::Base +class Epic < ApplicationRecord def self.link_reference_pattern nil end diff --git a/app/models/error_tracking/project_error_tracking_setting.rb b/app/models/error_tracking/project_error_tracking_setting.rb index 1e2bd3bda7f..70954bf8b05 100644 --- a/app/models/error_tracking/project_error_tracking_setting.rb +++ b/app/models/error_tracking/project_error_tracking_setting.rb @@ -1,10 +1,13 @@ # frozen_string_literal: true module ErrorTracking - class ProjectErrorTrackingSetting < ActiveRecord::Base + class ProjectErrorTrackingSetting < ApplicationRecord include Gitlab::Utils::StrongMemoize include ReactiveCaching + SENTRY_API_ERROR_TYPE_MISSING_KEYS = 'missing_keys_in_sentry_response' + SENTRY_API_ERROR_TYPE_NON_20X_RESPONSE = 'non_20x_response_from_sentry' + API_URL_PATH_REGEXP = %r{ \A (?<prefix>/api/0/projects/+) @@ -90,7 +93,9 @@ module ErrorTracking { issues: sentry_client.list_issues(**opts.symbolize_keys) } end rescue Sentry::Client::Error => e - { error: e.message } + { error: e.message, error_type: SENTRY_API_ERROR_TYPE_NON_20X_RESPONSE } + rescue Sentry::Client::MissingKeysError => e + { error: e.message, error_type: SENTRY_API_ERROR_TYPE_MISSING_KEYS } end # http://HOST/api/0/projects/ORG/PROJECT diff --git a/app/models/event.rb b/app/models/event.rb index 6a35bca72c5..593acf5edfe 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Event < ActiveRecord::Base +class Event < ApplicationRecord include Sortable include IgnorableColumn include FromUnion diff --git a/app/models/fork_network.rb b/app/models/fork_network.rb index 1b9bf93cbbc..0323a8d222a 100644 --- a/app/models/fork_network.rb +++ b/app/models/fork_network.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ForkNetwork < ActiveRecord::Base +class ForkNetwork < ApplicationRecord belongs_to :root_project, class_name: 'Project' has_many :fork_network_members has_many :projects, through: :fork_network_members diff --git a/app/models/fork_network_member.rb b/app/models/fork_network_member.rb index 36c66f21b0b..f18c306cf91 100644 --- a/app/models/fork_network_member.rb +++ b/app/models/fork_network_member.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ForkNetworkMember < ActiveRecord::Base +class ForkNetworkMember < ApplicationRecord belongs_to :fork_network belongs_to :project belongs_to :forked_from_project, class_name: 'Project' diff --git a/app/models/gpg_key.rb b/app/models/gpg_key.rb index 077afffd358..116beac5c2a 100644 --- a/app/models/gpg_key.rb +++ b/app/models/gpg_key.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class GpgKey < ActiveRecord::Base +class GpgKey < ApplicationRecord KEY_PREFIX = '-----BEGIN PGP PUBLIC KEY BLOCK-----'.freeze KEY_SUFFIX = '-----END PGP PUBLIC KEY BLOCK-----'.freeze diff --git a/app/models/gpg_key_subkey.rb b/app/models/gpg_key_subkey.rb index 440b588bc78..110bf451136 100644 --- a/app/models/gpg_key_subkey.rb +++ b/app/models/gpg_key_subkey.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class GpgKeySubkey < ActiveRecord::Base +class GpgKeySubkey < ApplicationRecord include ShaAttribute sha_attribute :keyid diff --git a/app/models/group.rb b/app/models/group.rb index 495bfe04499..c77586c4cdc 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -56,7 +56,7 @@ class Group < Namespace validates :two_factor_grace_period, presence: true, numericality: { greater_than_or_equal_to: 0 } - add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption) ? :optional : :required } + add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required } after_create :post_create_hook after_destroy :post_destroy_hook diff --git a/app/models/group_custom_attribute.rb b/app/models/group_custom_attribute.rb index 22f14885657..5ac6e5f2550 100644 --- a/app/models/group_custom_attribute.rb +++ b/app/models/group_custom_attribute.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class GroupCustomAttribute < ActiveRecord::Base +class GroupCustomAttribute < ApplicationRecord belongs_to :group validates :group, :key, :value, presence: true diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 1a8662db9fb..daf7ff4b771 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class WebHook < ActiveRecord::Base +class WebHook < ApplicationRecord include Sortable attr_encrypted :token, diff --git a/app/models/hooks/web_hook_log.rb b/app/models/hooks/web_hook_log.rb index 2d9f7594e8c..cfb1f3ec63b 100644 --- a/app/models/hooks/web_hook_log.rb +++ b/app/models/hooks/web_hook_log.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class WebHookLog < ActiveRecord::Base +class WebHookLog < ApplicationRecord belongs_to :web_hook serialize :request_headers, Hash # rubocop:disable Cop/ActiveRecordSerialize diff --git a/app/models/identity.rb b/app/models/identity.rb index acdde4f296b..8322b9bf35f 100644 --- a/app/models/identity.rb +++ b/app/models/identity.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Identity < ActiveRecord::Base +class Identity < ApplicationRecord include Sortable include CaseSensitivity diff --git a/app/models/identity/uniqueness_scopes.rb b/app/models/identity/uniqueness_scopes.rb index 674b735903f..ce68371ae87 100644 --- a/app/models/identity/uniqueness_scopes.rb +++ b/app/models/identity/uniqueness_scopes.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Identity < ActiveRecord::Base +class Identity < ApplicationRecord # This module and method are defined in a separate file to allow EE to # redefine the `scopes` method before it is used in the `Identity` model. module UniquenessScopes diff --git a/app/models/import_export_upload.rb b/app/models/import_export_upload.rb index f0cc5aafcd4..60f5491849a 100644 --- a/app/models/import_export_upload.rb +++ b/app/models/import_export_upload.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ImportExportUpload < ActiveRecord::Base +class ImportExportUpload < ApplicationRecord include WithUploads include ObjectStorage::BackgroundMove diff --git a/app/models/internal_id.rb b/app/models/internal_id.rb index e75c6eb2331..3f2d368a3f2 100644 --- a/app/models/internal_id.rb +++ b/app/models/internal_id.rb @@ -15,7 +15,7 @@ # In order to leverage InternalId for other usages, the idea is to # * Add `usage` value to enum # * (Optionally) add columns to `internal_ids` if needed for scope. -class InternalId < ActiveRecord::Base +class InternalId < ApplicationRecord belongs_to :project belongs_to :namespace diff --git a/app/models/issue.rb b/app/models/issue.rb index deab53d25e7..97c6dcc4745 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -2,7 +2,7 @@ require 'carrierwave/orm/activerecord' -class Issue < ActiveRecord::Base +class Issue < ApplicationRecord include AtomicInternalId include IidRoutes include Issuable diff --git a/app/models/issue/metrics.rb b/app/models/issue/metrics.rb index 0f5ee957ec9..8010cbc3d78 100644 --- a/app/models/issue/metrics.rb +++ b/app/models/issue/metrics.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Issue::Metrics < ActiveRecord::Base +class Issue::Metrics < ApplicationRecord belongs_to :issue def record! diff --git a/app/models/issue_assignee.rb b/app/models/issue_assignee.rb index 400c0256945..fbd9be1fb43 100644 --- a/app/models/issue_assignee.rb +++ b/app/models/issue_assignee.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class IssueAssignee < ActiveRecord::Base +class IssueAssignee < ApplicationRecord belongs_to :issue belongs_to :assignee, class_name: "User", foreign_key: :user_id end diff --git a/app/models/key.rb b/app/models/key.rb index 8f93418b88b..b097be8cc89 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -2,7 +2,7 @@ require 'digest/md5' -class Key < ActiveRecord::Base +class Key < ApplicationRecord include AfterCommitQueue include Sortable diff --git a/app/models/label.rb b/app/models/label.rb index 96bdb7f17c5..024daeb4fae 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Label < ActiveRecord::Base +class Label < ApplicationRecord include CacheMarkdownField include Referable include Subscribable diff --git a/app/models/label_link.rb b/app/models/label_link.rb index 1d93a55e8e9..ffc0afd8e85 100644 --- a/app/models/label_link.rb +++ b/app/models/label_link.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class LabelLink < ActiveRecord::Base +class LabelLink < ApplicationRecord include Importable belongs_to :target, polymorphic: true, inverse_of: :label_links # rubocop:disable Cop/PolymorphicAssociations diff --git a/app/models/label_priority.rb b/app/models/label_priority.rb index 8ed8bb7577f..8f8f36efbfe 100644 --- a/app/models/label_priority.rb +++ b/app/models/label_priority.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class LabelPriority < ActiveRecord::Base +class LabelPriority < ApplicationRecord belongs_to :project belongs_to :label diff --git a/app/models/lfs_file_lock.rb b/app/models/lfs_file_lock.rb index 431d37e12e9..624b1d02e1a 100644 --- a/app/models/lfs_file_lock.rb +++ b/app/models/lfs_file_lock.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class LfsFileLock < ActiveRecord::Base +class LfsFileLock < ApplicationRecord belongs_to :project belongs_to :user diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index 69c563545bb..e1aac691a64 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class LfsObject < ActiveRecord::Base +class LfsObject < ApplicationRecord include AfterCommitQueue include ObjectStorage::BackgroundMove diff --git a/app/models/lfs_objects_project.rb b/app/models/lfs_objects_project.rb index 353602800d7..f9afb18c1d7 100644 --- a/app/models/lfs_objects_project.rb +++ b/app/models/lfs_objects_project.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class LfsObjectsProject < ActiveRecord::Base +class LfsObjectsProject < ApplicationRecord belongs_to :project belongs_to :lfs_object diff --git a/app/models/list.rb b/app/models/list.rb index 682af761ba0..17b1a8510cf 100644 --- a/app/models/list.rb +++ b/app/models/list.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class List < ActiveRecord::Base +class List < ApplicationRecord belongs_to :board belongs_to :label diff --git a/app/models/member.rb b/app/models/member.rb index 5dbc0c2eec9..8a06bff51b5 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Member < ActiveRecord::Base +class Member < ApplicationRecord include AfterCommitQueue include Sortable include Importable diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb index 2c9e1ba1d80..510f856087d 100644 --- a/app/models/members/group_member.rb +++ b/app/models/members/group_member.rb @@ -14,6 +14,8 @@ class GroupMember < Member scope :in_groups, ->(groups) { where(source_id: groups.select(:id)) } + scope :count_users_by_group_id, -> { joins(:user).group(:source_id).count } + after_create :update_two_factor_requirement, unless: :invite? after_destroy :update_two_factor_requirement, unless: :invite? diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index ea9c29daafc..a5fd66ca64a 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class MergeRequest < ActiveRecord::Base +class MergeRequest < ApplicationRecord include AtomicInternalId include IidRoutes include Issuable @@ -66,7 +66,10 @@ class MergeRequest < ActiveRecord::Base has_many :cached_closes_issues, through: :merge_requests_closing_issues, source: :issue has_many :merge_request_pipelines, foreign_key: 'merge_request_id', class_name: 'Ci::Pipeline' + has_many :suggestions, through: :notes + has_many :merge_request_assignees + # Will be deprecated at https://gitlab.com/gitlab-org/gitlab-ce/issues/59457 belongs_to :assignee, class_name: "User" serialize :merge_params, Hash # rubocop:disable Cop/ActiveRecordSerialize @@ -76,6 +79,10 @@ class MergeRequest < ActiveRecord::Base after_update :reload_diff_if_branch_changed after_save :ensure_metrics + # Required until the codebase starts using this relation for single or multiple assignees. + # TODO: Remove at gitlab-ee#2004 implementation. + after_save :refresh_merge_request_assignees, if: :assignee_id_changed? + # When this attribute is true some MR validation is ignored # It allows us to close or modify broken merge requests attr_accessor :allow_broken @@ -232,7 +239,7 @@ class MergeRequest < ActiveRecord::Base # branch head commit, for example checking if a merge request can be merged. # For more information check: https://gitlab.com/gitlab-org/gitlab-ce/issues/40004 def actual_head_pipeline - head_pipeline&.sha == diff_head_sha ? head_pipeline : nil + head_pipeline&.matches_sha_or_source_sha?(diff_head_sha) ? head_pipeline : nil end def merge_pipeline @@ -671,6 +678,15 @@ class MergeRequest < ActiveRecord::Base merge_request_diff || create_merge_request_diff end + def refresh_merge_request_assignees + transaction do + # Using it instead relation.delete_all in order to avoid adding a + # dependent: :delete_all (we already have foreign key cascade deletion). + MergeRequestAssignee.where(merge_request_id: self).delete_all + merge_request_assignees.create(user_id: assignee_id) if assignee_id + end + end + def create_merge_request_diff fetch_ref! @@ -1109,6 +1125,10 @@ class MergeRequest < ActiveRecord::Base "refs/#{Repository::REF_MERGE_REQUEST}/#{iid}/merge" end + def self.merge_request_ref?(ref) + ref.start_with?("refs/#{Repository::REF_MERGE_REQUEST}/") + end + def in_locked_state begin lock_mr diff --git a/app/models/merge_request/metrics.rb b/app/models/merge_request/metrics.rb index 65e94a97b0a..05f8e18a2c1 100644 --- a/app/models/merge_request/metrics.rb +++ b/app/models/merge_request/metrics.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class MergeRequest::Metrics < ActiveRecord::Base +class MergeRequest::Metrics < ApplicationRecord belongs_to :merge_request belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :pipeline_id belongs_to :latest_closed_by, class_name: 'User' diff --git a/app/models/merge_request_assignee.rb b/app/models/merge_request_assignee.rb new file mode 100644 index 00000000000..f0e6be51b7f --- /dev/null +++ b/app/models/merge_request_assignee.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class MergeRequestAssignee < ApplicationRecord + belongs_to :merge_request + belongs_to :assignee, class_name: "User", foreign_key: :user_id +end diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index 98db1bf7de7..ac8d3b98266 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class MergeRequestDiff < ActiveRecord::Base +class MergeRequestDiff < ApplicationRecord include Sortable include Importable include ManualInverseAssociation @@ -12,6 +12,10 @@ class MergeRequestDiff < ActiveRecord::Base # Don't display more than 100 commits at once COMMITS_SAFE_SIZE = 100 + # Applies to closed or merged MRs when determining whether to migrate their + # diffs to external storage + EXTERNAL_DIFF_CUTOFF = 7.days.freeze + belongs_to :merge_request manual_inverse_association :merge_request, :merge_request_diff @@ -48,6 +52,81 @@ class MergeRequestDiff < ActiveRecord::Base end scope :recent, -> { order(id: :desc).limit(100) } + scope :files_in_database, -> { where(stored_externally: [false, nil]) } + + scope :not_latest_diffs, -> do + merge_requests = MergeRequest.arel_table + mr_diffs = arel_table + + join_condition = merge_requests[:id].eq(mr_diffs[:merge_request_id]) + .and(mr_diffs[:id].not_eq(merge_requests[:latest_merge_request_diff_id])) + + arel_join = mr_diffs.join(merge_requests).on(join_condition) + joins(arel_join.join_sources) + end + + scope :old_merged_diffs, -> (before) do + merge_requests = MergeRequest.arel_table + mr_metrics = MergeRequest::Metrics.arel_table + mr_diffs = arel_table + + mr_join = mr_diffs + .join(merge_requests) + .on(mr_diffs[:merge_request_id].eq(merge_requests[:id])) + + metrics_join_condition = mr_diffs[:merge_request_id] + .eq(mr_metrics[:merge_request_id]) + .and(mr_metrics[:merged_at].not_eq(nil)) + + metrics_join = mr_diffs.join(mr_metrics).on(metrics_join_condition) + + condition = MergeRequest.arel_table[:state].eq(:merged) + .and(MergeRequest::Metrics.arel_table[:merged_at].lteq(before)) + .and(MergeRequest::Metrics.arel_table[:merged_at].not_eq(nil)) + + joins(metrics_join.join_sources, mr_join.join_sources).where(condition) + end + + scope :old_closed_diffs, -> (before) do + condition = MergeRequest.arel_table[:state].eq(:closed) + .and(MergeRequest::Metrics.arel_table[:latest_closed_at].lteq(before)) + + joins(merge_request: :metrics).where(condition) + end + + def self.ids_for_external_storage_migration(limit:) + # No point doing any work unless the feature is enabled + return [] unless Gitlab.config.external_diffs.enabled + + case Gitlab.config.external_diffs.when + when 'always' + files_in_database.limit(limit).pluck(:id) + when 'outdated' + # Outdated is too complex to be a single SQL query, so split into three + before = EXTERNAL_DIFF_CUTOFF.ago + + ids = files_in_database + .old_merged_diffs(before) + .limit(limit) + .pluck(:id) + + return ids if ids.size >= limit + + ids += files_in_database + .old_closed_diffs(before) + .limit(limit - ids.size) + .pluck(:id) + + return ids if ids.size >= limit + + ids + files_in_database + .not_latest_diffs + .limit(limit - ids.size) + .pluck(:id) + else + [] + end + end mount_uploader :external_diff, ExternalDiffUploader @@ -55,7 +134,7 @@ class MergeRequestDiff < ActiveRecord::Base # It allows you to override variables like head_commit_sha before getting diff. after_create :save_git_content, unless: :importing? - after_save :update_external_diff_store, if: :external_diff_changed? + after_save :update_external_diff_store, if: -> { !importing? && external_diff_changed? } def self.find_by_diff_refs(diff_refs) find_by(start_commit_sha: diff_refs.start_sha, head_commit_sha: diff_refs.head_sha, base_commit_sha: diff_refs.base_sha) @@ -294,6 +373,23 @@ class MergeRequestDiff < ActiveRecord::Base end end + # Transactionally migrate the current merge_request_diff_files entries to + # external storage. If external storage isn't an option for this diff, the + # method is a no-op. + def migrate_files_to_external_storage! + return if stored_externally? || !use_external_diff? || merge_request_diff_files.count == 0 + + rows = build_merge_request_diff_files(merge_request_diff_files) + + transaction do + MergeRequestDiffFile.where(merge_request_diff_id: id).delete_all + create_merge_request_diff_files(rows) + save! + end + + merge_request_diff_files.reload + end + private def encode_in_base64?(diff_text) @@ -301,20 +397,7 @@ class MergeRequestDiff < ActiveRecord::Base diff_text.include?("\0") end - def create_merge_request_diff_files(diffs) - rows = - if has_attribute?(:external_diff) && Gitlab.config.external_diffs.enabled - build_external_merge_request_diff_files(diffs) - else - build_merge_request_diff_files(diffs) - end - - # Faster inserts - Gitlab::Database.bulk_insert('merge_request_diff_files', rows) - end - - def build_external_merge_request_diff_files(diffs) - rows = build_merge_request_diff_files(diffs) + def build_external_merge_request_diff_files(rows) tempfile = build_external_diff_tempfile(rows) self.external_diff = tempfile @@ -325,16 +408,21 @@ class MergeRequestDiff < ActiveRecord::Base tempfile&.unlink end + def create_merge_request_diff_files(rows) + rows = build_external_merge_request_diff_files(rows) if use_external_diff? + + # Faster inserts + Gitlab::Database.bulk_insert('merge_request_diff_files', rows) + end + def build_external_diff_tempfile(rows) Tempfile.open(external_diff.filename) do |file| - rows.inject(0) do |offset, row| + rows.each do |row| data = row.delete(:diff) - row[:external_diff_offset] = offset - row[:external_diff_size] = data.size + row[:external_diff_offset] = file.pos + row[:external_diff_size] = data.bytesize file.write(data) - - offset + data.size end file @@ -361,6 +449,47 @@ class MergeRequestDiff < ActiveRecord::Base end end + def use_external_diff? + return false unless has_attribute?(:external_diff) + return false unless Gitlab.config.external_diffs.enabled + + case Gitlab.config.external_diffs.when + when 'always' + true + when 'outdated' + outdated_by_merge? || outdated_by_closure? || old_version? + else + false # Disable external diffs if misconfigured + end + end + + def outdated_by_merge? + return false unless merge_request&.metrics&.merged_at + + merge_request.merged? && merge_request.metrics.merged_at < EXTERNAL_DIFF_CUTOFF.ago + end + + def outdated_by_closure? + return false unless merge_request&.metrics&.latest_closed_at + + merge_request.closed? && merge_request.metrics.latest_closed_at < EXTERNAL_DIFF_CUTOFF.ago + end + + # We can't rely on `merge_request.latest_merge_request_diff_id` because that + # may have been changed in `save_git_content` without being reflected in + # the association's instance. This query is always subject to races, but + # the worst case is that we *don't* make a diff external when we could. The + # background worker will make it external at a later date. + def old_version? + latest_id = MergeRequest + .where(id: merge_request_id) + .limit(1) + .pluck(:latest_merge_request_diff_id) + .first + + self.id != latest_id + end + def load_diffs(options) # Ensure all diff files operate on the same external diff file instance if # present. This reduces file open/close overhead. @@ -394,7 +523,8 @@ class MergeRequestDiff < ActiveRecord::Base if diff_collection.any? new_attributes[:state] = :collected - create_merge_request_diff_files(diff_collection) + rows = build_merge_request_diff_files(diff_collection) + create_merge_request_diff_files(rows) end # Set our state to 'overflow' to make the #empty? and #collected? diff --git a/app/models/merge_request_diff_commit.rb b/app/models/merge_request_diff_commit.rb index 4ad3690512d..b897bbc8cf5 100644 --- a/app/models/merge_request_diff_commit.rb +++ b/app/models/merge_request_diff_commit.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class MergeRequestDiffCommit < ActiveRecord::Base +class MergeRequestDiffCommit < ApplicationRecord include ShaAttribute belongs_to :merge_request_diff diff --git a/app/models/merge_request_diff_file.rb b/app/models/merge_request_diff_file.rb index 16ec4ed470f..01ee82ae398 100644 --- a/app/models/merge_request_diff_file.rb +++ b/app/models/merge_request_diff_file.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class MergeRequestDiffFile < ActiveRecord::Base +class MergeRequestDiffFile < ApplicationRecord include Gitlab::EncodingHelper include DiffFile diff --git a/app/models/merge_requests_closing_issues.rb b/app/models/merge_requests_closing_issues.rb index 242b65bedc0..61af50841ee 100644 --- a/app/models/merge_requests_closing_issues.rb +++ b/app/models/merge_requests_closing_issues.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class MergeRequestsClosingIssues < ActiveRecord::Base +class MergeRequestsClosingIssues < ApplicationRecord belongs_to :merge_request belongs_to :issue diff --git a/app/models/milestone.rb b/app/models/milestone.rb index d6f94cad1fb..b4aad9e512e 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Milestone < ActiveRecord::Base +class Milestone < ApplicationRecord # Represents a "No Milestone" state used for filtering Issues and Merge # Requests that have no milestone assigned. MilestoneStruct = Struct.new(:title, :name, :id) @@ -37,6 +37,7 @@ class Milestone < ActiveRecord::Base scope :active, -> { with_state(:active) } scope :closed, -> { with_state(:closed) } scope :for_projects, -> { where(group: nil).includes(:project) } + scope :started, -> { active.where('milestones.start_date <= CURRENT_DATE') } scope :for_projects_and_groups, -> (projects, groups) do projects = projects.compact if projects.is_a? Array diff --git a/app/models/note.rb b/app/models/note.rb index 2c9980b1a0d..081d6f91230 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -3,7 +3,7 @@ # A note on the root of an issue, merge request, commit, or snippet. # # A note of this type is never resolvable. -class Note < ActiveRecord::Base +class Note < ApplicationRecord extend ActiveModel::Naming include Participable include Mentionable diff --git a/app/models/note_diff_file.rb b/app/models/note_diff_file.rb index e369122003e..9afb94c869a 100644 --- a/app/models/note_diff_file.rb +++ b/app/models/note_diff_file.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class NoteDiffFile < ActiveRecord::Base +class NoteDiffFile < ApplicationRecord include DiffFile scope :for_commit_or_unresolved, -> do diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb index e82eaf4e069..61af5c09ae4 100644 --- a/app/models/notification_setting.rb +++ b/app/models/notification_setting.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class NotificationSetting < ActiveRecord::Base +class NotificationSetting < ApplicationRecord include IgnorableColumn ignore_column :events diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index 7a33ade826b..82901ceec01 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class PagesDomain < ActiveRecord::Base +class PagesDomain < ApplicationRecord VERIFICATION_KEY = 'gitlab-pages-verification-code'.freeze VERIFICATION_THRESHOLD = 3.days.freeze diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb index ed78a46eaf3..570112b63b7 100644 --- a/app/models/personal_access_token.rb +++ b/app/models/personal_access_token.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class PersonalAccessToken < ActiveRecord::Base +class PersonalAccessToken < ApplicationRecord include Expirable include IgnorableColumn include TokenAuthenticatable diff --git a/app/models/pool_repository.rb b/app/models/pool_repository.rb index 4635fc72dc7..35c718365b4 100644 --- a/app/models/pool_repository.rb +++ b/app/models/pool_repository.rb @@ -3,7 +3,7 @@ # The PoolRepository model is the database equivalent of an ObjectPool for Gitaly # That is; PoolRepository is the record in the database, ObjectPool is the # repository on disk -class PoolRepository < ActiveRecord::Base +class PoolRepository < ApplicationRecord include Shardable include AfterCommitQueue diff --git a/app/models/postgresql/replication_slot.rb b/app/models/postgresql/replication_slot.rb index e264fe88e47..74ccf23cf69 100644 --- a/app/models/postgresql/replication_slot.rb +++ b/app/models/postgresql/replication_slot.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Postgresql - class ReplicationSlot < ActiveRecord::Base + class ReplicationSlot < ApplicationRecord self.table_name = 'pg_replication_slots' # Returns true if there are any replication slots in use. diff --git a/app/models/programming_language.rb b/app/models/programming_language.rb index 5f0f313b7f9..375fbe9b5a9 100644 --- a/app/models/programming_language.rb +++ b/app/models/programming_language.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ProgrammingLanguage < ActiveRecord::Base +class ProgrammingLanguage < ApplicationRecord validates :name, presence: true validates :color, allow_blank: false, color: true diff --git a/app/models/project.rb b/app/models/project.rb index a3d55d390f4..82c2f9090c8 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -2,7 +2,7 @@ require 'carrierwave/orm/activerecord' -class Project < ActiveRecord::Base +class Project < ApplicationRecord include Gitlab::ConfigHelper include Gitlab::ShellAdapter include Gitlab::VisibilityLevel @@ -84,7 +84,7 @@ class Project < ActiveRecord::Base default_value_for :snippets_enabled, gitlab_config_features.snippets default_value_for :only_allow_merge_if_all_discussions_are_resolved, false - add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:projects_tokens_optional_encryption) ? :optional : :required } + add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:projects_tokens_optional_encryption, default_enabled: true) ? :optional : :required } before_validation :mark_remote_mirrors_for_removal, if: -> { RemoteMirror.table_exists? } @@ -459,14 +459,41 @@ class Project < ActiveRecord::Base # Returns a collection of projects that is either public or visible to the # logged in user. - def self.public_or_visible_to_user(user = nil) - if user - where('EXISTS (?) OR projects.visibility_level IN (?)', - user.authorizations_for_projects, - Gitlab::VisibilityLevel.levels_for_user(user)) - else - public_to_user - end + # + # requested_visiblity_levels: Normally all projects that are visible + # to the user (e.g. internal and public) are queried, but this + # parameter allows the caller to narrow the search space to optimize + # database queries. For instance, a caller may only want to see + # internal projects. Instead of querying for internal and public + # projects and throwing away public projects, this parameter allows + # the query to be targeted for only internal projects. + def self.public_or_visible_to_user(user = nil, requested_visibility_levels = []) + return public_to_user unless user + + visible_levels = Gitlab::VisibilityLevel.levels_for_user(user) + include_private = true + requested_visibility_levels = Array(requested_visibility_levels) + + if requested_visibility_levels.present? + visible_levels &= requested_visibility_levels + include_private = requested_visibility_levels.include?(Gitlab::VisibilityLevel::PRIVATE) + end + + public_or_internal_rel = + if visible_levels.present? + where('projects.visibility_level IN (?)', visible_levels) + else + Project.none + end + + private_rel = + if include_private + where('EXISTS (?)', user.authorizations_for_projects) + else + Project.none + end + + public_or_internal_rel.or(private_rel) end # project features may be "disabled", "internal", "enabled" or "public". If "internal", @@ -1384,6 +1411,7 @@ class Project < ActiveRecord::Base repository.raw_repository.write_ref('HEAD', "refs/heads/#{branch}") repository.copy_gitattributes(branch) repository.after_change_head + ProjectCacheWorker.perform_async(self.id, [], [:commit_count]) reload_default_branch else errors.add(:base, "Could not change HEAD: branch '#{branch}' does not exist") @@ -2002,12 +2030,8 @@ class Project < ActiveRecord::Base @storage = nil if storage_version_changed? end - def gl_repository(is_wiki:) - Gitlab::GlRepository.gl_repository(self, is_wiki) - end - - def reference_counter(wiki: false) - Gitlab::ReferenceCounter.new(gl_repository(is_wiki: wiki)) + def reference_counter(type: Gitlab::GlRepository::PROJECT) + Gitlab::ReferenceCounter.new(type.identifier_for_subject(self)) end def badges @@ -2151,7 +2175,7 @@ class Project < ActiveRecord::Base end def wiki_reference_count - reference_counter(wiki: true).value + reference_counter(type: Gitlab::GlRepository::WIKI).value end def check_repository_absence! diff --git a/app/models/project_authorization.rb b/app/models/project_authorization.rb index 2c590008db2..f95d3ab54e2 100644 --- a/app/models/project_authorization.rb +++ b/app/models/project_authorization.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ProjectAuthorization < ActiveRecord::Base +class ProjectAuthorization < ApplicationRecord include FromUnion belongs_to :user diff --git a/app/models/project_auto_devops.rb b/app/models/project_auto_devops.rb index e353a6443c4..f972c40f317 100644 --- a/app/models/project_auto_devops.rb +++ b/app/models/project_auto_devops.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ProjectAutoDevops < ActiveRecord::Base +class ProjectAutoDevops < ApplicationRecord belongs_to :project enum deploy_strategy: { diff --git a/app/models/project_ci_cd_setting.rb b/app/models/project_ci_cd_setting.rb index 1dad235cc2b..1414164b703 100644 --- a/app/models/project_ci_cd_setting.rb +++ b/app/models/project_ci_cd_setting.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ProjectCiCdSetting < ActiveRecord::Base +class ProjectCiCdSetting < ApplicationRecord belongs_to :project, inverse_of: :ci_cd_settings # The version of the schema that first introduced this model/table. diff --git a/app/models/project_custom_attribute.rb b/app/models/project_custom_attribute.rb index 4e767cb3b26..b0da586988a 100644 --- a/app/models/project_custom_attribute.rb +++ b/app/models/project_custom_attribute.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ProjectCustomAttribute < ActiveRecord::Base +class ProjectCustomAttribute < ApplicationRecord belongs_to :project validates :project, :key, :value, presence: true diff --git a/app/models/project_daily_statistic.rb b/app/models/project_daily_statistic.rb index ff115dd010f..5ee11ab186e 100644 --- a/app/models/project_daily_statistic.rb +++ b/app/models/project_daily_statistic.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ProjectDailyStatistic < ActiveRecord::Base +class ProjectDailyStatistic < ApplicationRecord belongs_to :project scope :of_project, -> (project) { where(project: project) } diff --git a/app/models/project_deploy_token.rb b/app/models/project_deploy_token.rb index 719c492a1ff..a55667496fb 100644 --- a/app/models/project_deploy_token.rb +++ b/app/models/project_deploy_token.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ProjectDeployToken < ActiveRecord::Base +class ProjectDeployToken < ApplicationRecord belongs_to :project belongs_to :deploy_token, inverse_of: :project_deploy_tokens diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb index e6787236c4e..0542581c6e0 100644 --- a/app/models/project_feature.rb +++ b/app/models/project_feature.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ProjectFeature < ActiveRecord::Base +class ProjectFeature < ApplicationRecord # == Project features permissions # # Grants access level to project tools diff --git a/app/models/project_group_link.rb b/app/models/project_group_link.rb index bc3759142ae..58b555c3581 100644 --- a/app/models/project_group_link.rb +++ b/app/models/project_group_link.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ProjectGroupLink < ActiveRecord::Base +class ProjectGroupLink < ApplicationRecord include Expirable GUEST = 10 diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index aa0c121fe99..580e8dfd833 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -2,7 +2,7 @@ require 'carrierwave/orm/activerecord' -class ProjectImportData < ActiveRecord::Base +class ProjectImportData < ApplicationRecord belongs_to :project, inverse_of: :import_data attr_encrypted :credentials, key: Settings.attr_encrypted_db_key_base, diff --git a/app/models/project_import_state.rb b/app/models/project_import_state.rb index 488f0cb5971..1605345efd5 100644 --- a/app/models/project_import_state.rb +++ b/app/models/project_import_state.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ProjectImportState < ActiveRecord::Base +class ProjectImportState < ApplicationRecord include AfterCommitQueue self.table_name = "project_mirror_data" diff --git a/app/models/project_repository.rb b/app/models/project_repository.rb index 38913f3f2f5..092efabd73f 100644 --- a/app/models/project_repository.rb +++ b/app/models/project_repository.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ProjectRepository < ActiveRecord::Base +class ProjectRepository < ApplicationRecord include Shardable belongs_to :project, inverse_of: :project_repository diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index 4cf3a7f3d84..f650dbd3726 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -131,8 +131,8 @@ class KubernetesService < DeploymentService # short time later def terminals(environment) with_reactive_cache do |data| - pods = filter_by_label(data[:pods], app: environment.slug) - terminals = pods.flat_map { |pod| terminals_for_pod(api_url, actual_namespace, pod) } + pods = filter_by_project_environment(data[:pods], project.full_path_slug, environment.slug) + terminals = pods.flat_map { |pod| terminals_for_pod(api_url, actual_namespace, pod) }.compact terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) } end end diff --git a/app/models/project_statistics.rb b/app/models/project_statistics.rb index 781a197d56f..c020e72908c 100644 --- a/app/models/project_statistics.rb +++ b/app/models/project_statistics.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ProjectStatistics < ActiveRecord::Base +class ProjectStatistics < ApplicationRecord belongs_to :project belongs_to :namespace diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index c43bd45a62f..268706a6aea 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -59,7 +59,7 @@ class ProjectWiki # Returns the Gitlab::Git::Wiki object. def wiki @wiki ||= begin - gl_repository = Gitlab::GlRepository.gl_repository(project, true) + gl_repository = Gitlab::GlRepository::WIKI.identifier_for_subject(project) raw_repository = Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', gl_repository, full_path) create_repo!(raw_repository) unless raw_repository.exists? @@ -151,7 +151,7 @@ class ProjectWiki end def repository - @repository ||= Repository.new(full_path, @project, disk_path: disk_path, is_wiki: true) + @repository ||= Repository.new(full_path, @project, disk_path: disk_path, repo_type: Gitlab::GlRepository::WIKI) end def default_branch @@ -183,7 +183,7 @@ class ProjectWiki end def commit_details(action, message = nil, title = nil) - commit_message = message || default_message(action, title) + commit_message = message.presence || default_message(action, title) git_user = Gitlab::Git::User.from_gitlab(@user) Gitlab::Git::Wiki::CommitDetails.new(@user.id, diff --git a/app/models/prometheus_metric.rb b/app/models/prometheus_metric.rb index 5594594a48d..62090444f79 100644 --- a/app/models/prometheus_metric.rb +++ b/app/models/prometheus_metric.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class PrometheusMetric < ActiveRecord::Base +class PrometheusMetric < ApplicationRecord belongs_to :project, validate: true, inverse_of: :prometheus_metrics enum group: { diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb index 597431be65a..ee0c94c20af 100644 --- a/app/models/protected_branch.rb +++ b/app/models/protected_branch.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ProtectedBranch < ActiveRecord::Base +class ProtectedBranch < ApplicationRecord include ProtectedRef protected_ref_access_levels :merge, :push diff --git a/app/models/protected_branch/merge_access_level.rb b/app/models/protected_branch/merge_access_level.rb index b0d5c64e931..de240e40316 100644 --- a/app/models/protected_branch/merge_access_level.rb +++ b/app/models/protected_branch/merge_access_level.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -class ProtectedBranch::MergeAccessLevel < ActiveRecord::Base +class ProtectedBranch::MergeAccessLevel < ApplicationRecord include ProtectedBranchAccess end diff --git a/app/models/protected_branch/push_access_level.rb b/app/models/protected_branch/push_access_level.rb index b2a88229853..bde1d29ad7f 100644 --- a/app/models/protected_branch/push_access_level.rb +++ b/app/models/protected_branch/push_access_level.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -class ProtectedBranch::PushAccessLevel < ActiveRecord::Base +class ProtectedBranch::PushAccessLevel < ApplicationRecord include ProtectedBranchAccess end diff --git a/app/models/protected_tag.rb b/app/models/protected_tag.rb index d28ebabfe49..6b507429e57 100644 --- a/app/models/protected_tag.rb +++ b/app/models/protected_tag.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ProtectedTag < ActiveRecord::Base +class ProtectedTag < ApplicationRecord include ProtectedRef validates :name, uniqueness: { scope: :project_id } diff --git a/app/models/protected_tag/create_access_level.rb b/app/models/protected_tag/create_access_level.rb index b06e55fb5dd..9fcfa7646a2 100644 --- a/app/models/protected_tag/create_access_level.rb +++ b/app/models/protected_tag/create_access_level.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class ProtectedTag::CreateAccessLevel < ActiveRecord::Base +class ProtectedTag::CreateAccessLevel < ApplicationRecord include ProtectedTagAccess def check_access(user) diff --git a/app/models/push_event_payload.rb b/app/models/push_event_payload.rb index c7769edf055..537859ec7b7 100644 --- a/app/models/push_event_payload.rb +++ b/app/models/push_event_payload.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class PushEventPayload < ActiveRecord::Base +class PushEventPayload < ApplicationRecord include ShaAttribute belongs_to :event, inverse_of: :push_event_payload diff --git a/app/models/redirect_route.rb b/app/models/redirect_route.rb index c6bd4bb6dfa..2e4769364c6 100644 --- a/app/models/redirect_route.rb +++ b/app/models/redirect_route.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class RedirectRoute < ActiveRecord::Base +class RedirectRoute < ApplicationRecord belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations validates :source, presence: true diff --git a/app/models/release.rb b/app/models/release.rb index 0dae5c90394..746fc31a038 100644 --- a/app/models/release.rb +++ b/app/models/release.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Release < ActiveRecord::Base +class Release < ApplicationRecord include CacheMarkdownField include Gitlab::Utils::StrongMemoize diff --git a/app/models/releases/link.rb b/app/models/releases/link.rb index 6c507c47752..36ec33d3e3e 100644 --- a/app/models/releases/link.rb +++ b/app/models/releases/link.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Releases - class Link < ActiveRecord::Base + class Link < ApplicationRecord self.table_name = 'release_links' belongs_to :release diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb index 5eba7ddd75c..5610cfe0f24 100644 --- a/app/models/remote_mirror.rb +++ b/app/models/remote_mirror.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class RemoteMirror < ActiveRecord::Base +class RemoteMirror < ApplicationRecord include AfterCommitQueue include MirrorAuthentication diff --git a/app/models/repository.rb b/app/models/repository.rb index ff355295862..574ce12b309 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -19,7 +19,7 @@ class Repository include Gitlab::RepositoryCacheAdapter - attr_accessor :full_path, :disk_path, :project, :is_wiki + attr_accessor :full_path, :disk_path, :project, :repo_type delegate :ref_name_for_sha, to: :raw_repository delegate :bundle_to_disk, to: :raw_repository @@ -60,12 +60,12 @@ class Repository xcode_config: :xcode_project? }.freeze - def initialize(full_path, project, disk_path: nil, is_wiki: false) + def initialize(full_path, project, disk_path: nil, repo_type: Gitlab::GlRepository::PROJECT) @full_path = full_path @disk_path = disk_path || full_path @project = project @commit_cache = {} - @is_wiki = is_wiki + @repo_type = repo_type end def ==(other) @@ -1112,7 +1112,7 @@ class Repository def initialize_raw_repository Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', - Gitlab::GlRepository.gl_repository(project, is_wiki), + repo_type.identifier_for_subject(project), project.full_path) end end diff --git a/app/models/repository_language.rb b/app/models/repository_language.rb index b18142a2ac4..e6867f905e2 100644 --- a/app/models/repository_language.rb +++ b/app/models/repository_language.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class RepositoryLanguage < ActiveRecord::Base +class RepositoryLanguage < ApplicationRecord belongs_to :project belongs_to :programming_language diff --git a/app/models/resource_label_event.rb b/app/models/resource_label_event.rb index 3fd96b9dc18..f2c7cb6a65d 100644 --- a/app/models/resource_label_event.rb +++ b/app/models/resource_label_event.rb @@ -2,7 +2,7 @@ # This model is not used yet, it will be used for: # https://gitlab.com/gitlab-org/gitlab-ce/issues/48483 -class ResourceLabelEvent < ActiveRecord::Base +class ResourceLabelEvent < ApplicationRecord include Importable include Gitlab::Utils::StrongMemoize include CacheMarkdownField diff --git a/app/models/route.rb b/app/models/route.rb index 4b23dfa5778..7e3db54d4fe 100644 --- a/app/models/route.rb +++ b/app/models/route.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Route < ActiveRecord::Base +class Route < ApplicationRecord include CaseSensitivity belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb index 6caab24143b..0427d5b9ca7 100644 --- a/app/models/sent_notification.rb +++ b/app/models/sent_notification.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class SentNotification < ActiveRecord::Base +class SentNotification < ApplicationRecord serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize belongs_to :project diff --git a/app/models/service.rb b/app/models/service.rb index da523bfa426..c6d5eb353dc 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -2,7 +2,7 @@ # To add new service you should build a class inherited from Service # and implement a set of methods -class Service < ActiveRecord::Base +class Service < ApplicationRecord include Sortable include Importable include ProjectServicesLoggable diff --git a/app/models/shard.rb b/app/models/shard.rb index e39d4232486..335a279c6aa 100644 --- a/app/models/shard.rb +++ b/app/models/shard.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Shard < ActiveRecord::Base +class Shard < ApplicationRecord # Store shard names from the configuration file in the database. This is not a # list of active shards - we just want to assign an immutable, unique ID to # every shard name for easy indexing / referencing. diff --git a/app/models/snippet.rb b/app/models/snippet.rb index f23ddd64fe3..f4fdac2558c 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Snippet < ActiveRecord::Base +class Snippet < ApplicationRecord include Gitlab::VisibilityLevel include Redactable include CacheMarkdownField diff --git a/app/models/spam_log.rb b/app/models/spam_log.rb index ef3f974b959..5b9ece8373f 100644 --- a/app/models/spam_log.rb +++ b/app/models/spam_log.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class SpamLog < ActiveRecord::Base +class SpamLog < ApplicationRecord belongs_to :user validates :user, presence: true diff --git a/app/models/subscription.rb b/app/models/subscription.rb index 0f6ee0ddf7e..24a2b8b5167 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Subscription < ActiveRecord::Base +class Subscription < ApplicationRecord belongs_to :user belongs_to :project belongs_to :subscribable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations diff --git a/app/models/suggestion.rb b/app/models/suggestion.rb index 09034646bff..22e2f11230d 100644 --- a/app/models/suggestion.rb +++ b/app/models/suggestion.rb @@ -1,11 +1,19 @@ # frozen_string_literal: true class Suggestion < ApplicationRecord + include Suggestible + belongs_to :note, inverse_of: :suggestions validates :note, presence: true validates :commit_id, presence: true, if: :applied? - delegate :original_position, :position, :noteable, to: :note + delegate :position, :noteable, to: :note + + scope :active, -> { where(outdated: false) } + + def diff_file + note.latest_diff_file + end def project noteable.source_project @@ -19,37 +27,37 @@ class Suggestion < ApplicationRecord position.file_path end - # For now, suggestions only serve as a way to send patches that - # will change a single line (being able to apply multiple in the same place), - # which explains `from_line` and `to_line` being the same line. - # We'll iterate on that in https://gitlab.com/gitlab-org/gitlab-ce/issues/53310 - # when allowing multi-line suggestions. - def from_line - position.new_line - end - alias_method :to_line, :from_line - - def from_original_line - original_position.new_line - end - alias_method :to_original_line, :from_original_line - # `from_line_index` and `to_line_index` represents diff/blob line numbers in # index-like way (N-1). def from_line_index from_line - 1 end - alias_method :to_line_index, :from_line_index - def appliable? - return false unless note.supports_suggestion? + def to_line_index + to_line - 1 + end + def appliable?(cached: true) !applied? && noteable.opened? && + !outdated?(cached: cached) && + note.supports_suggestion? && different_content? && note.active? end + # Overwrites outdated column + def outdated?(cached: true) + return super() if cached + return true unless diff_file + + from_content != fetch_from_content + end + + def target_line + position.new_line + end + private def different_content? diff --git a/app/models/system_note_metadata.rb b/app/models/system_note_metadata.rb index d555ebe5322..55da37c9545 100644 --- a/app/models/system_note_metadata.rb +++ b/app/models/system_note_metadata.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class SystemNoteMetadata < ActiveRecord::Base +class SystemNoteMetadata < ApplicationRecord # These notes's action text might contain a reference that is external. # We should always force a deep validation upon references that are found # in this note type. diff --git a/app/models/term_agreement.rb b/app/models/term_agreement.rb index 9b3c8ac68bd..a4a9dc10282 100644 --- a/app/models/term_agreement.rb +++ b/app/models/term_agreement.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class TermAgreement < ActiveRecord::Base +class TermAgreement < ApplicationRecord belongs_to :term, class_name: 'ApplicationSetting::Term' belongs_to :user diff --git a/app/models/timelog.rb b/app/models/timelog.rb index e04c644a53a..048134fbf04 100644 --- a/app/models/timelog.rb +++ b/app/models/timelog.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Timelog < ActiveRecord::Base +class Timelog < ApplicationRecord validates :time_spent, :user, presence: true validate :issuable_id_is_present diff --git a/app/models/todo.rb b/app/models/todo.rb index 2b0dee875a3..5dcc3e9945a 100644 --- a/app/models/todo.rb +++ b/app/models/todo.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Todo < ActiveRecord::Base +class Todo < ApplicationRecord include Sortable include FromUnion diff --git a/app/models/trending_project.rb b/app/models/trending_project.rb index 7b22e8cb760..810dee672b2 100644 --- a/app/models/trending_project.rb +++ b/app/models/trending_project.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class TrendingProject < ActiveRecord::Base +class TrendingProject < ApplicationRecord belongs_to :project # The number of months to include in the trending calculation. diff --git a/app/models/u2f_registration.rb b/app/models/u2f_registration.rb index 37598173fd1..b4645462314 100644 --- a/app/models/u2f_registration.rb +++ b/app/models/u2f_registration.rb @@ -2,7 +2,7 @@ # Registration information for U2F (universal 2nd factor) devices, like Yubikeys -class U2fRegistration < ActiveRecord::Base +class U2fRegistration < ApplicationRecord belongs_to :user def self.register(user, app_id, params, challenges) diff --git a/app/models/upload.rb b/app/models/upload.rb index 20860f14b83..9bffdcdb2e7 100644 --- a/app/models/upload.rb +++ b/app/models/upload.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Upload < ActiveRecord::Base +class Upload < ApplicationRecord # Upper limit for foreground checksum processing CHECKSUM_THRESHOLD = 100.megabytes diff --git a/app/models/user.rb b/app/models/user.rb index 0ebfb9a0ccb..e0c518a9b75 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -432,7 +432,7 @@ class User < ApplicationRecord fuzzy_arel_match(:name, query, lower_exact_match: true) .or(fuzzy_arel_match(:username, query, lower_exact_match: true)) .or(arel_table[:email].eq(query)) - ).reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, :name) + ).reorder(order % { query: ApplicationRecord.connection.quote(query) }, :name) end # Limits the result set to users _not_ in the given query/list of IDs. @@ -917,6 +917,10 @@ class User < ApplicationRecord DeployKey.unscoped.in_projects(authorized_projects.pluck(:id)).distinct(:id) end + def highest_role + members.maximum(:access_level) || Gitlab::Access::NO_ACCESS + end + def accessible_deploy_keys @accessible_deploy_keys ||= begin key_ids = project_deploy_keys.pluck(:id) diff --git a/app/models/user_agent_detail.rb b/app/models/user_agent_detail.rb index e2b2e7f1df9..fea1fce3c8d 100644 --- a/app/models/user_agent_detail.rb +++ b/app/models/user_agent_detail.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class UserAgentDetail < ActiveRecord::Base +class UserAgentDetail < ApplicationRecord belongs_to :subject, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations validates :user_agent, :ip_address, :subject_id, :subject_type, presence: true diff --git a/app/models/user_callout.rb b/app/models/user_callout.rb index 76e7bc06b4e..027ee44c6a9 100644 --- a/app/models/user_callout.rb +++ b/app/models/user_callout.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class UserCallout < ActiveRecord::Base +class UserCallout < ApplicationRecord belongs_to :user # We use `UserCalloutEnums.feature_names` here so that EE can more easily diff --git a/app/models/user_custom_attribute.rb b/app/models/user_custom_attribute.rb index e0ffe8ebbfd..727975c3f6e 100644 --- a/app/models/user_custom_attribute.rb +++ b/app/models/user_custom_attribute.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class UserCustomAttribute < ActiveRecord::Base +class UserCustomAttribute < ApplicationRecord belongs_to :user validates :user_id, :key, :value, presence: true diff --git a/app/models/user_interacted_project.rb b/app/models/user_interacted_project.rb index 5fc59b274f5..f6f72f4b77a 100644 --- a/app/models/user_interacted_project.rb +++ b/app/models/user_interacted_project.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class UserInteractedProject < ActiveRecord::Base +class UserInteractedProject < ApplicationRecord belongs_to :user belongs_to :project diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb index 32d0407800f..282b192167f 100644 --- a/app/models/user_preference.rb +++ b/app/models/user_preference.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class UserPreference < ActiveRecord::Base +class UserPreference < ApplicationRecord # We could use enums, but Rails 4 doesn't support multiple # enum options with same name for multiple fields, also it creates # extra methods that aren't really needed here. diff --git a/app/models/user_status.rb b/app/models/user_status.rb index 2bbb0c59ac1..6ced4f56823 100644 --- a/app/models/user_status.rb +++ b/app/models/user_status.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class UserStatus < ActiveRecord::Base +class UserStatus < ApplicationRecord include CacheMarkdownField self.primary_key = :user_id diff --git a/app/models/user_synced_attributes_metadata.rb b/app/models/user_synced_attributes_metadata.rb index 7115262942d..5aacf11b1cb 100644 --- a/app/models/user_synced_attributes_metadata.rb +++ b/app/models/user_synced_attributes_metadata.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class UserSyncedAttributesMetadata < ActiveRecord::Base +class UserSyncedAttributesMetadata < ApplicationRecord belongs_to :user validates :user, presence: true diff --git a/app/models/users_star_project.rb b/app/models/users_star_project.rb index bdaf58ae1c1..9be6bd2e6f3 100644 --- a/app/models/users_star_project.rb +++ b/app/models/users_star_project.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class UsersStarProject < ActiveRecord::Base +class UsersStarProject < ApplicationRecord belongs_to :project, counter_cache: :star_count, touch: true belongs_to :user |