Skip to content

ci: PR마다 뷰어를 Cloudflare Workers 프리뷰로 띄운다 - #102

Merged
say8425 merged 2 commits into
mainfrom
ci/pr-preview
Oct 23, 2026
Merged

say8425 merged 2 commits into
mainfrom
ci/pr-preview

Conversation

@say8425

@say8425 say8425 commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

요약

PR을 푸시하면 그 브랜치의 뷰어가 Cloudflare Workers에 뜨고, 프리뷰 URL이 PR 댓글로 달립니다.

  • 뷰어는 git을 부르는 로컬 서버가 있어야 그려집니다. 그래서 정적 페이지만 올리지 않고, CI가 실제 서버의 API 응답을 녹화하고 Worker가 그 녹화본을 재생합니다.
  • 프리뷰는 이 PR 브랜치를 auto base와 견준 diff, 즉 PR 자신의 변경을 보여 줍니다.
  • 뷰어 코드(apps/viewer)는 바꾸지 않았습니다.

구현

  • apps/preview/record.ts: 빌드한 뷰어와 체크아웃한 브랜치로 startDiffServer를 임시 포트에 띄웁니다. 첫 화면이 부르는 API(ping, refs, prs, summary, diff, diff에 든 이미지의 blob)를 불러 site/_snapshot/에 담습니다. 실패 응답도 그대로 담습니다. 토큰 파일은 임시 XDG_CACHE_HOME에 씁니다.
  • apps/preview/worker.ts: /api/*는 녹화본으로 답하고, 나머지는 정적 에셋으로 넘깁니다. if-none-match가 맞으면 304를 줍니다. repo 없이 맨 주소로 들어오면 녹화한 repo와 base를 실은 뷰어 URL로 리디렉션합니다.
  • 스냅숏은 선택 하나만 담습니다. 피커로 다른 브랜치를 고른 요청(head 있음)에는 서버처럼 400 unknown-head로 답합니다. 녹화본을 대신 주면 고른 것과 다른 비교를 보여 주기 때문입니다. 이렇게 답하면 상태 줄에 branch not found가 뜨고, 새로 열면 워킹트리(녹화본)로 돌아가는 버튼이 나옵니다.
  • 녹화기는 응답이 25MiB 에셋 상한을 넘으면 어느 응답인지 밝혀 실패합니다.
  • .github/workflows/preview.yml의 동작은 이벤트마다 다릅니다.
    • PR: wrangler preview --name pr-<번호>로 프리뷰를 만들고, /api/diff와 main.js를 curl로 확인한 뒤 댓글 하나를 갱신합니다. 댓글은 comment.sh가 숨은 표식으로 찾아 고칩니다.
    • PR 닫힘: 프리뷰를 지우고 댓글을 고칩니다.
    • main 푸시와 수동 실행: 본 Worker를 갱신합니다. 이때 base는 HEAD~1이라 마지막 머지 커밋을 보여 줍니다.
  • 시크릿이 없거나 포크 PR이면 gate 잡이 배포를 건너뜁니다. 필수 체크가 아니라서 Cloudflare 장애가 머지를 막지 않습니다.
  • wrangler는 devDep에 넣지 않고 bunx wrangler@4.137.0으로 부릅니다. devDep에 넣으면 모든 CI 잡이 workerd까지 받기 때문입니다. Workers Previews는 open beta입니다.
  • wrangler.jsonc에는 빈 previews 블록이 있어야 합니다. 없으면 wrangler preview가 업로드 전에 실패합니다. 또 --json을 줘도 업로드 로그가 표준 출력에 섞입니다. 그래서 URL은 WRANGLER_OUTPUT_FILE_PATH의 NDJSON에서 읽습니다. 두 가지 모두 리뷰가 wrangler 4.137.0 소스와 모의 API로 찾았고, 저도 소스에서 확인했습니다.
  • .claude/rules/preview.md에 계약을 적고 CLAUDE.md에 항목을 더했습니다. apps/preview/tsconfig.json을 typecheck에 넣었습니다. apps/preview/cli.ts는 커버리지 게이트에서 뺐습니다.

설정

Cloudflare 계정 토큰 "diffdeck GitHub Actions preview"를 만들었습니다. 권한은 Workers Scripts Write와 Account Settings Read뿐입니다. 토큰은 저장소 시크릿 CLOUDFLARE_API_TOKEN과 CLOUDFLARE_ACCOUNT_ID로 넣었습니다.

테스트 계획

  • bun test: apps/preview/__tests__에 18건을 더했습니다. 녹화기 테스트는 실제 서버와 임시 git 리포로 돕니다. test:coverage는 965 pass, 게이트 100%입니다.
  • 판별력: head 거부, 304 분기, 녹화기의 XDG_CACHE_HOME 덮어쓰기, 크기 상한 검사를 각각 지우면 해당 테스트가 실패합니다.
  • 로컬 실연: bun run build, bun apps/preview/cli.ts, wrangler dev(4.137.0) 순서로 띄웠습니다. 맨 주소가 302로 뷰어 URL에 갔고, Chrome에서 diff와 트리, 라벨(diffdeck · ci/pr-preview)이 그려졌습니다. 404는 favicon뿐이었고, 이는 로컬 서버와 같은 동작입니다.
  • bun run lint, format:check, typecheck가 통과합니다. actionlint와 shellcheck도 통과합니다.
  • 실제 Cloudflare 배포: Preview 워크플로가 성공했고, https://pr-102-diffdeck-preview.say8425.workers.dev 댓글이 달렸습니다. Chrome으로 열어 15개 파일, 라벨, PR 칩이 그려지는 것을 확인했습니다.

Screenshots

실제 배포한 프리뷰입니다.

preview-live.png

로컬 wrangler dev로 띄운 프리뷰입니다. 녹화 당시의 미커밋 변경 3파일을 보여 줍니다.

preview-local.png

🤖 Generated with Claude Code

https://claude.ai/code/session_01LnGBvUYgubedmy6AXgRzce

say8425 and others added 2 commits October 23, 2026 14:14
뷰어는 git을 부르는 로컬 서버가 있어야 그려진다. 그래서 CI가 PR 브랜치로
실제 서버를 띄워 첫 화면의 API 응답을 녹화하고(apps/preview/record.ts),
Worker가 빌드된 뷰어와 함께 그 녹화본을 재생한다(apps/preview/worker.ts).
preview.yml이 wrangler preview로 PR마다 프리뷰를 만들고 URL을 댓글로 단다.
PR이 닫히면 지우고, main 푸시는 본 Worker를 갱신한다.

Cloudflare 시크릿이 없거나 포크 PR이면 배포를 건너뛴다. 필수 체크가 아니다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LnGBvUYgubedmy6AXgRzce
- wrangler.jsonc에 빈 previews 블록을 둔다. 없으면 wrangler preview가
  업로드 전에 실패한다.
- --json이어도 업로드 로그가 stdout에 섞이므로 URL은
  WRANGLER_OUTPUT_FILE_PATH의 NDJSON에서 읽는다.
- 피커로 고른 head는 서버처럼 400 unknown-head로 답한다. 404면 라벨만
  바뀌고 화면은 녹화본에 남은 채 이유가 HTTP 404로만 보인다.
- 배포 뒤 /api/diff와 main.js를 curl로 확인하고, 댓글은 숨은 표식으로
  찾아 고친다(comment.sh). 닫히면 댓글을 고치고, cleanup은 main 설정을 쓴다.
- 녹화기가 25MiB 에셋 상한을 넘는 응답을 밝혀 실패하고, 효과 없던
  untracked=0을 뺀다. 캐시 디렉터리와 크기 상한에 회귀 테스트를 더한다.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LnGBvUYgubedmy6AXgRzce
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

PR이 닫혀 프리뷰를 지웠습니다.

@say8425
say8425 merged commit 3c5b9b0 into main Oct 23, 2026
14 checks passed
@say8425
say8425 deleted the ci/pr-preview branch October 23, 2026 08:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant