@@ -60,6 +60,19 @@ export class YarnBerryPlugin implements IPlugin, IPackManager {
6060 this . application . logger . reportDryrun ( `Would be publishing ${ packedPackage . packageName } using release tag ${ releaseTag } ` ) ;
6161 return ;
6262 } else {
63+ // Check if running in CI environment
64+ const isCI = process . env . CI === 'true' ||
65+ process . env . GITHUB_ACTIONS === 'true' ||
66+ process . env . GITLAB_CI === 'true' ||
67+ process . env . CIRCLECI === 'true' ||
68+ process . env . TRAVIS === 'true' ||
69+ process . env . JENKINS_URL !== undefined ||
70+ process . env . BUILDKITE === 'true' ;
71+
72+ if ( isCI && ! process . stdin . isTTY ) {
73+ this . application . logger . reportInfo ( 'Running in CI environment - ensuring non-interactive mode' ) ;
74+ }
75+
6376 const registry = npmConfigUtils . getPublishRegistry ( yarnWorkspace . manifest , { configuration : this . yarnConfiguration } ) ;
6477 const gitHead = await npmPublishUtils . getGitHead ( yarnWorkspace . cwd ) ;
6578
@@ -74,13 +87,42 @@ export class YarnBerryPlugin implements IPlugin, IPackManager {
7487
7588 if ( yarnWorkspace . manifest . name ) {
7689 const url = npmHttpUtils . getIdentUrl ( yarnWorkspace . manifest . name ) ;
77- await npmHttpUtils . put ( url , body , {
78- configuration : this . yarnConfiguration ,
79- registry,
80- ident : yarnWorkspace . manifest . name ,
81- // otp: this.otp,
82- jsonResponse : true ,
83- } ) ;
90+
91+ try {
92+ await npmHttpUtils . put ( url , body , {
93+ configuration : this . yarnConfiguration ,
94+ registry,
95+ ident : yarnWorkspace . manifest . name ,
96+ // otp: this.otp,
97+ jsonResponse : true ,
98+ } ) ;
99+ } catch ( error ) {
100+ // Check if error is related to OTP/2FA
101+ const errorMessage = error instanceof Error ? error . message : String ( error ) ;
102+ const errorString = JSON . stringify ( error ) ;
103+
104+ if ( isCI && ! process . stdin . isTTY &&
105+ ( errorMessage . includes ( 'one-time password' ) ||
106+ errorMessage . includes ( 'OTP' ) ||
107+ errorMessage . includes ( 'otpRequired' ) ||
108+ errorString . includes ( 'EOTP' ) ) ) {
109+ throw new Error (
110+ 'Publishing failed: npm registry is requesting a one-time password (OTP) in CI environment.\n' +
111+ 'This typically happens when:\n' +
112+ ' 1. Your npm account has 2FA enabled (good!)\n' +
113+ ' 2. The NPM_AUTH_TOKEN is a "Classic" or "Publish" token that requires OTP\n' +
114+ '\n' +
115+ 'To fix this:\n' +
116+ ' 1. Log in to npmjs.com\n' +
117+ ' 2. Go to Access Tokens → Generate New Token\n' +
118+ ' 3. Select "Automation" token type (bypasses OTP for CI/CD)\n' +
119+ ' 4. Update your NPM_AUTH_TOKEN secret with this new automation token\n' +
120+ '\n' +
121+ `Original error: ${ errorMessage } ` ,
122+ ) ;
123+ }
124+ throw error ;
125+ }
84126 }
85127 }
86128 }
0 commit comments