Class: Aidp::Watch::AutoMerger
- Inherits:
-
Object
- Object
- Aidp::Watch::AutoMerger
- Includes:
- MessageDisplay
- Defined in:
- lib/aidp/watch/auto_merger.rb
Overview
Automatically merges sub-issue PRs when CI passes and conditions are met. Never auto-merges parent PRs - those require human review.
Constant Summary collapse
- PARENT_PR_LABEL =
Labels that indicate PR type
"aidp-parent-pr"- SUB_PR_LABEL =
"aidp-sub-pr"- DEFAULT_CONFIG =
Default configuration
{ enabled: true, sub_issue_prs_only: true, require_ci_success: true, require_reviews: 0, merge_method: "squash", delete_branch: true }.freeze
Constants included from MessageDisplay
MessageDisplay::COLOR_MAP, MessageDisplay::CRITICAL_TYPES
Instance Attribute Summary collapse
-
#repository_client ⇒ Object
readonly
Returns the value of attribute repository_client.
-
#state_store ⇒ Object
readonly
Returns the value of attribute state_store.
Instance Method Summary collapse
-
#can_auto_merge?(pr_number) ⇒ Hash
Check if a PR can be auto-merged.
-
#initialize(repository_client:, state_store:, config: {}) ⇒ AutoMerger
constructor
A new instance of AutoMerger.
-
#list_sub_pr_candidates ⇒ Array<Hash>
List all PRs with the sub-PR label that are candidates for auto-merge.
-
#merge_pr(pr_number) ⇒ Hash
Attempt to merge a PR.
-
#process_auto_merge_candidates(prs) ⇒ Hash
Process all eligible PRs for auto-merge.
Methods included from MessageDisplay
#display_message, included, #message_display_prompt, #quiet_mode?
Constructor Details
#initialize(repository_client:, state_store:, config: {}) ⇒ AutoMerger
Returns a new instance of AutoMerger.
28 29 30 31 32 |
# File 'lib/aidp/watch/auto_merger.rb', line 28 def initialize(repository_client:, state_store:, config: {}) @repository_client = repository_client @state_store = state_store @config = DEFAULT_CONFIG.merge(config) end |
Instance Attribute Details
#repository_client ⇒ Object (readonly)
Returns the value of attribute repository_client.
26 27 28 |
# File 'lib/aidp/watch/auto_merger.rb', line 26 def repository_client @repository_client end |
#state_store ⇒ Object (readonly)
Returns the value of attribute state_store.
26 27 28 |
# File 'lib/aidp/watch/auto_merger.rb', line 26 def state_store @state_store end |
Instance Method Details
#can_auto_merge?(pr_number) ⇒ Hash
Check if a PR can be auto-merged
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 |
# File 'lib/aidp/watch/auto_merger.rb', line 37 def can_auto_merge?(pr_number) Aidp.log_debug("auto_merger", "checking_can_auto_merge", pr_number: pr_number) return {can_merge: false, reason: "Auto-merge is disabled"} unless @config[:enabled] # Fetch PR details pr = begin @repository_client.fetch_pull_request(pr_number) rescue => e Aidp.log_error("auto_merger", "Failed to fetch PR", pr_number: pr_number, error: e.) return {can_merge: false, reason: "Failed to fetch PR: #{e.}"} end # Check if it's a parent PR (never auto-merge) if pr[:labels].include?(PARENT_PR_LABEL) Aidp.log_debug("auto_merger", "skipping_parent_pr", pr_number: pr_number) return {can_merge: false, reason: "Parent PRs require human review"} end # Check if sub-PRs only mode requires the sub-PR label if @config[:sub_issue_prs_only] && !pr[:labels].include?(SUB_PR_LABEL) Aidp.log_debug("auto_merger", "not_a_sub_pr", pr_number: pr_number) return {can_merge: false, reason: "Not a sub-issue PR (missing #{SUB_PR_LABEL} label)"} end # Check PR state unless pr[:state] == "open" || pr[:state] == "OPEN" return {can_merge: false, reason: "PR is not open (state: #{pr[:state]})"} end # Check mergeability if pr[:mergeable] == false return {can_merge: false, reason: "PR has merge conflicts"} end # Check CI status if @config[:require_ci_success] ci_status = @repository_client.fetch_ci_status(pr_number) unless ci_status[:state] == "success" Aidp.log_debug("auto_merger", "ci_not_passed", pr_number: pr_number, ci_state: ci_status[:state]) return {can_merge: false, reason: "CI has not passed (status: #{ci_status[:state]})"} end end # All checks passed Aidp.log_debug("auto_merger", "can_auto_merge", pr_number: pr_number) {can_merge: true, reason: "All merge conditions met"} end |
#list_sub_pr_candidates ⇒ Array<Hash>
List all PRs with the sub-PR label that are candidates for auto-merge
162 163 164 165 166 167 |
# File 'lib/aidp/watch/auto_merger.rb', line 162 def list_sub_pr_candidates @repository_client.list_pull_requests(labels: [SUB_PR_LABEL], state: "open") rescue => e Aidp.log_error("auto_merger", "Failed to list sub-PR candidates", error: e.) [] end |
#merge_pr(pr_number) ⇒ Hash
Attempt to merge a PR
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 |
# File 'lib/aidp/watch/auto_merger.rb', line 90 def merge_pr(pr_number) Aidp.log_debug("auto_merger", "attempting_merge", pr_number: pr_number) # Verify can merge eligibility = can_auto_merge?(pr_number) unless eligibility[:can_merge] return {success: false, reason: eligibility[:reason]} end begin result = @repository_client.merge_pull_request( pr_number, merge_method: @config[:merge_method] ) Aidp.log_info("auto_merger", "pr_merged", pr_number: pr_number, merge_method: @config[:merge_method]) ("✅ Auto-merged PR ##{pr_number}", type: :success) # Post comment about auto-merge post_merge_comment(pr_number) # Update parent issue/PR if this was a sub-issue PR update_parent_after_merge(pr_number) {success: true, reason: "Successfully merged", result: result} rescue => e Aidp.log_error("auto_merger", "merge_failed", pr_number: pr_number, error: e.) ("❌ Failed to auto-merge PR ##{pr_number}: #{e.}", type: :error) {success: false, reason: "Merge failed: #{e.}"} end end |
#process_auto_merge_candidates(prs) ⇒ Hash
Process all eligible PRs for auto-merge
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/aidp/watch/auto_merger.rb', line 127 def process_auto_merge_candidates(prs) Aidp.log_debug("auto_merger", "processing_candidates", count: prs.size) merged = 0 skipped = 0 failed = 0 prs.each do |pr| pr_number = pr[:number] eligibility = can_auto_merge?(pr_number) unless eligibility[:can_merge] Aidp.log_debug("auto_merger", "skipping_pr", pr_number: pr_number, reason: eligibility[:reason]) skipped += 1 next end result = merge_pr(pr_number) if result[:success] merged += 1 else failed += 1 end end summary = {merged: merged, skipped: skipped, failed: failed} Aidp.log_info("auto_merger", "processing_complete", **summary) ("🔀 Auto-merge: #{merged} merged, #{skipped} skipped, #{failed} failed", type: :info) summary end |