diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 670a502..cc51556 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -139,16 +139,6 @@ jobs: # publish 가 NPM_TOKEN 미설정으로 fail 해도 tarball 은 보존됨 run: cd mcp && npm pack - - name: Publish MCP to npm - run: | - if [ -z "$NODE_AUTH_TOKEN" ]; then - echo "NPM_TOKEN is not configured; publish skipped after successful build, test, and pack." - else - cd mcp && npm publish --access public - fi - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - name: Install VS Code Extension dependencies run: cd extension && npm ci --workspaces=false @@ -180,3 +170,21 @@ jobs: extension/*.vsix desktop-dist/*.exe desktop-dist/latest*.yml + + # 이 스텝은 의도적으로 'Create GitHub Release' 뒤에 있다. + # npm 은 우리가 통제하지 못하는 외부 의존이고(토큰 만료·2FA·레지스트리 장애), + # 앞에 두면 그 실패 하나가 데스크톱 설치파일과 latest.yml 배포까지 막는다. + # 실제로 v2.4.1 이 토큰 만료(E404)로 여기서 세 번째로 멈췄다. + # + # continue-on-error 는 쓰지 않는다 — 그러면 run 이 초록으로 보고돼 미발행을 놓친다. + # 마지막에 두면 릴리스는 나가고 실패는 빨간불로 남는다. + # 발행 여부의 ground truth 는 항상 `npm view @boxlogodev/sapstack-mcp version`. + - name: Publish MCP to npm + run: | + if [ -z "$NODE_AUTH_TOKEN" ]; then + echo "NPM_TOKEN is not configured; publish skipped after successful build, test, and pack." + else + cd mcp && npm publish --access public + fi + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/decisions/active/2026-08-19-workflow-owns-release-upload.md b/decisions/active/2026-08-19-workflow-owns-release-upload.md new file mode 100644 index 0000000..3af2810 --- /dev/null +++ b/decisions/active/2026-08-19-workflow-owns-release-upload.md @@ -0,0 +1,56 @@ +# 릴리스 아티팩트는 워크플로가 올린다 (electron-builder 는 빌드만) + +**날짜** 2026-08-19 · **범위** `apps/desktop/apps/electron/scripts/build-win.ps1`, `.github/workflows/release.yml`, `electron-builder.yml` + +## 배경 + +v2.4.1 태그를 세 번 밀었고 세 번 다 Windows 잡에서 죽었다. 세 번째 원인이 이것이다: + +``` +Error: GitHub Personal Access Token is not set, neither programmatically, nor using env "GH_TOKEN" + at new GitHubPublisher (electron-publish/src/gitHubPublisher.ts:52:15) +``` + +`electron-builder.yml` 에 `publish: github` 가 있다. electron-builder 는 CI 환경 + 태그 +푸시를 감지하면 기본 정책이 업로드로 바뀌어, 산출물을 **스스로** GitHub Release 에 올리려 +한다. 워크플로는 토큰을 넘긴 적이 없으므로 3회 재시도 후 실패한다. + +로컬 빌드는 CI 환경변수도 태그도 없어 이 분기를 타지 않는다 — **로컬에서 재현 불가능한 +실패**다. + +## 결정 + +**업로드 주체는 워크플로 하나뿐이다.** `build-win.ps1` 은 `--publish never` 로 빌드만 +하고, 산출물은 `upload-artifact` → `download-artifact` → `softprops/action-gh-release` 로 +Release 에 붙는다. + +`electron-builder.yml` 의 `publish: github` 는 **남겨 둔다.** 그것이 설치본의 +`app-update.yml`(=electron-updater 가 어느 저장소를 볼지)과 `latest.yml` 생성을 결정한다. +지우면 자동 업데이트가 죽는다. + +## 근거 + +- `latest.yml` 은 `--publish never` 여도 생성된다. `app-builder-lib/out/publish/PublishManager.js` + 에서 업로드 루프는 `if (this.isPublish)` 안이고, `createUpdateInfoTasks`(업데이트 정보 쓰기)는 + 그 블록 **밖**의 별도 `if (event.isWriteUpdateInfo)` 다 — 두 관심사가 이미 분리돼 있다 +- 워크플로가 이미 `if-no-files-found: error` 로 `latest*.yml` 부재를 잡는다. 이 결정이 틀렸다면 + 릴리스가 조용히 나쁜 상태로 나가는 게 아니라 그 자리에서 선다 +- 업로드 주체가 둘이면 같은 태그에 draft/non-draft 가 경합한다 + +## 버린 대안 + +| 대안 | 왜 안 골랐나 | +| -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | +| `GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}` 주입 | 동작은 하지만 업로드 주체가 둘이 된다. electron-builder 가 draft 를 만들고 action-gh-release 가 뒤늦게 덮는 순서 의존 | +| `electron-builder.yml` 에서 `publish` 제거 | `app-update.yml` 과 `latest.yml` 이 사라져 **자동 업데이트가 죽는다**. 증상이 릴리스가 아니라 다음 업데이트 때 나온다 | +| electron-builder 가 릴리스까지 전담 | MCP tarball·VSIX·릴리스 노트가 같은 Release 에 붙어야 한다. 데스크톱만 아는 도구에 전체를 맡길 수 없다 | + +## 뒤집는 조건 + +- 데스크톱 설치파일이 다른 Release/채널로 분리되면 electron-builder 직접 업로드가 더 단순해진다 +- electron-builder 가 "업데이트 정보만 쓰고 업로드는 안 함"을 별도 플래그로 노출하면 `publish` 설정을 더 명시적으로 쓸 수 있다 + +## 주의 + +`--publish never` 를 지우면 로컬·PR CI 는 전부 통과하고 **태그를 민 순간에만** 실패한다. +릴리스 워크플로를 손댈 때 이 플래그를 제일 먼저 확인할 것.