Scarab 필드 테스트 #021 — pnpm 자체 업그레이드 시 Manifest 경계 부재 문제
요약
pnpm 자체 업그레이드(self-upgrade) 시 프로젝트 외부에서 실행할 경우 매니페스트 부재로 인해 오류가 발생하는 문제를 해결했습니다. 의존성 상태 확인 로직이 로컬 프로젝트 컨텍스트를 잘못 상속받아 자동 설치 경로로 흐르는 경계 문제를 수정했습니다.
핵심 포인트
- pnpm self-upgrade 시 발생하는 ERR_PNPM_NO_PKG_MANIFEST 오류 해결
- 글로벌 명령이 로컬 프로젝트 매니페스트 전제 조건을 상속받지 않도록 경계 설정
- 의존성 상태 확인 로직(runDepsStatusCheck.ts)의 가드 조건 정교화
- 매니페스트 부재와 루트 매니페스트 존재 시의 동작 차이 보존
대상: pnpm/pnpm
이슈: pnpm/pnpm#12240
PR: pnpm/pnpm#12301
공개 브랜치: https://github.com/scarab-systems/pnpm/tree/fix/deps-status-no-manifest
최근 푸시된 커밋: cb68ac1af0dcffbe4fb607a10b0df2046d2490ba
이번 필드 테스트는 pnpm 자체 업그레이드(self-upgrade)가 프로젝트 디렉토리 외부에서 다음과 같은 오류와 함께 실패하는 pnpm 명령 라우팅(command-routing) 실패 문제를 대상으로 했습니다:
ERR_PNPM_NO_PKG_MANIFEST
이 문제는 표면적으로는 단순해 보였습니다. 현재 작업 디렉토리(current working directory)가 패키지 내부에 있지 않다는 이유만으로 글로벌/자체(global/self) 명령이 프로젝트 매니페스트(project manifest)를 요구해서는 안 되기 때문입니다.
하지만 수정 경계(repair boundary)는 단순히 "누락된 매니페스트를 무시하라"는 것보다 더 구체적이었습니다.
문제는 의존성 상태(dependency-status) 확인 경로에 있었습니다.
프로젝트 매니페스트가 없어 의존성 상태를 확인할 수 없을 때, 명령이 자동 설치(auto-install) 경로로 흘러 들어갈 수 있었습니다. 이로 인해 자체 업그레이드/글로벌 스타일 명령이 마치 로컬 프로젝트 매니페스트가 필요한 것처럼 동작하게 되었습니다.
실패 형태
실패하는 동작은 다음과 같습니다:
pnpm self-upgrade 실행
프로젝트 디렉토리 외부에서 실행
프로젝트 매니페스트로부터 의존성 상태를 확립할 수 없음
명령 경로가 설치/매니페스트 기대치(install/manifest expectations)로 빠짐
결과: ERR_PNPM_NO_PKG_MANIFEST
이는 잘못된 소유권 경계(ownership boundary)입니다.
자체 업그레이드 명령은 로컬 프로젝트 컨텍스트(local project context)가 없을 때 프로젝트 매니페스트 전제 조건을 상속받아서는 안 됩니다.
경계
여기서의 경계는 다음과 같습니다:
글로벌/자체 명령 실행(global/self command execution)
대비
프로젝트 의존성 상태 확인(project dependency-status verification)
의존성 상태 확인은 명령이 프로젝트 내부에서 작동할 때는 유용할 수 있습니다.
하지만 프로젝트 매니페스트가 없고 명령이 재귀적(recursive)이거나 모든 프로젝트 대상(all-projects)이 아닌 경우, "의존성 상태를 사용할 수 없음"이 자동으로 "프로젝트 의존성을 자동 설치하려고 시도함"을 의미해서는 안 됩니다.
두 가지 서로 다른 케이스가 있습니다:
- 프로젝트 매니페스트 (project manifest)가 없어서 의존성 상태 (Dependency status)를 사용할 수 없음.
- 루트 프로젝트 매니페스트 (root project manifest)가 존재함에도 불구하고 의존성 상태 (Dependency status)를 예상치 못하게 사용할 수 없음.
이 두 케이스는 동일하게 동작해서는 안 됩니다.
이번 수정 사항은 그 차이점을 보존합니다.
변경 사항
패치는 다음 파일을 업데이트합니다:
exec/commands/src/runDepsStatusCheck.ts
이전의 가드 (guard) 조건은 단일한 truthy 체크였습니다:
if (upToDate) return
업데이트된 로직은 관련 케이스들을 명시적으로 처리합니다:
if (upToDate === true) return
if (upToDate === undefined && opts.allProjects == null && opts.rootProjectManifest == null) return
두 번째 조건이 중요한 부분입니다.
이 조건은 매니페스트가 없는 비재귀적 (non-recursive) 케이스에 대해서만 조기에 종료 (exit early)합니다.
모든 undefined 의존성 상태 (dependency-status) 결과를 "설치 건너뛰기 (skip install)"로 전환하지 않습니다.
루트 프로젝트 매니페스트 (root project manifest)가 존재하고 의존성 상태 (dependency status)가 예상치 못하게 사용할 수 없는 상태로 반환되더라도, 자동 설치 (auto-install) 동작은 그대로 유지됩니다.
첫 번째 버전이 변경된 이유
첫 번째 PR 형태는 더 넓은 범위의 가드 (guard)를 사용했습니다.
리뷰 자동화 도구는 upToDate !== false가 너무 광범위하다는 점을 정확하게 지적했습니다.
해당 버전은 의존성 상태 (dependency-status)를 사용할 수 없는 너무 많은 케이스를 건너뛰어도 안전한 것으로 취급했을 것입니다.
패치는 의도된 "매니페스트 없음" 경로에 대해서만 조기에 종료되도록 강화되었습니다.
이를 통해 수정 사항이 더욱 정밀해졌습니다:
프로젝트 매니페스트 없음
모든 프로젝트 대상 / 재귀적 (recursive) 아님
의존성 상태 (dependency status) 사용 불가
자동 설치 (auto-install)로 넘어가지 않음
하지만 프로젝트 매니페스트가 존재하고 의존성 상태 (dependency status)를 예상치 못하게 사용할 수 없는 경우에는 기존의 설치 경로를 유지합니다.
이것이 보고된 실패를 해결하는 것과 커맨드 계약 (command contract)을 약화시키는 것 사이의 차이점입니다.
테스트 커버리지
경계의 양쪽 모두에 대해 회귀 테스트 (Regression) 커버리지가 추가되었습니다.
테스트를 통해 다음이 증명됩니다:
프로젝트 매니페스트가 없어서 의존성 상태 (dependency status)를 사용할 수 없는 경우 자동 설치가 수행되지 않음
의존성 상태 (dependency status)를 예상치 못하게 사용할 수 없더라도 루트 프로젝트 매니페스트 (root project manifest)가 존재하면 자동 설치가 여전히 실행됨
두 번째 테스트가 중요합니다.
이는 수정 사항이 너무 광범위해지는 것을 방지합니다.
이 패치는 “undefined는 안전하다”라고 말하는 것이 아닙니다.
그것은 “이 명령 컨텍스트(command context) 내에 프로젝트 매니페스트(project manifest)가 없는 상태의 undefined는 프로젝트 설치(project install) 동작을 트리거해서는 안 된다”라고 말하는 것입니다.
Changeset
다음 항목에 대한 패치 Changeset이 추가되었습니다:
@pnpm/exec.commands
pnpm
Validation
업데이트된 패치는 다음 항목들을 통과했습니다:
pnpm run lint in exec/commands
focused Jest for test/runDepsStatusCheck.test.ts
focused Jest for test/createInstallArgs.test.ts
2 suites / 8 tests passed
tsgo --build --pretty false
git diff --check
pnpm pre-push hook completed and pushed
관련 없는 기존의 건너뛴 테스트(skipped-test) 경고만 나타났습니다.
Review state
첫 번째 리뷰 라운드를 통해 패치가 개선되었습니다.
더 넓은 범위의 가드(guard)가 수정되었습니다.
현재의 수정 사항은 더 좁은 범위를 가지며, 프로젝트 매니페스트가 존재할 때 예상치 못한 의존성 상태(dependency-status) 결과에 대한 기존의 폴백(fallback) 동작을 보존합니다.
업데이트 이후 CodeRabbit은 실행 가능한(actionable) 코멘트를 보고하지 않았습니다.
PR은 업스트림(upstream)에 오픈 상태로 남아 있습니다.
Field test result
이는 첫 번째 수정 방향이 근접했으나 충분히 정밀하지 않았던 필드 테스트(field test)의 좋은 사례였습니다.
문제는 단순히 다음과 같은 것이 아니었습니다:
자체 업그레이드(self-upgrade) 시 누락된 매니페스트를 무시해야 한다
더 나은 수정 경계(repair boundary)는 다음과 같았습니다:
프로젝트 매니페스트와 all-projects 컨텍스트가 없는 경우, 의존성 상태(dependency-status) 검증이 프로젝트 자동 설치(project auto-install)로 이어져서는 안 된다
이렇게 함으로써 글로벌/자체(global/self) 명령 경로를 프로젝트 의존성 복구(dependency repair) 동작과 분리하여 유지합니다.
최종 패치는 작지만, 경계(boundary)가 중요합니다.
패키지 매니저 명령은 프로젝트 외부에서 실행될 때 프로젝트 로컬 전제 조건(project-local precondition)을 실수로 상속받아서는 안 됩니다.
이번 수정은 그 계약(contract)을 온전하게 유지합니다.
What this field test showed
이 필드 테스트는 첫 번째 진단 단계에서 마법 같은 한 줄의 정답을 만들어내지는 못했습니다.
더 넓은 범위의 진단 실행을 통해 해당 문제 주변의 의존성/런타임/검증 압박(dependency/runtime/verification pressure)이 드러났습니다. 최종적인 제한된 수정(bounded repair)은 문제 컨텍스트, 드러난 수정 영역, 그리고 리뷰 피드백을 사용하여 국소화되었습니다.
이는 여전히 강력한 결과입니다.
이는 진단 과정이 올바른 수정 영역 (repair surface)을 좁히는 데 도움이 될 수 있음을 보여주지만, 동시에 광범위한 발견 사항에서 정확한 수정 후보 (repair candidate)로 넘어가는 과정에서 프로세스의 랭킹 (ranking) 성능이 여전히 개선될 필요가 있는 지점을 보여줍니다.
이는 유용한 신호입니다.
훌륭한 필드 테스트는 단순히 패치 (patch)를 증명하는 데 그치지 않습니다.
진단 워크플로 (diagnostic workflow)가 어디에서 더 정교해져야 하는지도 보여줍니다.
공개 주장 (Public claim)
이 필드 테스트에 대한 정확한 주장은 다음과 같습니다:
SDS/Scarab는 프로젝트 디렉토리 외부에서 실패하는 pnpm 자체 업그레이드 (self-upgrade) 시, 매니페스트가 없는 (no-manifest) 의존성 상태 (dependency-status) 사용 불가능 상태와 프로젝트 내부에서의 예기치 않은 의존성 상태 실패를 분리함으로써 제한된 수정 (bounded repair)을 유도하는 데 기여했습니다.
이 패치는 루트 프로젝트 매니페스트 (root project manifest)가 존재하는 경우의 기존 폴백 (fallback) 동작을 유지하면서, 매니페스트가 없는 자체 업그레이드 경로가 자동 설치 (auto-install)/프로젝트 매니페스트 (project-manifest) 동작으로 빠지는 것을 방지합니다.
이것이 수정 사항입니다.
공개 (Disclosure): 이 필드 보고서는 저의 개인 필드 테스트 노트, PR 기록, 검증 출력 및 수정 요약본을 바탕으로 AI 지원 편집을 통해 작성되었습니다. 기술적 주장, 패치 동작 및 최종 문구는 게시 전 검토를 마쳤습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기