diff options
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/blob.rb | 12 | ||||
-rw-r--r-- | app/models/ci/build.rb | 43 | ||||
-rw-r--r-- | app/models/ci/pipeline.rb | 11 | ||||
-rw-r--r-- | app/models/ci/variable.rb | 6 | ||||
-rw-r--r-- | app/models/commit_status.rb | 20 | ||||
-rw-r--r-- | app/models/concerns/has_status.rb | 9 | ||||
-rw-r--r-- | app/models/environment.rb | 12 | ||||
-rw-r--r-- | app/models/event.rb | 17 | ||||
-rw-r--r-- | app/models/group.rb | 7 | ||||
-rw-r--r-- | app/models/member.rb | 33 | ||||
-rw-r--r-- | app/models/merge_request.rb | 2 | ||||
-rw-r--r-- | app/models/namespace.rb | 5 | ||||
-rw-r--r-- | app/models/project.rb | 27 | ||||
-rw-r--r-- | app/models/repository.rb | 33 |
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 |