diff options
Diffstat (limited to 'app/models')
53 files changed, 370 insertions, 236 deletions
diff --git a/app/models/ability.rb b/app/models/ability.rb index d2b8a8447b5..0b6bcbde5d9 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -1,4 +1,4 @@ -require 'declarative_policy' +require_dependency 'declarative_policy' class Ability class << self diff --git a/app/models/appearance.rb b/app/models/appearance.rb index c79326e8427..f9c48482be7 100644 --- a/app/models/appearance.rb +++ b/app/models/appearance.rb @@ -10,5 +10,5 @@ class Appearance < ActiveRecord::Base mount_uploader :logo, AttachmentUploader mount_uploader :header_logo, AttachmentUploader - has_many :uploads, as: :model, dependent: :destroy + has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 668caef0d2c..b0d7f7ef5f5 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -13,13 +13,13 @@ class ApplicationSetting < ActiveRecord::Base [\r\n] # any number of newline characters }x - serialize :restricted_visibility_levels # rubocop:disable Cop/ActiverecordSerialize - serialize :import_sources # rubocop:disable Cop/ActiverecordSerialize - serialize :disabled_oauth_sign_in_sources, Array # rubocop:disable Cop/ActiverecordSerialize - serialize :domain_whitelist, Array # rubocop:disable Cop/ActiverecordSerialize - serialize :domain_blacklist, Array # rubocop:disable Cop/ActiverecordSerialize - serialize :repository_storages # rubocop:disable Cop/ActiverecordSerialize - serialize :sidekiq_throttling_queues, Array # rubocop:disable Cop/ActiverecordSerialize + serialize :restricted_visibility_levels # rubocop:disable Cop/ActiveRecordSerialize + serialize :import_sources # rubocop:disable Cop/ActiveRecordSerialize + serialize :disabled_oauth_sign_in_sources, Array # rubocop:disable Cop/ActiveRecordSerialize + serialize :domain_whitelist, Array # rubocop:disable Cop/ActiveRecordSerialize + serialize :domain_blacklist, Array # rubocop:disable Cop/ActiveRecordSerialize + serialize :repository_storages # rubocop:disable Cop/ActiveRecordSerialize + serialize :sidekiq_throttling_queues, Array # rubocop:disable Cop/ActiveRecordSerialize cache_markdown_field :sign_in_text cache_markdown_field :help_page_text diff --git a/app/models/audit_event.rb b/app/models/audit_event.rb index 46d412fbd72..112a8778b4e 100644 --- a/app/models/audit_event.rb +++ b/app/models/audit_event.rb @@ -1,5 +1,5 @@ class AuditEvent < ActiveRecord::Base - serialize :details, Hash # rubocop:disable Cop/ActiverecordSerialize + serialize :details, Hash # rubocop:disable Cop/ActiveRecordSerialize belongs_to :user, foreign_key: :author_id diff --git a/app/models/board.rb b/app/models/board.rb index 18081a32157..97d0f550925 100644 --- a/app/models/board.rb +++ b/app/models/board.rb @@ -1,7 +1,7 @@ class Board < ActiveRecord::Base belongs_to :project - has_many :lists, -> { order(:list_type, :position) }, dependent: :delete_all + has_many :lists, -> { order(:list_type, :position) }, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent validates :project, presence: true diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index a300536532b..48586ba8910 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -19,8 +19,8 @@ module Ci ) end - serialize :options # rubocop:disable Cop/ActiverecordSerialize - serialize :yaml_variables, Gitlab::Serializer::Ci::Variables # rubocop:disable Cop/ActiverecordSerialize + serialize :options # rubocop:disable Cop/ActiveRecordSerialize + serialize :yaml_variables, Gitlab::Serializer::Ci::Variables # rubocop:disable Cop/ActiveRecordSerialize delegate :name, to: :project, prefix: true @@ -176,13 +176,22 @@ module Ci # * Lowercased # * Anything not matching [a-z0-9-] is replaced with a - # * Maximum length is 63 bytes + # * First/Last Character is not a hyphen def ref_slug - slugified = ref.to_s.downcase - slugified.gsub(/[^a-z0-9]/, '-')[0..62] + ref.to_s + .downcase + .gsub(/[^a-z0-9]/, '-')[0..62] + .gsub(/(\A-+|-+\z)/, '') end # Variables whose value does not depend on environment def simple_variables + variables(environment: nil) + end + + # All variables, including those dependent on environment, which could + # contain unexpanded variables. + def variables(environment: persisted_environment) variables = predefined_variables variables += project.predefined_variables variables += pipeline.predefined_variables @@ -191,15 +200,11 @@ module Ci variables += project.deployment_variables if has_environment? variables += yaml_variables variables += user_variables - variables += project.secret_variables_for(ref).map(&:to_runner_variable) + variables += secret_variables(environment: environment) variables += trigger_request.user_variables if trigger_request - variables - end + variables += persisted_environment_variables if environment - # All variables, including those dependent on environment, which could - # contain unexpanded variables. - def variables - simple_variables.concat(persisted_environment_variables) + variables end def merge_request @@ -367,6 +372,11 @@ module Ci ] end + def secret_variables(environment: persisted_environment) + project.secret_variables_for(ref: ref, environment: environment) + .map(&:to_runner_variable) + end + def steps [Gitlab::Ci::Build::Step.from_commands(self), Gitlab::Ci::Build::Step.from_after_script(self)].compact diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 364858964b0..b646b32fc64 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -14,7 +14,7 @@ module Ci has_many :stages has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id has_many :builds, foreign_key: :commit_id - has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id + has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent # Merge requests for which the current pipeline is running against # the merge request's latest commit. @@ -326,10 +326,24 @@ module Ci end end + def ci_yaml_file_path + if project.ci_config_path.blank? + '.gitlab-ci.yml' + else + project.ci_config_path + end + end + def ci_yaml_file return @ci_yaml_file if defined?(@ci_yaml_file) - @ci_yaml_file = project.repository.gitlab_ci_yml_for(sha) rescue nil + @ci_yaml_file = begin + project.repository.gitlab_ci_yml_for(sha, ci_yaml_file_path) + rescue Rugged::ReferenceError, GRPC::NotFound, GRPC::Internal + self.yaml_errors = + "Failed to load CI/CD config file at #{ci_yaml_file_path}" + nil + end end def has_yaml_errors? @@ -377,7 +391,8 @@ module Ci def predefined_variables [ - { key: 'CI_PIPELINE_ID', value: id.to_s, public: true } + { key: 'CI_PIPELINE_ID', value: id.to_s, public: true }, + { key: 'CI_CONFIG_PATH', value: ci_yaml_file_path, public: true } ] end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index d12f96f3d0b..f5790f9744f 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -8,7 +8,7 @@ module Ci FORM_EDITABLE = %i[description tag_list active run_untagged locked].freeze has_many :builds - has_many :runner_projects, dependent: :destroy + has_many :runner_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :projects, through: :runner_projects has_one :last_build, ->() { order('id DESC') }, class_name: 'Ci::Build' diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb index 564334ad1ad..c58ce5c3717 100644 --- a/app/models/ci/trigger_request.rb +++ b/app/models/ci/trigger_request.rb @@ -6,7 +6,7 @@ module Ci belongs_to :pipeline, foreign_key: :commit_id has_many :builds - serialize :variables # rubocop:disable Cop/ActiverecordSerialize + serialize :variables # rubocop:disable Cop/ActiveRecordSerialize def user_variables return [] unless variables diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index 96d6e120998..0b8d0ff881a 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -5,7 +5,7 @@ module Ci belongs_to :project - validates :key, uniqueness: { scope: :project_id } + validates :key, uniqueness: { scope: [:project_id, :environment_scope] } scope :unprotected, -> { where(protected: false) } end diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb index a7fd0a15f0f..f4f9b037957 100644 --- a/app/models/concerns/awardable.rb +++ b/app/models/concerns/awardable.rb @@ -2,7 +2,7 @@ module Awardable extend ActiveSupport::Concern included do - has_many :award_emoji, -> { includes(:user).order(:id) }, as: :awardable, dependent: :destroy + has_many :award_emoji, -> { includes(:user).order(:id) }, as: :awardable, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent if self < Participable # By default we always load award_emoji user association diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb index eb32bf3d32a..95152dcd68c 100644 --- a/app/models/concerns/cache_markdown_field.rb +++ b/app/models/concerns/cache_markdown_field.rb @@ -78,7 +78,7 @@ module CacheMarkdownField def cached_html_up_to_date?(markdown_field) html_field = cached_markdown_fields.html_field(markdown_field) - cached = !cached_html_for(markdown_field).nil? && !__send__(markdown_field).nil? + cached = cached_html_for(markdown_field).present? && __send__(markdown_field).present? return false unless cached markdown_changed = attribute_changed?(markdown_field) || false diff --git a/app/models/concerns/feature_gate.rb b/app/models/concerns/feature_gate.rb new file mode 100644 index 00000000000..5db64fe82c4 --- /dev/null +++ b/app/models/concerns/feature_gate.rb @@ -0,0 +1,7 @@ +module FeatureGate + def flipper_id + return nil if new_record? + + "#{self.class.name}:#{id}" + end +end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index d178ee4422b..23cb85600da 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -30,7 +30,7 @@ module Issuable belongs_to :updated_by, class_name: "User" belongs_to :last_edited_by, class_name: 'User' belongs_to :milestone - has_many :notes, as: :noteable, inverse_of: :noteable, dependent: :destroy do + has_many :notes, as: :noteable, inverse_of: :noteable, dependent: :destroy do # rubocop:disable Cop/ActiveRecordDependent def authors_loaded? # We check first if we're loaded to not load unnecessarily. loaded? && to_a.all? { |note| note.association(:author).loaded? } @@ -42,9 +42,9 @@ module Issuable end end - has_many :label_links, as: :target, dependent: :destroy + has_many :label_links, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :labels, through: :label_links - has_many :todos, as: :target, dependent: :destroy + has_many :todos, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_one :metrics @@ -102,6 +102,14 @@ module Issuable def locking_enabled? title_changed? || description_changed? end + + def allows_multiple_assignees? + false + end + + def has_multiple_assignees? + assignees.count > 1 + end end module ClassMethods diff --git a/app/models/concerns/mentionable/reference_regexes.rb b/app/models/concerns/mentionable/reference_regexes.rb index 1848230ec7e..2d86a70c395 100644 --- a/app/models/concerns/mentionable/reference_regexes.rb +++ b/app/models/concerns/mentionable/reference_regexes.rb @@ -14,7 +14,7 @@ module Mentionable end EXTERNAL_PATTERN = begin - issue_pattern = ExternalIssue.reference_pattern + issue_pattern = IssueTrackerService.reference_pattern link_patterns = URI.regexp(%w(http https)) reference_pattern(link_patterns, issue_pattern) end diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb index 47e71c58557..fc6b840f7a8 100644 --- a/app/models/concerns/protected_ref.rb +++ b/app/models/concerns/protected_ref.rb @@ -17,7 +17,7 @@ module ProtectedRef class_methods do def protected_ref_access_levels(*types) types.each do |type| - has_many :"#{type}_access_levels", dependent: :destroy + has_many :"#{type}_access_levels", dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent validates :"#{type}_access_levels", length: { is: 1, message: "are restricted to a single instance per #{self.model_name.human}." } diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb index ec7796a9dbb..f5048d17d80 100644 --- a/app/models/concerns/routable.rb +++ b/app/models/concerns/routable.rb @@ -4,8 +4,8 @@ module Routable extend ActiveSupport::Concern included do - has_one :route, as: :source, autosave: true, dependent: :destroy - has_many :redirect_routes, as: :source, autosave: true, dependent: :destroy + has_one :route, as: :source, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :redirect_routes, as: :source, autosave: true, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent validates_associated :route validates :route, presence: true @@ -103,8 +103,12 @@ module Routable def full_path return uncached_full_path unless RequestStore.active? - key = "routable/full_path/#{self.class.name}/#{self.id}" - RequestStore[key] ||= uncached_full_path + RequestStore[full_path_key] ||= uncached_full_path + end + + def expires_full_path_cache + RequestStore.delete(full_path_key) if RequestStore.active? + @full_path = nil end def build_full_path @@ -135,6 +139,10 @@ module Routable path_changed? || parent_changed? end + def full_path_key + @full_path_key ||= "routable/full_path/#{self.class.name}/#{self.id}" + end + def build_full_name if parent && name parent.human_name + ' / ' + name diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb index a155a064032..fdacfa5a194 100644 --- a/app/models/concerns/sortable.rb +++ b/app/models/concerns/sortable.rb @@ -5,6 +5,25 @@ module Sortable extend ActiveSupport::Concern + module DropDefaultScopeOnFinders + # Override these methods to drop the `ORDER BY id DESC` default scope. + # See http://dba.stackexchange.com/a/110919 for why we do this. + %i[find find_by find_by!].each do |meth| + define_method meth do |*args, &block| + return super(*args, &block) if block + + unordered_relation = unscope(:order) + + # We cannot simply call `meth` on `unscope(:order)`, since that is also + # an instance of the same relation class this module is included into, + # which means we'd get infinite recursion. + # We explicitly use the original implementation to prevent this. + original_impl = method(__method__).super_method.unbind + original_impl.bind(unordered_relation).call(*args) + end + end + end + included do # By default all models should be ordered # by created_at field starting from newest @@ -18,6 +37,10 @@ module Sortable scope :order_updated_asc, -> { reorder(updated_at: :asc) } scope :order_name_asc, -> { reorder(name: :asc) } scope :order_name_desc, -> { reorder(name: :desc) } + + # All queries (relations) on this model are instances of this `relation_klass`. + relation_klass = relation_delegate_class(ActiveRecord::Relation) + relation_klass.prepend DropDefaultScopeOnFinders end module ClassMethods diff --git a/app/models/concerns/spammable.rb b/app/models/concerns/spammable.rb index 647a6cad3d7..bd75f25a210 100644 --- a/app/models/concerns/spammable.rb +++ b/app/models/concerns/spammable.rb @@ -8,7 +8,7 @@ module Spammable end included do - has_one :user_agent_detail, as: :subject, dependent: :destroy + has_one :user_agent_detail, as: :subject, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent attr_accessor :spam attr_accessor :spam_log diff --git a/app/models/concerns/subscribable.rb b/app/models/concerns/subscribable.rb index f60a0f8f438..274b38a7708 100644 --- a/app/models/concerns/subscribable.rb +++ b/app/models/concerns/subscribable.rb @@ -9,7 +9,7 @@ module Subscribable extend ActiveSupport::Concern included do - has_many :subscriptions, dependent: :destroy, as: :subscribable + has_many :subscriptions, dependent: :destroy, as: :subscribable # rubocop:disable Cop/ActiveRecordDependent end def subscribed?(user, project = nil) diff --git a/app/models/concerns/time_trackable.rb b/app/models/concerns/time_trackable.rb index 9cf83440784..b517ddaebd7 100644 --- a/app/models/concerns/time_trackable.rb +++ b/app/models/concerns/time_trackable.rb @@ -18,7 +18,7 @@ module TimeTrackable validates :time_estimate, numericality: { message: 'has an invalid format' }, allow_nil: false validate :check_negative_time_spent - has_many :timelogs, dependent: :destroy + has_many :timelogs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent end def spend_time(options) diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb index 053f2a11aa0..51768dd96bc 100644 --- a/app/models/deploy_key.rb +++ b/app/models/deploy_key.rb @@ -1,5 +1,5 @@ class DeployKey < Key - has_many :deploy_keys_projects, dependent: :destroy + has_many :deploy_keys_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :projects, through: :deploy_keys_projects scope :in_projects, ->(projects) { joins(:deploy_keys_projects).where('deploy_keys_projects.project_id in (?)', projects) } diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb index 20ef1378500..e9a60e6ce09 100644 --- a/app/models/diff_note.rb +++ b/app/models/diff_note.rb @@ -6,9 +6,9 @@ class DiffNote < Note NOTEABLE_TYPES = %w(MergeRequest Commit).freeze - serialize :original_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize - serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize - serialize :change_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize + serialize :original_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize + serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize + serialize :change_position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize validates :original_position, presence: true validates :position, presence: true diff --git a/app/models/environment.rb b/app/models/environment.rb index 66c96d0f586..e9ebf0637f3 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -6,7 +6,7 @@ class Environment < ActiveRecord::Base belongs_to :project, required: true, validate: true - has_many :deployments, dependent: :destroy + has_many :deployments, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_one :last_deployment, -> { order('deployments.id DESC') }, class_name: 'Deployment' before_validation :nullify_external_url @@ -218,8 +218,7 @@ class Environment < ActiveRecord::Base end def etag_cache_key - Gitlab::Routing.url_helpers.namespace_project_environments_path( - project.namespace, + Gitlab::Routing.url_helpers.project_environments_path( project, format: :json) end diff --git a/app/models/event.rb b/app/models/event.rb index 29bc141c5cd..8d93a228494 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -50,7 +50,7 @@ class Event < ActiveRecord::Base belongs_to :target, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations # For Hash only - serialize :data # rubocop:disable Cop/ActiverecordSerialize + serialize :data # rubocop:disable Cop/ActiveRecordSerialize # Callbacks after_create :reset_project_activity diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb index e63f89a9f85..0bf18e529f0 100644 --- a/app/models/external_issue.rb +++ b/app/models/external_issue.rb @@ -38,11 +38,6 @@ class ExternalIssue @project.id end - # Pattern used to extract `JIRA-123` issue references from text - def self.reference_pattern - @reference_pattern ||= %r{(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)} - end - def to_reference(_from_project = nil, full: nil) id end diff --git a/app/models/forked_project_link.rb b/app/models/forked_project_link.rb index 36cf7ad6a28..8d35864eff6 100644 --- a/app/models/forked_project_link.rb +++ b/app/models/forked_project_link.rb @@ -1,4 +1,4 @@ class ForkedProjectLink < ActiveRecord::Base - belongs_to :forked_to_project, class_name: 'Project' - belongs_to :forked_from_project, class_name: 'Project' + belongs_to :forked_to_project, -> { where.not(pending_delete: true) }, class_name: 'Project' + belongs_to :forked_from_project, -> { where.not(pending_delete: true) }, class_name: 'Project' end diff --git a/app/models/group.rb b/app/models/group.rb index a6fdb30f84c..b93fce6100d 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -8,7 +8,7 @@ class Group < Namespace include Referable include SelectForProjectAuthorization - has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source + has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent alias_method :members, :group_members has_many :users, through: :group_members has_many :owners, @@ -16,11 +16,11 @@ class Group < Namespace through: :group_members, source: :user - has_many :requesters, -> { where.not(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'GroupMember' + has_many :requesters, -> { where.not(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'GroupMember' # rubocop:disable Cop/ActiveRecordDependent - has_many :project_group_links, dependent: :destroy + has_many :project_group_links, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :shared_projects, through: :project_group_links, source: :project - has_many :notification_settings, dependent: :destroy, as: :source + has_many :notification_settings, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent has_many :labels, class_name: 'GroupLabel' validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? } @@ -31,7 +31,7 @@ class Group < Namespace validates :two_factor_grace_period, presence: true, numericality: { greater_than_or_equal_to: 0 } mount_uploader :avatar, AvatarUploader - has_many :uploads, as: :model, dependent: :destroy + has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent after_create :post_create_hook after_destroy :post_destroy_hook diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb index 7503f3739c3..7a9f8997959 100644 --- a/app/models/hooks/web_hook.rb +++ b/app/models/hooks/web_hook.rb @@ -12,7 +12,7 @@ class WebHook < ActiveRecord::Base default_value_for :repository_update_events, false default_value_for :enable_ssl_verification, true - has_many :web_hook_logs, dependent: :destroy + has_many :web_hook_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent scope :push_hooks, -> { where(push_events: true) } scope :tag_push_hooks, -> { where(tag_push_events: true) } diff --git a/app/models/hooks/web_hook_log.rb b/app/models/hooks/web_hook_log.rb index d73cfcf630d..e72c125fb69 100644 --- a/app/models/hooks/web_hook_log.rb +++ b/app/models/hooks/web_hook_log.rb @@ -1,9 +1,9 @@ class WebHookLog < ActiveRecord::Base belongs_to :web_hook - serialize :request_headers, Hash # rubocop:disable Cop/ActiverecordSerialize - serialize :request_data, Hash # rubocop:disable Cop/ActiverecordSerialize - serialize :response_headers, Hash # rubocop:disable Cop/ActiverecordSerialize + serialize :request_headers, Hash # rubocop:disable Cop/ActiveRecordSerialize + serialize :request_data, Hash # rubocop:disable Cop/ActiveRecordSerialize + serialize :response_headers, Hash # rubocop:disable Cop/ActiveRecordSerialize validates :web_hook, presence: true diff --git a/app/models/issue.rb b/app/models/issue.rb index 3a9a6dba601..01f985823e1 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -23,9 +23,14 @@ class Issue < ActiveRecord::Base belongs_to :project belongs_to :moved_to, class_name: 'Issue' - has_many :events, as: :target, dependent: :destroy + has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent - has_many :merge_requests_closing_issues, class_name: 'MergeRequestsClosingIssues', dependent: :delete_all + has_many :merge_requests_closing_issues, + class_name: 'MergeRequestsClosingIssues', + dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent + + has_many :issue_assignees + has_many :assignees, class_name: "User", through: :issue_assignees has_many :issue_assignees has_many :assignees, class_name: "User", through: :issue_assignees @@ -295,11 +300,7 @@ class Issue < ActiveRecord::Base end def expire_etag_cache - key = Gitlab::Routing.url_helpers.realtime_changes_namespace_project_issue_path( - project.namespace, - project, - self - ) + key = Gitlab::Routing.url_helpers.realtime_changes_project_issue_path(project, self) Gitlab::EtagCaching::Store.new.touch(key) end end diff --git a/app/models/label.rb b/app/models/label.rb index ed6a8411da9..674bb3f2720 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -15,9 +15,9 @@ class Label < ActiveRecord::Base default_value_for :color, DEFAULT_COLOR - has_many :lists, dependent: :destroy + has_many :lists, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :priorities, class_name: 'LabelPriority' - has_many :label_links, dependent: :destroy + has_many :label_links, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :issues, through: :label_links, source: :target, source_type: 'Issue' has_many :merge_requests, through: :label_links, source: :target, source_type: 'MergeRequest' diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb index 2d5909ab25e..c36be956ff0 100644 --- a/app/models/legacy_diff_note.rb +++ b/app/models/legacy_diff_note.rb @@ -7,7 +7,7 @@ class LegacyDiffNote < Note include NoteOnDiff - serialize :st_diff # rubocop:disable Cop/ActiverecordSerialize + serialize :st_diff # rubocop:disable Cop/ActiveRecordSerialize validates :line_code, presence: true, line_code: true diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb index 7712d5783e0..b7cf96abe83 100644 --- a/app/models/lfs_object.rb +++ b/app/models/lfs_object.rb @@ -1,5 +1,5 @@ class LfsObject < ActiveRecord::Base - has_many :lfs_objects_projects, dependent: :destroy + has_many :lfs_objects_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :projects, through: :lfs_objects_projects validates :oid, presence: true, uniqueness: true diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index c099d731082..2752181ed79 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -12,19 +12,21 @@ class MergeRequest < ActiveRecord::Base belongs_to :source_project, class_name: "Project" belongs_to :merge_user, class_name: "User" - has_many :merge_request_diffs, dependent: :destroy + has_many :merge_request_diffs has_one :merge_request_diff, -> { order('merge_request_diffs.id DESC') } belongs_to :head_pipeline, foreign_key: "head_pipeline_id", class_name: "Ci::Pipeline" - has_many :events, as: :target, dependent: :destroy + has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent - has_many :merge_requests_closing_issues, class_name: 'MergeRequestsClosingIssues', dependent: :delete_all + has_many :merge_requests_closing_issues, + class_name: 'MergeRequestsClosingIssues', + dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent belongs_to :assignee, class_name: "User" - serialize :merge_params, Hash # rubocop:disable Cop/ActiverecordSerialize + serialize :merge_params, Hash # rubocop:disable Cop/ActiveRecordSerialize after_create :ensure_merge_request_diff, unless: :importing? after_update :reload_diff_if_branch_changed @@ -197,11 +199,19 @@ class MergeRequest < ActiveRecord::Base } end - # This method is needed for compatibility with issues to not mess view and other code + # These method are needed for compatibility with issues to not mess view and other code def assignees Array(assignee) end + def assignee_ids + Array(assignee_id) + end + + def assignee_ids=(ids) + write_attribute(:assignee_id, ids.last) + end + def assignee_or_author?(user) author_id == user.id || assignee_id == user.id end diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index f1ee4d3f7a9..0cbf58a2325 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -12,8 +12,8 @@ class MergeRequestDiff < ActiveRecord::Base belongs_to :merge_request has_many :merge_request_diff_files, -> { order(:merge_request_diff_id, :relative_order) } - serialize :st_commits # rubocop:disable Cop/ActiverecordSerialize - serialize :st_diffs # rubocop:disable Cop/ActiverecordSerialize + serialize :st_commits # rubocop:disable Cop/ActiveRecordSerialize + serialize :st_diffs # rubocop:disable Cop/ActiveRecordSerialize state_machine :state, initial: :empty do state :collected diff --git a/app/models/milestone.rb b/app/models/milestone.rb index d2e2749f70d..c0ccbf8e27e 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -21,7 +21,7 @@ class Milestone < ActiveRecord::Base has_many :issues has_many :labels, -> { distinct.reorder('labels.title') }, through: :issues has_many :merge_requests - has_many :events, as: :target, dependent: :destroy + has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent scope :active, -> { with_state(:active) } scope :closed, -> { with_state(:closed) } diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 743e0513e02..15713fc5f6d 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -15,13 +15,13 @@ class Namespace < ActiveRecord::Base cache_markdown_field :description, pipeline: :description - has_many :projects, dependent: :destroy + has_many :projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :project_statistics belongs_to :owner, class_name: "User" belongs_to :parent, class_name: "Namespace" has_many :children, class_name: "Namespace", foreign_key: :parent_id - has_one :chat_team, dependent: :destroy + has_one :chat_team, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent validates :owner, presence: true, unless: ->(n) { n.type == "Group" } validates :name, @@ -219,6 +219,12 @@ class Namespace < ActiveRecord::Base parent.present? end + def soft_delete_without_removing_associations + # We can't use paranoia's `#destroy` since this will hard-delete projects. + # Project uses `pending_delete` instead of the acts_as_paranoia gem. + self.deleted_at = Time.now + end + private def repository_storage_paths diff --git a/app/models/note.rb b/app/models/note.rb index ca6999427c0..3d39047d32f 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -46,8 +46,8 @@ class Note < ActiveRecord::Base belongs_to :updated_by, class_name: "User" belongs_to :last_edited_by, class_name: 'User' - has_many :todos, dependent: :destroy - has_many :events, as: :target, dependent: :destroy + has_many :todos, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_one :system_note_metadata delegate :gfm_reference, :local_reference, to: :noteable @@ -330,8 +330,7 @@ class Note < ActiveRecord::Base def expire_etag_cache return unless for_issue? - key = Gitlab::Routing.url_helpers.namespace_project_noteable_notes_path( - noteable.project.namespace, + key = Gitlab::Routing.url_helpers.project_noteable_notes_path( noteable.project, target_type: noteable_type.underscore, target_id: noteable.id diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb index 6e13f9b2089..654be927ed8 100644 --- a/app/models/personal_access_token.rb +++ b/app/models/personal_access_token.rb @@ -3,7 +3,7 @@ class PersonalAccessToken < ActiveRecord::Base include TokenAuthenticatable add_authentication_token_field :token - serialize :scopes, Array # rubocop:disable Cop/ActiverecordSerialize + serialize :scopes, Array # rubocop:disable Cop/ActiveRecordSerialize belongs_to :user diff --git a/app/models/project.rb b/app/models/project.rb index a75c5209955..74c15d2508b 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -59,6 +59,7 @@ class Project < ActiveRecord::Base update_column(:last_repository_updated_at, self.created_at) end + before_destroy :remove_private_deploy_keys after_destroy :remove_pages # update visibility_level of forks @@ -80,96 +81,108 @@ class Project < ActiveRecord::Base belongs_to :namespace has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event' - has_many :boards, before_add: :validate_board_limit, dependent: :destroy + has_many :boards, before_add: :validate_board_limit # Project services - has_one :campfire_service, dependent: :destroy - has_one :drone_ci_service, dependent: :destroy - has_one :emails_on_push_service, dependent: :destroy - has_one :pipelines_email_service, dependent: :destroy - has_one :irker_service, dependent: :destroy - has_one :pivotaltracker_service, dependent: :destroy - has_one :hipchat_service, dependent: :destroy - has_one :flowdock_service, dependent: :destroy - has_one :assembla_service, dependent: :destroy - has_one :asana_service, dependent: :destroy - has_one :gemnasium_service, dependent: :destroy - has_one :mattermost_slash_commands_service, dependent: :destroy - has_one :mattermost_service, dependent: :destroy - has_one :slack_slash_commands_service, dependent: :destroy - has_one :slack_service, dependent: :destroy - has_one :buildkite_service, dependent: :destroy - has_one :bamboo_service, dependent: :destroy - has_one :teamcity_service, dependent: :destroy - has_one :pushover_service, dependent: :destroy - has_one :jira_service, dependent: :destroy - has_one :redmine_service, dependent: :destroy - has_one :custom_issue_tracker_service, dependent: :destroy - has_one :bugzilla_service, dependent: :destroy - has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project - has_one :external_wiki_service, dependent: :destroy - has_one :kubernetes_service, dependent: :destroy, inverse_of: :project - has_one :prometheus_service, dependent: :destroy, inverse_of: :project - has_one :mock_ci_service, dependent: :destroy - has_one :mock_deployment_service, dependent: :destroy - has_one :mock_monitoring_service, dependent: :destroy - has_one :microsoft_teams_service, dependent: :destroy - - has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" + has_one :campfire_service + has_one :drone_ci_service + has_one :emails_on_push_service + has_one :pipelines_email_service + has_one :irker_service + has_one :pivotaltracker_service + has_one :hipchat_service + has_one :flowdock_service + has_one :assembla_service + has_one :asana_service + has_one :gemnasium_service + has_one :mattermost_slash_commands_service + has_one :mattermost_service + has_one :slack_slash_commands_service + has_one :slack_service + has_one :buildkite_service + has_one :bamboo_service + has_one :teamcity_service + has_one :pushover_service + has_one :jira_service + has_one :redmine_service + has_one :custom_issue_tracker_service + has_one :bugzilla_service + has_one :gitlab_issue_tracker_service, inverse_of: :project + has_one :external_wiki_service + has_one :kubernetes_service, inverse_of: :project + has_one :prometheus_service, inverse_of: :project + has_one :mock_ci_service + has_one :mock_deployment_service + has_one :mock_monitoring_service + has_one :microsoft_teams_service + + has_one :forked_project_link, foreign_key: "forked_to_project_id" has_one :forked_from_project, through: :forked_project_link has_many :forked_project_links, foreign_key: "forked_from_project_id" has_many :forks, through: :forked_project_links, source: :forked_to_project # Merge Requests for target project should be removed with it - has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id' - has_many :issues, dependent: :destroy - has_many :labels, dependent: :destroy, class_name: 'ProjectLabel' - has_many :services, dependent: :destroy - has_many :events, dependent: :destroy - has_many :milestones, dependent: :destroy - has_many :notes, dependent: :destroy - has_many :snippets, dependent: :destroy, class_name: 'ProjectSnippet' - has_many :hooks, dependent: :destroy, class_name: 'ProjectHook' - has_many :protected_branches, dependent: :destroy - has_many :protected_tags, dependent: :destroy + has_many :merge_requests, foreign_key: 'target_project_id' + has_many :issues + has_many :labels, class_name: 'ProjectLabel' + has_many :services + has_many :events + has_many :milestones + has_many :notes + has_many :snippets, class_name: 'ProjectSnippet' + has_many :hooks, class_name: 'ProjectHook' + has_many :protected_branches + has_many :protected_tags has_many :project_authorizations has_many :authorized_users, through: :project_authorizations, source: :user, class_name: 'User' - has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source + has_many :project_members, -> { where(requested_at: nil) }, + as: :source, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent + alias_method :members, :project_members has_many :users, through: :project_members - has_many :requesters, -> { where.not(requested_at: nil) }, dependent: :destroy, as: :source, class_name: 'ProjectMember' + has_many :requesters, -> { where.not(requested_at: nil) }, + as: :source, class_name: 'ProjectMember', dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent - has_many :deploy_keys_projects, dependent: :destroy + has_many :deploy_keys_projects has_many :deploy_keys, through: :deploy_keys_projects - has_many :users_star_projects, dependent: :destroy + has_many :users_star_projects has_many :starrers, through: :users_star_projects, source: :user - has_many :releases, dependent: :destroy - has_many :lfs_objects_projects, dependent: :destroy + has_many :releases + has_many :lfs_objects_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :lfs_objects, through: :lfs_objects_projects - has_many :project_group_links, dependent: :destroy + has_many :project_group_links has_many :invited_groups, through: :project_group_links, source: :group - has_many :pages_domains, dependent: :destroy - has_many :todos, dependent: :destroy - has_many :notification_settings, dependent: :destroy, as: :source - - has_one :import_data, dependent: :delete, class_name: 'ProjectImportData' - has_one :project_feature, dependent: :destroy - has_one :statistics, class_name: 'ProjectStatistics', dependent: :delete - has_many :container_repositories, dependent: :destroy - - has_many :commit_statuses, dependent: :destroy - has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline' - has_many :builds, class_name: 'Ci::Build' # the builds are created from the commit_statuses - has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' + has_many :pages_domains + has_many :todos + has_many :notification_settings, as: :source, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent + + has_one :import_data, class_name: 'ProjectImportData' + has_one :project_feature + has_one :statistics, class_name: 'ProjectStatistics' + + # Container repositories need to remove data from the container registry, + # which is not managed by the DB. Hence we're still using dependent: :destroy + # here. + has_many :container_repositories, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + + has_many :commit_statuses + has_many :pipelines, class_name: 'Ci::Pipeline' + + # Ci::Build objects store data on the file system such as artifact files and + # build traces. Currently there's no efficient way of removing this data in + # bulk that doesn't involve loading the rows into memory. As a result we're + # still using `dependent: :destroy` here. + has_many :builds, class_name: 'Ci::Build', dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :runner_projects, class_name: 'Ci::RunnerProject' has_many :runners, through: :runner_projects, source: :runner, class_name: 'Ci::Runner' has_many :variables, class_name: 'Ci::Variable' - has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger' - has_many :environments, dependent: :destroy - has_many :deployments, dependent: :destroy - has_many :pipeline_schedules, dependent: :destroy, class_name: 'Ci::PipelineSchedule' + has_many :triggers, class_name: 'Ci::Trigger' + has_many :environments + has_many :deployments + has_many :pipeline_schedules, class_name: 'Ci::PipelineSchedule' has_many :active_runners, -> { active }, through: :runner_projects, source: :runner, class_name: 'Ci::Runner' @@ -186,6 +199,11 @@ class Project < ActiveRecord::Base # Validations validates :creator, presence: true, on: :create validates :description, length: { maximum: 2000 }, allow_blank: true + validates :ci_config_path, + format: { without: /\.{2}/, + message: 'cannot include directory traversal.' }, + length: { maximum: 255 }, + allow_blank: true validates :name, presence: true, length: { maximum: 255 }, @@ -219,7 +237,7 @@ class Project < ActiveRecord::Base before_save :ensure_runners_token mount_uploader :avatar, AvatarUploader - has_many :uploads, as: :model, dependent: :destroy + has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent # Scopes scope :pending_delete, -> { where(pending_delete: true) } @@ -521,6 +539,11 @@ class Project < ActiveRecord::Base import_data&.destroy end + def ci_config_path=(value) + # Strip all leading slashes so that //foo -> foo + super(value&.sub(%r{\A/+}, '')&.delete("\0")) + end + def import_url=(value) return super(value) unless Gitlab::UrlSanitizer.valid?(value) @@ -675,7 +698,7 @@ class Project < ActiveRecord::Base end def web_url - Gitlab::Routing.url_helpers.namespace_project_url(self.namespace, self) + Gitlab::Routing.url_helpers.project_url(self) end def new_issue_address(author) @@ -727,8 +750,8 @@ class Project < ActiveRecord::Base end end - def issue_reference_pattern - issues_tracker.reference_pattern + def external_issue_reference_pattern + external_issue_tracker.class.reference_pattern end def default_issues_tracker? @@ -815,7 +838,7 @@ class Project < ActiveRecord::Base end def ci_service - @ci_service ||= ci_services.reorder(nil).find_by(active: true) + @ci_service ||= ci_services.find_by(active: true) end def deployment_services @@ -823,7 +846,7 @@ class Project < ActiveRecord::Base end def deployment_service - @deployment_service ||= deployment_services.reorder(nil).find_by(active: true) + @deployment_service ||= deployment_services.find_by(active: true) end def monitoring_services @@ -831,7 +854,7 @@ class Project < ActiveRecord::Base end def monitoring_service - @monitoring_service ||= monitoring_services.reorder(nil).find_by(active: true) + @monitoring_service ||= monitoring_services.find_by(active: true) end def jira_tracker? @@ -851,7 +874,7 @@ class Project < ActiveRecord::Base def avatar_url(**args) # We use avatar_path instead of overriding avatar_url because of carrierwave. # See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11001/diffs#note_28659864 - avatar_path(args) || (Gitlab::Routing.url_helpers.namespace_project_avatar_url(namespace, self) if avatar_in_git) + avatar_path(args) || (Gitlab::Routing.url_helpers.project_avatar_url(self) if avatar_in_git) end # For compatibility with old code @@ -963,6 +986,7 @@ class Project < ActiveRecord::Base begin gitlab_shell.mv_repository(repository_storage_path, "#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki") send_move_instructions(old_path_with_namespace) + expires_full_path_cache @old_path_with_namespace = old_path_with_namespace @@ -1014,7 +1038,8 @@ class Project < ActiveRecord::Base namespace: namespace.name, visibility_level: visibility_level, path_with_namespace: path_with_namespace, - default_branch: default_branch + default_branch: default_branch, + ci_config_path: ci_config_path } # Backward compatibility @@ -1073,21 +1098,21 @@ class Project < ActiveRecord::Base merge_requests.where(source_project_id: self.id) end - def create_repository + def create_repository(force: false) # Forked import is handled asynchronously - unless forked? - if gitlab_shell.add_repository(repository_storage_path, path_with_namespace) - repository.after_create - true - else - errors.add(:base, 'Failed to create repository via gitlab-shell') - false - end + return if forked? && !force + + if gitlab_shell.add_repository(repository_storage_path, path_with_namespace) + repository.after_create + true + else + errors.add(:base, 'Failed to create repository via gitlab-shell') + false end end def ensure_repository - create_repository unless repository_exists? + create_repository(force: true) unless repository_exists? end def repository_exists? @@ -1228,7 +1253,13 @@ class Project < ActiveRecord::Base File.join(pages_path, 'public') end + def remove_private_deploy_keys + deploy_keys.where(public: false).delete_all + end + def remove_pages + ::Projects::UpdatePagesConfigurationService.new(self).execute + # 1. We rename pages to temporary directory # 2. We wait 5 minutes, due to NFS caching # 3. We asynchronously remove pages with force @@ -1314,7 +1345,8 @@ class Project < ActiveRecord::Base variables end - def secret_variables_for(ref) + def secret_variables_for(ref:, environment: nil) + # EE would use the environment if protected_for?(ref) variables else diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index e3cafd4d1c6..37730474324 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -10,7 +10,7 @@ class ProjectImportData < ActiveRecord::Base insecure_mode: true, algorithm: 'aes-256-cbc' - serialize :data, JSON # rubocop:disable Cop/ActiverecordSerialize + serialize :data, JSON # rubocop:disable Cop/ActiveRecordSerialize validates :project, presence: true diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb index ad4eb9536e1..5e31f393bbe 100644 --- a/app/models/project_services/gitlab_issue_tracker_service.rb +++ b/app/models/project_services/gitlab_issue_tracker_service.rb @@ -12,26 +12,26 @@ class GitlabIssueTrackerService < IssueTrackerService end def project_url - namespace_project_issues_url(project.namespace, project) + project_issues_url(project) end def new_issue_url - new_namespace_project_issue_url(namespace_id: project.namespace, project_id: project) + new_project_issue_url(project) end def issue_url(iid) - namespace_project_issue_url(namespace_id: project.namespace, project_id: project, id: iid) + project_issue_url(project, id: iid) end def project_path - namespace_project_issues_path(project.namespace, project) + project_issues_path(project) end def new_issue_path - new_namespace_project_issue_path(namespace_id: project.namespace, project_id: project) + new_project_issue_path(project) end def issue_path(iid) - namespace_project_issue_path(namespace_id: project.namespace, project_id: project, id: iid) + project_issue_path(project, id: iid) end end diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index ff138b9066d..1fa4cd4db30 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -5,7 +5,10 @@ class IssueTrackerService < Service # Pattern used to extract links from comments # Override this method on services that uses different patterns - def reference_pattern + # This pattern does not support cross-project references + # The other code assumes that this pattern is a superset of all + # overriden patterns. See ReferenceRegexes::EXTERNAL_PATTERN + def self.reference_pattern @reference_pattern ||= %r{(\b[A-Z][A-Z0-9_]+-|#{Issue.reference_prefix})(?<issue>\d+)} end @@ -18,7 +21,7 @@ class IssueTrackerService < Service end def project_path - project_url + read_attribute(:project_url) end def new_issue_path diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 2450fb43212..8af642b44aa 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -18,7 +18,7 @@ class JiraService < IssueTrackerService end # {PROJECT-KEY}-{NUMBER} Examples: JIRA-1, PROJECT-1 - def reference_pattern + def self.reference_pattern @reference_pattern ||= %r{(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)} end @@ -152,8 +152,8 @@ class JiraService < IssueTrackerService url: resource_url(user_path(author)) }, project: { - name: self.project.path_with_namespace, - url: resource_url(namespace_project_path(project.namespace, self.project)) + name: project.path_with_namespace, + url: resource_url(namespace_project_path(project.namespace, project)) # rubocop:disable Cop/ProjectPathHelper }, entity: { name: noteable_type.humanize.downcase, diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index 48e7802c557..62f7c057c5b 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -96,10 +96,13 @@ class KubernetesService < DeploymentService end def predefined_variables + config = YAML.dump(kubeconfig) + variables = [ { key: 'KUBE_URL', value: api_url, public: true }, { key: 'KUBE_TOKEN', value: token, public: false }, - { key: 'KUBE_NAMESPACE', value: actual_namespace, public: true } + { key: 'KUBE_NAMESPACE', value: actual_namespace, public: true }, + { key: 'KUBECONFIG', value: config, public: false, file: true } ] if ca_pem.present? @@ -135,6 +138,14 @@ class KubernetesService < DeploymentService private + def kubeconfig + to_kubeconfig( + url: api_url, + namespace: actual_namespace, + token: token, + ca_pem: ca_pem) + end + def namespace_placeholder default_namespace || TEMPLATE_PLACEHOLDER end diff --git a/app/models/project_services/slash_commands_service.rb b/app/models/project_services/slash_commands_service.rb index 4592cb747a0..eb4da68bb7e 100644 --- a/app/models/project_services/slash_commands_service.rb +++ b/app/models/project_services/slash_commands_service.rb @@ -5,7 +5,7 @@ class SlashCommandsService < Service prop_accessor :token - has_many :chat_names, foreign_key: :service_id, dependent: :destroy + has_many :chat_names, foreign_key: :service_id, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent def valid_token?(token) self.respond_to?(:token) && diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index f26ee57510c..beaadbbd1ab 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -31,7 +31,7 @@ class ProjectWiki end def web_url - Gitlab::Routing.url_helpers.namespace_project_wiki_url(@project.namespace, @project, :home) + Gitlab::Routing.url_helpers.project_wiki_url(@project, :home) end def url_to_repo diff --git a/app/models/repository.rb b/app/models/repository.rb index 8c24e722a8b..10b429c707e 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -931,7 +931,7 @@ class Repository def is_ancestor?(ancestor_id, descendant_id) return false if ancestor_id.nil? || descendant_id.nil? - + Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled| if is_enabled raw_repository.is_ancestor?(ancestor_id, descendant_id) @@ -1078,8 +1078,8 @@ class Repository blob_data_at(sha, '.gitlab/route-map.yml') end - def gitlab_ci_yml_for(sha) - blob_data_at(sha, '.gitlab-ci.yml') + def gitlab_ci_yml_for(sha, path = '.gitlab-ci.yml') + blob_data_at(sha, path) end private diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb index edde7bedbab..298569cb7a6 100644 --- a/app/models/sent_notification.rb +++ b/app/models/sent_notification.rb @@ -1,5 +1,5 @@ class SentNotification < ActiveRecord::Base - serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiverecordSerialize + serialize :position, Gitlab::Diff::Position # rubocop:disable Cop/ActiveRecordSerialize belongs_to :project belongs_to :noteable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations diff --git a/app/models/service.rb b/app/models/service.rb index 6a0b0a5c522..6b64079215f 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -2,7 +2,7 @@ # and implement a set of methods class Service < ActiveRecord::Base include Sortable - serialize :properties, JSON # rubocop:disable Cop/ActiverecordSerialize + serialize :properties, JSON # rubocop:disable Cop/ActiveRecordSerialize default_value_for :active, false default_value_for :push_events, true @@ -51,6 +51,14 @@ class Service < ActiveRecord::Base active end + def show_active_box? + true + end + + def editable? + true + end + def template? template end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 54014df43b0..09d5ff46618 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -30,16 +30,14 @@ class Snippet < ActiveRecord::Base belongs_to :author, class_name: 'User' belongs_to :project - has_many :notes, as: :noteable, dependent: :destroy + has_many :notes, as: :noteable, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent delegate :name, :email, to: :author, prefix: true, allow_nil: true validates :author, presence: true validates :title, presence: true, length: { maximum: 255 } validates :file_name, - length: { maximum: 255 }, - format: { with: Gitlab::Regex.file_name_regex, - message: Gitlab::Regex.file_name_regex_message } + length: { maximum: 255 } validates :content, presence: true validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values } diff --git a/app/models/user.rb b/app/models/user.rb index 35e0d021c47..4411a06d429 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -11,6 +11,7 @@ class User < ActiveRecord::Base include CaseSensitivity include TokenAuthenticatable include IgnorableColumn + include FeatureGate DEFAULT_NOTIFICATION_LEVEL = :participating @@ -40,7 +41,7 @@ class User < ActiveRecord::Base otp_secret_encryption_key: Gitlab::Application.secrets.otp_key_base devise :two_factor_backupable, otp_number_of_backup_codes: 10 - serialize :otp_backup_codes, JSON # rubocop:disable Cop/ActiverecordSerialize + serialize :otp_backup_codes, JSON # rubocop:disable Cop/ActiveRecordSerialize devise :lockable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :confirmable, :registerable @@ -66,24 +67,24 @@ class User < ActiveRecord::Base # # Namespace for personal projects - has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id, autosave: true + has_one :namespace, -> { where type: nil }, dependent: :destroy, foreign_key: :owner_id, autosave: true # rubocop:disable Cop/ActiveRecordDependent # Profile has_many :keys, -> do type = Key.arel_table[:type] where(type.not_eq('DeployKey').or(type.eq(nil))) - end, dependent: :destroy - has_many :deploy_keys, -> { where(type: 'DeployKey') }, dependent: :destroy + end, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :deploy_keys, -> { where(type: 'DeployKey') }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent - has_many :emails, dependent: :destroy - has_many :personal_access_tokens, dependent: :destroy - has_many :identities, dependent: :destroy, autosave: true - has_many :u2f_registrations, dependent: :destroy - has_many :chat_names, dependent: :destroy + has_many :emails, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :personal_access_tokens, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :identities, dependent: :destroy, autosave: true # rubocop:disable Cop/ActiveRecordDependent + has_many :u2f_registrations, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :chat_names, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent # Groups - has_many :members, dependent: :destroy - has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, source: 'GroupMember' + has_many :members, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, source: 'GroupMember' # rubocop:disable Cop/ActiveRecordDependent has_many :groups, through: :group_members has_many :owned_groups, -> { where members: { access_level: Gitlab::Access::OWNER } }, through: :group_members, source: :group has_many :masters_groups, -> { where members: { access_level: Gitlab::Access::MASTER } }, through: :group_members, source: :group @@ -91,35 +92,35 @@ class User < ActiveRecord::Base # Projects has_many :groups_projects, through: :groups, source: :projects has_many :personal_projects, through: :namespace, source: :projects - has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy + has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :projects, through: :project_members has_many :created_projects, foreign_key: :creator_id, class_name: 'Project' - has_many :users_star_projects, dependent: :destroy + has_many :users_star_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :starred_projects, through: :users_star_projects, source: :project has_many :project_authorizations has_many :authorized_projects, through: :project_authorizations, source: :project - has_many :snippets, dependent: :destroy, foreign_key: :author_id - has_many :notes, dependent: :destroy, foreign_key: :author_id - has_many :issues, dependent: :destroy, foreign_key: :author_id - has_many :merge_requests, dependent: :destroy, foreign_key: :author_id - has_many :events, dependent: :destroy, foreign_key: :author_id - has_many :subscriptions, dependent: :destroy + has_many :snippets, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent + has_many :notes, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent + has_many :issues, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent + has_many :merge_requests, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent + has_many :events, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent + has_many :subscriptions, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :recent_events, -> { order "id DESC" }, foreign_key: :author_id, class_name: "Event" - has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy - has_one :abuse_report, dependent: :destroy, foreign_key: :user_id - has_many :reported_abuse_reports, dependent: :destroy, foreign_key: :reporter_id, class_name: "AbuseReport" - has_many :spam_logs, dependent: :destroy - has_many :builds, dependent: :nullify, class_name: 'Ci::Build' - has_many :pipelines, dependent: :nullify, class_name: 'Ci::Pipeline' - has_many :todos, dependent: :destroy - has_many :notification_settings, dependent: :destroy - has_many :award_emoji, dependent: :destroy - has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :owner_id + has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_one :abuse_report, dependent: :destroy, foreign_key: :user_id # rubocop:disable Cop/ActiveRecordDependent + has_many :reported_abuse_reports, dependent: :destroy, foreign_key: :reporter_id, class_name: "AbuseReport" # rubocop:disable Cop/ActiveRecordDependent + has_many :spam_logs, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :builds, dependent: :nullify, class_name: 'Ci::Build' # rubocop:disable Cop/ActiveRecordDependent + has_many :pipelines, dependent: :nullify, class_name: 'Ci::Pipeline' # rubocop:disable Cop/ActiveRecordDependent + has_many :todos, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :notification_settings, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :award_emoji, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent + has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :owner_id # rubocop:disable Cop/ActiveRecordDependent has_many :issue_assignees has_many :assigned_issues, class_name: "Issue", through: :issue_assignees, source: :issue - has_many :assigned_merge_requests, dependent: :nullify, foreign_key: :assignee_id, class_name: "MergeRequest" + has_many :assigned_merge_requests, dependent: :nullify, foreign_key: :assignee_id, class_name: "MergeRequest" # rubocop:disable Cop/ActiveRecordDependent # # Validations @@ -210,7 +211,7 @@ class User < ActiveRecord::Base end mount_uploader :avatar, AvatarUploader - has_many :uploads, as: :model, dependent: :destroy + has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent # Scopes scope :admins, -> { where(admin: true) } |