Jenkins执行lerna bootstrap报npm E401认证错误 本地运行正常
Jenkins环境中执行lerna bootstrap命令失败,相同操作在本地机器可正常运行,报错信息如下:
npm ERR! code E401 npm ERR! Unable to authenticate, need: BASIC realm="Sonatype Nexus Repository Manager"
环境版本
- Node: v14.17.0
- npm: 6.14.13
根目录package.json配置
"scripts":{ "bootstrap": "lerna bootstrap --loglevel verbose", "publish-packages": "lerna publish --skip-git", "clean": "lerna clean" }, "dependencies": { "lerna": "^5.0.0" }
lerna.json配置
{ "packages": [ "packages/*" ], "npmClientArgs": [ "--strict-ssl=false", "--always-auth=true" ], "version": "independent" }
执行命令序列
npm i npm run bootstrap
执行结果:npm i可正常执行通过,仅npm run bootstrap执行失败。
- 在项目根目录.npmrc中添加私有仓库对应凭证,配置如下:
//my-repo-url:8081/nexus/repository/npm-repo/:_password=##base-64-encoded-password##
//my-repo-url:8081/nexus/repository/npm-repo/:username=##usernamr##
- 在.npmrc中添加配置
_auth=##base-64-encoded-username-and-password## - 在lerna.json的npmClientArgs配置项中添加或移除
--always-auth=true与--strict-ssl参数
核心根因
Lerna 5.x执行bootstrap时,会进入每个子包目录单独触发npm install操作,不会默认继承根目录下的项目级.npmrc配置;加上npm 6.x版本对私有仓库凭证的路径匹配规则严格,Jenkins执行任务时的工作目录上下文和本地手动执行的上下文存在差异,最终导致子包安装阶段读取不到Nexus私有仓库的认证信息,触发401报错。
另外已尝试的配置中存在username字段拼写错误(写为usernamr)、未显式指定私有仓库registry地址的问题,也会导致凭证匹配失败。
修复步骤
- 修正凭证配置的拼写错误,不要仅在项目根目录的.npmrc中写认证信息,在Jenkins执行脚本的前置步骤中,通过npm config命令把认证信息写入全局npm配置,避免子包安装时读不到配置:
npm config set //my-repo-url:8081/nexus/repository/npm-repo/:_password "##替换为实际base64编码后的密码##" npm config set //my-repo-url:8081/nexus/repository/npm-repo/:username "##替换为实际用户名##" npm config set //my-repo-url:8081/nexus/repository/npm-repo/:always-auth true npm config set strict-ssl false npm config set registry "http://my-repo-url:8081/nexus/repository/npm-repo/"
- 修改lerna.json的npmClientArgs配置,显式传入私有仓库地址,确保lerna调用npm安装子包依赖时不会跳转到公网源:
"npmClientArgs": [ "--strict-ssl=false", "--always-auth=true", "--registry=http://my-repo-url:8081/nexus/repository/npm-repo/" ]
注意:配置中的registry地址必须和凭证配置里的仓库路径完全一致,末尾斜杠、路径层级不能有差异,否则npm无法匹配到对应凭证。
3. 修改package.json中的bootstrap脚本,增加--use-npm-exec参数,强制lerna复用根目录的npm执行上下文,避免目录切换导致的配置丢失:
"scripts":{ "bootstrap": "lerna bootstrap --loglevel verbose --use-npm-exec", "publish-packages": "lerna publish --skip-git", "clean": "lerna clean" }
如果不想修改脚本,也可以在执行bootstrap前加一步复制操作,把根目录的.npmrc复制到所有子包目录下,保证每个子包目录下都有可读取的认证配置。
内容的提问来源于stack exchange,提问作者Anup Singh

