summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
Diffstat (limited to 'app/models')
-rw-r--r--app/models/appearance.rb2
-rw-r--r--app/models/application_setting.rb3
-rw-r--r--app/models/ci/artifact_blob.rb7
-rw-r--r--app/models/ci/build.rb159
-rw-r--r--app/models/ci/build_metadata.rb35
-rw-r--r--app/models/ci/group_variable.rb2
-rw-r--r--app/models/ci/job_artifact.rb10
-rw-r--r--app/models/ci/pipeline.rb28
-rw-r--r--app/models/ci/pipeline_schedule_variable.rb2
-rw-r--r--app/models/ci/runner.rb9
-rw-r--r--app/models/ci/variable.rb2
-rw-r--r--app/models/clusters/cluster.rb6
-rw-r--r--app/models/clusters/concerns/application_status.rb2
-rw-r--r--app/models/commit.rb5
-rw-r--r--app/models/commit_status.rb2
-rw-r--r--app/models/concerns/avatarable.rb3
-rw-r--r--app/models/concerns/chronic_duration_attribute.rb39
-rw-r--r--app/models/concerns/deployment_platform.rb22
-rw-r--r--app/models/concerns/issuable.rb2
-rw-r--r--app/models/concerns/milestoneish.rb4
-rw-r--r--app/models/deploy_key.rb4
-rw-r--r--app/models/event.rb10
-rw-r--r--app/models/group.rb8
-rw-r--r--app/models/issue.rb8
-rw-r--r--app/models/lfs_object.rb15
-rw-r--r--app/models/member.rb2
-rw-r--r--app/models/merge_request.rb27
-rw-r--r--app/models/milestone.rb2
-rw-r--r--app/models/note.rb7
-rw-r--r--app/models/notification_recipient.rb15
-rw-r--r--app/models/notification_setting.rb7
-rw-r--r--app/models/pages_domain.rb10
-rw-r--r--app/models/project.rb67
-rw-r--r--app/models/project_services/assembla_service.rb4
-rw-r--r--app/models/project_services/bamboo_service.rb12
-rw-r--r--app/models/project_services/buildkite_service.rb2
-rw-r--r--app/models/project_services/campfire_service.rb7
-rw-r--r--app/models/project_services/drone_ci_service.rb2
-rw-r--r--app/models/project_services/external_wiki_service.rb4
-rw-r--r--app/models/project_services/gemnasium_service.rb2
-rw-r--r--app/models/project_services/issue_tracker_service.rb4
-rw-r--r--app/models/project_services/mock_ci_service.rb2
-rw-r--r--app/models/project_services/packagist_service.rb2
-rw-r--r--app/models/project_services/pivotaltracker_service.rb4
-rw-r--r--app/models/project_services/pushover_service.rb5
-rw-r--r--app/models/project_services/teamcity_service.rb12
-rw-r--r--app/models/redirect_route.rb28
-rw-r--r--app/models/repository.rb8
-rw-r--r--app/models/route.rb26
-rw-r--r--app/models/service.rb1
-rw-r--r--app/models/todo.rb2
-rw-r--r--app/models/upload.rb19
-rw-r--r--app/models/user.rb24
53 files changed, 457 insertions, 239 deletions
diff --git a/app/models/appearance.rb b/app/models/appearance.rb
index dcd14c08f3c..2a6406d63c7 100644
--- a/app/models/appearance.rb
+++ b/app/models/appearance.rb
@@ -1,5 +1,7 @@
class Appearance < ActiveRecord::Base
include CacheMarkdownField
+ include AfterCommitQueue
+ include ObjectStorage::BackgroundMove
cache_markdown_field :description
cache_markdown_field :new_project_guidelines
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/artifact_blob.rb b/app/models/ci/artifact_blob.rb
index ec56cc53aea..760f01f225b 100644
--- a/app/models/ci/artifact_blob.rb
+++ b/app/models/ci/artifact_blob.rb
@@ -36,16 +36,15 @@ module Ci
def external_url(project, job)
return unless external_link?(job)
- full_path_parts = project.full_path_components
- top_level_group = full_path_parts.shift
+ url_project_path = project.full_path.partition('/').last
artifact_path = [
- '-', *full_path_parts, '-',
+ '-', url_project_path, '-',
'jobs', job.id,
'artifacts', path
].join('/')
- "#{pages_config.protocol}://#{top_level_group}.#{pages_config.host}/#{artifact_path}"
+ "#{project.pages_group_url}/#{artifact_path}"
end
def external_link?(job)
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 1e066b69c6e..18e96389199 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -3,8 +3,10 @@ module Ci
prepend ArtifactMigratable
include TokenAuthenticatable
include AfterCommitQueue
+ include ObjectStorage::BackgroundMove
include Presentable
include Importable
+ include Gitlab::Utils::StrongMemoize
MissingDependenciesError = Class.new(StandardError)
@@ -23,12 +25,18 @@ module Ci
has_one :job_artifacts_metadata, -> { where(file_type: Ci::JobArtifact.file_types[:metadata]) }, class_name: 'Ci::JobArtifact', inverse_of: :job, foreign_key: :job_id
has_one :job_artifacts_trace, -> { where(file_type: Ci::JobArtifact.file_types[:trace]) }, class_name: 'Ci::JobArtifact', inverse_of: :job, foreign_key: :job_id
- # The "environment" field for builds is a String, and is the unexpanded name
+ has_one :metadata, class_name: 'Ci::BuildMetadata'
+ delegate :timeout, to: :metadata, prefix: true, allow_nil: true
+
+ ##
+ # The "environment" field for builds is a String, and is the unexpanded name!
+ #
def persisted_environment
- @persisted_environment ||= Environment.find_by(
- name: expanded_environment_name,
- project: project
- )
+ return unless has_environment?
+
+ strong_memoize(:persisted_environment) do
+ Environment.find_by(name: expanded_environment_name, project: project)
+ end
end
serialize :options # rubocop:disable Cop/ActiveRecordSerialize
@@ -45,6 +53,7 @@ module Ci
where('(artifacts_file IS NOT NULL AND artifacts_file <> ?) OR EXISTS (?)',
'', Ci::JobArtifact.select(1).where('ci_builds.id = ci_job_artifacts.job_id').archive)
end
+ scope :with_artifacts_stored_locally, -> { with_artifacts_archive.where(artifacts_file_store: [nil, LegacyArtifactUploader::Store::LOCAL]) }
scope :with_artifacts_not_expired, ->() { with_artifacts_archive.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) }
scope :with_expired_artifacts, ->() { with_artifacts_archive.where('artifacts_expire_at < ?', Time.now) }
scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
@@ -151,6 +160,14 @@ module Ci
before_transition any => [:running] do |build|
build.validates_dependencies! unless Feature.enabled?('ci_disable_validates_dependencies')
end
+
+ before_transition pending: :running do |build|
+ build.ensure_metadata.update_timeout_state
+ end
+ end
+
+ def ensure_metadata
+ metadata || build_metadata(project: project)
end
def detailed_status(current_user)
@@ -198,7 +215,11 @@ module Ci
end
def expanded_environment_name
- ExpandVariables.expand(environment, simple_variables) if environment
+ return unless has_environment?
+
+ strong_memoize(:expanded_environment_name) do
+ ExpandVariables.expand(environment, simple_variables)
+ end
end
def has_environment?
@@ -229,10 +250,6 @@ module Ci
latest_builds.where('stage_idx < ?', stage_idx)
end
- def timeout
- project.build_timeout
- end
-
def triggered_by?(current_user)
user == current_user
end
@@ -248,31 +265,52 @@ module Ci
Gitlab::Utils.slugify(ref.to_s)
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)
- collection = Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ ##
+ # Variables in the environment name scope.
+ #
+ def scoped_variables(environment: expanded_environment_name)
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.concat(predefined_variables)
variables.concat(project.predefined_variables)
variables.concat(pipeline.predefined_variables)
variables.concat(runner.predefined_variables) if runner
- variables.concat(project.deployment_variables(environment: environment)) if has_environment?
+ variables.concat(project.deployment_variables(environment: environment)) if environment
variables.concat(yaml_variables)
variables.concat(user_variables)
- variables.concat(project.group.secret_variables_for(ref, project)) if project.group
- variables.concat(secret_variables(environment: environment))
+ variables.concat(secret_group_variables)
+ variables.concat(secret_project_variables(environment: environment))
variables.concat(trigger_request.user_variables) if trigger_request
variables.concat(pipeline.variables)
variables.concat(pipeline.pipeline_schedule.job_variables) if pipeline.pipeline_schedule
- variables.concat(persisted_environment_variables) if environment
end
+ end
+
+ ##
+ # Variables that do not depend on the environment name.
+ #
+ def simple_variables
+ strong_memoize(:simple_variables) do
+ scoped_variables(environment: nil).to_runner_variables
+ end
+ end
- collection.to_runner_variables
+ ##
+ # All variables, including persisted environment variables.
+ #
+ def variables
+ Gitlab::Ci::Variables::Collection.new
+ .concat(persisted_variables)
+ .concat(scoped_variables)
+ .concat(persisted_environment_variables)
+ .to_runner_variables
+ end
+
+ ##
+ # Regular Ruby hash of scoped variables, without duplicates that are
+ # possible to be present in an array of hashes returned from `variables`.
+ #
+ def scoped_variables_hash
+ scoped_variables.to_hash
end
def features
@@ -365,13 +403,19 @@ module Ci
project.running_or_pending_build_count(force: true)
end
+ def browsable_artifacts?
+ artifacts_metadata?
+ end
+
def artifacts_metadata_entry(path, **options)
- metadata = Gitlab::Ci::Build::Artifacts::Metadata.new(
- artifacts_metadata.path,
- path,
- **options)
+ artifacts_metadata.use_file do |metadata_path|
+ metadata = Gitlab::Ci::Build::Artifacts::Metadata.new(
+ metadata_path,
+ path,
+ **options)
- metadata.to_entry
+ metadata.to_entry
+ end
end
def erase_artifacts!
@@ -443,9 +487,14 @@ module Ci
end
end
- def secret_variables(environment: persisted_environment)
+ def secret_group_variables
+ return [] unless project.group
+
+ project.group.secret_variables_for(ref, project)
+ end
+
+ def secret_project_variables(environment: persisted_environment)
project.secret_variables_for(ref: ref, environment: environment)
- .map(&:to_runner_variable)
end
def steps
@@ -542,6 +591,21 @@ module Ci
CI_REGISTRY_USER = 'gitlab-ci-token'.freeze
+ def persisted_variables
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ return variables unless persisted?
+
+ variables
+ .append(key: 'CI_JOB_ID', value: id.to_s)
+ .append(key: 'CI_JOB_TOKEN', value: token, public: false)
+ .append(key: 'CI_BUILD_ID', value: id.to_s)
+ .append(key: 'CI_BUILD_TOKEN', value: token, public: false)
+ .append(key: 'CI_REGISTRY_USER', value: CI_REGISTRY_USER)
+ .append(key: 'CI_REGISTRY_PASSWORD', value: token, public: false)
+ .append(key: 'CI_REPOSITORY_URL', value: repo_url, public: false)
+ end
+ end
+
def predefined_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.append(key: 'CI', value: 'true')
@@ -550,16 +614,11 @@ module Ci
variables.append(key: 'CI_SERVER_NAME', value: 'GitLab')
variables.append(key: 'CI_SERVER_VERSION', value: Gitlab::VERSION)
variables.append(key: 'CI_SERVER_REVISION', value: Gitlab::REVISION)
- variables.append(key: 'CI_JOB_ID', value: id.to_s)
variables.append(key: 'CI_JOB_NAME', value: name)
variables.append(key: 'CI_JOB_STAGE', value: stage)
- variables.append(key: 'CI_JOB_TOKEN', value: token, public: false)
variables.append(key: 'CI_COMMIT_SHA', value: sha)
variables.append(key: 'CI_COMMIT_REF_NAME', value: ref)
variables.append(key: 'CI_COMMIT_REF_SLUG', value: ref_slug)
- variables.append(key: 'CI_REGISTRY_USER', value: CI_REGISTRY_USER)
- variables.append(key: 'CI_REGISTRY_PASSWORD', value: token, public: false)
- variables.append(key: 'CI_REPOSITORY_URL', value: repo_url, public: false)
variables.append(key: "CI_COMMIT_TAG", value: ref) if tag?
variables.append(key: "CI_PIPELINE_TRIGGERED", value: 'true') if trigger_request
variables.append(key: "CI_JOB_MANUAL", value: 'true') if action?
@@ -567,23 +626,8 @@ module Ci
end
end
- def persisted_environment_variables
- Gitlab::Ci::Variables::Collection.new.tap do |variables|
- return variables unless persisted_environment
-
- variables.concat(persisted_environment.predefined_variables)
-
- # Here we're passing unexpanded environment_url for runner to expand,
- # and we need to make sure that CI_ENVIRONMENT_NAME and
- # CI_ENVIRONMENT_SLUG so on are available for the URL be expanded.
- variables.append(key: 'CI_ENVIRONMENT_URL', value: environment_url) if environment_url
- end
- end
-
def legacy_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
- variables.append(key: 'CI_BUILD_ID', value: id.to_s)
- variables.append(key: 'CI_BUILD_TOKEN', value: token, public: false)
variables.append(key: 'CI_BUILD_REF', value: sha)
variables.append(key: 'CI_BUILD_BEFORE_SHA', value: before_sha)
variables.append(key: 'CI_BUILD_REF_NAME', value: ref)
@@ -596,6 +640,19 @@ module Ci
end
end
+ def persisted_environment_variables
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ return variables unless persisted? && persisted_environment.present?
+
+ variables.concat(persisted_environment.predefined_variables)
+
+ # Here we're passing unexpanded environment_url for runner to expand,
+ # and we need to make sure that CI_ENVIRONMENT_NAME and
+ # CI_ENVIRONMENT_SLUG so on are available for the URL be expanded.
+ variables.append(key: 'CI_ENVIRONMENT_URL', value: environment_url) if environment_url
+ end
+ end
+
def environment_url
options&.dig(:environment, :url) || persisted_environment&.external_url
end
diff --git a/app/models/ci/build_metadata.rb b/app/models/ci/build_metadata.rb
new file mode 100644
index 00000000000..96762f8845c
--- /dev/null
+++ b/app/models/ci/build_metadata.rb
@@ -0,0 +1,35 @@
+module Ci
+ # The purpose of this class is to store Build related data that can be disposed.
+ # Data that should be persisted forever, should be stored with Ci::Build model.
+ class BuildMetadata < ActiveRecord::Base
+ extend Gitlab::Ci::Model
+ include Presentable
+ include ChronicDurationAttribute
+
+ self.table_name = 'ci_builds_metadata'
+
+ belongs_to :build, class_name: 'Ci::Build'
+ belongs_to :project
+
+ validates :build, presence: true
+ validates :project, presence: true
+
+ chronic_duration_attr_reader :timeout_human_readable, :timeout
+
+ enum timeout_source: {
+ unknown_timeout_source: 1,
+ project_timeout_source: 2,
+ runner_timeout_source: 3
+ }
+
+ def update_timeout_state
+ return unless build.runner.present?
+
+ project_timeout = project&.build_timeout
+ timeout = [project_timeout, build.runner.maximum_timeout].compact.min
+ timeout_source = timeout < project_timeout ? :runner_timeout_source : :project_timeout_source
+
+ update(timeout: timeout, timeout_source: timeout_source)
+ end
+ end
+end
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/job_artifact.rb b/app/models/ci/job_artifact.rb
index 0a599f72bc7..df57b4f65e3 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -1,5 +1,7 @@
module Ci
class JobArtifact < ActiveRecord::Base
+ include AfterCommitQueue
+ include ObjectStorage::BackgroundMove
extend Gitlab::Ci::Model
belongs_to :project
@@ -7,9 +9,11 @@ module Ci
before_save :set_size, if: :file_changed?
+ scope :with_files_stored_locally, -> { where(file_store: [nil, ::JobArtifactUploader::Store::LOCAL]) }
+
mount_uploader :file, JobArtifactUploader
- delegate :open, :exists?, to: :file
+ delegate :exists?, :open, to: :file
enum file_type: {
archive: 1,
@@ -21,6 +25,10 @@ module Ci
self.where(project: project).sum(:size)
end
+ def local_store?
+ [nil, ::JobArtifactUploader::Store::LOCAL].include?(self.file_store)
+ end
+
def set_size
self.size = file.size
end
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/runner.rb b/app/models/ci/runner.rb
index 7173f88f1c7..5a4c56ec0dc 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -3,12 +3,13 @@ module Ci
extend Gitlab::Ci::Model
include Gitlab::SQL::Pattern
include RedisCacheable
+ include ChronicDurationAttribute
RUNNER_QUEUE_EXPIRY_TIME = 60.minutes
ONLINE_CONTACT_TIMEOUT = 1.hour
UPDATE_DB_RUNNER_INFO_EVERY = 40.minutes
AVAILABLE_SCOPES = %w[specific shared active paused online].freeze
- FORM_EDITABLE = %i[description tag_list active run_untagged locked access_level].freeze
+ FORM_EDITABLE = %i[description tag_list active run_untagged locked access_level maximum_timeout_human_readable].freeze
has_many :builds
has_many :runner_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
@@ -51,6 +52,12 @@ module Ci
cached_attr_reader :version, :revision, :platform, :architecture, :contacted_at, :ip_address
+ chronic_duration_attr :maximum_timeout_human_readable, :maximum_timeout
+
+ validates :maximum_timeout, allow_nil: true,
+ numericality: { greater_than_or_equal_to: 600,
+ message: 'needs to be at least 10 minutes' }
+
# Searches for runners matching the given query.
#
# This method uses ILIKE on PostgreSQL and LIKE on MySQL.
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/clusters/cluster.rb b/app/models/clusters/cluster.rb
index 49eb069016a..77947d515c1 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -10,6 +10,7 @@ module Clusters
Applications::Prometheus.application_name => Applications::Prometheus,
Applications::Runner.application_name => Applications::Runner
}.freeze
+ DEFAULT_ENVIRONMENT = '*'.freeze
belongs_to :user
@@ -50,6 +51,11 @@ module Clusters
scope :enabled, -> { where(enabled: true) }
scope :disabled, -> { where(enabled: false) }
+ scope :user_provided, -> { where(provider_type: ::Clusters::Cluster.provider_types[:user]) }
+ scope :gcp_provided, -> { where(provider_type: ::Clusters::Cluster.provider_types[:gcp]) }
+ scope :gcp_installed, -> { gcp_provided.includes(:provider_gcp).where(cluster_providers_gcp: { status: ::Clusters::Providers::Gcp.state_machines[:status].states[:created].value }) }
+
+ scope :default_environment, -> { where(environment_scope: DEFAULT_ENVIRONMENT) }
def status_name
if provider
diff --git a/app/models/clusters/concerns/application_status.rb b/app/models/clusters/concerns/application_status.rb
index 7b7c8eac773..8f3eb75bfa9 100644
--- a/app/models/clusters/concerns/application_status.rb
+++ b/app/models/clusters/concerns/application_status.rb
@@ -4,6 +4,8 @@ module Clusters
extend ActiveSupport::Concern
included do
+ scope :installed, -> { where(status: self.state_machines[:status].states[:installed].value) }
+
state_machine :status, initial: :not_installable do
state :not_installable, value: -2
state :errored, value: -1
diff --git a/app/models/commit.rb b/app/models/commit.rb
index cceae5efb72..3f7f36e83c0 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -32,7 +32,8 @@ class Commit
COMMIT_SHA_PATTERN = /\h{#{MIN_SHA_LENGTH},40}/.freeze
def banzai_render_context(field)
- context = { pipeline: :single_line, project: self.project }
+ pipeline = field == :description ? :commit_description : :single_line
+ context = { pipeline: pipeline, project: self.project }
context[:author] = self.author if self.author
context
@@ -175,7 +176,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/commit_status.rb b/app/models/commit_status.rb
index 9fb5b7efec6..3469d5d795c 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -141,7 +141,7 @@ class CommitStatus < ActiveRecord::Base
end
def group_name
- name.to_s.gsub(%r{\d+[\.\s:/\\]+\d+\s*}, '').strip
+ name.to_s.gsub(%r{\d+[\s:/\\]+\d+\s*}, '').strip
end
def failed_but_allowed?
diff --git a/app/models/concerns/avatarable.rb b/app/models/concerns/avatarable.rb
index d35e37935fb..7677891b9ce 100644
--- a/app/models/concerns/avatarable.rb
+++ b/app/models/concerns/avatarable.rb
@@ -3,6 +3,7 @@ module Avatarable
included do
prepend ShadowMethods
+ include ObjectStorage::BackgroundMove
validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
@@ -21,7 +22,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/chronic_duration_attribute.rb b/app/models/concerns/chronic_duration_attribute.rb
new file mode 100644
index 00000000000..fa1eafb1d7a
--- /dev/null
+++ b/app/models/concerns/chronic_duration_attribute.rb
@@ -0,0 +1,39 @@
+module ChronicDurationAttribute
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def chronic_duration_attr_reader(virtual_attribute, source_attribute)
+ define_method(virtual_attribute) do
+ chronic_duration_attributes[virtual_attribute] || output_chronic_duration_attribute(source_attribute)
+ end
+ end
+
+ def chronic_duration_attr_writer(virtual_attribute, source_attribute)
+ chronic_duration_attr_reader(virtual_attribute, source_attribute)
+
+ define_method("#{virtual_attribute}=") do |value|
+ chronic_duration_attributes[virtual_attribute] = value.presence || ''
+
+ begin
+ new_value = ChronicDuration.parse(value).to_i if value.present?
+ assign_attributes(source_attribute => new_value)
+ rescue ChronicDuration::DurationParseError
+ # ignore error as it will be caught by validation
+ end
+ end
+
+ validates virtual_attribute, allow_nil: true, duration: true
+ end
+
+ alias_method :chronic_duration_attr, :chronic_duration_attr_writer
+ end
+
+ def chronic_duration_attributes
+ @chronic_duration_attributes ||= {}
+ end
+
+ def output_chronic_duration_attribute(source_attribute)
+ value = attributes[source_attribute.to_s]
+ ChronicDuration.output(value, format: :short) if value
+ end
+end
diff --git a/app/models/concerns/deployment_platform.rb b/app/models/concerns/deployment_platform.rb
index faa94204e33..52851b3d0b2 100644
--- a/app/models/concerns/deployment_platform.rb
+++ b/app/models/concerns/deployment_platform.rb
@@ -1,16 +1,24 @@
module DeploymentPlatform
- # EE would override this and utilize the extra argument
+ # EE would override this and utilize environment argument
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
def deployment_platform(environment: nil)
- @deployment_platform ||=
- find_cluster_platform_kubernetes ||
- find_kubernetes_service_integration ||
- build_cluster_and_deployment_platform
+ @deployment_platform ||= {}
+
+ @deployment_platform[environment] ||= find_deployment_platform(environment)
end
private
- def find_cluster_platform_kubernetes
- clusters.find_by(enabled: true)&.platform_kubernetes
+ def find_deployment_platform(environment)
+ find_cluster_platform_kubernetes(environment: environment) ||
+ find_kubernetes_service_integration ||
+ build_cluster_and_deployment_platform
+ end
+
+ # EE would override this and utilize environment argument
+ def find_cluster_platform_kubernetes(environment: nil)
+ clusters.enabled.default_environment
+ .last&.platform_kubernetes
end
def find_kubernetes_service_integration
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 5a566f3ac02..b45395343cc 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -137,7 +137,7 @@ module Issuable
fuzzy_search(query, [:title, :description])
end
- def sort(method, excluded_labels: [])
+ def sort_by_attribute(method, excluded_labels: [])
sorted =
case method.to_s
when 'downvotes_desc' then order_downvotes_desc
diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb
index caf8afa97f9..5130ecec472 100644
--- a/app/models/concerns/milestoneish.rb
+++ b/app/models/concerns/milestoneish.rb
@@ -45,11 +45,11 @@ module Milestoneish
end
def sorted_issues(user)
- issues_visible_to_user(user).preload_associations.sort('label_priority')
+ issues_visible_to_user(user).preload_associations.sort_by_attribute('label_priority')
end
def sorted_merge_requests
- merge_requests.sort('label_priority')
+ merge_requests.sort_by_attribute('label_priority')
end
def upcoming?
diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb
index c2e0a5fa126..89a74b7dcb1 100644
--- a/app/models/deploy_key.rb
+++ b/app/models/deploy_key.rb
@@ -27,6 +27,10 @@ class DeployKey < Key
self.private?
end
+ def user
+ super || User.ghost
+ end
+
def has_access_to?(project)
deploy_keys_project_for(project).present?
end
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 f669b1a7009..3cfe21ac93b 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -53,7 +53,7 @@ class Group < Namespace
Gitlab::Database.postgresql?
end
- def sort(method)
+ def sort_by_attribute(method)
if method == 'storage_size_desc'
# storage_size is a virtual column so we need to
# pass a string to avoid AR adding the table name
@@ -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")
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 7bfc45c1f43..13abc6c1a0d 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -23,6 +23,7 @@ class Issue < ActiveRecord::Base
belongs_to :project
belongs_to :moved_to, class_name: 'Issue'
+ belongs_to :closed_by, class_name: 'User'
has_internal_id :iid, scope: :project, init: ->(s) { s&.project&.issues&.maximum(:iid) }
@@ -78,6 +79,11 @@ class Issue < ActiveRecord::Base
before_transition any => :closed do |issue|
issue.closed_at = Time.zone.now
end
+
+ before_transition closed: :opened do |issue|
+ issue.closed_at = nil
+ issue.closed_by = nil
+ end
end
class << self
@@ -110,7 +116,7 @@ class Issue < ActiveRecord::Base
'project_id'
end
- def self.sort(method, excluded_labels: [])
+ def self.sort_by_attribute(method, excluded_labels: [])
case method.to_s
when 'due_date' then order_due_date_asc
when 'due_date_asc' then order_due_date_asc
diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb
index b444812a4cf..b7de46fa202 100644
--- a/app/models/lfs_object.rb
+++ b/app/models/lfs_object.rb
@@ -1,15 +1,30 @@
class LfsObject < ActiveRecord::Base
+ include AfterCommitQueue
+ include ObjectStorage::BackgroundMove
+
has_many :lfs_objects_projects, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :projects, through: :lfs_objects_projects
+ scope :with_files_stored_locally, -> { where(file_store: [nil, LfsObjectUploader::Store::LOCAL]) }
+
validates :oid, presence: true, uniqueness: true
mount_uploader :file, LfsObjectUploader
+ before_save :update_file_store
+
+ def update_file_store
+ self.file_store = file.object_store
+ end
+
def project_allowed_access?(project)
projects.exists?(project.lfs_storage_project.id)
end
+ def local_store?
+ [nil, LfsObjectUploader::Store::LOCAL].include?(self.file_store)
+ end
+
def self.destroy_unreferenced
joins("LEFT JOIN lfs_objects_projects ON lfs_objects_projects.lfs_object_id = #{table_name}.id")
.where(lfs_objects_projects: { id: nil })
diff --git a/app/models/member.rb b/app/models/member.rb
index e1a32148538..eac4a22a03f 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -96,7 +96,7 @@ class Member < ActiveRecord::Base
joins(:user).merge(User.search(query))
end
- def sort(method)
+ def sort_by_attribute(method)
case method.to_s
when 'access_level_asc' then reorder(access_level: :asc)
when 'access_level_desc' then reorder(access_level: :desc)
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 7e6d89ec9c7..91d8be5559b 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -536,18 +536,25 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff(true)
end
+ def viewable_diffs
+ @viewable_diffs ||= merge_request_diffs.viewable.to_a
+ end
+
def merge_request_diff_for(diff_refs_or_sha)
- @merge_request_diffs_by_diff_refs_or_sha ||= Hash.new do |h, diff_refs_or_sha|
- diffs = merge_request_diffs.viewable
- h[diff_refs_or_sha] =
- if diff_refs_or_sha.is_a?(Gitlab::Diff::DiffRefs)
- diffs.find_by_diff_refs(diff_refs_or_sha)
- else
- diffs.find_by(head_commit_sha: diff_refs_or_sha)
- end
- end
+ matcher =
+ if diff_refs_or_sha.is_a?(Gitlab::Diff::DiffRefs)
+ {
+ 'start_commit_sha' => diff_refs_or_sha.start_sha,
+ 'head_commit_sha' => diff_refs_or_sha.head_sha,
+ 'base_commit_sha' => diff_refs_or_sha.base_sha
+ }
+ else
+ { 'head_commit_sha' => diff_refs_or_sha }
+ end
- @merge_request_diffs_by_diff_refs_or_sha[diff_refs_or_sha]
+ viewable_diffs.find do |diff|
+ diff.attributes.slice(*matcher.keys) == matcher
+ end
end
def version_params_for(diff_refs)
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index e7d397f40f5..dafae58d121 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -138,7 +138,7 @@ class Milestone < ActiveRecord::Base
User.joins(assigned_issues: :milestone).where("milestones.id = ?", id).uniq
end
- def self.sort(method)
+ def self.sort_by_attribute(method)
case method.to_s
when 'due_date_asc'
reorder(Gitlab::Database.nulls_last_order('due_date', 'ASC'))
diff --git a/app/models/note.rb b/app/models/note.rb
index 787a80f0196..0f5fb529a87 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -379,12 +379,15 @@ class Note < ActiveRecord::Base
def expire_etag_cache
return unless noteable&.discussions_rendered_on_frontend?
- key = Gitlab::Routing.url_helpers.project_noteable_notes_path(
+ Gitlab::EtagCaching::Store.new.touch(etag_key)
+ end
+
+ def etag_key
+ Gitlab::Routing.url_helpers.project_noteable_notes_path(
project,
target_type: noteable_type.underscore,
target_id: noteable_id
)
- Gitlab::EtagCaching::Store.new.touch(key)
end
def touch(*args)
diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb
index e95655e19f8..b3ffad00a07 100644
--- a/app/models/notification_recipient.rb
+++ b/app/models/notification_recipient.rb
@@ -48,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
@@ -96,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 e5ede967668..714a15ade9c 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?,
@@ -264,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 } }
@@ -432,7 +436,7 @@ class Project < ActiveRecord::Base
Gitlab::VisibilityLevel.options
end
- def sort(method)
+ def sort_by_attribute(method)
case method.to_s
when 'storage_size_desc'
# storage_size is a joined column so we need to
@@ -500,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
@@ -562,9 +566,7 @@ class Project < ActiveRecord::Base
def add_import_job
job_id =
if forked?
- RepositoryForkWorker.perform_async(id,
- forked_from_project.repository_storage_path,
- forked_from_project.disk_path)
+ RepositoryForkWorker.perform_async(id)
elsif gitlab_project_import?
# Do not retry on Import/Export until https://gitlab.com/gitlab-org/gitlab-ce/issues/26189 is solved.
RepositoryImportWorker.set(retry: false).perform_async(self.id)
@@ -734,6 +736,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
@@ -1322,20 +1344,19 @@ class Project < ActiveRecord::Base
Dir.exist?(public_pages_path)
end
- def pages_url
- subdomain, _, url_path = full_path.partition('/')
-
- # The hostname always needs to be in downcased
- # All web servers convert hostname to lowercase
- host = "#{subdomain}.#{Settings.pages.host}".downcase
-
+ def pages_group_url
# The host in URL always needs to be downcased
- url = Gitlab.config.pages.url.sub(%r{^https?://}) do |prefix|
- "#{prefix}#{subdomain}."
+ Gitlab.config.pages.url.sub(%r{^https?://}) do |prefix|
+ "#{prefix}#{pages_subdomain}."
end.downcase
+ end
+
+ def pages_url
+ url = pages_group_url
+ url_path = full_path.partition('/').last
# If the project path is the same as host, we serve it as group page
- return url if host == url_path
+ return url if url == "#{Settings.pages.protocol}://#{url_path}"
"#{url}/#{url_path}"
end
@@ -1521,8 +1542,8 @@ class Project < ActiveRecord::Base
@errors = original_errors
end
- def add_export_job(current_user:, params: {})
- job_id = ProjectExportWorker.perform_async(current_user.id, self.id, params)
+ def add_export_job(current_user:, after_export_strategy: nil, params: {})
+ job_id = ProjectExportWorker.perform_async(current_user.id, self.id, after_export_strategy, params)
if job_id
Rails.logger.info "Export job started for project ID #{self.id} with job ID #{job_id}"
@@ -1548,6 +1569,8 @@ class Project < ActiveRecord::Base
def export_status
if export_in_progress?
:started
+ elsif after_export_in_progress?
+ :after_export_action
elsif export_project_path
:finished
else
@@ -1559,12 +1582,22 @@ class Project < ActiveRecord::Base
import_export_shared.active_export_count > 0
end
+ def after_export_in_progress?
+ import_export_shared.after_export_in_progress?
+ end
+
def remove_exports
return nil unless export_path.present?
FileUtils.rm_rf(export_path)
end
+ def remove_exported_project_file
+ return unless export_project_path.present?
+
+ FileUtils.rm_f(export_project_path)
+ end
+
def full_path_slug
Gitlab::Utils.slugify(full_path.to_s)
end
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/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb
index 017a9b2df6e..26cbfd784ad 100644
--- a/app/models/project_services/gemnasium_service.rb
+++ b/app/models/project_services/gemnasium_service.rb
@@ -36,7 +36,7 @@ class GemnasiumService < Service
after: data[:after],
token: token,
api_key: api_key,
- repo: project.repository.path_to_repo
+ repo: project.repository.path_to_repo # Gitaly: fixed by https://gitlab.com/gitlab-org/security-products/gemnasium-migration/issues/9
)
end
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/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/redirect_route.rb b/app/models/redirect_route.rb
index 20532527346..31de204d824 100644
--- a/app/models/redirect_route.rb
+++ b/app/models/redirect_route.rb
@@ -17,32 +17,4 @@ class RedirectRoute < ActiveRecord::Base
where(wheres, path, "#{sanitize_sql_like(path)}/%")
end
-
- scope :permanent, -> do
- if column_permanent_exists?
- where(permanent: true)
- else
- none
- end
- end
-
- scope :temporary, -> do
- if column_permanent_exists?
- where(permanent: [false, nil])
- else
- all
- end
- end
-
- default_value_for :permanent, false
-
- def permanent=(value)
- if self.class.column_permanent_exists?
- super
- end
- end
-
- def self.column_permanent_exists?
- ActiveRecord::Base.connection.column_exists?(:redirect_routes, :permanent)
- end
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 42f1ac43e29..fd1afafe4df 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -93,10 +93,6 @@ class Repository
"#<#{self.class.name}:#{@disk_path}>"
end
- def create_hooks
- Gitlab::Git::Repository.create_hooks(path_to_repo, Gitlab.config.gitlab_shell.hooks_path)
- end
-
def commit(ref = 'HEAD')
return nil unless exists?
return ref if ref.is_a?(::Commit)
@@ -253,13 +249,13 @@ class Repository
end
def diverging_commit_counts(branch)
- root_ref_hash = raw_repository.commit(root_ref).id
+ @root_ref_hash ||= raw_repository.commit(root_ref).id
cache.fetch(:"diverging_commit_counts_#{branch.name}") do
# Rugged seems to throw a `ReferenceError` when given branch_names rather
# than SHA-1 hashes
number_commits_behind, number_commits_ahead =
raw_repository.count_commits_between(
- root_ref_hash,
+ @root_ref_hash,
branch.dereferenced_target.sha,
left_right: true,
max_count: MAX_DIVERGING_COUNT)
diff --git a/app/models/route.rb b/app/models/route.rb
index 07d96c21cf1..2d609920051 100644
--- a/app/models/route.rb
+++ b/app/models/route.rb
@@ -10,8 +10,6 @@ class Route < ActiveRecord::Base
presence: true,
uniqueness: { case_sensitive: false }
- validate :ensure_permanent_paths, if: :path_changed?
-
before_validation :delete_conflicting_orphaned_routes
after_create :delete_conflicting_redirects
after_update :delete_conflicting_redirects, if: :path_changed?
@@ -45,7 +43,7 @@ class Route < ActiveRecord::Base
# We are not calling route.delete_conflicting_redirects here, in hopes
# of avoiding deadlocks. The parent (self, in this method) already
# called it, which deletes conflicts for all descendants.
- route.create_redirect(old_path, permanent: permanent_redirect?) if attributes[:path]
+ route.create_redirect(old_path) if attributes[:path]
end
end
end
@@ -55,31 +53,17 @@ class Route < ActiveRecord::Base
end
def conflicting_redirects
- RedirectRoute.temporary.matching_path_and_descendants(path)
+ RedirectRoute.matching_path_and_descendants(path)
end
- def create_redirect(path, permanent: false)
- RedirectRoute.create(source: source, path: path, permanent: permanent)
+ def create_redirect(path)
+ RedirectRoute.create(source: source, path: path)
end
private
def create_redirect_for_old_path
- create_redirect(path_was, permanent: permanent_redirect?) if path_changed?
- end
-
- def permanent_redirect?
- source_type != "Project"
- end
-
- def ensure_permanent_paths
- return if path.nil?
-
- errors.add(:path, "has been taken before") if conflicting_redirect_exists?
- end
-
- def conflicting_redirect_exists?
- RedirectRoute.permanent.matching_path_and_descendants(path).exists?
+ create_redirect(path_was) if path_changed?
end
def delete_conflicting_orphaned_routes
diff --git a/app/models/service.rb b/app/models/service.rb
index 1dcb79157a2..7424cef0fc0 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -273,6 +273,7 @@ class Service < ActiveRecord::Base
def self.build_from_template(project_id, template)
service = template.dup
+ service.active = false unless service.valid?
service.template = false
service.project_id = project_id
service
diff --git a/app/models/todo.rb b/app/models/todo.rb
index 8afacd188e0..a2ab405fdbe 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -50,7 +50,7 @@ class Todo < ActiveRecord::Base
# Priority sorting isn't displayed in the dropdown, because we don't show
# milestones, but still show something if the user has a URL with that
# selected.
- def sort(method)
+ def sort_by_attribute(method)
sorted =
case method.to_s
when 'priority', 'label_priority' then order_by_labels_priority
diff --git a/app/models/upload.rb b/app/models/upload.rb
index 99ad37dc892..cf71a7b76fc 100644
--- a/app/models/upload.rb
+++ b/app/models/upload.rb
@@ -9,6 +9,8 @@ class Upload < ActiveRecord::Base
validates :model, presence: true
validates :uploader, presence: true
+ scope :with_files_stored_locally, -> { where(store: [nil, ObjectStorage::Store::LOCAL]) }
+
before_save :calculate_checksum!, if: :foreground_checksummable?
after_commit :schedule_checksum, if: :checksummable?
@@ -21,6 +23,7 @@ class Upload < ActiveRecord::Base
end
def absolute_path
+ raise ObjectStorage::RemoteStoreError, "Remote object has no absolute path." unless local?
return path unless relative_path?
uploader_class.absolute_path(self)
@@ -30,11 +33,11 @@ class Upload < ActiveRecord::Base
self.checksum = nil
return unless checksummable?
- self.checksum = self.class.hexdigest(absolute_path)
+ self.checksum = Digest::SHA256.file(absolute_path).hexdigest
end
- def build_uploader
- uploader_class.new(model, mount_point, **uploader_context).tap do |uploader|
+ def build_uploader(mounted_as = nil)
+ uploader_class.new(model, mounted_as || mount_point).tap do |uploader|
uploader.upload = self
uploader.retrieve_from_store!(identifier)
end
@@ -51,6 +54,12 @@ class Upload < ActiveRecord::Base
}.compact
end
+ def local?
+ return true if store.nil?
+
+ store == ObjectStorage::Store::LOCAL
+ end
+
private
def delete_file!
@@ -61,10 +70,6 @@ class Upload < ActiveRecord::Base
checksum.nil? && local? && exist?
end
- def local?
- true
- end
-
def foreground_checksummable?
checksummable? && size <= CHECKSUM_THRESHOLD
end
diff --git a/app/models/user.rb b/app/models/user.rb
index b8c55205ab8..ba51595e6a3 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -82,11 +82,8 @@ class User < ActiveRecord::Base
has_one :namespace, -> { where(type: nil) }, dependent: :destroy, foreign_key: :owner_id, inverse_of: :owner, 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 # rubocop:disable Cop/ActiveRecordDependent
- has_many :deploy_keys, -> { where(type: 'DeployKey') }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
+ has_many :keys, -> { where(type: ['Key', nil]) }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
+ has_many :deploy_keys, -> { where(type: 'DeployKey') }, dependent: :nullify # rubocop:disable Cop/ActiveRecordDependent
has_many :gpg_keys
has_many :emails, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
@@ -187,7 +184,7 @@ class User < ActiveRecord::Base
# User's Dashboard preference
# Note: When adding an option, it MUST go on the end of the array.
- enum dashboard: [:projects, :stars, :project_activity, :starred_project_activity, :groups, :todos]
+ enum dashboard: [:projects, :stars, :project_activity, :starred_project_activity, :groups, :todos, :issues, :merge_requests]
# User's Project preference
# Note: When adding an option, it MUST go on the end of the array.
@@ -259,7 +256,7 @@ class User < ActiveRecord::Base
end
end
- def sort(method)
+ def sort_by_attribute(method)
order_method = method || 'id_desc'
case order_method.to_s
@@ -623,9 +620,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 +1191,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)