Skip to content

์šด์˜ ๋ฐฐํฌ ์ „ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•œ ์Šคํ…Œ์ด์ง• ํ™˜๊ฒฝ ๊ตฌ์ถ•ย #21

@sounmind

Description

@sounmind

๋ฐฐ๊ฒฝ

ํ˜„์žฌ ๊ฐœ๋ฐœ ํ”„๋กœ์„ธ์Šค์—์„œ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์ด๋‚˜ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์šด์˜ ๋ฐฐํฌ ์ „์— ๊ฒ€์ฆํ•  ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค.

ํ˜„์žฌ ํ๋ฆ„:

์ฝ”๋“œ ๋ณ€๊ฒฝ โ†’ main ๋ณ‘ํ•ฉ โ†’ Cloudflare Workers ์šด์˜ ๋ฐฐํฌ โ†’ leetcode-study์—์„œ ์‹ค์ œ ๋™์ž‘ ํ™•์ธ

์šด์˜ ํ™˜๊ฒฝ์— ๋ฐฐํฌ๋œ ํ›„์—์•ผ webhook ๋™์ž‘์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด์„œ, ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์‹ค์ œ ์Šคํ„ฐ๋”” ์ฐธ์—ฌ์ž์—๊ฒŒ ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค. ์ตœ๊ทผ 3๊ฐœ์˜ PR์ด ์—ฐ์† ๋ฆฌ๋ฒ„ํŠธ๋œ ์ด๋ ฅ(#17, #18, #19)์ด ์ด๋ฅผ ์ž˜ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

์›ํ•˜๋Š” ํ๋ฆ„:

์ฝ”๋“œ ๋ณ€๊ฒฝ โ†’ ์Šคํ…Œ์ด์ง• ๋ฐฐํฌ โ†’ ํ…Œ์ŠคํŠธ PR๋กœ ๊ฒ€์ฆ โ†’ ํ™•์ธ ์™„๋ฃŒ โ†’ main ๋ณ‘ํ•ฉ โ†’ ์šด์˜ ๋ฐฐํฌ

์ œ์•ฝ ์กฐ๊ฑด

  • ๋ณ„๋„ ํ…Œ์ŠคํŠธ ๋ ˆํฌ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค โ€” ๊ด€๋ฆฌ ํฌ์ธํŠธ๊ฐ€ ๋Š˜์–ด๋‚˜๋Š” ๊ฒƒ์„ ์ตœ์†Œํ™”
  • ๊ธฐ์กด leetcode-study ๋ ˆํฌ ์•ˆ์—์„œ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค
  • ํ”„๋กœ๋•์…˜ ์›Œ์ปค ์ฝ”๋“œ์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ์€ ์ตœ์†Œํ™”ํ•œ๋‹ค

์„ ํƒํ•œ ์ ‘๊ทผ ๋ฐฉ์‹

๋ผ๋ฒจ ๊ธฐ๋ฐ˜ ์Šคํ…Œ์ด์ง• ๋ผ์šฐํŒ…

leetcode-study ๋ ˆํฌ์—์„œ ํ…Œ์ŠคํŠธ์šฉ PR์„ ์ƒ์„ฑํ•  ๋•Œ staging ๋ผ๋ฒจ์„ ๋ถ™์ด๋ฉด, ํ”„๋กœ๋•์…˜ ์›Œ์ปค๊ฐ€ ํ•ด๋‹น ์ด๋ฒคํŠธ๋ฅผ ์Šคํ…Œ์ด์ง• ์›Œ์ปค๋กœ ํฌ์›Œ๋”ฉํ•œ๋‹ค.

leetcode-study PR (๋ชจ๋“  webhook ์ด๋ฒคํŠธ)
        โ”‚
        โ–ผ
  Production Worker (github.dalestudy.com)
        โ”‚
        โ”œโ”€ staging ๋ผ๋ฒจ ๊ฐ์ง€?
        โ”‚      YES โ”€โ”€โ–บ Forward โ”€โ”€โ–บ Staging Worker (๋ณ„๋„ ์ธ์Šคํ„ด์Šค)
        โ”‚      NO  โ”€โ”€โ–บ ์ •์ƒ ์ฒ˜๋ฆฌ

์™œ Production์„ ๊ฑฐ์ณ์•ผ ํ•˜๋Š”๊ฐ€?

GitHub App์˜ webhook URL์€ ์•ฑ ์„ค์ •์—์„œ ํ•˜๋‚˜๋งŒ ์ง€์ • ๊ฐ€๋Šฅํ•˜๋‹ค. PR ๋‹จ์œ„๋กœ ๋‹ค๋ฅธ URL์„ ๋ณด๋‚ด๋Š” ๊ธฐ๋Šฅ์ด ์—†๋‹ค. ๋”ฐ๋ผ์„œ ๋ชจ๋“  webhook ์ด๋ฒคํŠธ๋Š” Production Worker(github.dalestudy.com)๋กœ ๋จผ์ € ๋„์ฐฉํ•œ๋‹ค.

Production Worker์™€ Staging Worker๋Š” ์„œ๋กœ ๋‹ค๋ฅธ URL์„ ๊ฐ€์ง„ ์™„์ „ํžˆ ๋…๋ฆฝ๋œ ์„œ๋ฒ„ ์ธ์Šคํ„ด์Šค์ด๋‹ค (์ฝ”๋“œ, Secrets, ๋ฉ”๋ชจ๋ฆฌ ๋ชจ๋‘ ๋ณ„๊ฐœ). Staging Worker๋Š” GitHub์ด ์กด์žฌ๋ฅผ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ์ง์ ‘ webhook์„ ๋ฐ›์„ ์ˆ˜ ์—†๋‹ค.

๊ทธ๋ž˜์„œ Production Worker๊ฐ€ staging ๋ผ๋ฒจ์„ ๊ฐ์ง€ํ•˜๋ฉด fetch("https://github-staging.xxx/webhooks", ...)๋กœ Staging Worker์— HTTP ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๋ฐฉ์‹์œผ๋กœ ์ค‘๊ณ„ํ•œ๋‹ค. ์ฝ”๋“œ ๋‚ด๋ถ€ ๋ถ„๊ธฐ๊ฐ€ ์•„๋‹ˆ๋ผ, ์„œ๋ฒ„ A โ†’ ์„œ๋ฒ„ B๋กœ์˜ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์ด๋‹ค.

์ด ๋ฐฉ์‹์„ ์„ ํƒํ•œ ์ด์œ 

  • ๋ณ„๋„ ๋ ˆํฌ ๋ถˆํ•„์š”: leetcode-study ์•ˆ์—์„œ ๋ผ๋ฒจ ํ•˜๋‚˜๋กœ ๊ตฌ๋ถ„
  • ํ”„๋กœ๋•์…˜ ๋ณ€๊ฒฝ ์ตœ์†Œํ™”: ๋ผ์šฐํŒ… ๋กœ์ง์€ ๋‹จ์ˆœ forward ๋ถ„๊ธฐ ํ•˜๋‚˜
  • ์™„์ „ ๋ถ„๋ฆฌ๋œ ์›Œ์ปค: Cloudflare Workers environments๋กœ Production/Staging์ด ๋…๋ฆฝ ์ธ์Šคํ„ด์Šค๋กœ ๋ฐฐํฌ๋จ
  • ๋ฆฌ์–ผํ•œ ํ…Œ์ŠคํŠธ: ์‹ค์ œ GitHub webhook ์ด๋ฒคํŠธ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌ

๊ตฌํ˜„ ์ŠคํŽ™

1. Cloudflare Workers ํ™˜๊ฒฝ ๋ถ„๋ฆฌ

wrangler.jsonc์— staging ํ™˜๊ฒฝ ์ถ”๊ฐ€:

{
  "name": "github",
  "main": "index.js",
  // ...
  "env": {
    "staging": {
      "name": "github-staging"
    }
  }
}
wrangler deploy              # โ†’ Production Worker
wrangler deploy --env staging # โ†’ Staging Worker (๋ณ„๋„ ์ธ์Šคํ„ด์Šค)

2. ์Šคํ…Œ์ด์ง• ๋ฐฐํฌ: GitHub Actions (workflow_dispatch)

์Šคํ…Œ์ด์ง• ๋ฐฐํฌ๋Š” ์ž๋™์ด ์•„๋‹Œ ์ˆ˜๋™ ํŠธ๋ฆฌ๊ฑฐ๋กœ ์›ํ•˜๋Š” ๋ธŒ๋žœ์น˜๋ฅผ ์„ ํƒํ•ด์„œ ๋ฐฐํฌํ•œ๋‹ค.

name: Deploy Staging ๐Ÿš€

on:
  workflow_dispatch:
    inputs:
      branch:
        description: "๋ฐฐํฌํ•  ๋ธŒ๋žœ์น˜"
        required: true
        type: string

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ inputs.branch }}
      - run: npx wrangler deploy --env staging
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}

์ˆ˜๋™ ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ์„ ํƒํ•œ ์ด์œ :

  • ์Šคํ…Œ์ด์ง•์€ ํ•ญ์ƒ ๋Œ๋ฆด ํ•„์š” ์—†์ด ํ…Œ์ŠคํŠธํ•  ๋•Œ๋งŒ ๋ฐฐํฌํ•˜๋ฉด ๋จ
  • ์–ด๋–ค ๋ธŒ๋žœ์น˜๋ฅผ ๋ฐฐํฌํ• ์ง€ ๋ช…์‹œ์ ์œผ๋กœ ์ œ์–ด ๊ฐ€๋Šฅ
  • ์ด๋ฏธ management.yaml์—์„œ workflow_dispatch ํŒจํ„ด์„ ์“ฐ๊ณ  ์žˆ์–ด์„œ ํŒ€์— ์ต์ˆ™

3. Production Worker์— ๋ผ์šฐํŒ… ๋กœ์ง ์ถ”๊ฐ€

/webhooks ํ•ธ๋“ค๋Ÿฌ ์ตœ์ƒ๋‹จ์— staging ๋ผ๋ฒจ ๊ฐ์ง€ ๋ฐ ํฌ์›Œ๋”ฉ:

const labels = payload.pull_request?.labels?.map(l => l.name) || [];
if (labels.includes("staging")) {
  return fetch(env.STAGING_WORKER_URL + "/webhooks", request);
}
  • STAGING_WORKER_URL์€ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ๊ด€๋ฆฌ
  • ํฌ์›Œ๋”ฉ ์‹คํŒจ ์‹œ์—๋„ ํ”„๋กœ๋•์…˜ ์ฒ˜๋ฆฌ์— ์˜ํ–ฅ ์—†์Œ

4. Secrets ์„ค์ •

Staging Worker Secrets (3๊ฐœ)

Cloudflare Workers secrets๋Š” write-only๋กœ, ๊ธฐ์กด Production ๊ฐ’์„ ์กฐํšŒ/๋ณต์ œํ•  ์ˆ˜ ์—†๋‹ค. Org owner ๋˜๋Š” Worker owner์—๊ฒŒ ์•„๋ž˜ 3๊ฐœ ๊ฐ’์„ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค:

Secret ํ™•์ธ ๋ฐฉ๋ฒ• ๋น„๊ณ 
APP_ID GitHub App ์„ค์ • ํŽ˜์ด์ง€์—์„œ ํ™•์ธ (์ˆซ์ž) ๋น„๋ฐ€ ์ •๋ณด ์•„๋‹˜
PRIVATE_KEY ์›๋ณธ PEM ํŒŒ์ผ ํ•„์š”. ์—†์œผ๋ฉด GitHub App ์„ค์ •์—์„œ ์ƒˆ ํ‚ค ๋ฐœ๊ธ‰ ๊ฐ€๋Šฅ ์ƒˆ ํ‚ค ๋ฐœ๊ธ‰ ์‹œ ๊ธฐ์กด ํ‚ค์™€ ๋ณ„๊ฐœ๋กœ ์ถ”๊ฐ€๋จ
OPENAI_API_KEY OpenAI ๋Œ€์‹œ๋ณด๋“œ์—์„œ ํ™•์ธ ๋˜๋Š” ์ƒˆ ํ‚ค ๋ฐœ๊ธ‰
npx wrangler secret put APP_ID --env staging
npx wrangler secret put PRIVATE_KEY --env staging
npx wrangler secret put OPENAI_API_KEY --env staging

WEBHOOK_SECRET์€ ๋ถˆํ•„์š”

Production Worker๊ฐ€ GitHub ์„œ๋ช…์„ ๊ฒ€์ฆํ•œ ํ›„์— Staging์œผ๋กœ ํฌ์›Œ๋”ฉํ•˜๋ฏ€๋กœ, Staging์—์„œ ๋‹ค์‹œ ๊ฒ€์ฆํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ์ฝ”๋“œ์—์„œ env.WEBHOOK_SECRET ๋ฏธ์„ค์ • ์‹œ ๊ฒ€์ฆ์„ ์ž๋™ ์Šคํ‚ตํ•œ๋‹ค:

GitHub  โ”€โ”€โ”€ X-Hub-Signature-256 โ”€โ”€โ”€โ–บ  Production Worker  โ”€โ”€โ”€ forward โ”€โ”€โ”€โ–บ  Staging Worker
                                          โœ… ๊ฒ€์ฆ ์™„๋ฃŒ                       ๊ฒ€์ฆ ์Šคํ‚ต
                                     (env.WEBHOOK_SECRET)             (WEBHOOK_SECRET ๋ฏธ์„ค์ •)

Production Worker ํ™˜๊ฒฝ๋ณ€์ˆ˜ (1๊ฐœ)

Staging Worker ๋ฐฐํฌ ํ›„ URL์„ ํ™•์ธํ•˜์—ฌ Production์— ์„ค์ •:

npx wrangler secret put STAGING_WORKER_URL
# ์ž…๋ ฅ: https://github-staging.daleseo.workers.dev

GitHub Actions Secret (1๊ฐœ)

GitHub repo Settings > Secrets > Actions์—์„œ ์„ค์ •:

  • CLOUDFLARE_API_TOKEN: Cloudflare API Token (Workers ๋ฐฐํฌ ๊ถŒํ•œ)

5. ํ…Œ์ŠคํŠธ ์›Œํฌํ”Œ๋กœ์šฐ

  1. ์ด ๋ ˆํฌ์—์„œ ๊ธฐ๋Šฅ ๋ธŒ๋žœ์น˜ ์ž‘์„ฑ
  2. GitHub Actions์—์„œ ํ•ด๋‹น ๋ธŒ๋žœ์น˜๋ฅผ ์„ ํƒํ•˜์—ฌ ์Šคํ…Œ์ด์ง• ๋ฐฐํฌ ์‹คํ–‰
  3. leetcode-study์—์„œ ํ…Œ์ŠคํŠธ PR ์ƒ์„ฑ, staging ๋ผ๋ฒจ ๋ถ€์ฐฉ
  4. Staging Worker๊ฐ€ ์ฒ˜๋ฆฌ โ†’ ํ…Œ์ŠคํŠธ PR์—์„œ ๋™์ž‘ ํ™•์ธ
  5. ํ™•์ธ ์™„๋ฃŒ โ†’ ๊ธฐ๋Šฅ ๋ธŒ๋žœ์น˜๋ฅผ main์— ๋ณ‘ํ•ฉ โ†’ ์šด์˜ ์ž๋™ ๋ฐฐํฌ
  6. ํ…Œ์ŠคํŠธ PR์€ close

6. ๊ณ ๋ ค์‚ฌํ•ญ

  • projects_v2_item ์ด๋ฒคํŠธ๋Š” payload์— PR ๋ผ๋ฒจ ์ •๋ณด๊ฐ€ ์ง์ ‘ ํฌํ•จ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ํ•ญ์ƒ Production์—์„œ ์ฒ˜๋ฆฌ๋จ
  • issue_comment, pull_request_review_comment ์ด๋ฒคํŠธ๋Š” payload.issue.labels ๋˜๋Š” payload.pull_request.labels๋กœ ๋ผ๋ฒจ ํ™•์ธ ๊ฐ€๋Šฅ
  • Staging Worker์˜ ๋Œ“๊ธ€/๋ฆฌ๋ทฐ๊ฐ€ ํ…Œ์ŠคํŠธ PR์— ์‹ค์ œ๋กœ ๋‹ฌ๋ฆฌ๋ฏ€๋กœ, ์Šคํ„ฐ๋”” ์ฐธ์—ฌ์ž์—๊ฒŒ ํ˜ผ๋™์ด ์—†๋„๋ก staging ๋ผ๋ฒจ์˜ ์šฉ๋„๋ฅผ ํŒ€์— ๊ณต์œ 

๊ธฐ๋Œ€ ํšจ๊ณผ

  • ์šด์˜ ํ™˜๊ฒฝ ์žฅ์•  ์˜ˆ๋ฐฉ (revert ํšŸ์ˆ˜ ๊ฐ์†Œ)
  • PR ๋ฆฌ๋ทฐ ์‹œ ์‹ค์ œ webhook ๋™์ž‘์„ ์Šคํ…Œ์ด์ง•์—์„œ ํ™•์ธ ๊ฐ€๋Šฅ
  • ๊ฐœ๋ฐœ ์†๋„ ํ–ฅ์ƒ (๋น ๋ฅธ ํ”ผ๋“œ๋ฐฑ ๋ฃจํ”„)

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions