@@ -12,6 +12,11 @@ const {
1212 getUnmarkedDeprecations,
1313 updateDeprecations
1414} = require ( './deprecations' ) ;
15+ const {
16+ getEOLDate,
17+ getStartLTSBlurb,
18+ updateTestProcessRelease
19+ } = require ( './release/utils' ) ;
1520
1621const isWindows = process . platform === 'win32' ;
1722
@@ -21,6 +26,7 @@ class ReleasePreparation {
2126 this . dir = dir ;
2227 this . isSecurityRelease = argv . security ;
2328 this . isLTS = false ;
29+ this . isLTSTransition = argv . startLTS ;
2430 this . ltsCodename = '' ;
2531 this . date = '' ;
2632 this . config = getMergedConfig ( this . dir ) ;
@@ -91,6 +97,20 @@ class ReleasePreparation {
9197 await this . createProposalBranch ( ) ;
9298 cli . stopSpinner ( `Created new proposal branch for ${ newVersion } ` ) ;
9399
100+ if ( this . isLTSTransition ) {
101+ // For releases transitioning into LTS, fetch the new code name.
102+ this . ltsCodename = await this . getLTSCodename ( versionComponents . major ) ;
103+ // Update test for new LTS code name.
104+ const testFile = path . resolve (
105+ 'test' ,
106+ 'parallel' ,
107+ 'test-process-release.js'
108+ ) ;
109+ cli . startSpinner ( `Updating ${ testFile } ` ) ;
110+ await this . updateTestProcessRelease ( testFile ) ;
111+ cli . stopSpinner ( `Updating ${ testFile } ` ) ;
112+ }
113+
94114 // Update version and release info in src/node_version.h.
95115 cli . startSpinner ( `Updating 'src/node_version.h' for ${ newVersion } ` ) ;
96116 await this . updateNodeVersion ( ) ;
@@ -218,7 +238,7 @@ class ReleasePreparation {
218238
219239 if ( changelog . includes ( 'SEMVER-MAJOR' ) ) {
220240 newVersion = `${ lastTag . major + 1 } .0.0` ;
221- } else if ( changelog . includes ( 'SEMVER-MINOR' ) ) {
241+ } else if ( changelog . includes ( 'SEMVER-MINOR' ) || this . isLTSTransition ) {
222242 newVersion = `${ lastTag . major } .${ lastTag . minor + 1 } .0` ;
223243 } else {
224244 newVersion = `${ lastTag . major } .${ lastTag . minor } .${ lastTag . patch + 1 } ` ;
@@ -249,6 +269,15 @@ class ReleasePreparation {
249269 ] ) . trim ( ) ;
250270 }
251271
272+ async getLTSCodename ( version ) {
273+ const { cli } = this ;
274+ return await cli . prompt (
275+ 'Enter the LTS code name for this release line\n' +
276+ '(Refs: https://github.com/nodejs/Release/blob/master/CODENAMES.md):' ,
277+ { questionType : 'input' , noSeparator : true , defaultAnswer : '' }
278+ ) ;
279+ }
280+
252281 async updateREPLACEMEs ( ) {
253282 const { newVersion } = this ;
254283
@@ -260,7 +289,7 @@ class ReleasePreparation {
260289 }
261290
262291 async updateMainChangelog ( ) {
263- const { versionComponents, newVersion } = this ;
292+ const { date , isLTSTransition , versionComponents, newVersion } = this ;
264293
265294 // Remove the leading 'v'.
266295 const lastRef = this . getLastRef ( ) . substring ( 1 ) ;
@@ -274,6 +303,20 @@ class ReleasePreparation {
274303 const lastRefLink = `<a href="${ hrefLink } #${ lastRef } ">${ lastRef } </a>` ;
275304
276305 for ( let idx = 0 ; idx < arr . length ; idx ++ ) {
306+ if ( isLTSTransition ) {
307+ if ( arr [ idx ] . includes ( hrefLink ) ) {
308+ const eolDate = getEOLDate ( date ) ;
309+ const eol = eolDate . toISOString ( ) . split ( '-' ) . slice ( 0 , 2 ) . join ( '-' ) ;
310+ arr [ idx ] = arr [ idx ] . replace ( '**Current**' , '**Long Term Support**' ) ;
311+ arr [ idx ] = arr [ idx ] . replace ( '"Current"' , `"LTS Until ${ eol } "` ) ;
312+ arr [ idx ] = arr [ idx ] . replace ( '<sup>Current</sup>' , '<sup>LTS</sup>' ) ;
313+ } else if ( arr [ idx ] . includes ( '**Long Term Support**' ) ) {
314+ arr [ idx ] = arr [ idx ] . replace (
315+ '**Long Term Support**' ,
316+ 'Long Term Support'
317+ ) ;
318+ }
319+ }
277320 if ( arr [ idx ] . includes ( `<b>${ lastRefLink } </b><br/>` ) ) {
278321 arr . splice ( idx , 1 , `<b>${ newRefLink } </b><br/>` , `${ lastRefLink } <br/>` ) ;
279322 break ;
@@ -289,6 +332,7 @@ class ReleasePreparation {
289332 newVersion,
290333 date,
291334 isLTS,
335+ isLTSTransition,
292336 ltsCodename,
293337 username
294338 } = this ;
@@ -313,15 +357,35 @@ class ReleasePreparation {
313357 const newHeader =
314358 `<a href="#${ newVersion } ">${ newVersion } </a><br/>` ;
315359 for ( let idx = 0 ; idx < arr . length ; idx ++ ) {
316- if ( arr [ idx ] . includes ( topHeader ) ) {
317- arr . splice ( idx , 0 , newHeader ) ;
360+ if ( isLTSTransition && arr [ idx ] . includes ( '<th>Current</th>' ) ) {
361+ // Create a new column for LTS.
362+ arr . splice ( idx , 0 , `<th>LTS '${ ltsCodename } '</th>` ) ;
318363 idx ++ ;
364+ } else if ( arr [ idx ] . includes ( topHeader ) ) {
365+ if ( isLTSTransition ) {
366+ // New release needs to go into the new column for LTS.
367+ const toAppend = [
368+ newHeader ,
369+ '</td>' ,
370+ arr [ idx - 1 ]
371+ ] ;
372+ arr . splice ( idx , 0 , ...toAppend ) ;
373+ idx += toAppend . length ;
374+ } else {
375+ arr . splice ( idx , 0 , newHeader ) ;
376+ idx ++ ;
377+ }
319378 } else if ( arr [ idx ] . includes ( `<a id="${ lastRef . substring ( 1 ) } "></a>` ) ) {
320379 const toAppend = [ ] ;
321380 toAppend . push ( `<a id="${ newVersion } "></a>` ) ;
322381 toAppend . push ( releaseHeader ) ;
323382 toAppend . push ( '### Notable Changes\n' ) ;
324- toAppend . push ( notableChanges ) ;
383+ if ( isLTSTransition ) {
384+ toAppend . push ( `${ getStartLTSBlurb ( this ) } \n` ) ;
385+ }
386+ if ( notableChanges . trim ( ) ) {
387+ toAppend . push ( notableChanges ) ;
388+ }
325389 toAppend . push ( '### Commits\n' ) ;
326390 toAppend . push ( allCommits ) ;
327391 toAppend . push ( '' ) ;
@@ -347,7 +411,7 @@ class ReleasePreparation {
347411 }
348412
349413 async updateNodeVersion ( ) {
350- const { versionComponents } = this ;
414+ const { ltsCodename , versionComponents } = this ;
351415
352416 const filePath = path . resolve ( 'src' , 'node_version.h' ) ;
353417 const data = await fs . readFile ( filePath , 'utf8' ) ;
@@ -364,7 +428,16 @@ class ReleasePreparation {
364428 arr [ idx ] = '#define NODE_VERSION_IS_RELEASE 1' ;
365429 } else if ( line . includes ( '#define NODE_VERSION_IS_LTS' ) ) {
366430 this . isLTS = arr [ idx ] . split ( ' ' ) [ 2 ] === '1' ;
367- this . ltsCodename = arr [ idx + 1 ] . split ( ' ' ) [ 2 ] . slice ( 1 , - 1 ) ;
431+ if ( this . isLTSTransition ) {
432+ if ( this . isLTS ) {
433+ throw new Error ( 'Previous release was already marked as LTS.' ) ;
434+ }
435+ this . isLTS = true ;
436+ arr [ idx ] = '#define NODE_VERSION_IS_LTS 1' ;
437+ arr [ idx + 1 ] = `#define NODE_VERSION_LTS_CODENAME "${ ltsCodename } "` ;
438+ } else {
439+ this . ltsCodename = arr [ idx + 1 ] . split ( ' ' ) [ 2 ] . slice ( 1 , - 1 ) ;
440+ }
368441 }
369442 } ) ;
370443
@@ -382,10 +455,17 @@ class ReleasePreparation {
382455 writeJson ( nmvFilePath , { NODE_MODULE_VERSION : nmvArray } ) ;
383456 }
384457
458+ async updateTestProcessRelease ( testFile ) {
459+ const data = await fs . readFile ( testFile , { encoding : 'utf8' } ) ;
460+ const updated = updateTestProcessRelease ( data , this ) ;
461+ await fs . writeFile ( testFile , updated ) ;
462+ }
463+
385464 async createReleaseCommit ( ) {
386465 const {
387466 cli,
388467 isLTS,
468+ isLTSTransition,
389469 ltsCodename,
390470 newVersion,
391471 isSecurityRelease,
@@ -405,6 +485,9 @@ class ReleasePreparation {
405485 format : 'plaintext'
406486 } ) ;
407487 messageBody . push ( 'Notable changes:\n\n' ) ;
488+ if ( isLTSTransition ) {
489+ messageBody . push ( `${ getStartLTSBlurb ( this ) } \n\n` ) ;
490+ }
408491 messageBody . push ( notableChanges ) ;
409492 messageBody . push ( '\nPR-URL: TODO' ) ;
410493
0 commit comments