33
44/**
55 * Checkout PR branch when PR context is available
6- * This script handles both pull_request events and comment events on PRs
6+ *
7+ * This script handles checkout for different GitHub event types:
8+ *
9+ * 1. pull_request: Runs in merge commit context (PR head + base merged)
10+ * - Can use direct git commands since we're already in PR context
11+ * - Branch exists in current checkout
12+ *
13+ * 2. pull_request_target: Runs in BASE repository context (not PR head)
14+ * - CRITICAL: For fork PRs, the head branch doesn't exist in base repo
15+ * - Must use `gh pr checkout` to fetch from the fork
16+ * - Has write permissions (be cautious with untrusted code)
17+ *
18+ * 3. Other PR events (issue_comment, pull_request_review, etc.):
19+ * - Also run in base repository context
20+ * - Must use `gh pr checkout` to get PR branch
721 */
822
923const { getErrorMessage } = require ( "./error_helpers.cjs" ) ;
1024const { renderTemplate } = require ( "./messages_core.cjs" ) ;
25+ const { detectForkPR } = require ( "./pr_helpers.cjs" ) ;
1126const fs = require ( "fs" ) ;
1227
28+ /**
29+ * Log detailed PR context information for debugging
30+ */
31+ function logPRContext ( eventName , pullRequest ) {
32+ core . startGroup ( "📋 PR Context Details" ) ;
33+
34+ core . info ( `Event type: ${ eventName } ` ) ;
35+ core . info ( `PR number: ${ pullRequest . number } ` ) ;
36+ core . info ( `PR state: ${ pullRequest . state || "unknown" } ` ) ;
37+
38+ // Log head information
39+ if ( pullRequest . head ) {
40+ core . info ( `Head ref: ${ pullRequest . head . ref || "unknown" } ` ) ;
41+ core . info ( `Head SHA: ${ pullRequest . head . sha || "unknown" } ` ) ;
42+
43+ if ( pullRequest . head . repo ) {
44+ core . info ( `Head repo: ${ pullRequest . head . repo . full_name || "unknown" } ` ) ;
45+ core . info ( `Head repo owner: ${ pullRequest . head . repo . owner ?. login || "unknown" } ` ) ;
46+ } else {
47+ core . warning ( "⚠️ Head repo information not available (repo may be deleted)" ) ;
48+ }
49+ }
50+
51+ // Log base information
52+ if ( pullRequest . base ) {
53+ core . info ( `Base ref: ${ pullRequest . base . ref || "unknown" } ` ) ;
54+ core . info ( `Base SHA: ${ pullRequest . base . sha || "unknown" } ` ) ;
55+
56+ if ( pullRequest . base . repo ) {
57+ core . info ( `Base repo: ${ pullRequest . base . repo . full_name || "unknown" } ` ) ;
58+ core . info ( `Base repo owner: ${ pullRequest . base . repo . owner ?. login || "unknown" } ` ) ;
59+ }
60+ }
61+
62+ // Determine if this is a fork PR using the helper function
63+ const { isFork, reason : forkReason } = detectForkPR ( pullRequest ) ;
64+ core . info ( `Is fork PR: ${ isFork } (${ forkReason } )` ) ;
65+
66+ // Log current repository context
67+ core . info ( `Current repository: ${ context . repo . owner } /${ context . repo . repo } ` ) ;
68+ core . info ( `GitHub SHA: ${ context . sha } ` ) ;
69+
70+ core . endGroup ( ) ;
71+
72+ return { isFork } ;
73+ }
74+
75+ /**
76+ * Log the checkout strategy being used
77+ */
78+ function logCheckoutStrategy ( eventName , strategy , reason ) {
79+ core . startGroup ( "🔄 Checkout Strategy" ) ;
80+ core . info ( `Event type: ${ eventName } ` ) ;
81+ core . info ( `Strategy: ${ strategy } ` ) ;
82+ core . info ( `Reason: ${ reason } ` ) ;
83+ core . endGroup ( ) ;
84+ }
85+
1386async function main ( ) {
1487 const eventName = context . eventName ;
1588 const pullRequest = context . payload . pull_request ;
@@ -24,30 +97,86 @@ async function main() {
2497 core . info ( `Pull Request #${ pullRequest . number } ` ) ;
2598
2699 try {
100+ // Log detailed context for debugging
101+ const { isFork } = logPRContext ( eventName , pullRequest ) ;
102+
27103 if ( eventName === "pull_request" ) {
28- // For pull_request events, use the head ref directly
104+ // For pull_request events, we run in the merge commit context
105+ // The PR branch is already available, so we can use direct git commands
29106 const branchName = pullRequest . head . ref ;
30- core . info ( `Checking out PR branch: ${ branchName } ` ) ;
31107
108+ logCheckoutStrategy ( eventName , "git fetch + checkout" , "pull_request event runs in merge commit context with PR branch available" ) ;
109+
110+ core . info ( `Fetching branch: ${ branchName } from origin` ) ;
32111 await exec . exec ( "git" , [ "fetch" , "origin" , branchName ] ) ;
112+
113+ core . info ( `Checking out branch: ${ branchName } ` ) ;
33114 await exec . exec ( "git" , [ "checkout" , branchName ] ) ;
34115
35116 core . info ( `✅ Successfully checked out branch: ${ branchName } ` ) ;
36117 } else {
37- // For comment events on PRs, use gh pr checkout with PR number
118+ // For pull_request_target and other PR events, we run in base repository context
119+ // IMPORTANT: For fork PRs, the head branch doesn't exist in the base repo
120+ // We must use `gh pr checkout` which handles fetching from forks
38121 const prNumber = pullRequest . number ;
39- core . info ( `Checking out PR #${ prNumber } using gh pr checkout` ) ;
40122
123+ const strategyReason = eventName === "pull_request_target" ? "pull_request_target runs in base repo context; for fork PRs, head branch doesn't exist in origin" : `${ eventName } event runs in base repo context; must fetch PR branch` ;
124+
125+ logCheckoutStrategy ( eventName , "gh pr checkout" , strategyReason ) ;
126+
127+ if ( isFork ) {
128+ core . warning ( "⚠️ Fork PR detected - gh pr checkout will fetch from fork repository" ) ;
129+ }
130+
131+ core . info ( `Checking out PR #${ prNumber } using gh CLI` ) ;
41132 await exec . exec ( "gh" , [ "pr" , "checkout" , prNumber . toString ( ) ] ) ;
42133
134+ // Log the resulting branch after checkout
135+ let currentBranch = "" ;
136+ await exec . exec ( "git" , [ "branch" , "--show-current" ] , {
137+ listeners : {
138+ stdout : data => {
139+ currentBranch += data . toString ( ) ;
140+ } ,
141+ } ,
142+ } ) ;
143+ currentBranch = currentBranch . trim ( ) ;
144+
43145 core . info ( `✅ Successfully checked out PR #${ prNumber } ` ) ;
146+ core . info ( `Current branch: ${ currentBranch || "detached HEAD" } ` ) ;
44147 }
45148
46149 // Set output to indicate successful checkout
47150 core . setOutput ( "checkout_pr_success" , "true" ) ;
48151 } catch ( error ) {
49152 const errorMsg = getErrorMessage ( error ) ;
50153
154+ // Log detailed error context
155+ core . startGroup ( "❌ Checkout Error Details" ) ;
156+ core . error ( `Event type: ${ eventName } ` ) ;
157+ core . error ( `PR number: ${ pullRequest . number } ` ) ;
158+ core . error ( `Error message: ${ errorMsg } ` ) ;
159+
160+ if ( pullRequest . head ?. ref ) {
161+ core . error ( `Attempted to check out: ${ pullRequest . head . ref } ` ) ;
162+ }
163+
164+ // Log current git state for debugging
165+ try {
166+ core . info ( "Current git status:" ) ;
167+ await exec . exec ( "git" , [ "status" ] ) ;
168+
169+ core . info ( "Available remotes:" ) ;
170+ await exec . exec ( "git" , [ "remote" , "-v" ] ) ;
171+
172+ core . info ( "Current branch:" ) ;
173+ await exec . exec ( "git" , [ "branch" , "--show-current" ] ) ;
174+ } catch ( gitError ) {
175+ core . warning ( `Could not retrieve git state: ${ getErrorMessage ( gitError ) } ` ) ;
176+ }
177+
178+ core . endGroup ( ) ;
179+
51180 // Set output to indicate checkout failure
52181 core . setOutput ( "checkout_pr_success" , "false" ) ;
53182
0 commit comments