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 142 143 144 145 146 147 148
|
# frozen_string_literal: true
module Git
class ProcessRefChangesService < BaseService
PIPELINE_PROCESS_LIMIT = 4
def execute
changes = params[:changes]
process_changes_by_action(:branch, changes.branch_changes)
process_changes_by_action(:tag, changes.tag_changes)
warn_if_over_process_limit(changes.branch_changes + changes.tag_changes)
perform_housekeeping
end
private
def process_changes_by_action(ref_type, changes)
changes_by_action = group_changes_by_action(changes)
changes_by_action.each do |action, changes|
process_changes(ref_type, action, changes, execute_project_hooks: execute_project_hooks?(changes)) if changes.any?
end
end
def group_changes_by_action(changes)
changes.group_by do |change|
change_action(change)
end
end
def change_action(change)
return :created if Gitlab::Git.blank_ref?(change[:oldrev])
return :removed if Gitlab::Git.blank_ref?(change[:newrev])
:pushed
end
def execute_project_hooks?(changes)
changes.size <= Gitlab::CurrentSettings.push_event_hooks_limit
end
def process_changes(ref_type, action, changes, execute_project_hooks:)
push_service_class = push_service_class_for(ref_type)
create_bulk_push_event = changes.size > Gitlab::CurrentSettings.push_event_activities_limit
merge_request_branches = merge_request_branches_for(ref_type, changes)
changes.each do |change|
options = {
change: change,
push_options: params[:push_options],
merge_request_branches: merge_request_branches,
create_pipelines: under_process_limit?(change),
execute_project_hooks: execute_project_hooks,
create_push_event: !create_bulk_push_event
}
if ref_type == :branch && Feature.enabled?(:throttle_with_process_commit_worker_pool, project)
options[:process_commit_worker_pool] = process_commit_worker_pool
end
push_service_class.new(
project,
current_user,
**options
).execute
end
create_bulk_push_event(ref_type, action, changes) if create_bulk_push_event
end
def under_process_limit?(change)
change[:index] < process_limit || Feature.enabled?(:git_push_create_all_pipelines, project)
end
def process_limit
PIPELINE_PROCESS_LIMIT
end
def warn_if_over_process_limit(changes)
return unless process_limit > 0
return if changes.length <= process_limit
# We don't know for sure whether the project has CI enabled or CI rules
# that might excluded pipelines from being created.
omitted_refs = possible_omitted_pipeline_refs(changes)
return unless omitted_refs.present?
# This notification only lets the admin know that we might have skipped some refs.
Gitlab::AppJsonLogger.info(
message: "Some pipelines may not have been created because ref count exceeded limit",
ref_limit: process_limit,
total_ref_count: changes.length,
possible_omitted_refs: omitted_refs,
possible_omitted_ref_count: omitted_refs.length,
**Gitlab::ApplicationContext.current
)
end
def possible_omitted_pipeline_refs(changes)
# Pipelines can only be created on pushed for branch creation or updates
omitted_changes = changes.select do |change|
change[:index] >= process_limit &&
change_action(change) != :removed
end
# rubocop:disable CodeReuse/ActiveRecord -- not an ActiveRecord model
# rubocop:disable Database/AvoidUsingPluckWithoutLimit -- not an ActiveRecord model
omitted_changes.pluck(:ref).sort
# rubocop:enable CodeReuse/ActiveRecord
# rubocop:enable Database/AvoidUsingPluckWithoutLimit
end
def create_bulk_push_event(ref_type, action, changes)
EventCreateService.new.bulk_push(
project,
current_user,
Gitlab::DataBuilder::Push.build_bulk(action: action, ref_type: ref_type, changes: changes)
)
end
def push_service_class_for(ref_type)
return Git::TagPushService if ref_type == :tag
Git::BranchPushService
end
def merge_request_branches_for(ref_type, changes)
return [] if ref_type == :tag
MergeRequests::PushedBranchesService.new(project: project, current_user: current_user, params: { changes: changes }).execute
end
def perform_housekeeping
housekeeping = Repositories::HousekeepingService.new(project)
housekeeping.increment!
housekeeping.execute if housekeeping.needed?
rescue Repositories::HousekeepingService::LeaseTaken
end
def process_commit_worker_pool
@process_commit_worker_pool ||= Gitlab::Git::ProcessCommitWorkerPool.new
end
end
end
|