22
33interface PR {
44 number : number
5- headRefName : string
6- headRefOid : string
7- createdAt : string
8- isDraft : boolean
95 title : string
106}
117
8+ interface RunResult {
9+ exitCode : number
10+ stdout : string
11+ stderr : string
12+ }
13+
1214async function main ( ) {
1315 console . log ( "Fetching open contributor PRs..." )
1416
15- const prsResult =
16- await $ `gh pr list --label contributor --state open --json number,headRefName,headRefOid,createdAt,isDraft,title --limit 100` . nothrow ( )
17+ const prsResult = await $ `gh pr list --label contributor --state open --json number,title --limit 100` . nothrow ( )
1718 if ( prsResult . exitCode !== 0 ) {
1819 throw new Error ( `Failed to fetch PRs: ${ prsResult . stderr } ` )
1920 }
2021
21- const allPRs : PR [ ] = JSON . parse ( prsResult . stdout )
22- const prs = allPRs . filter ( ( pr ) => ! pr . isDraft )
23-
24- console . log ( `Found ${ prs . length } open non-draft contributor PRs` )
22+ const prs : PR [ ] = JSON . parse ( prsResult . stdout )
23+ console . log ( `Found ${ prs . length } open contributor PRs` )
2524
2625 console . log ( "Fetching latest dev branch..." )
2726 const fetchDev = await $ `git fetch origin dev` . nothrow ( )
@@ -41,71 +40,52 @@ async function main() {
4140 for ( const pr of prs ) {
4241 console . log ( `\nProcessing PR #${ pr . number } : ${ pr . title } ` )
4342
44- // Fetch the PR
45- const fetchPR = await $ ` git fetch origin pull/${ pr . number } /head:pr- ${ pr . number } `. nothrow ( )
46- if ( fetchPR . exitCode !== 0 ) {
47- console . log ( ` Failed to fetch PR # ${ pr . number } , skipping ` )
48- skipped . push ( { number : pr . number , reason : "Failed to fetch" } )
43+ console . log ( " Fetching PR head..." )
44+ const fetch = await run ( [ " git" , " fetch" , " origin" , ` pull/${ pr . number } /head:pr/ ${ pr . number } `] )
45+ if ( fetch . exitCode !== 0 ) {
46+ console . log ( ` Failed to fetch PR head: ${ fetch . stderr } ` )
47+ skipped . push ( { number : pr . number , reason : `Fetch failed: ${ fetch . stderr } ` } )
4948 continue
5049 }
5150
52- // Try to rebase onto current beta branch
53- console . log ( ` Attempting to rebase PR # ${ pr . number } ...` )
54- const rebase = await $ `git rebase beta pr- ${ pr . number } ` . nothrow ( )
55- if ( rebase . exitCode !== 0 ) {
56- console . log ( ` Rebase failed for PR # ${ pr . number } (has conflicts)` )
57- await $ `git rebase --abort ` . nothrow ( )
58- await $ `git checkout beta ` . nothrow ( )
59- skipped . push ( { number : pr . number , reason : "Rebase failed ( conflicts) " } )
51+ console . log ( " Merging..." )
52+ const merge = await run ( [ "git" , "merge" , "--no-commit" , "--no-ff" , `pr/ ${ pr . number } ` ] )
53+ if ( merge . exitCode !== 0 ) {
54+ console . log ( " Failed to merge (conflicts)" )
55+ await $ `git merge --abort` . nothrow ( )
56+ await $ `git checkout -- . ` . nothrow ( )
57+ await $ `git clean -fd ` . nothrow ( )
58+ skipped . push ( { number : pr . number , reason : "Has conflicts" } )
6059 continue
6160 }
6261
63- // Move rebased commits to pr-${pr.number} branch and checkout back to beta
64- await $ `git checkout -B pr-${ pr . number } ` . nothrow ( )
65- await $ `git checkout beta` . nothrow ( )
66-
67- console . log ( ` Successfully rebased PR #${ pr . number } ` )
68-
69- // Now squash merge the rebased PR
70- const merge = await $ `git merge --squash pr-${ pr . number } ` . nothrow ( )
71- if ( merge . exitCode !== 0 ) {
72- console . log ( ` Squash merge failed for PR #${ pr . number } ` )
73- console . log ( ` Error: ${ merge . stderr } ` )
74- await $ `git reset --hard HEAD` . nothrow ( )
75- skipped . push ( { number : pr . number , reason : `Squash merge failed: ${ merge . stderr } ` } )
62+ const mergeHead = await $ `git rev-parse -q --verify MERGE_HEAD` . nothrow ( )
63+ if ( mergeHead . exitCode !== 0 ) {
64+ console . log ( " No changes, skipping" )
65+ skipped . push ( { number : pr . number , reason : "No changes" } )
7666 continue
7767 }
7868
7969 const add = await $ `git add -A` . nothrow ( )
8070 if ( add . exitCode !== 0 ) {
81- console . log ( ` Failed to stage changes for PR #${ pr . number } ` )
82- await $ `git reset --hard HEAD` . nothrow ( )
71+ console . log ( " Failed to stage" )
72+ await $ `git checkout -- .` . nothrow ( )
73+ await $ `git clean -fd` . nothrow ( )
8374 skipped . push ( { number : pr . number , reason : "Failed to stage" } )
8475 continue
8576 }
8677
87- const status = await $ `git status --porcelain` . nothrow ( )
88- if ( status . exitCode !== 0 || ! status . stdout . trim ( ) ) {
89- console . log ( ` No changes to commit for PR #${ pr . number } , skipping` )
90- await $ `git reset --hard HEAD` . nothrow ( )
91- skipped . push ( { number : pr . number , reason : "No changes to commit" } )
92- continue
93- }
94-
9578 const commitMsg = `Apply PR #${ pr . number } : ${ pr . title } `
96- const commit = await Bun . spawn ( [ "git" , "commit" , "-m" , commitMsg ] , { stdout : "pipe" , stderr : "pipe" } )
97- const commitExit = await commit . exited
98- const commitStderr = await Bun . readableStreamToText ( commit . stderr )
99-
100- if ( commitExit !== 0 ) {
101- console . log ( ` Failed to commit PR #${ pr . number } ` )
102- console . log ( ` Error: ${ commitStderr } ` )
103- await $ `git reset --hard HEAD` . nothrow ( )
104- skipped . push ( { number : pr . number , reason : `Commit failed: ${ commitStderr } ` } )
79+ const commit = await run ( [ "git" , "commit" , "-m" , commitMsg ] )
80+ if ( commit . exitCode !== 0 ) {
81+ console . log ( ` Failed to commit: ${ commit . stderr } ` )
82+ await $ `git checkout -- .` . nothrow ( )
83+ await $ `git clean -fd` . nothrow ( )
84+ skipped . push ( { number : pr . number , reason : `Commit failed: ${ commit . stderr } ` } )
10585 continue
10686 }
10787
108- console . log ( ` Successfully applied PR # ${ pr . number } ` )
88+ console . log ( " Applied successfully" )
10989 applied . push ( pr . number )
11090 }
11191
@@ -116,7 +96,7 @@ async function main() {
11696 skipped . forEach ( ( x ) => console . log ( ` - PR #${ x . number } : ${ x . reason } ` ) )
11797
11898 console . log ( "\nForce pushing beta branch..." )
119- const push = await $ `git push origin beta --force` . nothrow ( )
99+ const push = await $ `git push origin beta --force --no-verify ` . nothrow ( )
120100 if ( push . exitCode !== 0 ) {
121101 throw new Error ( `Failed to push beta branch: ${ push . stderr } ` )
122102 }
@@ -129,6 +109,18 @@ main().catch((err) => {
129109 process . exit ( 1 )
130110} )
131111
112+ async function run ( args : string [ ] , stdin ?: Uint8Array ) : Promise < RunResult > {
113+ const proc = Bun . spawn ( args , {
114+ stdin : stdin ?? "inherit" ,
115+ stdout : "pipe" ,
116+ stderr : "pipe" ,
117+ } )
118+ const exitCode = await proc . exited
119+ const stdout = await new Response ( proc . stdout ) . text ( )
120+ const stderr = await new Response ( proc . stderr ) . text ( )
121+ return { exitCode, stdout, stderr }
122+ }
123+
132124function $ ( strings : TemplateStringsArray , ...values : unknown [ ] ) {
133125 const cmd = strings . reduce ( ( acc , str , i ) => acc + str + ( values [ i ] ?? "" ) , "" )
134126 return {
0 commit comments