Class: Ace::Git::Molecules::PrMetadataFetcher
- Inherits:
-
Object
- Object
- Ace::Git::Molecules::PrMetadataFetcher
- Defined in:
- lib/ace/git/molecules/pr_metadata_fetcher.rb
Overview
Fetch PR metadata via gh CLI Consolidated from ace-bundle GhPrExecutor and ace-review GhPrFetcher
Constant Summary collapse
- PR_NOT_FOUND_PATTERN =
Error message patterns from gh CLI
/not found|Could not resolve/i- AUTH_ERROR_PATTERN =
/authentication|Unauthorized|not logged in|auth login/i- VALID_IDENTIFIER_PATTERN =
Valid characters for PR identifiers (owner/repo#number format) Allows: alphanumeric, hyphens, underscores, dots, forward slashes, hash, at, colon This prevents shell metacharacters from reaching command execution
/\A[\w\/.\-#@:]+\z/- PR_FIELDS =
Fields to fetch for PR metadata Extracted as constant for maintainability
%w[ number state isDraft title author headRefName baseRefName url isCrossRepository headRepositoryOwner ].freeze
Class Method Summary collapse
-
.fetch_all_prs(limit: 15, timeout: Ace::Git.network_timeout) ⇒ Hash
Fetch all recent PRs in a single call for optimal performance Returns open, merged, and closed PRs - caller filters by state locally.
-
.fetch_diff(identifier, timeout: Ace::Git.network_timeout) ⇒ Hash
Fetch PR diff content.
-
.fetch_metadata(identifier, timeout: Ace::Git.network_timeout) ⇒ Hash
Fetch PR metadata (state, draft status, title, etc.).
-
.fetch_open_prs(exclude_branch: nil, limit: Ace::Git.open_prs_limit, timeout: Ace::Git.network_timeout) ⇒ Hash
Fetch open PRs.
-
.fetch_pr(identifier, timeout: Ace::Git.network_timeout) ⇒ Hash
Fetch both diff and metadata.
-
.fetch_recently_merged(limit: Ace::Git.merged_prs_limit, timeout: Ace::Git.network_timeout) ⇒ Hash
Fetch recently merged PRs.
-
.find_pr_for_branch(timeout: Ace::Git.network_timeout) ⇒ String|nil
Find PR number for current branch.
-
.gh_authenticated? ⇒ Boolean
Check if gh CLI is authenticated.
-
.gh_installed? ⇒ Boolean
Check if gh CLI is installed.
Class Method Details
.fetch_all_prs(limit: 15, timeout: Ace::Git.network_timeout) ⇒ Hash
Fetch all recent PRs in a single call for optimal performance Returns open, merged, and closed PRs - caller filters by state locally
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/ace/git/molecules/pr_metadata_fetcher.rb', line 206 def fetch_all_prs(limit: 15, timeout: Ace::Git.network_timeout) result = execute_gh_command( ["gh", "pr", "list", "--state", "all", "--limit", limit.to_s, "--json", "number,title,state,mergedAt,author,headRefName,isDraft,baseRefName,url"], timeout: timeout ) if result[:success] prs = JSON.parse(result[:output]) {success: true, prs: prs} else {success: false, error: result[:error], prs: []} end rescue JSON::ParserError => e {success: false, error: "Failed to parse PR list: #{e.}", prs: []} rescue Errno::ENOENT {success: false, error: "GitHub CLI (gh) not installed", prs: []} end |
.fetch_diff(identifier, timeout: Ace::Git.network_timeout) ⇒ Hash
Fetch PR diff content
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/ace/git/molecules/pr_metadata_fetcher.rb', line 54 def fetch_diff(identifier, timeout: Ace::Git.network_timeout) parsed = Atoms::PrIdentifierParser.parse(identifier) raise ArgumentError, "Invalid PR identifier: #{identifier}" if parsed.nil? # Validate identifier characters before command execution (defense in depth) validate_identifier_characters(parsed.gh_format) result = execute_gh_command(["gh", "pr", "diff", parsed.gh_format], timeout: timeout) if result[:success] { success: true, diff: result[:output], identifier: parsed.gh_format, source: build_source_label(parsed) } else handle_error(result[:error], parsed.gh_format) end rescue Errno::ENOENT raise Ace::Git::GhNotInstalledError, "GitHub CLI (gh) not installed. Install with: brew install gh" end |
.fetch_metadata(identifier, timeout: Ace::Git.network_timeout) ⇒ Hash
Fetch PR metadata (state, draft status, title, etc.)
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 |
# File 'lib/ace/git/molecules/pr_metadata_fetcher.rb', line 81 def (identifier, timeout: Ace::Git.network_timeout) parsed = Atoms::PrIdentifierParser.parse(identifier) raise ArgumentError, "Invalid PR identifier: #{identifier}" if parsed.nil? # Validate identifier characters before command execution (defense in depth) validate_identifier_characters(parsed.gh_format) result = execute_gh_command( ["gh", "pr", "view", parsed.gh_format, "--json", PR_FIELDS.join(",")], timeout: timeout ) if result[:success] = JSON.parse(result[:output]) { success: true, metadata: , identifier: parsed.gh_format, parsed: {number: parsed.number, repo: parsed.repo} } else handle_error(result[:error], parsed.gh_format) end rescue JSON::ParserError => e { success: false, error: "Failed to parse PR metadata: #{e.}" } rescue Errno::ENOENT raise Ace::Git::GhNotInstalledError, "GitHub CLI (gh) not installed. Install with: brew install gh" end |
.fetch_open_prs(exclude_branch: nil, limit: Ace::Git.open_prs_limit, timeout: Ace::Git.network_timeout) ⇒ Hash
Fetch open PRs
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/ace/git/molecules/pr_metadata_fetcher.rb', line 178 def fetch_open_prs(exclude_branch: nil, limit: Ace::Git.open_prs_limit, timeout: Ace::Git.network_timeout) result = execute_gh_command( ["gh", "pr", "list", "--state", "open", "--limit", limit.to_s, "--json", "number,title,author,headRefName"], timeout: timeout ) if result[:success] prs = JSON.parse(result[:output]) # Filter out current branch if specified if exclude_branch prs = prs.reject { |pr| pr["headRefName"] == exclude_branch } end {success: true, prs: prs} else {success: false, error: result[:error], prs: []} end rescue JSON::ParserError => e {success: false, error: "Failed to parse open PRs: #{e.}", prs: []} rescue Errno::ENOENT {success: false, error: "GitHub CLI (gh) not installed", prs: []} end |
.fetch_pr(identifier, timeout: Ace::Git.network_timeout) ⇒ Hash
Fetch both diff and metadata
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/ace/git/molecules/pr_metadata_fetcher.rb', line 117 def fetch_pr(identifier, timeout: Ace::Git.network_timeout) diff_result = fetch_diff(identifier, timeout: timeout) return diff_result unless diff_result[:success] = (identifier, timeout: timeout) return unless [:success] { success: true, diff: diff_result[:diff], metadata: [:metadata], identifier: diff_result[:identifier], source: diff_result[:source] } end |
.fetch_recently_merged(limit: Ace::Git.merged_prs_limit, timeout: Ace::Git.network_timeout) ⇒ Hash
Fetch recently merged PRs
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
# File 'lib/ace/git/molecules/pr_metadata_fetcher.rb', line 154 def fetch_recently_merged(limit: Ace::Git.merged_prs_limit, timeout: Ace::Git.network_timeout) result = execute_gh_command( ["gh", "pr", "list", "--state", "merged", "--limit", limit.to_s, "--json", "number,title,mergedAt,author"], timeout: timeout ) if result[:success] prs = JSON.parse(result[:output]) {success: true, prs: prs} else {success: false, error: result[:error], prs: []} end rescue JSON::ParserError => e {success: false, error: "Failed to parse merged PRs: #{e.}", prs: []} rescue Errno::ENOENT {success: false, error: "GitHub CLI (gh) not installed", prs: []} end |
.find_pr_for_branch(timeout: Ace::Git.network_timeout) ⇒ String|nil
Find PR number for current branch
136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/ace/git/molecules/pr_metadata_fetcher.rb', line 136 def find_pr_for_branch(timeout: Ace::Git.network_timeout) result = execute_gh_command( ["gh", "pr", "view", "--json", "number"], timeout: timeout ) return nil unless result[:success] data = JSON.parse(result[:output]) data["number"]&.to_s rescue JSON::ParserError, Errno::ENOENT nil end |
.gh_authenticated? ⇒ Boolean
Check if gh CLI is authenticated
45 46 47 48 |
# File 'lib/ace/git/molecules/pr_metadata_fetcher.rb', line 45 def gh_authenticated? result = Atoms::CommandExecutor.execute("gh", "auth", "status") result[:success] end |
.gh_installed? ⇒ Boolean
Check if gh CLI is installed
38 39 40 41 |
# File 'lib/ace/git/molecules/pr_metadata_fetcher.rb', line 38 def gh_installed? result = Atoms::CommandExecutor.execute("gh", "--version") result[:success] end |