1+ import { appendFileSync } from "node:fs" ;
12import { args , color , Exit , log , spinner } from "@r5n/cli-core" ;
23import { BaseCommand , type Ctx } from "../../base-command" ;
34import { Package , Stone } from "../../domain" ;
@@ -6,8 +7,6 @@ import { ChangelogGenerator, PackageUpdater, StoneManager, WorkspaceScanner } fr
67
78const RELEASE_BRANCH = "sisyphus/release" ;
89const RELEASE_LABEL = "sisyphus-release" ;
9- const RELEASE_LABEL_DESCRIPTION = "Sisyphus release PR" ;
10- const RELEASE_LABEL_COLOR = "6f42c1" ;
1110const PR_TITLE_PREFIX = "chore(release):" ;
1211
1312const releasePrArgs = args ( {
@@ -18,7 +17,7 @@ type ReleasePrCtx = Ctx<typeof releasePrArgs>;
1817
1918export class ActionsReleasePrCommand extends BaseCommand {
2019 name = "release-pr" ;
21- description = "Create or update a release PR from pending stones " ;
20+ description = "Prepare release branch for PR creation " ;
2221 args = releasePrArgs ;
2322
2423 private provider : GitProvider | null = null ;
@@ -37,6 +36,7 @@ export class ActionsReleasePrCommand extends BaseCommand {
3736
3837 if ( stones . length === 0 ) {
3938 log . info ( color . dim ( "No pending stones found, skipping release PR" ) ) ;
39+ this . setOutput ( "skipped" , "true" ) ;
4040 return ;
4141 }
4242
@@ -55,38 +55,26 @@ export class ActionsReleasePrCommand extends BaseCommand {
5555 log . info ( `${ color . dim ( "Packages:" ) } ${ updatedPackages . map ( ( p ) => p . name ) . join ( ", " ) } ` ) ;
5656
5757 if ( ctx . args . dryRun ) {
58- log . info ( color . yellow ( "\n[dry-run] Would create/update release PR " ) ) ;
58+ log . info ( color . yellow ( "\n[dry-run] Would prepare release branch " ) ) ;
5959 log . info ( color . dim ( "\nPR Body preview:" ) ) ;
6060 log . info ( prBody ) ;
6161 return ;
6262 }
6363
6464 const s = spinner ( ) ;
6565
66- const existingPr = await this . findExistingReleasePr ( ) ;
67-
6866 try {
69- if ( existingPr ) {
70- s . start ( "Updating release branch..." ) ;
71- await this . updateReleaseBranch ( ctx , stones , updatedPackages ) ;
72- s . stop ( "Release branch updated" ) ;
73-
74- s . start ( "Updating PR..." ) ;
75- await this . updatePr ( existingPr . number , prTitle , prBody ) ;
76- s . stop ( `PR #${ existingPr . number } updated` ) ;
77-
78- log . info ( `\n${ color . green ( "Release PR updated:" ) } ${ existingPr . url } ` ) ;
79- } else {
80- s . start ( "Creating release branch..." ) ;
81- await this . createReleaseBranch ( ctx , stones , updatedPackages ) ;
82- s . stop ( "Release branch created" ) ;
83-
84- s . start ( "Creating PR..." ) ;
85- const pr = await this . createPr ( prTitle , prBody ) ;
86- s . stop ( `PR #${ pr . number } created` ) ;
87-
88- log . info ( `\n${ color . green ( "Release PR created:" ) } ${ pr . url } ` ) ;
89- }
67+ s . start ( "Preparing release branch..." ) ;
68+ await this . prepareReleaseBranch ( ctx , stones , updatedPackages ) ;
69+ s . stop ( "Release branch ready" ) ;
70+
71+ this . setOutput ( "skipped" , "false" ) ;
72+ this . setOutput ( "branch" , RELEASE_BRANCH ) ;
73+ this . setOutput ( "title" , prTitle ) ;
74+ this . setOutput ( "label" , RELEASE_LABEL ) ;
75+ this . setOutputMultiline ( "body" , prBody ) ;
76+
77+ log . info ( `\n${ color . green ( "Release branch prepared:" ) } ${ RELEASE_BRANCH } ` ) ;
9078 } catch ( error ) {
9179 s . stop ( "Failed" ) ;
9280 await this . restoreMainBranch ( ) ;
@@ -134,13 +122,7 @@ export class ActionsReleasePrCommand extends BaseCommand {
134122 return lines . join ( "\n" ) ;
135123 }
136124
137- private async findExistingReleasePr ( ) : Promise < { number : number ; url : string } | null > {
138- const provider = await this . getProvider ( ) ;
139- const pr = await provider . findPr ( { head : RELEASE_BRANCH , label : RELEASE_LABEL } ) ;
140- return pr ? { number : pr . number , url : pr . url } : null ;
141- }
142-
143- private async createReleaseBranch ( ctx : ReleasePrCtx , stones : Stone [ ] , packages : Package [ ] ) {
125+ private async prepareReleaseBranch ( ctx : ReleasePrCtx , stones : Stone [ ] , packages : Package [ ] ) {
144126 const provider = await this . getProvider ( ) ;
145127 const baseBranch = await provider . getDefaultBranch ( ) ;
146128
@@ -155,28 +137,6 @@ export class ActionsReleasePrCommand extends BaseCommand {
155137 await Bun . $ `git checkout ${ baseBranch } ` . quiet ( ) ;
156138 }
157139
158- private async updateReleaseBranch ( ctx : ReleasePrCtx , stones : Stone [ ] , packages : Package [ ] ) {
159- const provider = await this . getProvider ( ) ;
160- const baseBranch = await provider . getDefaultBranch ( ) ;
161-
162- await Bun . $ `git fetch origin ${ baseBranch } ` . quiet ( ) ;
163- await Bun . $ `git checkout ${ RELEASE_BRANCH } ` . quiet ( ) ;
164- await Bun . $ `git reset --hard origin/${ baseBranch } ` . quiet ( ) ;
165-
166- await this . applyReleaseChanges ( ctx , stones , packages ) ;
167-
168- await Bun . $ `git add -A` . quiet ( ) ;
169-
170- const hasChanges = await Bun . $ `git diff --cached --quiet` . nothrow ( ) . quiet ( ) ;
171- if ( hasChanges . exitCode !== 0 ) {
172- await Bun . $ `git commit -m ${ `${ PR_TITLE_PREFIX } prepare release` } ` . quiet ( ) ;
173- }
174-
175- await Bun . $ `git push origin ${ RELEASE_BRANCH } --force` . quiet ( ) ;
176-
177- await Bun . $ `git checkout ${ baseBranch } ` . quiet ( ) ;
178- }
179-
180140 private async applyReleaseChanges ( ctx : ReleasePrCtx , stones : Stone [ ] , packages : Package [ ] ) {
181141 const changelogConfig = ctx . config . get ( "changelog" ) ;
182142 const generator = new ChangelogGenerator ( changelogConfig ) ;
@@ -201,36 +161,26 @@ export class ActionsReleasePrCommand extends BaseCommand {
201161 ctx . config . set ( "currentRelease" , { packages : packageVersions , stoneIds : stones . map ( ( s ) => s . id ) , timestamp } ) ;
202162 }
203163
204- private async createPr ( title : string , body : string ) : Promise < { number : number ; url : string } > {
205- const provider = await this . getProvider ( ) ;
206- const baseBranch = await provider . getDefaultBranch ( ) ;
207-
208- await provider . ensureLabelExists ( RELEASE_LABEL , {
209- color : RELEASE_LABEL_COLOR ,
210- description : RELEASE_LABEL_DESCRIPTION ,
211- } ) ;
212-
213- const pr = await provider . createPr ( {
214- base : baseBranch ,
215- body,
216- head : RELEASE_BRANCH ,
217- labels : [ RELEASE_LABEL ] ,
218- title,
219- } ) ;
220-
221- return { number : pr . number , url : pr . url } ;
222- }
223-
224- private async updatePr ( prNumber : number , title : string , body : string ) {
225- const provider = await this . getProvider ( ) ;
226- await provider . updatePr ( prNumber , { body, title } ) ;
227- }
228-
229164 private async restoreMainBranch ( ) {
230165 const provider = await this . getProvider ( ) ;
231166 const baseBranch = await provider . getDefaultBranch ( ) ;
232167 try {
233168 await Bun . $ `git checkout ${ baseBranch } ` . quiet ( ) ;
234169 } catch { }
235170 }
171+
172+ private setOutput ( name : string , value : string ) {
173+ const outputFile = process . env . GITHUB_OUTPUT ;
174+ if ( outputFile ) {
175+ appendFileSync ( outputFile , `${ name } =${ value } \n` ) ;
176+ }
177+ }
178+
179+ private setOutputMultiline ( name : string , value : string ) {
180+ const outputFile = process . env . GITHUB_OUTPUT ;
181+ if ( outputFile ) {
182+ const delimiter = `EOF_${ Date . now ( ) } ` ;
183+ appendFileSync ( outputFile , `${ name } <<${ delimiter } \n${ value } \n${ delimiter } \n` ) ;
184+ }
185+ }
236186}
0 commit comments