@@ -4,7 +4,7 @@ import { promises as fs } from 'node:fs';
4
4
import semver from 'semver' ;
5
5
import { replaceInFile } from 'replace-in-file' ;
6
6
7
- import { runAsync , runSync } from './run.js' ;
7
+ import { forceRunAsync , runAsync , runSync } from './run.js' ;
8
8
import { writeJson , readJson } from './file.js' ;
9
9
import Request from './request.js' ;
10
10
import auth from './auth.js' ;
@@ -171,7 +171,7 @@ export default class ReleasePreparation extends Session {
171
171
// Check the branch diff to determine if the releaser
172
172
// wants to backport any more commits before proceeding.
173
173
cli . startSpinner ( 'Fetching branch-diff' ) ;
174
- const raw = this . getBranchDiff ( {
174
+ const raw = await this . getBranchDiff ( {
175
175
onlyNotableChanges : false ,
176
176
comparisonBranch : newVersion
177
177
} ) ;
@@ -181,10 +181,9 @@ export default class ReleasePreparation extends Session {
181
181
182
182
const outstandingCommits = diff . length - 1 ;
183
183
if ( outstandingCommits !== 0 ) {
184
- const staging = `v${ semver . major ( newVersion ) } .x-staging` ;
185
184
const proceed = await cli . prompt (
186
185
`There are ${ outstandingCommits } commits that may be ` +
187
- `backported to ${ staging } - do you still want to proceed?` ,
186
+ `backported to ${ this . stagingBranch } - do you still want to proceed?` ,
188
187
{ defaultAnswer : false } ) ;
189
188
190
189
if ( ! proceed ) {
@@ -335,18 +334,8 @@ export default class ReleasePreparation extends Session {
335
334
return missing ;
336
335
}
337
336
338
- async calculateNewVersion ( major ) {
339
- const { cli } = this ;
340
-
341
- cli . startSpinner ( `Parsing CHANGELOG for most recent release of v${ major } .x` ) ;
342
- const data = await fs . readFile (
343
- path . resolve ( `doc/changelogs/CHANGELOG_V${ major } .md` ) ,
344
- 'utf8'
345
- ) ;
346
- const [ , , minor , patch ] = / < a h r e f = " # ( \d + ) \. ( \d + ) \. ( \d + ) " > \1\. \2\. \3< \/ a > < b r \/ > / . exec ( data ) ;
347
-
348
- cli . stopSpinner ( `Latest release on ${ major } .x line is ${ major } .${ minor } .${ patch } ` ) ;
349
- const changelog = this . getChangelog ( `v${ major } .${ minor } .${ patch } ` ) ;
337
+ async calculateNewVersion ( { tagName, major, minor, patch } ) {
338
+ const changelog = this . getChangelog ( tagName ) ;
350
339
351
340
const newVersion = { major, minor, patch } ;
352
341
if ( changelog . includes ( 'SEMVER-MAJOR' ) ) {
@@ -478,7 +467,7 @@ export default class ReleasePreparation extends Session {
478
467
const data = await fs . readFile ( majorChangelogPath , 'utf8' ) ;
479
468
const arr = data . split ( '\n' ) ;
480
469
const allCommits = this . getChangelog ( ) ;
481
- const notableChanges = this . getBranchDiff ( { onlyNotableChanges : true } ) ;
470
+ const notableChanges = await this . getBranchDiff ( { onlyNotableChanges : true } ) ;
482
471
let releaseHeader = `## ${ date } , Version ${ newVersion } ` +
483
472
` ${ releaseInfo } , @${ username } \n` ;
484
473
if ( isSecurityRelease ) {
@@ -532,14 +521,14 @@ export default class ReleasePreparation extends Session {
532
521
}
533
522
534
523
async createProposalBranch ( base = this . stagingBranch ) {
535
- const { upstream , newVersion } = this ;
524
+ const { newVersion } = this ;
536
525
const proposalBranch = `v${ newVersion } -proposal` ;
537
526
538
527
await runAsync ( 'git' , [
539
528
'checkout' ,
540
529
'-b' ,
541
530
proposalBranch ,
542
- ` ${ upstream } / ${ base } `
531
+ base
543
532
] ) ;
544
533
return proposalBranch ;
545
534
}
@@ -614,7 +603,7 @@ export default class ReleasePreparation extends Session {
614
603
messageBody . push ( 'This is a security release.\n\n' ) ;
615
604
}
616
605
617
- const notableChanges = this . getBranchDiff ( {
606
+ const notableChanges = await this . getBranchDiff ( {
618
607
onlyNotableChanges : true ,
619
608
format : 'plaintext'
620
609
} ) ;
@@ -641,8 +630,9 @@ export default class ReleasePreparation extends Session {
641
630
return useMessage ;
642
631
}
643
632
644
- getBranchDiff ( opts ) {
633
+ async getBranchDiff ( opts ) {
645
634
const {
635
+ cli,
646
636
versionComponents = { } ,
647
637
upstream,
648
638
newVersion,
@@ -670,6 +660,10 @@ export default class ReleasePreparation extends Session {
670
660
'semver-minor'
671
661
] ;
672
662
663
+ await forceRunAsync ( 'git' , [ 'remote' , 'set-branches' , '--add' , upstream , releaseBranch ] , {
664
+ ignoreFailures : false
665
+ } ) ;
666
+ await forceRunAsync ( 'git' , [ 'fetch' , upstream , releaseBranch ] , { ignoreFailures : false } ) ;
673
667
branchDiffOptions = [
674
668
`${ upstream } /${ releaseBranch } ` ,
675
669
proposalBranch ,
@@ -688,20 +682,43 @@ export default class ReleasePreparation extends Session {
688
682
'baking-for-lts'
689
683
] ;
690
684
691
- let comparisonBranch = 'main' ;
685
+ let comparisonBranch = this . config . branch || 'main' ;
692
686
const isSemverMinor = versionComponents . patch === 0 ;
693
687
if ( isLTS ) {
688
+ const res = await fetch ( 'https://nodejs.org/dist/index.json' ) ;
689
+ if ( ! res . ok ) throw new Error ( 'Failed to fetch' , { cause : res } ) ;
690
+ const [ latest ] = await res . json ( ) ;
694
691
// Assume Current branch matches tag with highest semver value.
695
- const tags = runSync ( 'git' ,
696
- [ 'tag' , '-l' , '--sort' , '-version:refname' ] ) . trim ( ) ;
697
- const highestVersionTag = tags . split ( '\n' ) [ 0 ] ;
698
- comparisonBranch = `v${ semver . coerce ( highestVersionTag ) . major } .x` ;
692
+ comparisonBranch = `v${ semver . coerce ( latest . version ) . major } .x` ;
699
693
700
694
if ( ! isSemverMinor ) {
701
695
excludeLabels . push ( 'semver-minor' ) ;
702
696
}
703
697
}
704
698
699
+ await forceRunAsync ( 'git' , [ 'fetch' , upstream , comparisonBranch ] , { ignoreFailures : false } ) ;
700
+ const commits = await forceRunAsync ( 'git' , [ 'rev-parse' , 'FETCH_HEAD' , comparisonBranch ] , {
701
+ captureStdout : 'lines' ,
702
+ ignoreFailures : true
703
+ } ) ;
704
+ if ( commits == null ) {
705
+ const shouldCreateCompareBranch = await cli . prompt (
706
+ `No local branch ${ comparisonBranch } , do you want to create it?` ) ;
707
+ if ( shouldCreateCompareBranch ) {
708
+ await forceRunAsync ( 'git' , [ 'branch' , comparisonBranch , 'FETCH_HEAD' ] , {
709
+ ignoreFailures : false
710
+ } ) ;
711
+ }
712
+ } else if ( commits [ 0 ] !== commits [ 1 ] ) {
713
+ const shouldUpBranch = cli . prompt ( `Local ${ comparisonBranch } branch is not in sync with ${
714
+ upstream } /${ comparisonBranch } , do you want to update it?`) ;
715
+ if ( shouldUpBranch ) {
716
+ await forceRunAsync ( 'git' , [ 'branch' , '-f' , comparisonBranch , 'FETCH_HEAD' ] , {
717
+ ignoreFailures : false
718
+ } ) ;
719
+ }
720
+ }
721
+
705
722
branchDiffOptions = [
706
723
stagingBranch ,
707
724
comparisonBranch ,
@@ -718,6 +735,27 @@ export default class ReleasePreparation extends Session {
718
735
return runSync ( branchDiff , branchDiffOptions ) ;
719
736
}
720
737
738
+ async getLastRelease ( major ) {
739
+ const { cli } = this ;
740
+
741
+ cli . startSpinner ( `Parsing CHANGELOG for most recent release of v${ major } .x` ) ;
742
+ const data = await fs . readFile (
743
+ path . resolve ( `doc/changelogs/CHANGELOG_V${ major } .md` ) ,
744
+ 'utf8'
745
+ ) ;
746
+ const [ , , minor , patch ] = / < a h r e f = " # ( \d + ) \. ( \d + ) \. ( \d + ) " > \1\. \2\. \3< \/ a > < b r \/ > / . exec ( data ) ;
747
+ this . isLTS = data . includes ( '<th>LTS ' ) ;
748
+
749
+ cli . stopSpinner ( `Latest release on ${ major } .x line is ${ major } .${ minor } .${ patch } ${
750
+ this . isLTS ? ' (LTS)' : ''
751
+ } `) ;
752
+
753
+ return {
754
+ tagName : await this . getLastRef ( `v${ major } .${ minor } .${ patch } ` ) ,
755
+ major, minor, patch
756
+ } ;
757
+ }
758
+
721
759
async prepareLocalBranch ( ) {
722
760
const { cli } = this ;
723
761
if ( this . newVersion ) {
@@ -736,6 +774,7 @@ export default class ReleasePreparation extends Session {
736
774
this . stagingBranch = `v${ newVersion . major } .x-staging` ;
737
775
this . releaseBranch = `v${ newVersion . major } .x` ;
738
776
await this . tryResetBranch ( ) ;
777
+ await this . getLastRelease ( newVersion . major ) ;
739
778
return ;
740
779
}
741
780
@@ -751,7 +790,7 @@ export default class ReleasePreparation extends Session {
751
790
}
752
791
this . stagingBranch = currentBranch ;
753
792
await this . tryResetBranch ( ) ;
754
- this . versionComponents = await this . calculateNewVersion ( match [ 1 ] ) ;
793
+ this . versionComponents = await this . calculateNewVersion ( await this . getLastRelease ( match [ 1 ] ) ) ;
755
794
const { major, minor, patch } = this . versionComponents ;
756
795
this . newVersion = `${ major } .${ minor } .${ patch } ` ;
757
796
this . releaseBranch = `v${ major } .x` ;
0 commit comments