Welcome to mirror list, hosted at ThFree Co, Russian Federation.

protected_branch.rb « models « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: cab3530477877ab8a66c33df8b0498ee73e9521d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# frozen_string_literal: true

class ProtectedBranch < ApplicationRecord
  include ProtectedRef
  include Gitlab::SQL::Pattern
  include FromUnion

  belongs_to :group, foreign_key: :namespace_id, touch: true, inverse_of: :protected_branches

  validate :validate_either_project_or_top_group

  scope :requiring_code_owner_approval, -> { where(code_owner_approval_required: true) }
  scope :allowing_force_push, -> { where(allow_force_push: true) }
  scope :sorted_by_name, -> { order(name: :asc) }

  scope :for_group, ->(group) { where(group: group) }

  protected_ref_access_levels :merge, :push

  def self.get_ids_by_name(name)
    where(name: name).pluck(:id)
  end

  def self.protected_ref_accessible_to?(ref, user, project:, action:, protected_refs: nil)
    # Maintainers, owners and admins are allowed to create the default branch

    if project.empty_repo? && project.default_branch_protected?
      return true if user.admin? || project.team.max_member_access(user.id) > Gitlab::Access::DEVELOPER
    end

    super
  end

  # Check if branch name is marked as protected in the system
  def self.protected?(project, ref_name)
    return true if project.empty_repo? && project.default_branch_protected?
    return false if ref_name.blank?

    dry_run = Feature.disabled?(:rely_on_protected_branches_cache, project)

    new_cache_result = new_cache(project, ref_name, dry_run: dry_run)

    return new_cache_result unless new_cache_result.nil?

    deprecated_cache(project, ref_name)
  end

  def self.new_cache(project, ref_name, dry_run: true)
    ProtectedBranches::CacheService.new(project).fetch(ref_name, dry_run: dry_run) do # rubocop: disable CodeReuse/ServiceClass
      self.matching(ref_name, protected_refs: protected_refs(project)).present?
    end
  end

  # Deprecated: https://gitlab.com/gitlab-org/gitlab/-/issues/370608
  # ----------------------------------------------------------------
  CACHE_EXPIRE_IN = 1.hour

  def self.deprecated_cache(project, ref_name)
    Rails.cache.fetch(protected_ref_cache_key(project, ref_name), expires_in: CACHE_EXPIRE_IN) do
      self.matching(ref_name, protected_refs: protected_refs(project)).present?
    end
  end

  def self.protected_ref_cache_key(project, ref_name)
    "protected_ref-#{project.cache_key}-#{Digest::SHA1.hexdigest(ref_name)}"
  end
  # End of deprecation --------------------------------------------

  def self.allow_force_push?(project, ref_name)
    if Feature.enabled?(:group_protected_branches)
      protected_branches = project.all_protected_branches.matching(ref_name)

      project_protected_branches, group_protected_branches = protected_branches.partition(&:project_id)

      # Group owner can be able to enforce the settings
      return group_protected_branches.any?(&:allow_force_push) if group_protected_branches.present?
      return project_protected_branches.any?(&:allow_force_push) if project_protected_branches.present?

      false
    else
      project.protected_branches.allowing_force_push.matching(ref_name).any?
    end
  end

  def self.any_protected?(project, ref_names)
    protected_refs(project).any? do |protected_ref|
      ref_names.any? do |ref_name|
        protected_ref.matches?(ref_name)
      end
    end
  end

  def self.protected_refs(project)
    if Feature.enabled?(:group_protected_branches)
      project.all_protected_branches
    else
      project.protected_branches
    end
  end

  # overridden in EE
  def self.branch_requires_code_owner_approval?(project, branch_name)
    false
  end

  def self.by_name(query)
    return none if query.blank?

    where(fuzzy_arel_match(:name, query.downcase))
  end

  def allow_multiple?(type)
    type == :push
  end

  def self.downcase_humanized_name
    name.underscore.humanize.downcase
  end

  def default_branch?
    name == project.default_branch
  end

  def entity
    group || project
  end

  private

  def validate_either_project_or_top_group
    if !project && !group
      errors.add(:base, _('must be associated with a Group or a Project'))
    elsif project && group
      errors.add(:base, _('cannot be associated with both a Group and a Project'))
    elsif group && group.subgroup?
      errors.add(:base, _('cannot be associated with a subgroup'))
    end
  end
end

ProtectedBranch.prepend_mod_with('ProtectedBranch')