Module: ChronoForge::Executor::Methods::MergeBranches

Included in:
ChronoForge::Executor::Methods
Defined in:
lib/chrono_forge/executor/methods/merge_branches.rb

Instance Method Summary collapse

Instance Method Details

#merge_branches(*names, min_interval: 5.seconds, max_interval: 5.minutes) ⇒ Object Also known as: merge_branch

Join one or more named branches. Separate from dispatch so branches run concurrently. Does one immediate check; if not done, hands off to the lightweight BranchMergeJob and halts (the heavy parent is not replayed per poll). Cadence clamps between min/max, scaled by pending.



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
# File 'lib/chrono_forge/executor/methods/merge_branches.rb', line 9

def merge_branches(*names, min_interval: 5.seconds, max_interval: 5.minutes)
  names.each do |nm|
    validate_step_name_segment!(nm)  # rejects "$"
    if nm.to_s.include?(",")
      raise InvalidStepName,
        "branch name may not contain ',' (reserved merge separator): #{nm.inspect}"
    end
  end

  # Validate cadence here, in the parent, so a misconfiguration fails at the
  # call site instead of deep inside the poller — where (pending * FACTOR)
  # .clamp(min, max) would raise ArgumentError, a non-transient error that
  # dead-letters BranchMergeJob and orphans the parent.
  if min_interval > max_interval
    raise ArgumentError,
      "min_interval (#{min_interval}) must be <= max_interval (#{max_interval})"
  end

  names = names.map(&:to_s).uniq
  step_name = "merge$#{names.sort.join(",")}"
  log = find_or_create_execution_log!(step_name) { |l| l.started_at = Time.current }

  if log.completed?
    # Already done — remove from registry so the completion gate does not
    # see these as unmerged, then skip.
    names.each { |nm| @open_branches&.delete(nm.to_s) }
    return
  end

  branch_log_ids = names.map { |nm| open_branch!(nm)[:log_id] }

  if branches_done?(branch_log_ids)
    names.each { |nm| @open_branches&.delete(nm.to_s) }
    log.update!(state: :completed, completed_at: Time.current)
    return
  end

  enqueue_branch_merge_job(branch_log_ids, min_interval, max_interval)
  halt_execution!
end