@@ -4,6 +4,10 @@ import { GIT_TAG, getAllPackagesForGitTag, getGitState } from '../../utils/git';
4
4
import { printHelpDocs } from '../../help/docs' ;
5
5
import { Package } from '../../utils/package' ;
6
6
import { SEMVER_VERSION } from '../../utils/channel' ;
7
+ import chalk from 'chalk' ;
8
+ import { colorName } from '../publish/steps/print-strategy' ;
9
+ import { exec } from '../../utils/cmd' ;
10
+ import { question } from '../publish/steps/confirm-strategy' ;
7
11
8
12
export async function promoteToLTS ( args : string [ ] ) {
9
13
// get user supplied config
@@ -16,14 +20,11 @@ export async function promoteToLTS(args: string[]) {
16
20
17
21
const packages = await getAllPackagesForGitTag ( gitTag ) ;
18
22
const versionsToPromote = getPublicPackageVersions ( packages ) ;
19
- const dryRun = config . full . get ( 'dry_run' ) as boolean ;
20
23
21
- console . log ( config . full ) ;
22
- console . log ( gitTag ) ;
23
- console . log ( versionsToPromote ) ;
24
+ await updateTags ( config . full , versionsToPromote ) ;
24
25
}
25
26
26
- export async function getPublicPackageVersions ( packages : Map < string , Package > ) : Promise < Map < string , SEMVER_VERSION > > {
27
+ export function getPublicPackageVersions ( packages : Map < string , Package > ) : Map < string , SEMVER_VERSION > {
27
28
const publicPackages = new Map < string , SEMVER_VERSION > ( ) ;
28
29
packages . forEach ( ( pkg , name ) => {
29
30
if ( ! pkg . pkgData . private ) {
@@ -32,3 +33,96 @@ export async function getPublicPackageVersions(packages: Map<string, Package>):
32
33
} ) ;
33
34
return publicPackages ;
34
35
}
36
+
37
+ export async function updateTags (
38
+ config : Map < string , string | number | boolean | null > ,
39
+ packages : Map < string , SEMVER_VERSION >
40
+ ) {
41
+ const distTag = config . get ( 'tag' ) as string ;
42
+ const NODE_AUTH_TOKEN = process . env . NODE_AUTH_TOKEN ;
43
+ const CI = process . env . CI ;
44
+ let token : string | undefined ;
45
+
46
+ // allow OTP token usage locally
47
+ if ( ! NODE_AUTH_TOKEN ) {
48
+ if ( CI ) {
49
+ console . log (
50
+ chalk . red (
51
+ '🚫 NODE_AUTH_TOKEN not found in ENV. NODE_AUTH_TOKEN is required in ENV to publish from CI. Exiting...'
52
+ )
53
+ ) ;
54
+ process . exit ( 1 ) ;
55
+ }
56
+ token = await getOTPToken ( distTag ) ;
57
+ } else {
58
+ if ( ! CI ) {
59
+ const result = await question (
60
+ `\n${ chalk . cyan ( 'NODE_AUTH_TOKEN' ) } found in ENV.\nPublish ${ config . get ( 'increment' ) } release in ${ config . get (
61
+ 'channel'
62
+ ) } channel to the ${ config . get ( 'tag' ) } tag on the npm registry? ${ chalk . yellow ( '[y/n]' ) } :`
63
+ ) ;
64
+ const input = result . trim ( ) . toLowerCase ( ) ;
65
+ if ( input !== 'y' && input !== 'yes' ) {
66
+ console . log ( chalk . red ( '🚫 Publishing not confirmed. Exiting...' ) ) ;
67
+ process . exit ( 1 ) ;
68
+ }
69
+ }
70
+ }
71
+
72
+ const dryRun = config . get ( 'dry_run' ) as boolean ;
73
+
74
+ for ( const [ pkgName , version ] of packages ) {
75
+ token = await updateDistTag ( pkgName , version , distTag , dryRun , token ) ;
76
+ console . log ( chalk . green ( `\t✅ ${ colorName ( pkgName ) } ${ chalk . green ( version ) } => ${ chalk . magenta ( distTag ) } ` ) ) ;
77
+ }
78
+
79
+ console . log (
80
+ `✅ ` + chalk . cyan ( `Moved ${ chalk . greenBright ( packages . size ) } 📦 packages to ${ chalk . magenta ( distTag ) } channel` )
81
+ ) ;
82
+ }
83
+
84
+ async function getOTPToken ( distTag : string , reprompt ?: boolean ) {
85
+ const prompt = reprompt
86
+ ? `The provided OTP token has expired. Please enter a new OTP token: `
87
+ : `\nℹ️ ${ chalk . cyan (
88
+ 'NODE_AUTH_TOKEN'
89
+ ) } not found in ENV.\n\nConfiguring NODE_AUTH_TOKEN is the preferred mechanism by which to publish. Alternatively you may continue using an OTP token.\n\nUpdating ${ distTag } tag on the npm registry.\n\nEnter your OTP token: `;
90
+
91
+ let token = await question ( prompt ) ;
92
+
93
+ return token . trim ( ) ;
94
+ }
95
+
96
+ async function updateDistTag (
97
+ pkg : string ,
98
+ version : string ,
99
+ distTag : string ,
100
+ dryRun : boolean ,
101
+ otp ?: string
102
+ ) : Promise < string | undefined > {
103
+ let cmd = `npm dist-tag add ${ pkg } @${ version } ${ distTag } ` ;
104
+
105
+ if ( otp ) {
106
+ cmd += ` --otp=${ otp } ` ;
107
+ }
108
+
109
+ if ( dryRun ) {
110
+ cmd += ' --dry-run' ;
111
+ }
112
+
113
+ try {
114
+ await exec ( { cmd, condense : true } ) ;
115
+ } catch ( e ) {
116
+ if ( ! otp || ! ( e instanceof Error ) ) {
117
+ throw e ;
118
+ }
119
+ if ( e . message . includes ( 'E401' ) || e . message . includes ( 'EOTP' ) ) {
120
+ otp = await getOTPToken ( distTag , true ) ;
121
+ return updateDistTag ( pkg , version , distTag , dryRun , otp ) ;
122
+ } else {
123
+ throw e ;
124
+ }
125
+ }
126
+
127
+ return otp ;
128
+ }
0 commit comments