package-lock의 resolved가 옛 레지스트리를 가리킴 — npm ci 인증 실패
비공개 패키지를 GitHub Packages 에서 내부 레지스트리(Nexus)로 옮기는 작업을
했습니다. 각 프로젝트의 .npmrc 를 새 레지스트리로 바꿨고, 로컬에서 설치도
잘 됐습니다. 그런데 CI 에서 npm ci 가 일부 패키지에서 401/403 으로
계속 실패했습니다.
증상 — .npmrc는 바꿨는데 옛 주소로
.npmrc 는 분명히 새 레지스트리를 가리키는데, 에러에 찍힌 요청 주소는 옛
GitHub Packages 였습니다.
npm error 401 Unauthorized - GET https://npm.pkg.github.com/@scope%2f...
GitHub 토큰이 없는 새 환경이라 401 이 나는 건데, 애초에 왜 아직도 GitHub Packages 로 가느냐가 문제였습니다.
원인 — 옛 URL로 고정된 lockfile resolved
package-lock.json 은 의존성마다 어디서 받아왔는지를 resolved URL 로 박아
둡니다. 이 lock 이 GitHub Packages 를 쓰던 시절에 만들어졌다 보니, 그 URL 들이
전부 npm.pkg.github.com 을 가리키고 있었습니다.
"node_modules/@scope/pkg": {
"resolved": "https://npm.pkg.github.com/@scope/pkg/-/pkg-1.2.3.tgz",
...
}
여기에 npm ci 의 동작이 겹칩니다. npm ci 는 lockfile 을 신뢰하고 그대로
재현하는 명령이라, .npmrc 의 레지스트리보다 lock 의 resolved URL 을
우선해서 그 주소로 직접 받으러 갑니다. 그러니 .npmrc 를 아무리 바꿔도, lock
안의 옛 GitHub 주소로 가서 토큰이 없어 401 이 났던 겁니다. (npm install 은
필요하면 lock 을 갱신하니 로컬에선 문제가 안 보였고, lock 을 고정하는 CI 의
npm ci 에서만 터졌습니다.)
해결 — 새 레지스트리 기준으로 lockfile 재생성
.npmrc 만 바꾸는 걸로는 부족하고, lock 의 resolved 도 새 레지스트리로
옮겨야 합니다. lock 을 재생성하면 됩니다.
rm package-lock.json
npm install # 새 .npmrc 기준으로 resolved가 다시 써짐
이후 package-lock.json 의 resolved 가 내부 레지스트리 주소로 바뀐 걸
확인하고 커밋했습니다. 스코프 패키지는 .npmrc 에 스코프별 레지스트리도
명시해 둡니다.
@scope:registry=https://<내부-레지스트리>/repository/npm-group/
lockfile의 resolved와 npm ci의 lock 고정
레지스트리를 옮길 때 사람들은 .npmrc 만 바꾸고 끝났다고 생각하지만,
lockfile 에도 레지스트리 주소가 resolved 로 박혀 있습니다. 그리고
npm ci 는 lock 을 그대로 따라가므로, lock 을 재생성하지 않으면 옛 주소로
계속 갑니다. “로컬 npm install 은 되는데 CI npm ci 만 실패한다” 는 이
둘의 동작 차이(lock 갱신 vs lock 고정)를 정확히 가리키는 신호였습니다.
댓글 0