summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
authorLuke "Jared" Bennett <lbennett@gitlab.com>2017-04-07 21:00:40 +0100
committerLuke "Jared" Bennett <lbennett@gitlab.com>2017-04-07 21:00:40 +0100
commit8b98ca50b3068fecc76233714fa7d363703ee55c (patch)
tree97d67331552de76faa9bfb8cec11616a369351fe /app/models
parent501d403202d8b4f603dd1864790f56e85633b8ef (diff)
parentd37f1f1cd013a8b7a307e1004e020df086cba9d5 (diff)
downloadgitlab-ce-8b98ca50b3068fecc76233714fa7d363703ee55c.tar.gz
Merge branch 'master' into new-resolvable-discussion
Diffstat (limited to 'app/models')
-rw-r--r--app/models/ci/pipeline.rb6
-rw-r--r--app/models/ci/pipeline_status.rb86
-rw-r--r--app/models/ci/trigger.rb6
-rw-r--r--app/models/ci/trigger_schedule.rb21
-rw-r--r--app/models/concerns/has_status.rb2
-rw-r--r--app/models/concerns/protected_branch_access.rb16
-rw-r--r--app/models/concerns/protected_ref.rb42
-rw-r--r--app/models/concerns/protected_ref_access.rb18
-rw-r--r--app/models/concerns/protected_tag_access.rb11
-rw-r--r--app/models/merge_request.rb2
-rw-r--r--app/models/project.rb26
-rw-r--r--app/models/protectable_dropdown.rb33
-rw-r--r--app/models/protected_branch.rb58
-rw-r--r--app/models/protected_ref_matcher.rb54
-rw-r--r--app/models/protected_tag.rb14
-rw-r--r--app/models/protected_tag/create_access_level.rb21
16 files changed, 231 insertions, 185 deletions
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 37a81fa7781..445247f1b41 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -31,7 +31,6 @@ module Ci
validate :valid_commit_sha, unless: :importing?
after_create :keep_around_commits, unless: :importing?
- after_create :refresh_build_status_cache
state_machine :status, initial: :created do
event :enqueue do
@@ -351,7 +350,6 @@ module Ci
when 'manual' then block
end
end
- refresh_build_status_cache
end
def predefined_variables
@@ -393,10 +391,6 @@ module Ci
.fabricate!
end
- def refresh_build_status_cache
- Ci::PipelineStatus.new(project, sha: sha, status: status).store_in_cache_if_needed
- end
-
private
def pipeline_data
diff --git a/app/models/ci/pipeline_status.rb b/app/models/ci/pipeline_status.rb
deleted file mode 100644
index 048047d0e34..00000000000
--- a/app/models/ci/pipeline_status.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-# This class is not backed by a table in the main database.
-# It loads the latest Pipeline for the HEAD of a repository, and caches that
-# in Redis.
-module Ci
- class PipelineStatus
- attr_accessor :sha, :status, :project, :loaded
-
- delegate :commit, to: :project
-
- def self.load_for_project(project)
- new(project).tap do |status|
- status.load_status
- end
- end
-
- def initialize(project, sha: nil, status: nil)
- @project = project
- @sha = sha
- @status = status
- end
-
- def has_status?
- loaded? && sha.present? && status.present?
- end
-
- def load_status
- return if loaded?
-
- if has_cache?
- load_from_cache
- else
- load_from_commit
- store_in_cache
- end
-
- self.loaded = true
- end
-
- def load_from_commit
- return unless commit
-
- self.sha = commit.sha
- self.status = commit.status
- end
-
- # We only cache the status for the HEAD commit of a project
- # This status is rendered in project lists
- def store_in_cache_if_needed
- return unless sha
- return delete_from_cache unless commit
- store_in_cache if commit.sha == self.sha
- end
-
- def load_from_cache
- Gitlab::Redis.with do |redis|
- self.sha, self.status = redis.hmget(cache_key, :sha, :status)
- end
- end
-
- def store_in_cache
- Gitlab::Redis.with do |redis|
- redis.mapped_hmset(cache_key, { sha: sha, status: status })
- end
- end
-
- def delete_from_cache
- Gitlab::Redis.with do |redis|
- redis.del(cache_key)
- end
- end
-
- def has_cache?
- Gitlab::Redis.with do |redis|
- redis.exists(cache_key)
- end
- end
-
- def loaded?
- self.loaded
- end
-
- def cache_key
- "projects/#{project.id}/build_status"
- end
- end
-end
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index 0a89f3e0640..b59e235c425 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -14,6 +14,8 @@ module Ci
before_validation :set_default_values
+ accepts_nested_attributes_for :trigger_schedule
+
def set_default_values
self.token = SecureRandom.hex(15) if self.token.blank?
end
@@ -37,5 +39,9 @@ module Ci
def can_access_project?
self.owner_id.blank? || Ability.allowed?(self.owner, :create_build, project)
end
+
+ def trigger_schedule
+ super || build_trigger_schedule(project: project)
+ end
end
end
diff --git a/app/models/ci/trigger_schedule.rb b/app/models/ci/trigger_schedule.rb
index 10ea381ee31..012a18eb439 100644
--- a/app/models/ci/trigger_schedule.rb
+++ b/app/models/ci/trigger_schedule.rb
@@ -8,15 +8,19 @@ module Ci
belongs_to :project
belongs_to :trigger
- delegate :ref, to: :trigger
-
validates :trigger, presence: { unless: :importing? }
- validates :cron, cron: true, presence: { unless: :importing? }
- validates :cron_timezone, cron_timezone: true, presence: { unless: :importing? }
- validates :ref, presence: { unless: :importing? }
+ validates :cron, unless: :importing_or_inactive?, cron: true, presence: { unless: :importing_or_inactive? }
+ validates :cron_timezone, cron_timezone: true, presence: { unless: :importing_or_inactive? }
+ validates :ref, presence: { unless: :importing_or_inactive? }
before_save :set_next_run_at
+ scope :active, -> { where(active: true) }
+
+ def importing_or_inactive?
+ importing? || !active?
+ end
+
def set_next_run_at
self.next_run_at = Gitlab::Ci::CronParser.new(cron, cron_timezone).next_time_from(Time.now)
end
@@ -26,5 +30,12 @@ module Ci
rescue ActiveRecord::RecordInvalid
update_attribute(:next_run_at, nil) # update without validation
end
+
+ def real_next_run(
+ worker_cron: Settings.cron_jobs['trigger_schedule_worker']['cron'],
+ worker_time_zone: Time.zone.name)
+ Gitlab::Ci::CronParser.new(worker_cron, worker_time_zone)
+ .next_time_from(next_run_at)
+ end
end
end
diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb
index 2f61709110c..dff7b6e3523 100644
--- a/app/models/concerns/has_status.rb
+++ b/app/models/concerns/has_status.rb
@@ -68,7 +68,7 @@ module HasStatus
end
scope :created, -> { where(status: 'created') }
- scope :relevant, -> { where.not(status: 'created') }
+ scope :relevant, -> { where(status: AVAILABLE_STATUSES - ['created']) }
scope :running, -> { where(status: 'running') }
scope :pending, -> { where(status: 'pending') }
scope :success, -> { where(status: 'success') }
diff --git a/app/models/concerns/protected_branch_access.rb b/app/models/concerns/protected_branch_access.rb
index 9dd4d9c6f24..c41b807df8a 100644
--- a/app/models/concerns/protected_branch_access.rb
+++ b/app/models/concerns/protected_branch_access.rb
@@ -2,20 +2,10 @@ module ProtectedBranchAccess
extend ActiveSupport::Concern
included do
- belongs_to :protected_branch
- delegate :project, to: :protected_branch
-
- scope :master, -> { where(access_level: Gitlab::Access::MASTER) }
- scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) }
- end
+ include ProtectedRefAccess
- def humanize
- self.class.human_access_levels[self.access_level]
- end
-
- def check_access(user)
- return true if user.is_admin?
+ belongs_to :protected_branch
- project.team.max_member_access(user.id) >= access_level
+ delegate :project, to: :protected_branch
end
end
diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb
new file mode 100644
index 00000000000..62eaec2407f
--- /dev/null
+++ b/app/models/concerns/protected_ref.rb
@@ -0,0 +1,42 @@
+module ProtectedRef
+ extend ActiveSupport::Concern
+
+ included do
+ belongs_to :project
+
+ validates :name, presence: true
+ validates :project, presence: true
+
+ delegate :matching, :matches?, :wildcard?, to: :ref_matcher
+
+ def self.protected_ref_accessible_to?(ref, user, action:)
+ access_levels_for_ref(ref, action: action).any? do |access_level|
+ access_level.check_access(user)
+ end
+ end
+
+ def self.developers_can?(action, ref)
+ access_levels_for_ref(ref, action: action).any? do |access_level|
+ access_level.access_level == Gitlab::Access::DEVELOPER
+ end
+ end
+
+ def self.access_levels_for_ref(ref, action:)
+ self.matching(ref).map(&:"#{action}_access_levels").flatten
+ end
+
+ def self.matching(ref_name, protected_refs: nil)
+ ProtectedRefMatcher.matching(self, ref_name, protected_refs: protected_refs)
+ end
+ end
+
+ def commit
+ project.commit(self.name)
+ end
+
+ private
+
+ def ref_matcher
+ @ref_matcher ||= ProtectedRefMatcher.new(self)
+ end
+end
diff --git a/app/models/concerns/protected_ref_access.rb b/app/models/concerns/protected_ref_access.rb
new file mode 100644
index 00000000000..c4f158e569a
--- /dev/null
+++ b/app/models/concerns/protected_ref_access.rb
@@ -0,0 +1,18 @@
+module ProtectedRefAccess
+ extend ActiveSupport::Concern
+
+ included do
+ scope :master, -> { where(access_level: Gitlab::Access::MASTER) }
+ scope :developer, -> { where(access_level: Gitlab::Access::DEVELOPER) }
+ end
+
+ def humanize
+ self.class.human_access_levels[self.access_level]
+ end
+
+ def check_access(user)
+ return true if user.admin?
+
+ project.team.max_member_access(user.id) >= access_level
+ end
+end
diff --git a/app/models/concerns/protected_tag_access.rb b/app/models/concerns/protected_tag_access.rb
new file mode 100644
index 00000000000..ee65de24dd8
--- /dev/null
+++ b/app/models/concerns/protected_tag_access.rb
@@ -0,0 +1,11 @@
+module ProtectedTagAccess
+ extend ActiveSupport::Concern
+
+ included do
+ include ProtectedRefAccess
+
+ belongs_to :protected_tag
+
+ delegate :project, to: :protected_tag
+ end
+end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 77c902fdf09..b71a9e17a93 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -443,7 +443,7 @@ class MergeRequest < ActiveRecord::Base
end
def can_remove_source_branch?(current_user)
- !source_project.protected_branch?(source_branch) &&
+ !ProtectedBranch.protected?(source_project, source_branch) &&
!source_project.root_ref?(source_branch) &&
Ability.allowed?(current_user, :push_code, source_project) &&
diff_head_commit == source_branch_head
diff --git a/app/models/project.rb b/app/models/project.rb
index 639615b91a2..a160efba912 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -135,6 +135,7 @@ class Project < ActiveRecord::Base
has_many :snippets, dependent: :destroy, class_name: 'ProjectSnippet'
has_many :hooks, dependent: :destroy, class_name: 'ProjectHook'
has_many :protected_branches, dependent: :destroy
+ has_many :protected_tags, dependent: :destroy
has_many :project_authorizations
has_many :authorized_users, through: :project_authorizations, source: :user, class_name: 'User'
@@ -859,14 +860,6 @@ class Project < ActiveRecord::Base
@repo_exists = false
end
- # Branches that are not _exactly_ matched by a protected branch.
- def open_branches
- exact_protected_branch_names = protected_branches.reject(&:wildcard?).map(&:name)
- branch_names = repository.branches.map(&:name)
- non_open_branch_names = Set.new(exact_protected_branch_names).intersection(Set.new(branch_names))
- repository.branches.reject { |branch| non_open_branch_names.include? branch.name }
- end
-
def root_ref?(branch)
repository.root_ref == branch
end
@@ -881,16 +874,8 @@ class Project < ActiveRecord::Base
Gitlab::UrlSanitizer.new("#{web_url}.git", credentials: credentials).full_url
end
- # Check if current branch name is marked as protected in the system
- def protected_branch?(branch_name)
- return true if empty_repo? && default_branch_protected?
-
- @protected_branches ||= self.protected_branches.to_a
- ProtectedBranch.matching(branch_name, protected_branches: @protected_branches).present?
- end
-
def user_can_push_to_empty_repo?(user)
- !default_branch_protected? || team.max_member_access(user.id) > Gitlab::Access::DEVELOPER
+ !ProtectedBranch.default_branch_protected? || team.max_member_access(user.id) > Gitlab::Access::DEVELOPER
end
def forked?
@@ -1197,7 +1182,7 @@ class Project < ActiveRecord::Base
end
def pipeline_status
- @pipeline_status ||= Ci::PipelineStatus.load_for_project(self)
+ @pipeline_status ||= Gitlab::Cache::Ci::ProjectPipelineStatus.load_for_project(self)
end
def mark_import_as_failed(error_message)
@@ -1353,11 +1338,6 @@ class Project < ActiveRecord::Base
"projects/#{id}/pushes_since_gc"
end
- def default_branch_protected?
- current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_FULL ||
- current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE
- end
-
# Similar to the normal callbacks that hook into the life cycle of an
# Active Record object, you can also define callbacks that get triggered
# when you add an object to an association collection. If any of these
diff --git a/app/models/protectable_dropdown.rb b/app/models/protectable_dropdown.rb
new file mode 100644
index 00000000000..122fbce257d
--- /dev/null
+++ b/app/models/protectable_dropdown.rb
@@ -0,0 +1,33 @@
+class ProtectableDropdown
+ def initialize(project, ref_type)
+ @project = project
+ @ref_type = ref_type
+ end
+
+ # Tags/branches which are yet to be individually protected
+ def protectable_ref_names
+ @protectable_ref_names ||= ref_names - non_wildcard_protected_ref_names
+ end
+
+ def hash
+ protectable_ref_names.map { |ref_name| { text: ref_name, id: ref_name, title: ref_name } }
+ end
+
+ private
+
+ def refs
+ @project.repository.public_send(@ref_type)
+ end
+
+ def ref_names
+ refs.map(&:name)
+ end
+
+ def protections
+ @project.public_send("protected_#{@ref_type}")
+ end
+
+ def non_wildcard_protected_ref_names
+ protections.reject(&:wildcard?).map(&:name)
+ end
+end
diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb
index 39e979ef15b..28b7d5ad072 100644
--- a/app/models/protected_branch.rb
+++ b/app/models/protected_branch.rb
@@ -1,9 +1,6 @@
class ProtectedBranch < ActiveRecord::Base
include Gitlab::ShellAdapter
-
- belongs_to :project
- validates :name, presence: true
- validates :project, presence: true
+ include ProtectedRef
has_many :merge_access_levels, dependent: :destroy
has_many :push_access_levels, dependent: :destroy
@@ -14,54 +11,15 @@ class ProtectedBranch < ActiveRecord::Base
accepts_nested_attributes_for :push_access_levels
accepts_nested_attributes_for :merge_access_levels
- def commit
- project.commit(self.name)
- end
-
- # Returns all protected branches that match the given branch name.
- # This realizes all records from the scope built up so far, and does
- # _not_ return a relation.
- #
- # This method optionally takes in a list of `protected_branches` to search
- # through, to avoid calling out to the database.
- def self.matching(branch_name, protected_branches: nil)
- (protected_branches || all).select { |protected_branch| protected_branch.matches?(branch_name) }
- end
-
- # Returns all branches (among the given list of branches [`Gitlab::Git::Branch`])
- # that match the current protected branch.
- def matching(branches)
- branches.select { |branch| self.matches?(branch.name) }
- end
-
- # Checks if the protected branch matches the given branch name.
- def matches?(branch_name)
- return false if self.name.blank?
-
- exact_match?(branch_name) || wildcard_match?(branch_name)
- end
-
- # Checks if this protected branch contains a wildcard
- def wildcard?
- self.name && self.name.include?('*')
- end
-
- protected
-
- def exact_match?(branch_name)
- self.name == branch_name
- end
+ # Check if branch name is marked as protected in the system
+ def self.protected?(project, ref_name)
+ return true if project.empty_repo? && default_branch_protected?
- def wildcard_match?(branch_name)
- wildcard_regex === branch_name
+ self.matching(ref_name, protected_refs: project.protected_branches).present?
end
- def wildcard_regex
- @wildcard_regex ||= begin
- name = self.name.gsub('*', 'STAR_DONT_ESCAPE')
- quoted_name = Regexp.quote(name)
- regex_string = quoted_name.gsub('STAR_DONT_ESCAPE', '.*?')
- /\A#{regex_string}\z/
- end
+ def self.default_branch_protected?
+ current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_FULL ||
+ current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE
end
end
diff --git a/app/models/protected_ref_matcher.rb b/app/models/protected_ref_matcher.rb
new file mode 100644
index 00000000000..d970f2b01fc
--- /dev/null
+++ b/app/models/protected_ref_matcher.rb
@@ -0,0 +1,54 @@
+class ProtectedRefMatcher
+ def initialize(protected_ref)
+ @protected_ref = protected_ref
+ end
+
+ # Returns all protected refs that match the given ref name.
+ # This checks all records from the scope built up so far, and does
+ # _not_ return a relation.
+ #
+ # This method optionally takes in a list of `protected_refs` to search
+ # through, to avoid calling out to the database.
+ def self.matching(type, ref_name, protected_refs: nil)
+ (protected_refs || type.all).select { |protected_ref| protected_ref.matches?(ref_name) }
+ end
+
+ # Returns all branches/tags (among the given list of refs [`Gitlab::Git::Branch`])
+ # that match the current protected ref.
+ def matching(refs)
+ refs.select { |ref| @protected_ref.matches?(ref.name) }
+ end
+
+ # Checks if the protected ref matches the given ref name.
+ def matches?(ref_name)
+ return false if @protected_ref.name.blank?
+
+ exact_match?(ref_name) || wildcard_match?(ref_name)
+ end
+
+ # Checks if this protected ref contains a wildcard
+ def wildcard?
+ @protected_ref.name && @protected_ref.name.include?('*')
+ end
+
+ protected
+
+ def exact_match?(ref_name)
+ @protected_ref.name == ref_name
+ end
+
+ def wildcard_match?(ref_name)
+ return false unless wildcard?
+
+ wildcard_regex === ref_name
+ end
+
+ def wildcard_regex
+ @wildcard_regex ||= begin
+ name = @protected_ref.name.gsub('*', 'STAR_DONT_ESCAPE')
+ quoted_name = Regexp.quote(name)
+ regex_string = quoted_name.gsub('STAR_DONT_ESCAPE', '.*?')
+ /\A#{regex_string}\z/
+ end
+ end
+end
diff --git a/app/models/protected_tag.rb b/app/models/protected_tag.rb
new file mode 100644
index 00000000000..83964095516
--- /dev/null
+++ b/app/models/protected_tag.rb
@@ -0,0 +1,14 @@
+class ProtectedTag < ActiveRecord::Base
+ include Gitlab::ShellAdapter
+ include ProtectedRef
+
+ has_many :create_access_levels, dependent: :destroy
+
+ validates :create_access_levels, length: { is: 1, message: "are restricted to a single instance per protected tag." }
+
+ accepts_nested_attributes_for :create_access_levels
+
+ def self.protected?(project, ref_name)
+ self.matching(ref_name, protected_refs: project.protected_tags).present?
+ end
+end
diff --git a/app/models/protected_tag/create_access_level.rb b/app/models/protected_tag/create_access_level.rb
new file mode 100644
index 00000000000..c7e1319719d
--- /dev/null
+++ b/app/models/protected_tag/create_access_level.rb
@@ -0,0 +1,21 @@
+class ProtectedTag::CreateAccessLevel < ActiveRecord::Base
+ include ProtectedTagAccess
+
+ validates :access_level, presence: true, inclusion: { in: [Gitlab::Access::MASTER,
+ Gitlab::Access::DEVELOPER,
+ Gitlab::Access::NO_ACCESS] }
+
+ def self.human_access_levels
+ {
+ Gitlab::Access::MASTER => "Masters",
+ Gitlab::Access::DEVELOPER => "Developers + Masters",
+ Gitlab::Access::NO_ACCESS => "No one"
+ }.with_indifferent_access
+ end
+
+ def check_access(user)
+ return false if access_level == Gitlab::Access::NO_ACCESS
+
+ super
+ end
+end