diff options
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/ci/build.rb | 5 | ||||
-rw-r--r-- | app/models/ci/pipeline.rb | 3 | ||||
-rw-r--r-- | app/models/ci/runner.rb | 2 | ||||
-rw-r--r-- | app/models/ci/stage.rb | 4 | ||||
-rw-r--r-- | app/models/concerns/awardable.rb | 15 | ||||
-rw-r--r-- | app/models/concerns/editable.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/milestoneish.rb | 8 | ||||
-rw-r--r-- | app/models/concerns/protected_ref.rb | 2 | ||||
-rw-r--r-- | app/models/dashboard_milestone.rb | 2 | ||||
-rw-r--r-- | app/models/deployment.rb | 2 | ||||
-rw-r--r-- | app/models/group.rb | 4 | ||||
-rw-r--r-- | app/models/group_milestone.rb | 2 | ||||
-rw-r--r-- | app/models/issue.rb | 7 | ||||
-rw-r--r-- | app/models/merge_request.rb | 22 | ||||
-rw-r--r-- | app/models/milestone.rb | 6 | ||||
-rw-r--r-- | app/models/network/graph.rb | 4 | ||||
-rw-r--r-- | app/models/notification_recipient.rb | 53 | ||||
-rw-r--r-- | app/models/project.rb | 9 | ||||
-rw-r--r-- | app/models/project_services/chat_notification_service.rb | 6 | ||||
-rw-r--r-- | app/models/project_services/hipchat_service.rb | 6 | ||||
-rw-r--r-- | app/models/repository.rb | 70 | ||||
-rw-r--r-- | app/models/user.rb | 3 |
22 files changed, 153 insertions, 84 deletions
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 4692fb5644a..095192e9894 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -46,7 +46,10 @@ module Ci before_save :ensure_token before_destroy { unscoped_project } - after_create :execute_hooks + after_create do |build| + run_after_commit { BuildHooksWorker.perform_async(build.id) } + end + after_commit :update_project_statistics_after_save, on: [:create, :update] after_commit :update_project_statistics, on: :destroy diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index ea7331cb27f..2d40f8012a3 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -393,7 +393,8 @@ module Ci def predefined_variables [ { key: 'CI_PIPELINE_ID', value: id.to_s, public: true }, - { key: 'CI_CONFIG_PATH', value: ci_yaml_file_path, public: true } + { key: 'CI_CONFIG_PATH', value: ci_yaml_file_path, public: true }, + { key: 'CI_PIPELINE_SOURCE', value: source.to_s, public: true } ] end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index c6d23898560..906a76ec560 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -142,7 +142,7 @@ module Ci expire: RUNNER_QUEUE_EXPIRY_TIME, overwrite: false) end - def is_runner_queue_value_latest?(value) + def runner_queue_value_latest?(value) ensure_runner_queue_value == value if value.present? end diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index 4ee972fa68d..754c37518b3 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -17,6 +17,10 @@ module Ci validates :pipeline, presence: true, unless: :importing? validates :name, presence: true, unless: :importing? + after_initialize do |stage| + self.status = DEFAULT_STATUS if self.status.nil? + end + state_machine :status, initial: :created do event :enqueue do transition created: :pending diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb index f4f9b037957..9adc309a22b 100644 --- a/app/models/concerns/awardable.rb +++ b/app/models/concerns/awardable.rb @@ -11,6 +11,21 @@ module Awardable end module ClassMethods + def awarded(user, name) + sql = <<~EOL + EXISTS ( + SELECT TRUE + FROM award_emoji + WHERE user_id = :user_id AND + name = :name AND + awardable_type = :awardable_type AND + awardable_id = #{self.arel_table.name}.id + ) + EOL + + where(sql, user_id: user.id, name: name, awardable_type: self.name) + end + def order_upvotes_desc order_votes_desc(AwardEmoji::UPVOTE_NAME) end diff --git a/app/models/concerns/editable.rb b/app/models/concerns/editable.rb index 28623d257a6..c0a3099f676 100644 --- a/app/models/concerns/editable.rb +++ b/app/models/concerns/editable.rb @@ -1,7 +1,7 @@ module Editable extend ActiveSupport::Concern - def is_edited? + def edited? last_edited_at.present? && last_edited_at != created_at end diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb index f0998465822..710fc1ed647 100644 --- a/app/models/concerns/milestoneish.rb +++ b/app/models/concerns/milestoneish.rb @@ -70,19 +70,19 @@ module Milestoneish due_date && due_date.past? end - def is_group_milestone? + def group_milestone? false end - def is_project_milestone? + def project_milestone? false end - def is_legacy_group_milestone? + def legacy_group_milestone? false end - def is_dashboard_milestone? + def dashboard_milestone? false end diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb index ef95d6b0f98..454374121f3 100644 --- a/app/models/concerns/protected_ref.rb +++ b/app/models/concerns/protected_ref.rb @@ -23,7 +23,7 @@ module ProtectedRef # If we don't `protected_branch` or `protected_tag` would be empty and # `project` cannot be delegated to it, which in turn would cause validations # to fail. - has_many :"#{type}_access_levels", dependent: :destroy, inverse_of: self.model_name.singular # rubocop:disable Cop/ActiveRecordDependent + has_many :"#{type}_access_levels", inverse_of: self.model_name.singular # 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/dashboard_milestone.rb b/app/models/dashboard_milestone.rb index fac7c5e5c85..86eb4ec76fc 100644 --- a/app/models/dashboard_milestone.rb +++ b/app/models/dashboard_milestone.rb @@ -3,7 +3,7 @@ class DashboardMilestone < GlobalMilestone { authorized_only: true } end - def is_dashboard_milestone? + def dashboard_milestone? true end end diff --git a/app/models/deployment.rb b/app/models/deployment.rb index 056c49e7162..7bcded5b5e1 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -49,7 +49,7 @@ class Deployment < ActiveRecord::Base # created before then could have a `sha` referring to a commit that no # longer exists in the repository, so just ignore those. begin - project.repository.is_ancestor?(commit.id, sha) + project.repository.ancestor?(commit.id, sha) rescue Rugged::OdbError false end diff --git a/app/models/group.rb b/app/models/group.rb index 2816a68257c..cb3ee032f69 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -206,9 +206,9 @@ class Group < Namespace SystemHooksService.new end - def refresh_members_authorized_projects + def refresh_members_authorized_projects(blocking: true) UserProjectAccessChangedService.new(user_ids_for_project_authorizations) - .execute + .execute(blocking: blocking) end def user_ids_for_project_authorizations diff --git a/app/models/group_milestone.rb b/app/models/group_milestone.rb index 65249bd7bfc..98135ee3c8b 100644 --- a/app/models/group_milestone.rb +++ b/app/models/group_milestone.rb @@ -17,7 +17,7 @@ class GroupMilestone < GlobalMilestone { group_id: group.id } end - def is_legacy_group_milestone? + def legacy_group_milestone? true end end diff --git a/app/models/issue.rb b/app/models/issue.rb index 043da9967a1..b9aa937d2f9 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -50,7 +50,10 @@ class Issue < ActiveRecord::Base scope :preload_associations, -> { preload(:labels, project: :namespace) } + scope :public_only, -> { where(confidential: false) } + after_save :expire_etag_cache + after_commit :update_project_counter_caches, on: :destroy attr_spammable :title, spam_title: true attr_spammable :description, spam_description: true @@ -266,6 +269,10 @@ class Issue < ActiveRecord::Base end end + def update_project_counter_caches + Projects::OpenIssuesCountService.new(project).refresh_cache + end + private # Returns `true` if the given User can read the current Issue. diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 8361039f301..5be2f6d4e82 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -31,6 +31,7 @@ class MergeRequest < ActiveRecord::Base after_create :ensure_merge_request_diff, unless: :importing? after_update :reload_diff_if_branch_changed + after_commit :update_project_counter_caches, on: :destroy # When this attribute is true some MR validation is ignored # It allows us to close or modify broken merge requests @@ -240,6 +241,14 @@ class MergeRequest < ActiveRecord::Base end end + # Calls `MergeWorker` to proceed with the merge process and + # updates `merge_jid` with the MergeWorker#jid. + # This helps tracking enqueued and ongoing merge jobs. + def merge_async(user_id, params) + jid = MergeWorker.perform_async(id, user_id, params) + update_column(:merge_jid, jid) + end + def first_commit merge_request_diff ? merge_request_diff.first_commit : compare_commits.first end @@ -383,9 +392,7 @@ class MergeRequest < ActiveRecord::Base end def merge_ongoing? - return false unless merge_jid - - Gitlab::SidekiqStatus.num_running([merge_jid]) > 0 + !!merge_jid && !merged? end def closed_without_fork? @@ -682,9 +689,8 @@ class MergeRequest < ActiveRecord::Base if !include_description && closes_issues_references.present? message << "Closes #{closes_issues_references.to_sentence}" end - message << "#{description}" if include_description && description.present? - message << "See merge request #{to_reference}" + message << "See merge request #{to_reference(full: true)}" message.join("\n\n") end @@ -819,7 +825,7 @@ class MergeRequest < ActiveRecord::Base lock_mr yield ensure - unlock_mr if locked? + unlock_mr end end @@ -936,6 +942,10 @@ class MergeRequest < ActiveRecord::Base true end + def update_project_counter_caches + Projects::OpenMergeRequestsCountService.new(target_project).refresh_cache + end + private def write_ref diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 01e0d0155a3..a3070a12b7c 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -163,7 +163,7 @@ class Milestone < ActiveRecord::Base # Milestone.first.to_reference(same_namespace_project) # => "gitlab-ce%1" # def to_reference(from_project = nil, format: :iid, full: false) - return if is_group_milestone? && format != :name + return if group_milestone? && format != :name format_reference = milestone_format_reference(format) reference = "#{self.class.reference_prefix}#{format_reference}" @@ -207,11 +207,11 @@ class Milestone < ActiveRecord::Base group || project end - def is_group_milestone? + def group_milestone? group_id.present? end - def is_project_milestone? + def project_milestone? project_id.present? end diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb index 0e5acb22d50..3845e485413 100644 --- a/app/models/network/graph.rb +++ b/app/models/network/graph.rb @@ -152,14 +152,14 @@ module Network end def find_free_parent_space(range, space_base, space_step, space_default) - if is_overlap?(range, space_default) + if overlap?(range, space_default) find_free_space(range, space_step, space_base, space_default) else space_default end end - def is_overlap?(range, overlap_space) + def overlap?(range, overlap_space) range.each do |i| if i != range.first && i != range.last && diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb index dc862565a71..183e098d819 100644 --- a/app/models/notification_recipient.rb +++ b/app/models/notification_recipient.rb @@ -27,46 +27,45 @@ class NotificationRecipient @notification_setting ||= find_notification_setting end - def raw_notification_level - notification_setting&.level&.to_sym - end - def notification_level - # custom is treated the same as watch if it's enabled - otherwise it's - # set to :custom, meaning to send exactly when our type is :participating - # or :mention. - @notification_level ||= - case raw_notification_level - when :custom - if @custom_action && notification_setting&.event_enabled?(@custom_action) - :watch - else - :custom - end - else - raw_notification_level - end + @notification_level ||= notification_setting&.level&.to_sym end def notifiable? return false unless has_access? return false if own_activity? - return true if @type == :subscription - - return false if notification_level.nil? || notification_level == :disabled - - return %i[participating mention].include?(@type) if notification_level == :custom + # even users with :disabled notifications receive manual subscriptions + return !unsubscribed? if @type == :subscription - return false if %i[watch participating].include?(notification_level) && excluded_watcher_action? - - return false unless NotificationSetting.levels[notification_level] <= NotificationSetting.levels[@type] + return false unless suitable_notification_level? + # check this last because it's expensive + # nobody should receive notifications if they've specifically unsubscribed return false if unsubscribed? true end + def suitable_notification_level? + case notification_level + when :disabled, nil + false + when :custom + custom_enabled? || %i[participating mention].include?(@type) + when :watch, :participating + !excluded_watcher_action? + when :mention + @type == :mention + else + false + end + end + + def custom_enabled? + @custom_action && notification_setting&.event_enabled?(@custom_action) + end + def unsubscribed? return false unless @target return false unless @target.respond_to?(:subscriptions) @@ -98,7 +97,7 @@ class NotificationRecipient def excluded_watcher_action? return false unless @custom_action - return false if raw_notification_level == :custom + return false if notification_level == :custom NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(@custom_action) end diff --git a/app/models/project.rb b/app/models/project.rb index f86f75fbbdc..9d7bea4eb66 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -247,6 +247,7 @@ class Project < ActiveRecord::Base scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) } scope :starred_by, ->(user) { joins(:users_star_projects).where('users_star_projects.user_id': user.id) } scope :visible_to_user, ->(user) { where(id: user.authorized_projects.select(:id).reorder(nil)) } + scope :archived, -> { where(archived: true) } scope :non_archived, -> { where(archived: false) } scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct } scope :with_push, -> { joins(:events).where('events.action = ?', Event::PUSHED) } @@ -1013,7 +1014,7 @@ class Project < ActiveRecord::Base name: name, description: description, web_url: web_url, - avatar_url: avatar_url, + avatar_url: avatar_url(only_path: false), git_ssh_url: ssh_url_to_repo, git_http_url: http_url_to_repo, namespace: namespace.name, @@ -1163,7 +1164,11 @@ class Project < ActiveRecord::Base end def open_issues_count - issues.opened.count + Projects::OpenIssuesCountService.new(self).count + end + + def open_merge_requests_count + Projects::OpenMergeRequestsCountService.new(self).count end def visibility_level_allowed_as_fork?(level = self.visibility_level) diff --git a/app/models/project_services/chat_notification_service.rb b/app/models/project_services/chat_notification_service.rb index 7b15a5dd04d..818cfb01b14 100644 --- a/app/models/project_services/chat_notification_service.rb +++ b/app/models/project_services/chat_notification_service.rb @@ -101,9 +101,9 @@ class ChatNotificationService < Service when "push", "tag_push" ChatMessage::PushMessage.new(data) when "issue" - ChatMessage::IssueMessage.new(data) unless is_update?(data) + ChatMessage::IssueMessage.new(data) unless update?(data) when "merge_request" - ChatMessage::MergeMessage.new(data) unless is_update?(data) + ChatMessage::MergeMessage.new(data) unless update?(data) when "note" ChatMessage::NoteMessage.new(data) when "pipeline" @@ -136,7 +136,7 @@ class ChatNotificationService < Service project.web_url end - def is_update?(data) + def update?(data) data[:object_attributes][:action] == 'update' end diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index f422e0ea036..976d85246a8 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -85,9 +85,9 @@ class HipchatService < Service when "push", "tag_push" create_push_message(data) when "issue" - create_issue_message(data) unless is_update?(data) + create_issue_message(data) unless update?(data) when "merge_request" - create_merge_request_message(data) unless is_update?(data) + create_merge_request_message(data) unless update?(data) when "note" create_note_message(data) when "pipeline" @@ -282,7 +282,7 @@ class HipchatService < Service "<a href=\"#{project_url}\">#{project_name}</a>" end - def is_update?(data) + def update?(data) data[:object_attributes][:action] == 'update' end diff --git a/app/models/repository.rb b/app/models/repository.rb index b917eb4c302..5758aacd57f 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -72,6 +72,10 @@ class Repository @project = project end + def ==(other) + @disk_path == other.disk_path + end + def raw_repository return nil unless full_path @@ -87,6 +91,10 @@ class Repository ) end + def inspect + "#<#{self.class.name}:#{@disk_path}>" + end + # # Git repository can contains some hidden refs like: # /refs/notes/* @@ -218,12 +226,18 @@ class Repository end def branch_exists?(branch_name) - branch_names.include?(branch_name) + return false unless raw_repository + + @branch_exists_memo ||= Hash.new do |hash, key| + hash[key] = raw_repository.branch_exists?(key) + end + + @branch_exists_memo[branch_name] end def ref_exists?(ref) - rugged.references.exist?(ref) - rescue Rugged::ReferenceError + !!raw_repository&.ref_exists?(ref) + rescue ArgumentError false end @@ -278,6 +292,7 @@ class Repository def expire_branches_cache expire_method_caches(%i(branch_names branch_count)) @local_branches = nil + @branch_exists_memo = nil end def expire_statistics_caches @@ -949,7 +964,7 @@ class Repository if branch_commit same_head = branch_commit.id == root_ref_commit.id - !same_head && is_ancestor?(branch_commit.id, root_ref_commit.id) + !same_head && ancestor?(branch_commit.id, root_ref_commit.id) else nil end @@ -963,12 +978,12 @@ class Repository nil end - def is_ancestor?(ancestor_id, descendant_id) + def 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) + raw_repository.ancestor?(ancestor_id, descendant_id) else rugged_is_ancestor?(ancestor_id, descendant_id) end @@ -997,25 +1012,22 @@ class Repository end def with_repo_branch_commit(start_repository, start_branch_name) - return yield(nil) if start_repository.empty_repo? + return yield nil if start_repository.empty_repo? - branch_name_or_sha = - if start_repository == self - start_branch_name - else - tmp_ref = fetch_ref( - start_repository.path_to_repo, - "#{Gitlab::Git::BRANCH_REF_PREFIX}#{start_branch_name}", - "refs/tmp/#{SecureRandom.hex}/head" - ) + if start_repository == self + yield commit(start_branch_name) + else + sha = start_repository.commit(start_branch_name).sha - start_repository.commit(start_branch_name).sha + if branch_commit = commit(sha) + yield branch_commit + else + with_repo_tmp_commit( + start_repository, start_branch_name, sha) do |tmp_commit| + yield tmp_commit + end end - - yield(commit(branch_name_or_sha)) - - ensure - rugged.references.delete(tmp_ref) if tmp_ref + end end def add_remote(name, url) @@ -1197,7 +1209,7 @@ class Repository end def initialize_raw_repository - Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git') + Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', Gitlab::GlRepository.gl_repository(project, false)) end def circuit_breaker @@ -1224,4 +1236,16 @@ class Repository .commits_by_message(query, revision: ref, path: path, limit: limit, offset: offset) .map { |c| commit(c) } end + + def with_repo_tmp_commit(start_repository, start_branch_name, sha) + tmp_ref = fetch_ref( + start_repository.path_to_repo, + "#{Gitlab::Git::BRANCH_REF_PREFIX}#{start_branch_name}", + "refs/tmp/#{SecureRandom.hex}/head" + ) + + yield commit(sha) + ensure + rugged.references.delete(tmp_ref) if tmp_ref + end end diff --git a/app/models/user.rb b/app/models/user.rb index fbd08bc4d0a..70787de4b40 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -5,6 +5,7 @@ class User < ActiveRecord::Base include Gitlab::ConfigHelper include Gitlab::CurrentSettings + include Gitlab::SQL::Pattern include Avatarable include Referable include Sortable @@ -303,7 +304,7 @@ class User < ActiveRecord::Base # Returns an ActiveRecord::Relation. def search(query) table = arel_table - pattern = "%#{query}%" + pattern = User.to_pattern(query) order = <<~SQL CASE |