summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
Diffstat (limited to 'app/models')
-rw-r--r--app/models/blob.rb12
-rw-r--r--app/models/ci/build.rb43
-rw-r--r--app/models/ci/pipeline.rb11
-rw-r--r--app/models/ci/variable.rb6
-rw-r--r--app/models/commit_status.rb20
-rw-r--r--app/models/concerns/has_status.rb9
-rw-r--r--app/models/environment.rb12
-rw-r--r--app/models/event.rb17
-rw-r--r--app/models/group.rb7
-rw-r--r--app/models/member.rb33
-rw-r--r--app/models/merge_request.rb2
-rw-r--r--app/models/namespace.rb5
-rw-r--r--app/models/project.rb27
-rw-r--r--app/models/repository.rb33
14 files changed, 149 insertions, 88 deletions
diff --git a/app/models/blob.rb b/app/models/blob.rb
index 12cc5aaafba..ab92e820335 100644
--- a/app/models/blob.rb
+++ b/app/models/blob.rb
@@ -22,6 +22,18 @@ class Blob < SimpleDelegator
new(blob)
end
+ # Returns the data of the blob.
+ #
+ # If the blob is a text based blob the content is converted to UTF-8 and any
+ # invalid byte sequences are replaced.
+ def data
+ if binary?
+ super
+ else
+ @data ||= super.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
+ end
+ end
+
def no_highlighting?
size && size > 1.megabyte
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index fb16bc06d71..dd984aef318 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -1,5 +1,7 @@
module Ci
class Build < CommitStatus
+ include TokenAuthenticatable
+
belongs_to :runner, class_name: 'Ci::Runner'
belongs_to :trigger_request, class_name: 'Ci::TriggerRequest'
belongs_to :erased_by, class_name: 'User'
@@ -23,7 +25,10 @@ module Ci
acts_as_taggable
+ add_authentication_token_field :token
+
before_save :update_artifacts_size, if: :artifacts_file_changed?
+ before_save :ensure_token
before_destroy { project }
after_create :execute_hooks
@@ -38,6 +43,7 @@ module Ci
new_build.status = 'pending'
new_build.runner_id = nil
new_build.trigger_request_id = nil
+ new_build.token = nil
new_build.save
end
@@ -79,11 +85,14 @@ module Ci
after_transition any => [:success] do |build|
if build.environment.present?
- service = CreateDeploymentService.new(build.project, build.user,
- environment: build.environment,
- sha: build.sha,
- ref: build.ref,
- tag: build.tag)
+ service = CreateDeploymentService.new(
+ build.project, build.user,
+ environment: build.environment,
+ sha: build.sha,
+ ref: build.ref,
+ tag: build.tag,
+ options: build.options[:environment],
+ variables: build.variables)
service.execute(build)
end
end
@@ -173,7 +182,7 @@ module Ci
end
def repo_url
- auth = "gitlab-ci-token:#{token}@"
+ auth = "gitlab-ci-token:#{ensure_token!}@"
project.http_url_to_repo.sub(/^https?:\/\//) do |prefix|
prefix + auth
end
@@ -235,12 +244,7 @@ module Ci
end
def trace
- trace = raw_trace
- if project && trace.present? && project.runners_token.present?
- trace.gsub(project.runners_token, 'xxxxxx')
- else
- trace
- end
+ hide_secrets(raw_trace)
end
def trace_length
@@ -253,6 +257,7 @@ module Ci
def trace=(trace)
recreate_trace_dir
+ trace = hide_secrets(trace)
File.write(path_to_trace, trace)
end
@@ -266,6 +271,8 @@ module Ci
def append_trace(trace_part, offset)
recreate_trace_dir
+ trace_part = hide_secrets(trace_part)
+
File.truncate(path_to_trace, offset) if File.exist?(path_to_trace)
File.open(path_to_trace, 'ab') do |f|
f.write(trace_part)
@@ -341,12 +348,8 @@ module Ci
)
end
- def token
- project.runners_token
- end
-
def valid_token?(token)
- project.valid_runners_token?(token)
+ self.token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.token)
end
def has_tags?
@@ -488,5 +491,11 @@ module Ci
pipeline.config_processor.build_attributes(name)
end
+
+ def hide_secrets(trace)
+ trace = Ci::MaskSecret.mask(trace, project.runners_token) if project
+ trace = Ci::MaskSecret.mask(trace, token)
+ trace
+ end
end
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 0b1df9f4294..70647b8532b 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -2,6 +2,7 @@ module Ci
class Pipeline < ActiveRecord::Base
extend Ci::Model
include HasStatus
+ include Importable
self.table_name = 'ci_commits'
@@ -12,12 +13,12 @@ module Ci
has_many :builds, class_name: 'Ci::Build', foreign_key: :commit_id
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest', foreign_key: :commit_id
- validates_presence_of :sha
- validates_presence_of :ref
- validates_presence_of :status
- validate :valid_commit_sha
+ validates_presence_of :sha, unless: :importing?
+ validates_presence_of :ref, unless: :importing?
+ validates_presence_of :status, unless: :importing?
+ validate :valid_commit_sha, unless: :importing?
- after_save :keep_around_commits
+ after_save :keep_around_commits, unless: :importing?
delegate :stages, to: :statuses
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index c9c47ec7419..6959223aed9 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -1,7 +1,7 @@
module Ci
class Variable < ActiveRecord::Base
extend Ci::Model
-
+
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
validates_uniqueness_of :key, scope: :gl_project_id
@@ -11,7 +11,9 @@ module Ci
format: { with: /\A[a-zA-Z0-9_]+\z/,
message: "can contain only letters, digits and '_'." }
- attr_encrypted :value,
+ scope :order_key_asc, -> { reorder(key: :asc) }
+
+ attr_encrypted :value,
mode: :per_attribute_iv_and_salt,
insecure_mode: true,
key: Gitlab::Application.secrets.db_key_base,
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 4a628924499..c85561291c8 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -69,17 +69,15 @@ class CommitStatus < ActiveRecord::Base
commit_status.update_attributes finished_at: Time.now
end
- # We use around_transition to process pipeline on next stages as soon as possible, before the `after_*` is executed
- around_transition any => [:success, :failed, :canceled] do |commit_status, block|
- block.call
-
- commit_status.pipeline.try(:process!)
- end
-
after_transition do |commit_status, transition|
commit_status.pipeline.try(:build_updated) unless transition.loopback?
end
+ after_transition any => [:success, :failed, :canceled] do |commit_status|
+ commit_status.pipeline.try(:process!)
+ true
+ end
+
after_transition [:created, :pending, :running] => :success do |commit_status|
MergeRequests::MergeWhenBuildSucceedsService.new(commit_status.pipeline.project, nil).trigger(commit_status)
end
@@ -95,6 +93,10 @@ class CommitStatus < ActiveRecord::Base
pipeline.before_sha || Gitlab::Git::BLANK_SHA
end
+ def group_name
+ name.gsub(/\d+[\s:\/\\]+\d+\s*/, '').strip
+ end
+
def self.stages
# We group by stage name, but order stages by theirs' index
unscoped.from(all, :sg).group('stage').order('max(stage_idx)', 'stage').pluck('sg.stage')
@@ -113,6 +115,10 @@ class CommitStatus < ActiveRecord::Base
allow_failure? && (failed? || canceled?)
end
+ def playable?
+ false
+ end
+
def duration
calculate_duration
end
diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb
index f7b8352405c..0fa4df0fb56 100644
--- a/app/models/concerns/has_status.rb
+++ b/app/models/concerns/has_status.rb
@@ -8,8 +8,9 @@ module HasStatus
class_methods do
def status_sql
- scope = all.relevant
+ scope = all
builds = scope.select('count(*)').to_sql
+ created = scope.created.select('count(*)').to_sql
success = scope.success.select('count(*)').to_sql
ignored = scope.ignored.select('count(*)').to_sql if scope.respond_to?(:ignored)
ignored ||= '0'
@@ -19,12 +20,12 @@ module HasStatus
skipped = scope.skipped.select('count(*)').to_sql
deduce_status = "(CASE
- WHEN (#{builds})=0 THEN NULL
+ WHEN (#{builds})=(#{created}) THEN 'created'
WHEN (#{builds})=(#{skipped}) THEN 'skipped'
WHEN (#{builds})=(#{success})+(#{ignored})+(#{skipped}) THEN 'success'
- WHEN (#{builds})=(#{pending})+(#{skipped}) THEN 'pending'
+ WHEN (#{builds})=(#{created})+(#{pending})+(#{skipped}) THEN 'pending'
WHEN (#{builds})=(#{canceled})+(#{success})+(#{ignored})+(#{skipped}) THEN 'canceled'
- WHEN (#{running})+(#{pending})>0 THEN 'running'
+ WHEN (#{running})+(#{pending})+(#{created})>0 THEN 'running'
ELSE 'failed'
END)"
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 75e6f869786..33c9abf382a 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -4,6 +4,7 @@ class Environment < ActiveRecord::Base
has_many :deployments
before_validation :nullify_external_url
+ before_save :set_environment_type
validates :name,
presence: true,
@@ -26,6 +27,17 @@ class Environment < ActiveRecord::Base
self.external_url = nil if self.external_url.blank?
end
+ def set_environment_type
+ names = name.split('/')
+
+ self.environment_type =
+ if names.many?
+ names.first
+ else
+ nil
+ end
+ end
+
def includes_commit?(commit)
return false unless last_deployment
diff --git a/app/models/event.rb b/app/models/event.rb
index a0b7b0dc2b5..b6e8bef3f67 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -13,6 +13,8 @@ class Event < ActiveRecord::Base
LEFT = 9 # User left project
DESTROYED = 10
+ RESET_PROJECT_ACTIVITY_INTERVAL = 1.hour
+
delegate :name, :email, to: :author, prefix: true, allow_nil: true
delegate :title, to: :issue, prefix: true, allow_nil: true
delegate :title, to: :merge_request, prefix: true, allow_nil: true
@@ -324,8 +326,17 @@ class Event < ActiveRecord::Base
end
def reset_project_activity
- if project && Gitlab::ExclusiveLease.new("project:update_last_activity_at:#{project.id}", timeout: 60).try_obtain
- project.update_column(:last_activity_at, self.created_at)
- end
+ return unless project
+
+ # Don't even bother obtaining a lock if the last update happened less than
+ # 60 minutes ago.
+ return if project.last_activity_at > RESET_PROJECT_ACTIVITY_INTERVAL.ago
+
+ return unless Gitlab::ExclusiveLease.
+ new("project:update_last_activity_at:#{project.id}",
+ timeout: RESET_PROJECT_ACTIVITY_INTERVAL.to_i).
+ try_obtain
+
+ project.update_column(:last_activity_at, created_at)
end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index c48869ae465..aefb94b2ada 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -95,6 +95,13 @@ class Group < Namespace
end
end
+ def lfs_enabled?
+ return false unless Gitlab.config.lfs.enabled
+ return Gitlab.config.lfs.enabled if self[:lfs_enabled].nil?
+
+ self[:lfs_enabled]
+ end
+
def add_users(user_ids, access_level, current_user: nil, expires_at: nil)
user_ids.each do |user_id|
Member.add_user(
diff --git a/app/models/member.rb b/app/models/member.rb
index 64e0d33fb20..69406379948 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -28,17 +28,34 @@ class Member < ActiveRecord::Base
allow_nil: true
}
+ # This scope encapsulates (most of) the conditions a row in the member table
+ # must satisfy if it is a valid permission. Of particular note:
+ #
+ # * Access requests must be excluded
+ # * Blocked users must be excluded
+ # * Invitations take effect immediately
+ # * expires_at is not implemented. A background worker purges expired rows
+ scope :active, -> do
+ is_external_invite = arel_table[:user_id].eq(nil).and(arel_table[:invite_token].not_eq(nil))
+ user_is_active = User.arel_table[:state].eq(:active)
+
+ includes(:user).references(:users)
+ .where(is_external_invite.or(user_is_active))
+ .where(requested_at: nil)
+ end
+
scope :invite, -> { where.not(invite_token: nil) }
scope :non_invite, -> { where(invite_token: nil) }
scope :request, -> { where.not(requested_at: nil) }
- scope :has_access, -> { where('access_level > 0') }
-
- scope :guests, -> { where(access_level: GUEST) }
- scope :reporters, -> { where(access_level: REPORTER) }
- scope :developers, -> { where(access_level: DEVELOPER) }
- scope :masters, -> { where(access_level: MASTER) }
- scope :owners, -> { where(access_level: OWNER) }
- scope :owners_and_masters, -> { where(access_level: [OWNER, MASTER]) }
+
+ scope :has_access, -> { active.where('access_level > 0') }
+
+ scope :guests, -> { active.where(access_level: GUEST) }
+ scope :reporters, -> { active.where(access_level: REPORTER) }
+ scope :developers, -> { active.where(access_level: DEVELOPER) }
+ scope :masters, -> { active.where(access_level: MASTER) }
+ scope :owners, -> { active.where(access_level: OWNER) }
+ scope :owners_and_masters, -> { active.where(access_level: [OWNER, MASTER]) }
before_validation :generate_invite_token, on: :create, if: -> (member) { member.invite_email.present? }
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index f7d1253d957..75f48fd4ba5 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -652,7 +652,7 @@ class MergeRequest < ActiveRecord::Base
end
def environments
- return unless diff_head_commit
+ return [] unless diff_head_commit
target_project.environments.select do |environment|
environment.includes_commit?(diff_head_commit)
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 7c29d27ce97..919b3b1f095 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -141,6 +141,11 @@ class Namespace < ActiveRecord::Base
projects.joins(:forked_project_link).find_by('forked_project_links.forked_from_project_id = ?', project.id)
end
+ def lfs_enabled?
+ # User namespace will always default to the global setting
+ Gitlab.config.lfs.enabled
+ end
+
private
def repository_storage_paths
diff --git a/app/models/project.rb b/app/models/project.rb
index 4017cabe9f0..d7f20070be0 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -393,10 +393,9 @@ class Project < ActiveRecord::Base
end
def lfs_enabled?
- return false unless Gitlab.config.lfs.enabled
- return Gitlab.config.lfs.enabled if self[:lfs_enabled].nil?
+ return namespace.lfs_enabled? if self[:lfs_enabled].nil?
- self[:lfs_enabled]
+ self[:lfs_enabled] && Gitlab.config.lfs.enabled
end
def repository_storage_path
@@ -1138,12 +1137,6 @@ class Project < ActiveRecord::Base
self.runners_token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.runners_token)
end
- # TODO (ayufan): For now we use runners_token (backward compatibility)
- # In 8.4 every build will have its own individual token valid for time of build
- def valid_build_token?(token)
- self.builds_enabled? && self.runners_token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.runners_token)
- end
-
def build_coverage_enabled?
build_coverage_regex.present?
end
@@ -1288,8 +1281,24 @@ class Project < ActiveRecord::Base
end
end
+ def pushes_since_gc
+ Gitlab::Redis.with { |redis| redis.get(pushes_since_gc_redis_key).to_i }
+ end
+
+ def increment_pushes_since_gc
+ Gitlab::Redis.with { |redis| redis.incr(pushes_since_gc_redis_key) }
+ end
+
+ def reset_pushes_since_gc
+ Gitlab::Redis.with { |redis| redis.del(pushes_since_gc_redis_key) }
+ end
+
private
+ def pushes_since_gc_redis_key
+ "projects/#{id}/pushes_since_gc"
+ end
+
# Prevents the creation of project_feature record for every project
def setup_project_feature
build_project_feature unless project_feature
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 3c354c25c6f..c69e5a22a69 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -990,37 +990,6 @@ class Repository
Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/)
end
- def parse_search_result(result)
- ref = nil
- filename = nil
- basename = nil
- startline = 0
-
- result.each_line.each_with_index do |line, index|
- if line =~ /^.*:.*:\d+:/
- ref, filename, startline = line.split(':')
- startline = startline.to_i - index
- extname = Regexp.escape(File.extname(filename))
- basename = filename.sub(/#{extname}$/, '')
- break
- end
- end
-
- data = ""
-
- result.each_line do |line|
- data << line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '')
- end
-
- OpenStruct.new(
- filename: filename,
- basename: basename,
- ref: ref,
- startline: startline,
- data: data
- )
- end
-
def fetch_ref(source_path, source_ref, target_ref)
args = %W(#{Gitlab.config.git.bin_path} fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
Gitlab::Popen.popen(args, path_to_repo)
@@ -1048,7 +1017,7 @@ class Repository
GitHooksService.new.execute(current_user, path_to_repo, oldrev, newrev, ref) do
update_ref!(ref, newrev, oldrev)
-
+
if was_empty || !target_branch
# If repo was empty expire cache
after_create if was_empty