diff options
Diffstat (limited to 'app/models')
37 files changed, 346 insertions, 82 deletions
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 3cbbf8b5dfa..862933bf127 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -330,7 +330,8 @@ class ApplicationSetting < ActiveRecord::Base usage_ping_enabled: Settings.gitlab['usage_ping_enabled'], gitaly_timeout_fast: 10, gitaly_timeout_medium: 30, - gitaly_timeout_default: 55 + gitaly_timeout_default: 55, + allow_local_requests_from_hooks_and_services: false } end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index c1da2081465..1e066b69c6e 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -140,7 +140,11 @@ module Ci next if build.retries_max.zero? if build.retries_count < build.retries_max - Ci::Build.retry(build, build.user) + begin + Ci::Build.retry(build, build.user) + rescue Gitlab::Access::AccessDeniedError => ex + Rails.logger.error "Unable to auto-retry job #{build.id}: #{ex}" + end end end @@ -328,8 +332,7 @@ module Ci end def erase_old_trace! - write_attribute(:trace, nil) - save + update_column(:trace, nil) end def needs_touch? diff --git a/app/models/ci/group_variable.rb b/app/models/ci/group_variable.rb index 1dd0e050ba9..62d768cc6cf 100644 --- a/app/models/ci/group_variable.rb +++ b/app/models/ci/group_variable.rb @@ -6,6 +6,8 @@ module Ci belongs_to :group + alias_attribute :secret_value, :value + validates :key, uniqueness: { scope: :group_id, message: "(%{value}) has already been taken" diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index f2edcdd61fd..434b9b64c65 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -6,6 +6,7 @@ module Ci include AfterCommitQueue include Presentable include Gitlab::OptimisticLocking + include Gitlab::Utils::StrongMemoize belongs_to :project, inverse_of: :pipelines belongs_to :user @@ -14,7 +15,7 @@ module Ci has_many :stages has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline - has_many :builds, foreign_key: :commit_id + has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent has_many :variables, class_name: 'Ci::PipelineVariable' @@ -361,21 +362,23 @@ module Ci def stage_seeds return [] unless config_processor - @stage_seeds ||= config_processor.stage_seeds(self) + strong_memoize(:stage_seeds) do + seeds = config_processor.stages_attributes.map do |attributes| + Gitlab::Ci::Pipeline::Seed::Stage.new(self, attributes) + end + + seeds.select(&:included?) + end end def seeds_size - @seeds_size ||= stage_seeds.sum(&:size) + stage_seeds.sum(&:size) end def has_kubernetes_active? project.deployment_platform&.active? end - def has_stage_seeds? - stage_seeds.any? - end - def has_warnings? builds.latest.failed_but_allowed.any? end @@ -388,6 +391,9 @@ module Ci end end + ## + # TODO, setting yaml_errors should be moved to the pipeline creation chain. + # def config_processor return unless ci_yaml_file return @config_processor if defined?(@config_processor) @@ -472,6 +478,14 @@ module Ci end end + def protected_ref? + strong_memoize(:protected_ref) { project.protected_for?(ref) } + end + + def legacy_trigger + strong_memoize(:legacy_trigger) { trigger_requests.first } + end + def predefined_variables Gitlab::Ci::Variables::Collection.new .append(key: 'CI_PIPELINE_ID', value: id.to_s) diff --git a/app/models/ci/pipeline_schedule_variable.rb b/app/models/ci/pipeline_schedule_variable.rb index af989fb14b4..03df4e3e638 100644 --- a/app/models/ci/pipeline_schedule_variable.rb +++ b/app/models/ci/pipeline_schedule_variable.rb @@ -5,6 +5,8 @@ module Ci belongs_to :pipeline_schedule + alias_attribute :secret_value, :value + validates :key, uniqueness: { scope: :pipeline_schedule_id } end end diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index 7c71291de84..452cb910bca 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -6,6 +6,8 @@ module Ci belongs_to :project + alias_attribute :secret_value, :value + validates :key, uniqueness: { scope: [:project_id, :environment_scope], message: "(%{value}) has already been taken" diff --git a/app/models/commit.rb b/app/models/commit.rb index cceae5efb72..b64462fb768 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -175,7 +175,7 @@ class Commit if safe_message.blank? no_commit_message else - safe_message.split("\n", 2).first + safe_message.split(/[\r\n]/, 2).first end end diff --git a/app/models/concerns/atomic_internal_id.rb b/app/models/concerns/atomic_internal_id.rb new file mode 100644 index 00000000000..4b66725a3e6 --- /dev/null +++ b/app/models/concerns/atomic_internal_id.rb @@ -0,0 +1,46 @@ +# Include atomic internal id generation scheme for a model +# +# This allows us to atomically generate internal ids that are +# unique within a given scope. +# +# For example, let's generate internal ids for Issue per Project: +# ``` +# class Issue < ActiveRecord::Base +# has_internal_id :iid, scope: :project, init: ->(s) { s.project.issues.maximum(:iid) } +# end +# ``` +# +# This generates unique internal ids per project for newly created issues. +# The generated internal id is saved in the `iid` attribute of `Issue`. +# +# This concern uses InternalId records to facilitate atomicity. +# In the absence of a record for the given scope, one will be created automatically. +# In this situation, the `init` block is called to calculate the initial value. +# In the example above, we calculate the maximum `iid` of all issues +# within the given project. +# +# Note that a model may have more than one internal id associated with possibly +# different scopes. +module AtomicInternalId + extend ActiveSupport::Concern + + module ClassMethods + def has_internal_id(column, scope:, init:) # rubocop:disable Naming/PredicateName + before_validation(on: :create) do + if read_attribute(column).blank? + scope_attrs = { scope => association(scope).reader } + usage = self.class.table_name.to_sym + + new_iid = InternalId.generate_next(self, scope_attrs, usage, init) + write_attribute(column, new_iid) + end + end + + validates column, presence: true, numericality: true + end + end + + def to_param + iid.to_s + end +end diff --git a/app/models/concerns/avatarable.rb b/app/models/concerns/avatarable.rb index d35e37935fb..318df11727e 100644 --- a/app/models/concerns/avatarable.rb +++ b/app/models/concerns/avatarable.rb @@ -21,7 +21,7 @@ module Avatarable def avatar_type unless self.avatar.image? - self.errors.add :avatar, "only images allowed" + errors.add :avatar, "file format is not supported. Please try one of the following supported formats: #{AvatarUploader::IMAGE_EXT.join(', ')}" end end diff --git a/app/models/concerns/internal_id.rb b/app/models/concerns/nonatomic_internal_id.rb index 01079fb8bd6..9d0c9b8512f 100644 --- a/app/models/concerns/internal_id.rb +++ b/app/models/concerns/nonatomic_internal_id.rb @@ -1,4 +1,4 @@ -module InternalId +module NonatomicInternalId extend ActiveSupport::Concern included do diff --git a/app/models/deployment.rb b/app/models/deployment.rb index 66e61c06765..e18ea8bfea4 100644 --- a/app/models/deployment.rb +++ b/app/models/deployment.rb @@ -1,5 +1,5 @@ class Deployment < ActiveRecord::Base - include InternalId + include NonatomicInternalId belongs_to :project, required: true belongs_to :environment, required: true diff --git a/app/models/event.rb b/app/models/event.rb index 17a198d52c7..3805f6cf857 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -52,12 +52,12 @@ class Event < ActiveRecord::Base belongs_to :target, -> { # If the association for "target" defines an "author" association we want to # eager-load this so Banzai & friends don't end up performing N+1 queries to - # get the authors of notes, issues, etc. - if reflections['events'].active_record.reflect_on_association(:author) - includes(:author) - else - self + # get the authors of notes, issues, etc. (likewise for "noteable"). + incs = %i(author noteable).select do |a| + reflections['events'].active_record.reflect_on_association(a) end + + incs.reduce(self) { |obj, a| obj.includes(a) } }, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations has_one :push_event_payload diff --git a/app/models/group.rb b/app/models/group.rb index 8d183006c65..d99af79b5fe 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -189,12 +189,6 @@ class Group < Namespace owners.include?(user) && owners.size == 1 end - def avatar_type - unless self.avatar.image? - self.errors.add :avatar, "only images allowed" - end - end - def post_create_hook Gitlab::AppLogger.info("Group \"#{name}\" was created") @@ -230,13 +224,13 @@ class Group < Namespace end GroupMember - .active_without_invites + .active_without_invites_and_requests .where(source_id: source_ids) end def members_with_descendants GroupMember - .active_without_invites + .active_without_invites_and_requests .where(source_id: self_and_descendants.reorder(nil).select(:id)) end diff --git a/app/models/internal_id.rb b/app/models/internal_id.rb new file mode 100644 index 00000000000..cbec735c2dd --- /dev/null +++ b/app/models/internal_id.rb @@ -0,0 +1,125 @@ +# An InternalId is a strictly monotone sequence of integers +# generated for a given scope and usage. +# +# For example, issues use their project to scope internal ids: +# In that sense, scope is "project" and usage is "issues". +# Generated internal ids for an issue are unique per project. +# +# See InternalId#usage enum for available usages. +# +# 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 + belongs_to :project + + enum usage: { issues: 0 } + + validates :usage, presence: true + + REQUIRED_SCHEMA_VERSION = 20180305095250 + + # Increments #last_value and saves the record + # + # The operation locks the record and gathers a `ROW SHARE` lock (in PostgreSQL). + # As such, the increment is atomic and safe to be called concurrently. + def increment_and_save! + lock! + self.last_value = (last_value || 0) + 1 + save! + last_value + end + + class << self + def generate_next(subject, scope, usage, init) + # Shortcut if `internal_ids` table is not available (yet) + # This can be the case in other (unrelated) migration specs + return (init.call(subject) || 0) + 1 unless available? + + InternalIdGenerator.new(subject, scope, usage, init).generate + end + + def available? + @available_flag ||= ActiveRecord::Migrator.current_version >= REQUIRED_SCHEMA_VERSION # rubocop:disable Gitlab/PredicateMemoization + end + + # Flushes cached information about schema + def reset_column_information + @available_flag = nil + super + end + end + + class InternalIdGenerator + # Generate next internal id for a given scope and usage. + # + # For currently supported usages, see #usage enum. + # + # The method implements a locking scheme that has the following properties: + # 1) Generated sequence of internal ids is unique per (scope and usage) + # 2) The method is thread-safe and may be used in concurrent threads/processes. + # 3) The generated sequence is gapless. + # 4) In the absence of a record in the internal_ids table, one will be created + # and last_value will be calculated on the fly. + # + # subject: The instance we're generating an internal id for. Gets passed to init if called. + # scope: Attributes that define the scope for id generation. + # usage: Symbol to define the usage of the internal id, see InternalId.usages + # init: Block that gets called to initialize InternalId record if not present + # Make sure to not throw exceptions in the absence of records (if this is expected). + attr_reader :subject, :scope, :init, :scope_attrs, :usage + + def initialize(subject, scope, usage, init) + @subject = subject + @scope = scope + @init = init + @usage = usage + + raise ArgumentError, 'Scope is not well-defined, need at least one column for scope (given: 0)' if scope.empty? + + unless InternalId.usages.has_key?(usage.to_s) + raise ArgumentError, "Usage '#{usage}' is unknown. Supported values are #{InternalId.usages.keys} from InternalId.usages" + end + end + + # Generates next internal id and returns it + def generate + subject.transaction do + # Create a record in internal_ids if one does not yet exist + # and increment its last value + # + # Note this will acquire a ROW SHARE lock on the InternalId record + (lookup || create_record).increment_and_save! + end + end + + private + + # Retrieve InternalId record for (project, usage) combination, if it exists + def lookup + InternalId.find_by(**scope, usage: usage_value) + end + + def usage_value + @usage_value ||= InternalId.usages[usage.to_s] + end + + # Create InternalId record for (scope, usage) combination, if it doesn't exist + # + # We blindly insert without synchronization. If another process + # was faster in doing this, we'll realize once we hit the unique key constraint + # violation. We can safely roll-back the nested transaction and perform + # a lookup instead to retrieve the record. + def create_record + subject.transaction(requires_new: true) do + InternalId.create!( + **scope, + usage: usage_value, + last_value: init.call(subject) || 0 + ) + end + rescue ActiveRecord::RecordNotUnique + lookup + end + end +end diff --git a/app/models/issue.rb b/app/models/issue.rb index c81f7e52bb1..7bfc45c1f43 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -1,7 +1,7 @@ require 'carrierwave/orm/activerecord' class Issue < ActiveRecord::Base - include InternalId + include AtomicInternalId include Issuable include Noteable include Referable @@ -24,6 +24,8 @@ class Issue < ActiveRecord::Base belongs_to :project belongs_to :moved_to, class_name: 'Issue' + has_internal_id :iid, scope: :project, init: ->(s) { s&.project&.issues&.maximum(:iid) } + has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :merge_requests_closing_issues, diff --git a/app/models/member.rb b/app/models/member.rb index ec8156bbb01..e1a32148538 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -52,7 +52,7 @@ class Member < ActiveRecord::Base end # Like active, but without invites. For when a User is required. - scope :active_without_invites, -> do + scope :active_without_invites_and_requests, -> do left_join_users .where(users: { state: 'active' }) .non_request diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 149ef7ec429..7e6d89ec9c7 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1,5 +1,5 @@ class MergeRequest < ActiveRecord::Base - include InternalId + include NonatomicInternalId include Issuable include Noteable include Referable diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 77c19380e66..e7d397f40f5 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -8,7 +8,7 @@ class Milestone < ActiveRecord::Base Started = MilestoneStruct.new('Started', '#started', -3) include CacheMarkdownField - include InternalId + include NonatomicInternalId include Sortable include Referable include StripAttribute diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb index fd70e920c7e..b3ffad00a07 100644 --- a/app/models/notification_recipient.rb +++ b/app/models/notification_recipient.rb @@ -35,7 +35,8 @@ class NotificationRecipient # check this last because it's expensive # nobody should receive notifications if they've specifically unsubscribed - return false if unsubscribed? + # except if they were mentioned. + return false if @type != :mention && unsubscribed? true end @@ -47,7 +48,7 @@ class NotificationRecipient when :custom custom_enabled? || %i[participating mention].include?(@type) when :watch, :participating - !excluded_watcher_action? + !action_excluded? when :mention @type == :mention else @@ -95,13 +96,22 @@ class NotificationRecipient end end + def action_excluded? + excluded_watcher_action? || excluded_participating_action? + end + def excluded_watcher_action? - return false unless @custom_action - return false if notification_level == :custom + return false unless @custom_action && notification_level == :watch NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(@custom_action) end + def excluded_participating_action? + return false unless @custom_action && notification_level == :participating + + NotificationSetting::EXCLUDED_PARTICIPATING_EVENTS.include?(@custom_action) + end + private def read_ability diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb index 245f8dddcf9..f6d9b0215fc 100644 --- a/app/models/notification_setting.rb +++ b/app/models/notification_setting.rb @@ -33,6 +33,7 @@ class NotificationSetting < ActiveRecord::Base :close_issue, :reassign_issue, :new_merge_request, + :push_to_merge_request, :reopen_merge_request, :close_merge_request, :reassign_merge_request, @@ -41,10 +42,14 @@ class NotificationSetting < ActiveRecord::Base :success_pipeline ].freeze - EXCLUDED_WATCHER_EVENTS = [ + EXCLUDED_PARTICIPATING_EVENTS = [ :success_pipeline ].freeze + EXCLUDED_WATCHER_EVENTS = [ + :push_to_merge_request + ].push(*EXCLUDED_PARTICIPATING_EVENTS).freeze + def self.find_or_create_for(source) setting = find_or_initialize_by(source: source) diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb index 588bd50ed77..2e478a24778 100644 --- a/app/models/pages_domain.rb +++ b/app/models/pages_domain.rb @@ -6,8 +6,10 @@ class PagesDomain < ActiveRecord::Base validates :domain, hostname: { allow_numeric_hostname: true } validates :domain, uniqueness: { case_sensitive: false } - validates :certificate, certificate: true, allow_nil: true, allow_blank: true - validates :key, certificate_key: true, allow_nil: true, allow_blank: true + validates :certificate, presence: { message: 'must be present if HTTPS-only is enabled' }, if: ->(domain) { domain.project&.pages_https_only? } + validates :certificate, certificate: true, if: ->(domain) { domain.certificate.present? } + validates :key, presence: { message: 'must be present if HTTPS-only is enabled' }, if: ->(domain) { domain.project&.pages_https_only? } + validates :key, certificate_key: true, if: ->(domain) { domain.key.present? } validates :verification_code, presence: true, allow_blank: false validate :validate_pages_domain @@ -46,6 +48,10 @@ class PagesDomain < ActiveRecord::Base !Gitlab::CurrentSettings.pages_domain_verification_enabled? || enabled_until.present? end + def https? + certificate.present? + end + def to_param domain end diff --git a/app/models/project.rb b/app/models/project.rb index d6e663f14e4..6a420663644 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -38,6 +38,9 @@ class Project < ActiveRecord::Base attachments: 2 }.freeze + # Valids ports to import from + VALID_IMPORT_PORTS = [22, 80, 443].freeze + cache_markdown_field :description, pipeline: :description delegate :feature_available?, :builds_enabled?, :wiki_enabled?, @@ -188,6 +191,8 @@ class Project < ActiveRecord::Base has_many :todos has_many :notification_settings, as: :source, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent + has_many :internal_ids + has_one :import_data, class_name: 'ProjectImportData', inverse_of: :project, autosave: true has_one :project_feature, inverse_of: :project has_one :statistics, class_name: 'ProjectStatistics' @@ -262,6 +267,7 @@ class Project < ActiveRecord::Base validate :visibility_level_allowed_by_group validate :visibility_level_allowed_as_fork validate :check_wiki_path_conflict + validate :validate_pages_https_only, if: -> { changes.has_key?(:pages_https_only) } validates :repository_storage, presence: true, inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } } @@ -498,7 +504,7 @@ class Project < ActiveRecord::Base end def repository_storage_path - Gitlab.config.repositories.storages[repository_storage].try(:[], 'path') + Gitlab.config.repositories.storages[repository_storage]&.legacy_disk_path end def team @@ -732,6 +738,26 @@ class Project < ActiveRecord::Base end end + def pages_https_only + return false unless Gitlab.config.pages.external_https + + super + end + + def pages_https_only? + return false unless Gitlab.config.pages.external_https + + super + end + + def validate_pages_https_only + return unless pages_https_only? + + unless pages_domains.all?(&:https?) + errors.add(:pages_https_only, "cannot be enabled unless all domains have TLS certificates") + end + end + def to_param if persisted? && errors.include?(:path) path_was diff --git a/app/models/project_services/assembla_service.rb b/app/models/project_services/assembla_service.rb index ae6af732ed4..4234b8044e5 100644 --- a/app/models/project_services/assembla_service.rb +++ b/app/models/project_services/assembla_service.rb @@ -1,6 +1,4 @@ class AssemblaService < Service - include HTTParty - prop_accessor :token, :subdomain validates :token, presence: true, if: :activated? @@ -31,6 +29,6 @@ class AssemblaService < Service return unless supported_events.include?(data[:object_kind]) url = "https://atlas.assembla.com/spaces/#{subdomain}/github_tool?secret_key=#{token}" - AssemblaService.post(url, body: { payload: data }.to_json, headers: { 'Content-Type' => 'application/json' }) + Gitlab::HTTP.post(url, body: { payload: data }.to_json, headers: { 'Content-Type' => 'application/json' }) end end diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb index 42939ea0ec8..54e4b3278db 100644 --- a/app/models/project_services/bamboo_service.rb +++ b/app/models/project_services/bamboo_service.rb @@ -117,14 +117,14 @@ class BambooService < CiService url = build_url(path) if username.blank? && password.blank? - HTTParty.get(url, verify: false) + Gitlab::HTTP.get(url, verify: false) else url << '&os_authType=basic' - HTTParty.get(url, verify: false, - basic_auth: { - username: username, - password: password - }) + Gitlab::HTTP.get(url, verify: false, + basic_auth: { + username: username, + password: password + }) end end end diff --git a/app/models/project_services/buildkite_service.rb b/app/models/project_services/buildkite_service.rb index fc30f6e3365..d2aaff8817a 100644 --- a/app/models/project_services/buildkite_service.rb +++ b/app/models/project_services/buildkite_service.rb @@ -71,7 +71,7 @@ class BuildkiteService < CiService end def calculate_reactive_cache(sha, ref) - response = HTTParty.get(commit_status_path(sha), verify: false) + response = Gitlab::HTTP.get(commit_status_path(sha), verify: false) status = if response.code == 200 && response['status'] diff --git a/app/models/project_services/campfire_service.rb b/app/models/project_services/campfire_service.rb index 8d7a4fceb08..cb4af73807b 100644 --- a/app/models/project_services/campfire_service.rb +++ b/app/models/project_services/campfire_service.rb @@ -1,6 +1,4 @@ class CampfireService < Service - include HTTParty - prop_accessor :token, :subdomain, :room validates :token, presence: true, if: :activated? @@ -31,7 +29,6 @@ class CampfireService < Service def execute(data) return unless supported_events.include?(data[:object_kind]) - self.class.base_uri base_uri message = build_message(data) speak(self.room, message, auth) end @@ -69,14 +66,14 @@ class CampfireService < Service } } } - res = self.class.post(path, auth.merge(body)) + res = Gitlab::HTTP.post(path, base_uri: base_uri, **auth.merge(body)) res.code == 201 ? res : nil end # Returns a list of rooms, or []. # https://github.com/basecamp/campfire-api/blob/master/sections/rooms.md#get-rooms def rooms(auth) - res = self.class.get("/rooms.json", auth) + res = Gitlab::HTTP.get("/rooms.json", base_uri: base_uri, **auth) res.code == 200 ? res["rooms"] : [] end diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb index c93f1632652..71b10fc6bc1 100644 --- a/app/models/project_services/drone_ci_service.rb +++ b/app/models/project_services/drone_ci_service.rb @@ -49,7 +49,7 @@ class DroneCiService < CiService end def calculate_reactive_cache(sha, ref) - response = HTTParty.get(commit_status_path(sha, ref), verify: enable_ssl_verification) + response = Gitlab::HTTP.get(commit_status_path(sha, ref), verify: enable_ssl_verification) status = if response.code == 200 && response['status'] diff --git a/app/models/project_services/external_wiki_service.rb b/app/models/project_services/external_wiki_service.rb index 720ad61162e..1553f169827 100644 --- a/app/models/project_services/external_wiki_service.rb +++ b/app/models/project_services/external_wiki_service.rb @@ -1,6 +1,4 @@ class ExternalWikiService < Service - include HTTParty - prop_accessor :external_wiki_url validates :external_wiki_url, presence: true, url: true, if: :activated? @@ -24,7 +22,7 @@ class ExternalWikiService < Service end def execute(_data) - @response = HTTParty.get(properties['external_wiki_url'], verify: true) rescue nil + @response = Gitlab::HTTP.get(properties['external_wiki_url'], verify: true) rescue nil if @response != 200 nil end diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb index 5fb15c383ca..df6dcd90985 100644 --- a/app/models/project_services/issue_tracker_service.rb +++ b/app/models/project_services/issue_tracker_service.rb @@ -77,13 +77,13 @@ class IssueTrackerService < Service result = false begin - response = HTTParty.head(self.project_url, verify: true) + response = Gitlab::HTTP.head(self.project_url, verify: true) if response message = "#{self.type} received response #{response.code} when attempting to connect to #{self.project_url}" result = true end - rescue HTTParty::Error, Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED, OpenSSL::SSL::SSLError => error + rescue Gitlab::HTTP::Error, Timeout::Error, SocketError, Errno::ECONNRESET, Errno::ECONNREFUSED, OpenSSL::SSL::SSLError => error message = "#{self.type} had an error when trying to connect to #{self.project_url}: #{error.message}" end Rails.logger.info(message) diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index 601a6a077f5..ed4bbfb6cfc 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -14,9 +14,8 @@ class JiraService < IssueTrackerService alias_method :project_url, :url - # This is confusing, but JiraService does not really support these events. - # The values here are required to display correct options in the service - # configuration screen. + # When these are false GitLab does not create cross reference + # comments on JIRA except when an issue gets transitioned. def self.supported_events %w(commit merge_request) end @@ -318,4 +317,13 @@ class JiraService < IssueTrackerService url_changed? end + + def self.event_description(event) + case event + when "merge_request", "merge_request_events" + "JIRA comments will be created when an issue gets referenced in a merge request." + when "commit", "commit_events" + "JIRA comments will be created when an issue gets referenced in a commit." + end + end end diff --git a/app/models/project_services/mock_ci_service.rb b/app/models/project_services/mock_ci_service.rb index 72ddf9a4be3..2221459c90b 100644 --- a/app/models/project_services/mock_ci_service.rb +++ b/app/models/project_services/mock_ci_service.rb @@ -52,7 +52,7 @@ class MockCiService < CiService # # def commit_status(sha, ref) - response = HTTParty.get(commit_status_path(sha), verify: false) + response = Gitlab::HTTP.get(commit_status_path(sha), verify: false) read_commit_status(response) rescue Errno::ECONNREFUSED :error diff --git a/app/models/project_services/packagist_service.rb b/app/models/project_services/packagist_service.rb index f68a0c1a3c3..ba62a5b7ac0 100644 --- a/app/models/project_services/packagist_service.rb +++ b/app/models/project_services/packagist_service.rb @@ -1,6 +1,4 @@ class PackagistService < Service - include HTTParty - prop_accessor :username, :token, :server validates :username, presence: true, if: :activated? diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb index f9dfa2e91c3..3476e7d2283 100644 --- a/app/models/project_services/pivotaltracker_service.rb +++ b/app/models/project_services/pivotaltracker_service.rb @@ -1,6 +1,4 @@ class PivotaltrackerService < Service - include HTTParty - API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'.freeze prop_accessor :token, :restrict_to_branch @@ -52,7 +50,7 @@ class PivotaltrackerService < Service 'message' => commit[:message] } } - PivotaltrackerService.post( + Gitlab::HTTP.post( API_ENDPOINT, body: message.to_json, headers: { diff --git a/app/models/project_services/pushover_service.rb b/app/models/project_services/pushover_service.rb index e3a1ca2d45f..8777a44b72f 100644 --- a/app/models/project_services/pushover_service.rb +++ b/app/models/project_services/pushover_service.rb @@ -1,6 +1,5 @@ class PushoverService < Service - include HTTParty - base_uri 'https://api.pushover.net/1' + BASE_URI = 'https://api.pushover.net/1'.freeze prop_accessor :api_key, :user_key, :device, :priority, :sound validates :api_key, :user_key, :priority, presence: true, if: :activated? @@ -99,6 +98,6 @@ class PushoverService < Service pushover_data[:sound] = sound end - PushoverService.post('/messages.json', body: pushover_data) + Gitlab::HTTP.post('/messages.json', base_uri: BASE_URI, body: pushover_data) end end diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb index cbe137452bd..145313b8e71 100644 --- a/app/models/project_services/teamcity_service.rb +++ b/app/models/project_services/teamcity_service.rb @@ -83,7 +83,7 @@ class TeamcityService < CiService branch = Gitlab::Git.ref_name(data[:ref]) - HTTParty.post( + Gitlab::HTTP.post( build_url('httpAuth/app/rest/buildQueue'), body: "<build branchName=\"#{branch}\">"\ "<buildType id=\"#{build_type}\"/>"\ @@ -134,10 +134,10 @@ class TeamcityService < CiService end def get_path(path) - HTTParty.get(build_url(path), verify: false, - basic_auth: { - username: username, - password: password - }) + Gitlab::HTTP.get(build_url(path), verify: false, + basic_auth: { + username: username, + password: password + }) end end diff --git a/app/models/service.rb b/app/models/service.rb index 2556db68146..1dcb79157a2 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -304,6 +304,29 @@ class Service < ActiveRecord::Base end end + def self.event_description(event) + case event + when "push", "push_events" + "Event will be triggered by a push to the repository" + when "tag_push", "tag_push_events" + "Event will be triggered when a new tag is pushed to the repository" + when "note", "note_events" + "Event will be triggered when someone adds a comment" + when "issue", "issue_events" + "Event will be triggered when an issue is created/updated/closed" + when "confidential_issue", "confidential_issue_events" + "Event will be triggered when a confidential issue is created/updated/closed" + when "merge_request", "merge_request_events" + "Event will be triggered when a merge request is created/updated/merged" + when "pipeline", "pipeline_events" + "Event will be triggered when a pipeline status changes" + when "wiki_page", "wiki_page_events" + "Event will be triggered when a wiki page is created/updated" + when "commit", "commit_events" + "Event will be triggered when a commit is created/updated" + end + end + def valid_recipients? activated? && !importing? end diff --git a/app/models/user.rb b/app/models/user.rb index b8c55205ab8..fa54581d220 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -623,9 +623,7 @@ class User < ActiveRecord::Base end def owned_projects - @owned_projects ||= - Project.where('namespace_id IN (?) OR namespace_id = ?', - owned_groups.select(:id), namespace.id).joins(:namespace) + @owned_projects ||= Project.from("(#{owned_projects_union.to_sql}) AS projects") end # Returns projects which user can admin issues on (for example to move an issue to that project). @@ -1196,6 +1194,15 @@ class User < ActiveRecord::Base private + def owned_projects_union + Gitlab::SQL::Union.new([ + Project.where(namespace: namespace), + Project.joins(:project_authorizations) + .where("projects.namespace_id <> ?", namespace.id) + .where(project_authorizations: { user_id: id, access_level: Gitlab::Access::OWNER }) + ], remove_duplicates: false) + end + def ci_projects_union scope = { access_level: [Gitlab::Access::MASTER, Gitlab::Access::OWNER] } groups = groups_projects.where(members: scope) |