[Chore] CI 구축 및 Vercel 배포 후 fork sync 자동화#278
Conversation
🚀 빌드 결과🩵 린트 검사 완료 |
📝 WalkthroughWalkthrough두 개의 GitHub Actions 워크플로우가 추가/교체됩니다. ChangesCI workflow
Vercel sync workflow
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 👉 Get your free trial and get 200 agent minutes per Slack user (a $50 value). Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 0/1 reviews remaining, refill in 60 minutes.Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)
96-104:assign잡은 job-levelif로 올리면 러너 낭비를 줄일 수 있어요.지금은 push 이벤트에서도 job이 생성된 뒤 step에서만 skip돼. job-level 조건으로 올리면 워크플로우가 더 깔끔해져.
리팩터링 예시 diff
assign: + if: github.event_name == 'pull_request' && github.event.action == 'opened' runs-on: ubuntu-latest steps: - name: auto-assign-author-and-reviewer - if: github.event_name == 'pull_request' && github.event.action == 'opened' uses: hkusu/review-assign-action@v1🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/ci.yml around lines 96 - 104, 현재 assign job에 if 조건이 steps 안에 있어 푸시 이벤트에서 불필요하게 러너가 할당됩니다; assign job 블록(심볼: assign) 바로 아래에 if: github.event_name == 'pull_request' && github.event.action == 'opened'를 옮겨서 job 레벨에서 평가되도록 하고, 해당 조건은 steps의 auto-assign-author-and-reviewer step(uses: hkusu/review-assign-action@v1)에서 제거하여 전체 워크플로우가 푸시 이벤트 시 이 job을 생성조차 하지 않게 변경하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 21-25: The workflow pins GitHub Actions using mutable tags (e.g.,
actions/checkout@v4, pnpm/action-setup@v4, actions/setup-node@v4,
marocchino/sticky-pull-request-comment@v2, hkusu/review-assign-action@v1);
replace each tag reference with the corresponding immutable commit SHA for that
action (lookup the desired release commit SHA on the action's GitHub/Marketplace
page and replace the uses value, e.g., actions/checkout@<commit-sha>), ensuring
every occurrence of those identifiers is updated to the specific SHA to prevent
tag re-assignment risks.
- Around line 66-90: The Notify Discord Alarm step injects PR fields directly
into the shell and JSON, risking command injection and broken JSON; update the
step to set PR_TITLE, PR_USER, PR_URL and DISCORD_WEBHOOK_URL via an env: block
(not inline expansion), validate that the DISCORD_WEBHOOK_URL secret exists
before running, and build the JSON payload using a safe encoder like jq with
--arg to escape title/author/url and include steps.build_step.outcome to choose
COLOR/TITLE; reference the existing variables PR_TITLE, PR_USER, PR_URL,
steps.build_step.outcome and secrets.DISCORD_WEBHOOK_URL when implementing these
changes.
In @.github/workflows/vercel-sync.yml:
- Around line 8-10: The workflow grants unnecessary write permissions: the
permissions block currently lists "contents: write" and "pull-requests: write";
reduce privileges by removing or downgrading "pull-requests: write" (e.g.,
remove the "pull-requests" entry or change it to "pull-requests: read") so only
required rights remain (keep "contents: write" if checkout/fork sync needs it);
update the permissions block accordingly to follow least-privilege principles.
---
Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 96-104: 현재 assign job에 if 조건이 steps 안에 있어 푸시 이벤트에서 불필요하게 러너가
할당됩니다; assign job 블록(심볼: assign) 바로 아래에 if: github.event_name == 'pull_request'
&& github.event.action == 'opened'를 옮겨서 job 레벨에서 평가되도록 하고, 해당 조건은 steps의
auto-assign-author-and-reviewer step(uses: hkusu/review-assign-action@v1)에서 제거하여
전체 워크플로우가 푸시 이벤트 시 이 job을 생성조차 하지 않게 변경하세요.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 204bd68f-d47d-43af-9791-8038ca6ac350
📒 Files selected for processing (2)
.github/workflows/ci.yml.github/workflows/vercel-sync.yml
| - name: Checkout Code | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Install pnpm | ||
| uses: pnpm/action-setup@v4 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# SHA pinning 누락된 uses 식별
rg -nP '^\s*uses:\s*[^@]+@(v[0-9]+(\.[0-9]+(\.[0-9]+)?)?|main|master)\s*$' .github/workflows/*.ymlRepository: DdingSroom/dding-sroom-frontend
Length of output: 568
🏁 Script executed:
cat -n .github/workflows/ci.yml | head -110 | tail -90Repository: DdingSroom/dding-sroom-frontend
Length of output: 3502
🏁 Script executed:
git ls-files .github/workflows/Repository: DdingSroom/dding-sroom-frontend
Length of output: 133
🏁 Script executed:
find . -maxdepth 2 -type f \( -name "SECURITY.md" -o -name "security.md" -o -name ".github" -o -name "README.md" \) | head -20Repository: DdingSroom/dding-sroom-frontend
Length of output: 86
🏁 Script executed:
cat -n README.md | head -50Repository: DdingSroom/dding-sroom-frontend
Length of output: 1590
GitHub Actions의 SHA 커밋 해시로 고정해서 공급망 공격 리스크 줄이기
현재 워크플로우에서 사용하는 액션들이 버전 태그(v4, v2, v1)로만 고정되어 있는데, 태그는 언제든지 다른 커밋을 가리키도록 재지정될 수 있어요. 누군가가 해당 태그를 의도적으로(또는 실수로) 변경하면 예상과 다른 코드가 실행되는 보안 사고로 이어질 수 있습니다.
특히 CI/CD 워크플로우는 저장소에 대한 권한을 가진 상태에서 실행되기 때문에, 이 지점을 통한 공급망 공격(supply chain attack)은 상당히 심각한 결과를 초래할 수 있어요.
다음처럼 구체적인 커밋 SHA로 고정하는 것이 안전합니다:
수정 예시
# 현재 (위험함)
- uses: actions/checkout@v4
# 변경 후 (안전함)
- uses: actions/checkout@c85c95e3d7251135ab7dc9f2f1aaf36f27022370각 액션의 최신 SHA는 GitHub Marketplace에서 확인할 수 있습니다. 액션 페이지의 "Used by" 섹션이나 릴리스 페이지에서 원하는 버전의 커밋 해시를 찾아 적용하면 돼요.
수정이 필요한 라인들:
- 22:
actions/checkout@v4 - 25:
pnpm/action-setup@v4 - 30:
actions/setup-node@v4 - 50:
marocchino/sticky-pull-request-comment@v2 - 101:
hkusu/review-assign-action@v1
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/ci.yml around lines 21 - 25, The workflow pins GitHub
Actions using mutable tags (e.g., actions/checkout@v4, pnpm/action-setup@v4,
actions/setup-node@v4, marocchino/sticky-pull-request-comment@v2,
hkusu/review-assign-action@v1); replace each tag reference with the
corresponding immutable commit SHA for that action (lookup the desired release
commit SHA on the action's GitHub/Marketplace page and replace the uses value,
e.g., actions/checkout@<commit-sha>), ensuring every occurrence of those
identifiers is updated to the specific SHA to prevent tag re-assignment risks.
| - name: Notify Discord Alarm | ||
| if: github.event_name == 'pull_request' && always() | ||
| run: | | ||
| PR_TITLE="${{ github.event.pull_request.title }}" | ||
| PR_USER="${{ github.event.pull_request.user.login }}" | ||
| PR_URL="${{ github.event.pull_request.html_url }}" | ||
|
|
||
| if [ "${{ steps.build_step.outcome }}" == "success" ]; then | ||
| COLOR=3066993 | ||
| TITLE="💙 빌드 성공" | ||
| else | ||
| COLOR=15158332 | ||
| TITLE="😭 빌드 실패" | ||
| fi | ||
|
|
||
| DESCRIPTION="**📄 Title : \`$PR_TITLE\`\n ✏️ 작성자 : \`$PR_USER\`\n\n[🔎 PR보러가기]($PR_URL)" | ||
| curl -H "Content-Type: application/json" \ | ||
| -d "{ | ||
| \"embeds\": [{ | ||
| \"title\": \"$TITLE\", | ||
| \"description\": \"$DESCRIPTION\", | ||
| \"color\": $COLOR | ||
| }] | ||
| }" \ | ||
| "${{ secrets.DISCORD_WEBHOOK_URL }}" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 직접 삽입 패턴(잠재적 인젝션 지점) 확인
rg -nP '\${{\s*github\.event\.pull_request\.(title|body|user\.login|html_url)\s*}}' .github/workflows/ci.yml -C2
# 문자열로 JSON을 수동 조합하는 패턴 확인
rg -nP 'curl .* -d "\{' .github/workflows/ci.yml -C2Repository: DdingSroom/dding-sroom-frontend
Length of output: 426
🏁 Script executed:
cat -n .github/workflows/ci.yml | sed -n '60,95p'Repository: DdingSroom/dding-sroom-frontend
Length of output: 1615
🏁 Script executed:
# 워크플로우 파일에서 다른 PR 메타데이터 삽입 패턴 확인
rg -n 'github\.event\.pull_request\.' .github/workflows/ci.yml
# 다른 curl/JSON 구성 패턴 확인
rg -n 'curl.*-d' .github/workflows/ci.yml -A3Repository: DdingSroom/dding-sroom-frontend
Length of output: 268
PR 메타데이터 직접 삽입으로 인한 커맨드 인젝션 및 JSON 페이로드 손상 위험
69~71줄의 ${{ github.event.pull_request.title }} 같은 값들이 셸 스크립트 본문에 직접 삽입되면, 제목에 백틱이나 $() 같은 셸 메타문자가 포함될 때 명령 치환이 발생할 수 있어.
더불어 81~89줄처럼 변수를 문자열 기반으로 JSON에 직접 넣으면 PR 제목이나 저자명에 큰따옴표나 개행이 들어왔을 때 JSON 구조가 깨진다. 포크 PR에서는 DISCORD_WEBHOOK_URL 시크릿이 주입되지 않아 단계가 예기치 않게 실패할 수도 있고.
다음처럼 개선하는 게 좋아:
env:블록에서 PR 메타데이터와 시크릿을 환경 변수로 설정jq --arg를 활용해 값을 제대로 이스케이프한 JSON 생성- 시크릿 존재 여부를 조건에서 미리 확인
개선 예시
- name: Notify Discord Alarm
- if: github.event_name == 'pull_request' && always()
+ if: github.event_name == 'pull_request' && always() && secrets.DISCORD_WEBHOOK_URL != ''
+ env:
+ PR_TITLE: ${{ github.event.pull_request.title }}
+ PR_USER: ${{ github.event.pull_request.user.login }}
+ PR_URL: ${{ github.event.pull_request.html_url }}
+ DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
run: |
- PR_TITLE="${{ github.event.pull_request.title }}"
- PR_USER="${{ github.event.pull_request.user.login }}"
- PR_URL="${{ github.event.pull_request.html_url }}"
-
if [ "${{ steps.build_step.outcome }}" == "success" ]; then
COLOR=3066993
TITLE="💙 빌드 성공"
@@
- DESCRIPTION="**📄 Title : \`$PR_TITLE\`\n ✏️ 작성자 : \`$PR_USER\`\n\n[🔎 PR보러가기]($PR_URL)"
- curl -H "Content-Type: application/json" \
- -d "{
- \"embeds\": [{
- \"title\": \"$TITLE\",
- \"description\": \"$DESCRIPTION\",
- \"color\": $COLOR
- }]
- }" \
- "${{ secrets.DISCORD_WEBHOOK_URL }}"
+ DESCRIPTION="**📄 Title : \`$PR_TITLE\`\n ✏️ 작성자 : \`$PR_USER\`\n\n[🔎 PR보러가기]($PR_URL)"
+ jq -n \
+ --arg title "$TITLE" \
+ --arg description "$DESCRIPTION" \
+ --argjson color "$COLOR" \
+ '{embeds:[{title:$title,description:$description,color:$color}]}' \
+ | curl -fsS -H "Content-Type: application/json" --data `@-` "$DISCORD_WEBHOOK_URL"🧰 Tools
🪛 actionlint (1.7.12)
[error] 68-68: "github.event.pull_request.title" is potentially untrusted. avoid using it directly in inline scripts. instead, pass it through an environment variable. see https://docs.github.com/en/actions/reference/security/secure-use#good-practices-for-mitigating-script-injection-attacks for more details
(expression)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/ci.yml around lines 66 - 90, The Notify Discord Alarm step
injects PR fields directly into the shell and JSON, risking command injection
and broken JSON; update the step to set PR_TITLE, PR_USER, PR_URL and
DISCORD_WEBHOOK_URL via an env: block (not inline expansion), validate that the
DISCORD_WEBHOOK_URL secret exists before running, and build the JSON payload
using a safe encoder like jq with --arg to escape title/author/url and include
steps.build_step.outcome to choose COLOR/TITLE; reference the existing variables
PR_TITLE, PR_USER, PR_URL, steps.build_step.outcome and
secrets.DISCORD_WEBHOOK_URL when implementing these changes.
There was a problem hiding this comment.
♻️ Duplicate comments (2)
.github/workflows/ci.yml (2)
22-25:⚠️ Potential issue | 🟠 MajorGitHub Actions를 태그 대신 커밋 SHA로 고정해줘.
uses: ...@v4/v2/v1형태는 태그 재지정 리스크가 있어서 CI 공급망 보안에 취약해. 전부 불변 커밋 SHA로 고정하는 게 안전해.#!/bin/bash # mutable tag 사용 여부 점검 (read-only) rg -nP '^\s*uses:\s*[^@]+@(v[0-9]+(\.[0-9]+(\.[0-9]+)?)?|main|master)\s*$' .github/workflows/*.ymlAlso applies to: 28-28, 48-48, 99-99
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/ci.yml around lines 22 - 25, Replace mutable GitHub Action tags with immutable commit SHAs: for each occurrence like uses: actions/checkout@v4 and uses: pnpm/action-setup@v4 (and other uses: ...@vX or `@main/`@master), look up the corresponding commit SHA on the action's upstream repository and update the workflow lines to uses: actions/checkout@<full-commit-sha> and uses: pnpm/action-setup@<full-commit-sha>; ensure you update every instance mentioned (including the ones at the other reported locations) and keep the exact action name and inputs unchanged so only the reference is hardened.
66-69:⚠️ Potential issue | 🔴 CriticalPR 메타데이터를 인라인 셸/수동 JSON에 직접 넣는 패턴은 인젝션·페이로드 깨짐 위험이 있어.
github.event.pull_request.*값을run스크립트 내부에서 직접 확장하고,curl -d "{...}"로 JSON을 수동 조합하고 있어.
env:로 값 전달 +jq --arg로 JSON 생성 +secrets.DISCORD_WEBHOOK_URL != ''가드까지 넣어서 안전하게 바꿔줘.Also applies to: 79-88
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/ci.yml around lines 66 - 69, The run step currently expands github.event.pull_request.* directly into shell variables (PR_TITLE, PR_USER, PR_URL) and builds JSON with curl -d, which risks injection; change the job to pass these values via the step's env: mapping (e.g., set PR_TITLE, PR_USER, PR_URL in env), add a conditional guard checking secrets.DISCORD_WEBHOOK_URL != '' for the step, and construct the payload using jq with --arg (e.g., jq -n --arg title "$PR_TITLE" --arg user "$PR_USER" --arg url "$PR_URL" '{title:$title, user:$user, url:$url}') and pipe that to curl --data `@-` instead of inlining shell-expanded JSON.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In @.github/workflows/ci.yml:
- Around line 22-25: Replace mutable GitHub Action tags with immutable commit
SHAs: for each occurrence like uses: actions/checkout@v4 and uses:
pnpm/action-setup@v4 (and other uses: ...@vX or `@main/`@master), look up the
corresponding commit SHA on the action's upstream repository and update the
workflow lines to uses: actions/checkout@<full-commit-sha> and uses:
pnpm/action-setup@<full-commit-sha>; ensure you update every instance mentioned
(including the ones at the other reported locations) and keep the exact action
name and inputs unchanged so only the reference is hardened.
- Around line 66-69: The run step currently expands github.event.pull_request.*
directly into shell variables (PR_TITLE, PR_USER, PR_URL) and builds JSON with
curl -d, which risks injection; change the job to pass these values via the
step's env: mapping (e.g., set PR_TITLE, PR_USER, PR_URL in env), add a
conditional guard checking secrets.DISCORD_WEBHOOK_URL != '' for the step, and
construct the payload using jq with --arg (e.g., jq -n --arg title "$PR_TITLE"
--arg user "$PR_USER" --arg url "$PR_URL" '{title:$title, user:$user,
url:$url}') and pipe that to curl --data `@-` instead of inlining shell-expanded
JSON.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 81f86a72-3b0d-4662-aaa5-ccf8e933071f
📒 Files selected for processing (1)
.github/workflows/ci.yml
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
.github/workflows/ci.yml (2)
64-89:⚠️ Potential issue | 🔴 CriticalDiscord 알림 step의 인라인 PR 값 주입/수동 JSON 조합을 제거해줘
Line 67-69의 직접 삽입은 셸 인젝션 면에서 취약하고, Line 81-88 문자열 JSON은 제목/작성자에 따옴표·개행이 들어오면 깨질 수 있어. 또한 시크릿 미존재 시 실행 자체를 막는 가드도 필요해. 이전 코멘트와 동일 이슈가 남아 있어.
개선 예시
- - name: Notify Discord Alarm - if: github.event_name == 'pull_request' && always() - run: | - PR_TITLE="${{ github.event.pull_request.title }}" - PR_USER="${{ github.event.pull_request.user.login }}" - PR_URL="${{ github.event.pull_request.html_url }}" + - name: Notify Discord Alarm + if: github.event_name == 'pull_request' && always() && secrets.DISCORD_WEBHOOK_URL != '' + env: + PR_TITLE: ${{ github.event.pull_request.title }} + PR_USER: ${{ github.event.pull_request.user.login }} + PR_URL: ${{ github.event.pull_request.html_url }} + DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} + run: | if [ "${{ steps.build_step.outcome }}" == "success" ]; then COLOR=3066993 TITLE="💙 빌드 성공" else COLOR=15158332 TITLE="😭 빌드 실패" fi DESCRIPTION="📄 Title : \`$PR_TITLE\`\n ✏️ 작성자 : \`$PR_USER\`\n\n[🔎 PR보러가기]($PR_URL)" - curl -H "Content-Type: application/json" \ - -d "{ - \"embeds\": [{ - \"title\": \"$TITLE\", - \"description\": \"$DESCRIPTION\", - \"color\": $COLOR - }] - }" \ - "${{ secrets.DISCORD_WEBHOOK_URL }}" + jq -n \ + --arg title "$TITLE" \ + --arg description "$DESCRIPTION" \ + --argjson color "$COLOR" \ + '{embeds:[{title:$title,description:$description,color:$color}]}' \ + | curl -fsS -H "Content-Type: application/json" --data `@-` "$DISCORD_WEBHOOK_URL"#!/bin/bash # 인라인 PR 메타데이터 삽입 및 수동 JSON 구성 패턴 재검증 rg -nP '\${{\s*github\.event\.pull_request\.(title|body|user\.login|html_url)\s*}}' .github/workflows/ci.yml -C2 rg -nP 'curl .* -d "\{' .github/workflows/ci.yml -C2🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/ci.yml around lines 64 - 89, The Discord notification step ("Notify Discord Alarm") currently injects PR fields into shell variables and constructs JSON inline (PR_TITLE, PR_USER, PR_URL) which is unsafe and brittle; change it to (1) guard early by checking that the DISCORD_WEBHOOK_URL secret is set and exit the step if not, (2) stop interpolating GitHub context directly into shell by passing PR metadata via GitHub Actions env/input (or use the built-in github context in a YAML-encoded action step), and (3) build/encode the JSON payload safely using a JSON-encoder (e.g., jq, gh api --input, or GitHub Actions' toJson) instead of manual string concatenation so quotes/newlines are escaped. Also keep the existing logic that selects COLOR/TITLE based on steps.build_step.outcome but reference that outcome safely from the job context rather than injecting into shell unescaped.
22-28:⚠️ Potential issue | 🟠 MajorGitHub Action
uses를 태그가 아니라 커밋 SHA로 고정해줘Line 22, Line 25, Line 28, Line 48이 모두 가변 태그(
v4,v2)라 공급망 리스크가 남아 있어. 이전 코멘트에서 이미 지적된 내용이고 아직 반영되지 않았어.예시 수정
- - name: Checkout Code - uses: actions/checkout@v4 + - name: Checkout Code + uses: actions/checkout@<full-commit-sha> - - name: Install pnpm - uses: pnpm/action-setup@v4 + - name: Install pnpm + uses: pnpm/action-setup@<full-commit-sha> - - name: Setup Node.js - uses: actions/setup-node@v4 + - name: Setup Node.js + uses: actions/setup-node@<full-commit-sha> - - name: Output build result - uses: marocchino/sticky-pull-request-comment@v2 + - name: Output build result + uses: marocchino/sticky-pull-request-comment@<full-commit-sha>#!/bin/bash # mutable tag 사용 지점 재검증 rg -nP '^\s*uses:\s*[^@]+@(v[0-9]+(\.[0-9]+(\.[0-9]+)?)?|main|master)\s*$' .github/workflows/*.ymlAlso applies to: 48-48
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/ci.yml around lines 22 - 28, The workflow uses mutable tags for third-party actions (e.g., uses: actions/checkout@v4, pnpm/action-setup@v4, actions/setup-node@v4 and any other uses with v2/v4), so replace each mutable tag with the corresponding pinned commit SHA from the action's GitHub repo (e.g., actions/checkout@<commit-sha>, pnpm/action-setup@<commit-sha>, actions/setup-node@<commit-sha>); locate the uses lines for actions/checkout, pnpm/action-setup and actions/setup-node and update their `@vX` tags to the specific commit SHAs for the exact release you want, then commit and run the workflow lint/CI to verify no syntax issues.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/ci.yml:
- Around line 47-48: The sticky comment step using
marocchino/sticky-pull-request-comment@v2 can fail on forked PRs because
GITHUB_TOKEN is read-only for pull_request events; update the step's if
condition (currently checking github.event_name == 'pull_request' && always())
to also guard that the PR head repo matches the base repo (e.g., require
github.event.pull_request.head.repo.full_name == github.repository or
github.event.pull_request.head.repo.owner.login == github.repository_owner) so
the step runs only for same-repo PRs; alternatively consider switching the
workflow trigger to pull_request_target but take care when checking out code.
---
Duplicate comments:
In @.github/workflows/ci.yml:
- Around line 64-89: The Discord notification step ("Notify Discord Alarm")
currently injects PR fields into shell variables and constructs JSON inline
(PR_TITLE, PR_USER, PR_URL) which is unsafe and brittle; change it to (1) guard
early by checking that the DISCORD_WEBHOOK_URL secret is set and exit the step
if not, (2) stop interpolating GitHub context directly into shell by passing PR
metadata via GitHub Actions env/input (or use the built-in github context in a
YAML-encoded action step), and (3) build/encode the JSON payload safely using a
JSON-encoder (e.g., jq, gh api --input, or GitHub Actions' toJson) instead of
manual string concatenation so quotes/newlines are escaped. Also keep the
existing logic that selects COLOR/TITLE based on steps.build_step.outcome but
reference that outcome safely from the job context rather than injecting into
shell unescaped.
- Around line 22-28: The workflow uses mutable tags for third-party actions
(e.g., uses: actions/checkout@v4, pnpm/action-setup@v4, actions/setup-node@v4
and any other uses with v2/v4), so replace each mutable tag with the
corresponding pinned commit SHA from the action's GitHub repo (e.g.,
actions/checkout@<commit-sha>, pnpm/action-setup@<commit-sha>,
actions/setup-node@<commit-sha>); locate the uses lines for actions/checkout,
pnpm/action-setup and actions/setup-node and update their `@vX` tags to the
specific commit SHAs for the exact release you want, then commit and run the
workflow lint/CI to verify no syntax issues.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: bcf27952-3704-4130-a49d-fa6bda02bd9c
📒 Files selected for processing (1)
.github/workflows/ci.yml
| if: github.event_name == 'pull_request' && always() | ||
| uses: marocchino/sticky-pull-request-comment@v2 |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
For GitHub Actions pull_request events from forks, is GITHUB_TOKEN write permission (pull-requests: write) downgraded to read-only, and what is the recommended guard pattern for comment-posting steps?
💡 Result:
Yes, for GitHub Actions workflows triggered by pull_request events from forks, the GITHUB_TOKEN has its write permissions (including pull-requests: write) downgraded to read-only. This is a security measure to prevent malicious forks from performing write actions on the base repository. The recommended guard pattern for comment-posting steps (which require pull-requests: write) is to use the pull_request_target event instead of pull_request. This event runs in the context of the base branch (trusted code), providing a full read/write GITHUB_TOKEN even for forks. Example workflow snippet: on: pull_request_target: types: [opened, synchronize] permissions: pull-requests: write contents: read jobs: comment: runs-on: ubuntu-latest steps: - name: Post comment run: | gh pr comment ${{ github.event.pull_request.number }} --body "Your comment here" env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} Security notes: - Do not checkout the PR head sha (untrusted code) with actions/checkout using ref: ${{ github.event.pull_request.head.sha }} unless strictly sandboxed. - For read-only tasks like building/testing, stick to pull_request. - Explicitly set minimal permissions with the permissions key. - Optionally, enable repo setting "Send write tokens to workflows from pull requests" for pull_request events, but pull_request_target is preferred for write actions like commenting.
Citations:
- 1: https://github.blog/changelog/2021-04-20-github-actions-control-permissions-for-github_token
- 2: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository
- 3: Support review of pull requests created from forks google-github-actions/run-gemini-cli#191
- 4: Warn about the limitations of GitHub tokens actions/labeler#491
- 5: Labels not applied on PRs FROM forks actions/labeler#399
- 6: https://stackoverflow.com/questions/74957218/what-is-the-difference-between-pull-request-and-pull-request-target-event-in-git
- 7: https://stackoverflow.com/questions/75744849/permission-to-repo-denied-to-github-actions-for-pr-coming-from-a-fork-only
- 8: https://github.com/iFixit/actions-comment-pull-request
포크 PR에서 sticky comment 실패 가능성에 대한 보호 필요
정확한 지적이야. GitHub Actions의 보안 정책상 fork PR에서 pull_request 이벤트로 실행되면 GITHUB_TOKEN의 쓰기 권한이 읽기 전용으로 제한돼. 따라서 현재 상태에서는 fork PR에서 코멘트를 작성하려는 이 단계가 실패할 수 있어.
제시한 가드 패턴은 정확한 해결책이야. PR이 같은 저장소에서 나온 경우에만 단계를 실행하도록 제한하면 fork PR에서의 실패를 방지할 수 있어:
가드 패턴 적용
if: github.event_name == 'pull_request' && always() && github.event.pull_request.head.repo.full_name == github.repository
uses: marocchino/sticky-pull-request-comment@v2참고로 더 근본적인 방법은 pull_request_target 이벤트로 변경하는 거야. 이 이벤트는 base 브랜치 컨텍스트에서 실행되기 때문에 fork PR에서도 전체 쓰기 권한을 갖게 돼. 다만 untrusted 코드 실행 위험이 있으니 코드 체크아웃할 때 조심해야 해.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if: github.event_name == 'pull_request' && always() | |
| uses: marocchino/sticky-pull-request-comment@v2 | |
| if: github.event_name == 'pull_request' && always() && github.event.pull_request.head.repo.full_name == github.repository | |
| uses: marocchino/sticky-pull-request-comment@v2 |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/ci.yml around lines 47 - 48, The sticky comment step using
marocchino/sticky-pull-request-comment@v2 can fail on forked PRs because
GITHUB_TOKEN is read-only for pull_request events; update the step's if
condition (currently checking github.event_name == 'pull_request' && always())
to also guard that the PR head repo matches the base repo (e.g., require
github.event.pull_request.head.repo.full_name == github.repository or
github.event.pull_request.head.repo.owner.login == github.repository_owner) so
the step runs only for same-repo PRs; alternatively consider switching the
workflow trigger to pull_request_target but take care when checking out code.
odukong
left a comment
There was a problem hiding this comment.
actions에 대한 버전을 최신버전으로 가져와 사용할 수도 있겠지만, 안정성을 위해 한 단계 낮은 버전(v4)을 사용하거나 package.json에 명시된 명령어를 통해 CI 빌드 과정을 수행하면서 명령어가 수정된다하여도 CI 워크플로우 파일을 수정하지 않아도 된다는 점을 고려하여 설계해준 점이 좋다고 생각이 듭니다!
그런데 한 가지 궁극적으로 궁금한 점이 있습니다.
"개인 fork 레포를 기반으로 배포가 이루어지는 흐름으로 구성되어 배포 이후 fork 레포의 상태를 최신으로 맞춰주는 후처리 작업"을 위해 vercel-sync.yml 워크플로우를 두셨다고 한 점인데요.
vercel-sync.yml도 결국엔 개인 fork repo가 업데이트가 되어야지 실행되는 워크플로우입니다. 해당 워크플로우가 실행되기 위해서는 배포가 성공했을 때(vercel.deployment.success)를 감지하여 즉, 개인 fork repo가 업데이트되고 vercel이 배포하면 vercel-sync.yml이 실행되는 구조니까요.
그런데 여기서 문제가 되는 부분이 조직 repo의 main브랜치로 push가 되었을 때, 이 업데이트 내역이 바로 개인 fork repo에 업데이트되는 과정이 생략되었다는 것 같다는 점입니다. 말씀하신 "Vercel 배포 성공 이후 vercel-sync.yml로 개인 fork를 동기화하는 방식"에서 배포가 되기 위해서는 우선적으로 조직 repo와 개인 fork가 동기화되어야 하는데, 두 명제의 선후 관계가 바뀐 것 같다는 느낌을 받았습니다.
(+"Vercel은 PR 생성 시에도 preview 배포를 생성하기 때문에, 모든 배포 성공 이벤트에 반응하면 PR preview 배포 때마다 fork sync가 반복될 수 있다는 것", 역시 vercel은 개인 repo fork를 바라보고 있기 때문에 조직 PR 이벤트가 발생되어도 배포 성공 이벤트는 일어나지 않을 것 같다는 생각입니다)
즉, 결국엔 현재 구조만으로는 개인 fork repo에서 수동으로 fork를 하는 작업(ex. sync fork 버튼 클릭?)이 여전히 필요하기 때문에 완전한 배포 워크플로우 자동화 가 이루어지지 못하고 있다고 생각이 듭니다.
그렇기 때문에 to. reviewer에 말씀해주신 방향대로 처음부터 조직 repo의 업데이트에 따라 fork가 이루어짐으로써 배포 자동화가 수행되는 방향으로 수정하는 것이 올바른 방향이라고 생각합니다.
우선은 현재 프로젝트에서 설계된 워크플로우를 이해한 대로 리뷰를 남겨두었는데, 잘못 이해한 부분이 있다면 말씀해주시면 감사하겠습니다!
➕ to. Reviewer에 남겨주신 부분에 대해 말씀드리면, 배포 전에 빌드 에러를 확인하여 미리 고치는 과정이 필요하기 때문에 CI 단계에서도 build를 포함하는 방향이 적절하다고 생각하고, 추가적으로 DISCORD 알림은 알림을 보내는 이유가 사실상 pr이 업로드되었으니 확인해달라는 의미에서 비롯되기 때문에..!PR 기준 범위가 충분하다고 생각합니다!
|
좋은 리뷰 감사합니다 🙂 먼저 실행 순서를 조금 더 명확하게 고려했어야 했는데, 이 부분을 놓친 것 같습니다. 제가 처음 의도했던 다만 제가 트리거를 아래처럼 Vercel 배포 성공 이후로 잡아두면서 문제가 생겼습니다. on:
repository_dispatch:
types: [vercel.deployment.success]현재 띵스룸 구조에서는 Vercel이 조직 레포가 아니라 개인 fork 레포를 바라보고 있기 때문에, 즉, 현재 제가 작성한 흐름은 아래와 같습니다. 결국 말씀해주신 것처럼 fork sync가 먼저 이루어져야 Vercel 배포가 발생할 수 있는데, 현재 구조는 Vercel 배포가 성공해야 fork sync를 실행하는 구조라서 선후 관계가 뒤집혀 있었습니다. 그래서 이 부분은 아래 방향으로 수정해보고자 하는데, 어떻게 생각하시나요? 즉, 기존의 트리거도 아래처럼 변경하는 방향을 생각하고 있습니다. on:
push:
branches: [main]
workflow_dispatch:이렇게 하면 조직 레포의 정리하면, 제가 처음에는 “Vercel 배포 성공 이후 fork sync”라고 설명했지만, 실제 띵스룸 구조에서는 **“조직 레포 main 반영 이후 fork sync → Vercel 배포”**가 맞는 흐름이라고 이해했습니다. @Sohyunnnn 소현님도 수빈님 의견과 제가 정리한 방향 각각에 대해서 어떻게 생각하시는지 의견 부탁드립니다 |
|
말씀해주신 방향( |
|
수빈님의 말씀에 동의합니다! 현재 Vercel이 개인 fork 레포를 바라보고 있는 구조라면, 배포가 발생하기 위해서는 개인 fork가 먼저 조직 레포의 main 변경사항을 반영하고 있어야 한다고 알고 있습니다. 따라서 “Vercel 배포 성공 이후 fork sync”가 아니라, 흐름으로 수정하는 방향이 이번 PR의 핵심 목적에 더 적합해 보입니다. 말씀해주신 방향대로 수정해주시면 정상적으로 배포 파이프라인이 동작할 것 같습니다! |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/vercel-sync.yml:
- Around line 16-20: Remove the unnecessary PAT from the checkout step: stop
passing the FORK_SYNC_TOKEN into actions/checkout@v4 and instead rely on the
default GITHUB_TOKEN; add persist-credentials: false to the checkout step to
avoid writing creds into .git/config. Keep the FORK_SYNC_TOKEN only where the
fork-sync action requires it (the later fork-sync step), passing it there
explicitly (e.g., via with: or env:) so the PAT is scoped to that single action.
- Around line 17-22: Replace mutable action tags with their immutable full
commit SHAs: change uses: actions/checkout@v4 to the corresponding full commit
SHA for actions/checkout and change uses: tgymnich/fork-sync@v2.3.1 to the
corresponding full commit SHA for tgymnich/fork-sync; update the workflow file
to reference those full-length git commit hashes (you can obtain them from each
repo’s tags page or via git ls-remote --tags) so the workflow uses immutable
action revisions instead of movable tags.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b0603416-12c5-4d54-9eb4-f455ea3ec7de
📒 Files selected for processing (1)
.github/workflows/vercel-sync.yml
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| token: ${{ secrets.FORK_SYNC_TOKEN }} | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/vercel-sync.ymlRepository: DdingSroom/dding-sroom-frontend
Length of output: 793
checkout 단계에서 불필요한 PAT 노출을 줄이자
현재 코드는 checkout 단계(19줄)에서 FORK_SYNC_TOKEN을 전달하고 있는데, checkout은 사실 기본 GITHUB_TOKEN만으로도 레포지토리 접근이 충분해. 이 PAT는 실제로는 아래 fork-sync 액션(24줄)에서만 필요해.
최소 권한 원칙을 적용하면, checkout은 기본 토큰으로 동작하고 PAT는 정말 필요한 fork-sync 단계에만 전달하는 게 안전해. 여기에 persist-credentials: false를 추가하면 자격증명이 .git/config에 저장되지 않아서 한층 더 방어할 수 있어.
개선 예시
- name: Checkout repository
uses: actions/checkout@v4
with:
- token: ${{ secrets.FORK_SYNC_TOKEN }}
+ persist-credentials: false🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/vercel-sync.yml around lines 16 - 20, Remove the
unnecessary PAT from the checkout step: stop passing the FORK_SYNC_TOKEN into
actions/checkout@v4 and instead rely on the default GITHUB_TOKEN; add
persist-credentials: false to the checkout step to avoid writing creds into
.git/config. Keep the FORK_SYNC_TOKEN only where the fork-sync action requires
it (the later fork-sync step), passing it there explicitly (e.g., via with: or
env:) so the PAT is scoped to that single action.
| uses: actions/checkout@v4 | ||
| with: | ||
| token: ${{ secrets.FORK_SYNC_TOKEN }} | ||
|
|
||
| - name: Sync upstream -> fork (u-zzn) | ||
| uses: tgymnich/fork-sync@v2.3.1 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/vercel-sync.yml | head -30Repository: DdingSroom/dding-sroom-frontend
Length of output: 793
🌐 Web query:
GitHub Actions security SHA pinning vs version tags supply chain risk
💡 Result:
SHA pinning provides significantly better supply chain security than version tags in GitHub Actions. Version tags (e.g., @v1, @v2) are mutable pointers that can be force-pushed to malicious commits if a maintainer account is compromised or tags are rewritten, as seen in real incidents like tj-actions/changed-files (CVE-2025-30066) and aquasecurity/trivy where attackers rewrote tags affecting thousands of workflows. SHA pinning (e.g., @692973e3d937129bcbf40652eb9f2f61becf3332 # v1.0.0) locks to an immutable commit hash, preventing automatic execution of malicious updates since Git commit SHAs cannot change retroactively without a computationally infeasible SHA-1 collision. GitHub officially recommends pinning to full-length commit SHAs as "the only way to use an action as an immutable release," especially for third-party actions. Administrators can now enforce SHA pinning via organization policies, blocking workflows without it. Use tools like Dependabot or Renovate to automate updates by creating PRs for new SHAs with version comments. To find the SHA for a tag: git ls-remote --tags https://github.com/owner/repo.git | grep refs/tags/vX or check the tag page on GitHub. Add comments for readability (e.g., # v4.1.1). Note limitations: SHA pinning secures the action's repo code but not its transitive dependencies (e.g., Docker images pulled by the action, which remain mutable). GitHub's immutable releases feature helps action maintainers make specific tags unchangeable, but consumers should still prefer SHAs. Example: Before (risky): - uses: actions/checkout@v4 After (secure): - uses: actions/checkout@b4ffde6 # v4.1.1
Citations:
- 1: https://github.blog/changelog/2025-08-15-github-actions-policy-now-supports-blocking-and-sha-pinning-actions/
- 2: Pin GitHub Actions to commit SHAs in release workflow github/gh-aw#10021
- 3: Pin all GitHub Actions to immutable SHA commits for supply chain security github/gh-aw#5691
- 4: https://www.stepsecurity.io/blog/pinning-github-actions-for-enhanced-security-a-complete-guide
- 5: https://dev.to/kanywst/hacking-github-from-tag-rewrites-to-dangling-commits-where-the-git-protocol-trusts-you-without-2o4h
- 6: ci: Pin GitHub Actions dependencies to commit SHAs for supply-chain security FalkorDB/benchmark#128
- 7: https://cloudnativeengineer.substack.com/p/github-actions-reproducibility-security
- 8: https://actsense.dev/vulnerabilities/no_hash_pinning/
- 9: Pin GitHub Actions to commit SHAs instead of version tags ankitects/anki#4722
- 10: https://stackoverflow.com/questions/78903499/how-do-i-pin-an-action-to-a-specific-sha
- 11: https://github.com/ihs7/action-sha-pinned-actions
- 12: Pin third party actions to commit sha cli/cli#10731
- 13: https://adaptive-enforcement-lab.com/secure/github-actions-security/action-pinning/
- 14: https://paloaltonetworks.com/blog/cloud-security/unpinnable-actions-github-security
서드파티 액션은 버전 태그 대신 커밋 SHA로 고정해야 한다.
배포 및 동기화 워크플로우에서는 공급망 보안이 특히 중요한데, 버전 태그(예: @v4, @v2.3.1)는 변경 가능한 포인터라 계정이 침해되거나 태그가 다시 지정될 경우 악의적인 커밋으로 업데이트될 수 있다. 실제로 tj-actions/changed-files(CVE-2025-30066), aquasecurity/trivy 등에서 태그 재지정으로 인한 공격이 발생했다.
반면 커밋 SHA는 변경 불가능한 Git 해시로, 자동으로 악의적인 업데이트를 실행할 수 없다. GitHub도 공식적으로 "action을 불변 릴리스로 사용하는 유일한 방법"으로 전체 길이의 커밋 SHA 고정을 권장하고 있다.
다음처럼 각 액션의 태그에 해당하는 커밋 SHA로 변경하자:
- name: Checkout repository
- uses: actions/checkout@v4
+ uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
with:
token: ${{ secrets.FORK_SYNC_TOKEN }}
- name: Sync upstream -> fork (u-zzn)
- uses: tgymnich/fork-sync@v2.3.1
+ uses: tgymnich/fork-sync@<fork-sync-commit-sha> # v2.3.1SHA는 각 저장소의 태그 페이지에서 확인하거나 git ls-remote --tags 명령으로 찾을 수 있다. 나중에 Dependabot이나 Renovate 같은 도구로 자동 업데이트를 설정하면 새 SHA로 PR을 생성해주므로 관리 부담도 크지 않다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/vercel-sync.yml around lines 17 - 22, Replace mutable
action tags with their immutable full commit SHAs: change uses:
actions/checkout@v4 to the corresponding full commit SHA for actions/checkout
and change uses: tgymnich/fork-sync@v2.3.1 to the corresponding full commit SHA
for tgymnich/fork-sync; update the workflow file to reference those full-length
git commit hashes (you can obtain them from each repo’s tags page or via git
ls-remote --tags) so the workflow uses immutable action revisions instead of
movable tags.
|
넵!! 트리거 수정하는 과정에서 더 이상 사용하지 않는 Vercel payload 기반 조건문과 debug step도 함께 제거했고, fork sync에 필요하지 않은 저희 이야기 나눈 흐름대로 조직 repo main merge → GitHub Actions 실행 → 개인 fork 자동 sync → Vercel 배포 순서로 동작하도록 정리된 상태인데 확인 한 번씩 부탁드리고, 추가로 고려해야 할 부분이 있다면 편하게 말씀해주시면 감사하겠습니다 |
|
@u-zzn 수정하신 내용 확인했습니다! 조직 repo main push → fork sync → Vercel 배포 순서로 흐름이 잘 잡힌것같고, 배포 yml 파일을 수정하면서 조건문도 제거할 수 있게 되면서 의미가 더 명확해진 것 같네요 귣~ 일단 어푸 |
📌 Issue Number
🔍 Changes
GitHub Actions 기반으로 CI 파이프라인 구성 + Vercel 배포 이후 fork 동기화 자동화를 추가했습니다.
📃 Describe
✔️ 왜 CI/CD 자동화를 도입했을까요?
지금까지는 PR을 올린 후에 빌드가 정상 동작하는지, 린트나 타입 오류가 없는지 수동으로 확인해야 했습니다. 이 과정은 사람이 매번 수동으로 진행하기에는 반복적이고, 놓치기 쉬운 부분이 많다고 생각했습니다.
또한 현재 띵스룸은 Vercel 배포 + 개인 fork 기반 작업 구조를 사용하고 있기 때문에 배포 이후 fork와의 동기화도 수동으로 관리해야 하는 불편함이 있었습니다 😥
그래서 PR 단계에서 코드 품질을 자동 검증하고, 배포 이후 fork 상태까지 자동으로 맞추는 CI + 배포 후 후처리 자동화 구조를 도입하게 되었습니다 💪
✔️ CI 파이프라인 구조 (ci.yml)
PR 및 main/develop 브랜치에 대해 아래 순서로 자동 검증이 수행됩니다.
lint → typecheck → build☑️ 트리거 전략
현재 브랜치 전략 기준으로 보면
develop→ 기능 통합 및 테스트 단계main→ 실제 배포 기준 브랜치이 두 브랜치는 모두 코드 안정성이 중요한 구간이기 때문에 PR 또는 push 시 CI를 통해 반드시 검증이 필요하다고 판단했습니다.
반면, feature 브랜치까지 CI를 모두 실행할 경우 잦은 push로 인해 불필요한 Actions 실행이 증가하고 로그 관리가 복잡해질 것 같아서
develop브랜치와main브랜치에만 CI 검증을 적용했습니다.☑️ concurrency 설정
같은 브랜치에서 여러 번 push가 발생하면 이전 CI를 자동으로 취소하여 가장 최신 커밋 기준의 CI 결과만 확인할 수 있도록 했습니다.
github.ref기준으로 브랜치별 CI 그룹을 만들고, 새로운 push가 들어오면 기존 실행을 자동 취소하도록 설정했습니다.☑️ pnpm 캐시 최적화
기존에는
actions/cache를 사용해 pnpm store 경로를 직접 구하고, 캐시 key까지 수동으로 설정해야 했습니다.이번 작업을 통해
actions/setup-node@v4의 내장 캐시 옵션을 사용해 설정을 간소화했습니다.pnpm-lock.yaml기준으로 의존성 캐시가 관리되기 때문에, 불필요한 설치 시간을 줄이면서도 워크플로우 설정을 더 간단하게 유지할 수 있도록 했습니다 :)☑️ typecheck 스크립트 재사용
pnpm run lint && pnpm run typechecktsc --noEmit을 CI에서 직접 실행하는 대신,package.json에 정의된typecheck스크립트를 재사용하도록 했습니다.이렇게 하면 로컬에서 실행하는 검사 방식과 CI에서 실행하는 검사 방식이 동일해지고, 나중에 typecheck 옵션이 변경되더라도
package.json만 수정하면 되어 유지보수가 더 쉬워진다고 생각했습니다!☑️ PR 결과 자동 코멘트
marocchino/sticky-pull-request-commentPR에서 CI가 실행될 때마다 lint / build 결과를 코멘트로 남기도록 설정했습니다.
일반 코멘트 방식은 push할 때마다 새로운 코멘트가 계속 쌓일 수 있지만,
sticky-pull-request-comment는 동일한 header의 코멘트를 덮어쓰는 방식으로 동작합니다.결과적으로 PR에는 항상 가장 최신 CI 결과 하나만 유지됩니다!
☑️ Discord 알림
PR 기준으로만 Discord 알림이 전송되도록 설정했습니다.
전에 타 프로젝트에서 작업할 때 commit마다 알림을 보냈었는데, push가 여러 번 발생할 때마다 Discord 메시지가 계속 쌓여서 개별 commit보다는 PR 단위로 빌드 성공/실패 상태를 공유해도 괜찮지 않을까? 생각했습니다.
이제 GitHub Actions 탭을 직접 확인하지 않아도, Discord에서 PR의 검증 상태를 빠르게 확인하실 수 있습니다 :)☺️
혹시 알림 단위를 commit 별로 변경하는 것이 더 좋으면 알려주세요
☑️ assign job 제거
처음에는 CI workflow 안에서도 reviewer / assignee를 자동 지정하도록 코드를 구현했지만, 이미 사전에 작업한
labeler.yml에서 동일한 역할을 처리하고 있었습니다.두 workflow에서 같은 작업을 동시에 수행할 필요가 없다고 생각했고, CI의 목적도 코드 검증이 아닌 PR 관리 자동화까지 섞이게 된다고 판단했습니다.
그래서 reviewer / assignee 지정은
labeler.yml에서만 담당하도록 정리하고, CI에서는 lint / typecheck / build 같은 순수 검증 역할만 수행하도록 분리했습니다 :)✔️ Vercel 배포 후 fork 자동 동기화 (vercel-sync.yml)
☑️ 왜 필요한가?
현재 띵스룸은 아래와 같은 구조로 운영되고 있습니다.
이 구조에서는 조직 레포의
main브랜치에 변경사항이 머지되더라도, 개인 fork 레포가 자동으로 최신 상태로 맞춰지지 않아서 배포 이후 개인 fork에서 직접 sync를 맞춰줘야 했기 때문에, 누락 가능성도 있고 반복 작업이 불필요하게 계속 발생한다고 생각해왔습니다 😥그래서 Vercel 배포가 성공한 이후, 개인 fork 레포도 자동으로 최신 상태가 되도록 동기화 작업을 추가했습니다!
☑️ 트리거 방식
Vercel에서 배포가 성공했을 때 전달되는
vercel.deployment.success이벤트를 기준으로 workflow가 실행되도록 설정했습니다.즉, 단순히
main에 머지되었을 때 바로 sync하는 것이 아니라, Vercel 배포가 성공한 이후에 fork sync가 실행되도록 구성했습니다!또한
workflow_dispatch도 함께 추가하여, 초기 테스트나 sync가 누락된 경우에는 GitHub Actions 탭에서 수동으로 실행할 수 있도록 했습니다 :)☑️ 실행 조건
production + main 브랜치만 동작Vercel은 PR 생성 시에도 preview 배포를 생성하기 때문에, 모든 배포 성공 이벤트에 반응하면 PR preview 배포 때마다 fork sync가 반복될 수 있다는 것을 작업 하면서 새롭게 알게 되었습니다.
fork 동기화는 실제 배포 기준인
main브랜치의 production 배포 이후에만 필요하다고 판단해서production환경이면서main브랜치에서 발생한 배포 성공 이벤트일 때만 동작하도록 조건을 제한했습니다!☑️ fork 동기화 방식
tgymnich/fork-syncfork 동기화는
tgymnich/fork-sync액션을 사용하도록 구성했습니다.기본
GITHUB_TOKEN만으로는 개인 fork 레포에 접근하거나 push 권한이 부족할 수 있기 때문에, 별도로 발급한FORK_SYNC_TOKEN을 GitHub Secrets에 등록하여 사용하도록 했습니다.이를 통해 Vercel 배포 성공 이후
upstream → fork방향으로 자동 동기화가 이루어지도록 했습니다!☑️ Debug step 추가
초기 연동 단계에서는 Vercel에서 전달되는 webhook payload가 예상한 형태로 들어오는지 확인이 필요하다고 판단했습니다.
그래서
environment,git_ref,deployment_url등의 값을 Actions 로그에 출력하도록 Debug step을 추가했습니다.이를 통해
production배포인지,main브랜치 기준 배포인지, 배포 URL이 정상적으로 전달되는지 확인할 수 있습니다.추후 webhook 동작이 안정적으로 확인되면 해당 step은 제거하거나 유지 여부를 다시 검토할 수 있을 것 같습니다 🙂
☑️ GitHub Actions 버전 수정
초기에는
actions/checkout@v5,actions/setup-node@v5처럼@v5버전을 사용하고자 했습니다.하지만 GitHub Actions는 각 action마다 개별적으로 버전이 관리되기 때문에, 모든 action이 동일하게 최신 버전을 제공하지는 않습니다. 현재 기준에서 안정적으로 사용할 수 있는 버전은
@v4였으며,@v5는 해당 action에서 제공되지 않아 workflow 실행 시 오류가 발생할 수 있었습니다.따라서 실제 사용 가능한 stable 버전인
@v4로 수정하여, workflow가 정상적으로 동작하도록 구성했습니다 :)☑️ pnpm 버전 관리 방식 개선
초기에는 workflow 안에서 pnpm 버전을 직접 지정하려고 했지만, 이미
package.json에packageManager필드로 pnpm 버전이 명시되어 있었습니다.그래서 workflow에서
version을 하드코딩하지 않고,package.json의packageManager값을 기준으로 pnpm 버전이 결정되도록 수정했습니다.✔️ 왜 CD 워크플로우를 따로 만들지 않았을까요?
초기에는 GitHub Actions 기반으로
cd.yml을 별도로 구성하여 배포까지 자동화하는 방향을 고려했습니다.하지만 현재 띵스룸은 Vercel과 개인 fork 레포가 연결된 구조로 이미 운영되고 있었기 때문에, 일반적인 CD 파이프라인을 그대로 적용하는 것은 적절하지 않다고 판단했습니다.
현재 구조에서는 코드가 main 브랜치에 머지되면 Vercel이 자동으로 build와 deploy를 수행하고, 실제 서비스 배포 역시 Vercel이 담당하고 있습니다. 즉, 배포 자체는 이미 완전히 자동화되어 있는 상태이기 때문에, GitHub Actions에서 다시 배포(CD)를 구성하면 역할이 중복되는 구조가 됩니다.
또한 띵스룸은 이미 개인 fork 레포를 기반으로 배포가 이루어지는 흐름으로 이루어져 있는 상황이기에, 단순히 “배포를 실행하는 CD”보다 배포 이후 fork 레포의 상태를 최신으로 맞춰주는 후처리 작업이 더 중요한 상황이라고 판단했습니다.
이러한 이유로 CD 워크플로우를 따로 두는 대신,
와 같이 역할을 분리하고, Vercel 배포 성공 이후에 fork 레포를 자동 동기화하는 vercel-sync.yml을 별도로 구성하게 되었습니다!
하나의 워크플로우에서 build와 deploy를 모두 처리하는 방식이 아니라, 현재 띵스룸 구조에 맞게 “배포는 Vercel에 맡기고, GitHub Actions는 검증과 후처리에 집중하는 구조”로 설계하는 것이 더 적절하다고 생각했습니다☺️
👀 To Reviewer
concurrency설정이 현재 띵스룸 서비스 규모에서 적절한지vercel-sync.yml로 개인 fork를 동기화하는 방식이 안정적인지확인 부탁드립니다 🙂
또한 현재는 배포 이후 sync를 맞춰야 하는 구조를 기준으로 자동화를 구성했는데, 해당 방식을 제거하고 처음부터 조직 레포 기준으로 CD 플로우를 다시 구축하는 것이 더 나을지도 함께 의견 부탁드립니다 🙇
📸 Screenshot
Summary by CodeRabbit
Chores