Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Deploy ๐Ÿš€

on:
push:
branches: [main]
workflow_dispatch:

concurrency:
group: deploy-production
cancel-in-progress: false

jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v7

- uses: oven-sh/setup-bun@v2

- run: bun install --frozen-lockfile

- run: bun run build

- name: Write secrets file
env:
GITHUB_CLIENT_SECRET: ${{ secrets.APP_CLIENT_SECRET }}
run: printf 'GITHUB_CLIENT_SECRET=%s\n' "$GITHUB_CLIENT_SECRET" > "$RUNNER_TEMP/secrets.env"

- name: Deploy
uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
packageManager: bun
command: deploy --secrets-file ${{ runner.temp }}/secrets.env
77 changes: 77 additions & 0 deletions .github/workflows/preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Preview ๐Ÿ”

on:
pull_request:
types: [opened, synchronize, reopened, closed]

concurrency:
group: preview-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
preview:
# ํฌํฌ PR์—๋Š” secret์ด ์ „๋‹ฌ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๊ฐ™์€ ์ €์žฅ์†Œ์˜ PR๋งŒ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ๋งŒ๋“ ๋‹ค
if: >
github.event.action != 'closed' &&
github.event.pull_request.head.repo.full_name == github.repository &&
github.actor != 'dependabot[bot]'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v7

- uses: oven-sh/setup-bun@v2

- run: bun install --frozen-lockfile

# ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ผ๋ฆฌ ํ•จ๊ป˜ ์“ฐ๋Š” D1(feedback-preview)์— ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์„ ๋จผ์ € ์ ์šฉํ•œ๋‹ค
- name: Migrate preview D1
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
run: bun run db:migrate:preview

- run: bun run build

- name: Write secrets file
env:
GITHUB_CLIENT_SECRET: ${{ secrets.APP_CLIENT_SECRET }}
run: printf 'GITHUB_CLIENT_SECRET=%s\n' "$GITHUB_CLIENT_SECRET" > "$RUNNER_TEMP/secrets.env"

- name: Deploy preview
id: preview
uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
packageManager: bun
command: preview --name pr-${{ github.event.pull_request.number }} --secrets-file ${{ runner.temp }}/secrets.env

- name: Comment
if: always()
uses: marocchino/sticky-pull-request-comment@v3
with:
header: cloudflare-workers
message: |
${{ steps.preview.outcome == 'success' && 'โœ…' || 'โŒ' }} ๋ฏธ๋ฆฌ๋ณด๊ธฐ: https://pr-${{ github.event.pull_request.number }}.feedback-preview.dalestudy.com ยท [์‹คํ–‰ ๋กœ๊ทธ](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})

cleanup:
if: >
github.event.action == 'closed' &&
github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v7

- uses: oven-sh/setup-bun@v2

- run: bun install --frozen-lockfile

- name: Delete preview
uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
packageManager: bun
command: preview delete --name pr-${{ github.event.pull_request.number }} -y
13 changes: 6 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,25 @@ bun run db:seed:local <json> # ์„ค๋ฌธ JSON(ํŽธ์ง‘์žยท๋ฌธํ•ญ)์„ ๋กœ์ปฌ D1 ์—
bunx drizzle-kit generate --name <desc> # ์Šคํ‚ค๋งˆ ๋ณ€๊ฒฝ ํ›„ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ƒ์„ฑ
bun run db:migrate:remote # ํ”„๋กœ๋•์…˜ D1 ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ (์•„๋ž˜ Gotchas ์˜ ์ˆœ์„œ๋ฅผ ์ง€ํ‚จ๋‹ค)
bun run db:migrate:preview # ๋ฏธ๋ฆฌ๋ณด๊ธฐ D1 ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜
bun run deploy:preview # ์ง€๊ธˆ ๋ธŒ๋žœ์น˜์˜ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ˆ˜๋™ ๋ฐฐํฌ (๋ณดํ†ต์€ push ๋•Œ Workers Builds ๊ฐ€ ํ•œ๋‹ค)
bun run deploy:preview # ์ง€๊ธˆ ๋ธŒ๋žœ์น˜์˜ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ˆ˜๋™ ๋ฐฐํฌ (๋ณดํ†ต์€ PR ๋•Œ GitHub Actions ๊ฐ€ ํ•œ๋‹ค)
```

## Local setup

- ๋กœ์ปฌ ์‹œํฌ๋ฆฟ์€ `.dev.vars` (git ์— ์˜ฌ๋ผ๊ฐ€์ง€ ์•Š์Œ): `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET` โ€” DaleStudy GitHub App ์˜ Client IDยทsecret. ์—†์œผ๋ฉด ๋กœ๊ทธ์ธ ๋Œ€์‹  `users`ยท`sessions` ์— ํ–‰์„ ์ง์ ‘ ๋„ฃ๊ณ  `Cookie: __Host-session=<id>` ๋กœ ์š”์ฒญํ•œ๋‹ค. ์‹ค์ œ GitHub ๋กœ๊ทธ์ธ์€ PR ๋ฏธ๋ฆฌ๋ณด๊ธฐ์—์„œ ๋œ๋‹ค.
- ๋กœ์ปฌ ์‹œํฌ๋ฆฟ์€ `.dev.vars` (git ์— ์˜ฌ๋ผ๊ฐ€์ง€ ์•Š์Œ): `GITHUB_CLIENT_SECRET` โ€” DaleStudy GitHub App ์˜ client secret (Client ID ๋Š” ๊ณต๊ฐœ ๊ฐ’์ด๋ผ `wrangler.jsonc` ์˜ `vars` ์— ์žˆ๋‹ค). ์—†์œผ๋ฉด ๋กœ๊ทธ์ธ ๋Œ€์‹  `users`ยท`sessions` ์— ํ–‰์„ ์ง์ ‘ ๋„ฃ๊ณ  `Cookie: __Host-session=<id>` ๋กœ ์š”์ฒญํ•œ๋‹ค. ์‹ค์ œ GitHub ๋กœ๊ทธ์ธ์€ PR ๋ฏธ๋ฆฌ๋ณด๊ธฐ์—์„œ ๋œ๋‹ค.
- `__Host-` ์ ‘๋‘์‚ฌ ์ฟ ํ‚ค๋Š” Secure ๊ฐ€ ํ•„์ˆ˜๋ผ, localhost ๋ฅผ secure context ๋กœ ์ทจ๊ธ‰ํ•˜๋Š” Chrome/Firefox ์—์„œ ๊ฐœ๋ฐœํ•œ๋‹ค.
- seed JSON(`seed/*.json`, ํ˜•์‹์€ `seed/example.json`)์€ ๋กœ์ปฌ D1 ์šฉ์ด๋‹ค. ํ”„๋กœ๋•์…˜ ์„ค๋ฌธ์€ ํ™”๋ฉด์—์„œ ๋งŒ๋“ค๊ฑฐ๋‚˜ ๋ณต์ œํ•œ๋‹ค. `editors` ๊ฐ€ ํŽธ์ง‘์ž, `survey.id` ๋ฅผ ๋นผ๋ฉด nanoid ๋กœ ๋งŒ๋“ ๋‹ค. ๊ฐ™์€ id ๊ฐ€ ์žˆ์œผ๋ฉด ์‹คํŒจํ•œ๋‹ค. ๋ฌธํ•ญ์€ `{ "common": "<key>", "required"? }`(๊ณตํ†ต ๋ฌธํ•ญ, ๋ฌธ๊ตฌ๋Š” `vars` ๋กœ ์ฑ„์›€) ๋˜๋Š” `{ "type", "label", "required"?, "config"? }`(์ด ์„ค๋ฌธ๋งŒ์˜ ๋ฌธํ•ญ). ์œ ํ˜•๋ณ„ ์ €์žฅ๊ฐ’ยท๊ฒ€์ฆยท`config` ๋Š” `docs/questions.md`.

## Deployment

- ๋ณ€๊ฒฝ์€ ๋ธŒ๋žœ์น˜ โ†’ PR โ†’ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์—์„œ ํ™•์ธ โ†’ squash merge. `main` ์— ์ง์ ‘ pushยทforce push ํ•˜์ง€ ์•Š๋Š”๋‹ค โ€” ๋‹ค๋ฅธ ์‚ฌ๋žŒ๊ณผ ํ•จ๊ป˜ ์“ฐ๋Š” ์ €์žฅ์†Œ๊ฐ€ ๋๊ณ , ๋จธ์ง€ ๊ธฐ๋ก์ด ๊ณง ๋ฐฐํฌ ๊ธฐ๋ก์ด๋‹ค.
- `main` ์— ๋จธ์ง€๋˜๋ฉด Cloudflare Workers Builds ๊ฐ€ `bun run build` โ†’ `npx wrangler deploy`. ๋นŒ๋“œ ๊ฒฐ๊ณผ๋Š” ์ปค๋ฐ‹์˜ check run `Workers Builds: feedback` ์œผ๋กœ ํ™•์ธํ•œ๋‹ค. `bun run deploy` ๋Š” ์ˆ˜๋™ fallback.
- **๋ฏธ๋ฆฌ๋ณด๊ธฐ**: `main` ์ด ์•„๋‹Œ ๋ธŒ๋žœ์น˜๋ฅผ push ํ•˜๋ฉด Workers Builds ๊ฐ€ `bun run db:migrate:preview && bun run deploy:preview`(Workers Previews, `wrangler preview`)๋กœ `https://<๋ธŒ๋žœ์น˜>.feedback-preview.dalestudy.com` ์„ ๋งŒ๋“ ๋‹ค. ๋ชจ๋“  ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋Š” D1 `feedback-preview` ๋ฅผ ํ•จ๊ป˜ ์“ด๋‹ค โ€” ํ”„๋กœ๋•์…˜ ์‘๋‹ต์„ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š” ๊ฒŒ ๋ชฉ์ ์ด๋‹ค.
- `main` ์— ๋จธ์ง€๋˜๋ฉด GitHub Actions(`.github/workflows/deploy.yml`)๊ฐ€ `bun run build` โ†’ `wrangler deploy --secrets-file` ๋กœ ๋ฐฐํฌํ•œ๋‹ค. `GITHUB_CLIENT_SECRET` ์€ org secret `APP_CLIENT_SECRET` ์—์„œ ๋ฐฐํฌ์™€ ํ•จ๊ป˜ ๋„ฃ๋Š”๋‹ค(๋”ฐ๋กœ `wrangler secret put` ํ•˜์ง€ ์•Š๋Š”๋‹ค โ€” ๋‹ค์Œ ๋ฐฐํฌ ๋•Œ GitHub ๊ฐ’์œผ๋กœ ๋ฎ์ธ๋‹ค). `bun run deploy` ๋Š” ์ˆ˜๋™ fallback.
- **๋ฏธ๋ฆฌ๋ณด๊ธฐ**: ๊ฐ™์€ ์ €์žฅ์†Œ์˜ PR ์„ ์—ด๊ฑฐ๋‚˜ push ํ•˜๋ฉด GitHub Actions(`.github/workflows/preview.yml`)๊ฐ€ `bun run db:migrate:preview` โ†’ `wrangler preview --name pr-<๋ฒˆํ˜ธ> --secrets-file` ๋กœ `https://pr-<๋ฒˆํ˜ธ>.feedback-preview.dalestudy.com` ์„ ๋งŒ๋“ค๊ณ  PR ๋Œ“๊ธ€๋กœ ์•Œ๋ฆฐ๋‹ค. PR ์ด ๋‹ซํžˆ๋ฉด ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ์ง€์šด๋‹ค. ๋ชจ๋“  ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋Š” D1 `feedback-preview` ๋ฅผ ํ•จ๊ป˜ ์“ด๋‹ค โ€” ํ”„๋กœ๋•์…˜ ์‘๋‹ต์„ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š” ๊ฒŒ ๋ชฉ์ ์ด๋‹ค.
- `wrangler.jsonc` ์˜ `previews` ๋ธ”๋ก์— D1 ์„ ๋‹ค์‹œ ์„ ์–ธํ•œ๋‹ค(์ƒ์†๋˜์ง€ ์•Š๋Š”๋‹ค). ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์€ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ „์šฉ ํ™˜๊ฒฝ `env.migrate-preview` ๋กœ ํ•œ๋‹ค โ€” `previews` ๋ธ”๋ก์˜ D1 ์€ `d1 migrations apply` ๊ฐ€ ์ฐพ์ง€ ๋ชปํ•˜๊ณ , `preview_database_id` ๋ฅผ ์“ฐ๋ฉด ๋กœ์ปฌ ๊ฐœ๋ฐœ๊นŒ์ง€ ๊ทธ id ์˜ ๋นˆ DB ๋ฅผ ์“ฐ๊ฒŒ ๋œ๋‹ค.
- `versions upload` ์˜ ๋ฒ„์ „ URL ์€ ํ”„๋กœ๋•์…˜ D1 ์„ ์จ์„œ ์“ฐ์ง€ ์•Š๋Š”๋‹ค(`preview_urls` ๋„ ๋”).
- ๋ธŒ๋žœ์น˜๋ณ„ ํŠธ๋ฆฌ๊ฑฐ์™€ ์‹œํฌ๋ฆฟ์€ ๋ธŒ๋žœ์น˜๊ฐ€ ์ฒ˜์Œ ๋นŒ๋“œ๋  ๋•Œ์˜ base config ๋ฅผ ๋ณต์‚ฌํ•œ๋‹ค. ๋Œ€์‹œ๋ณด๋“œ์˜ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ๋ช…๋ น์ด๋‚˜ `wrangler preview base-config secret put` ์„ ๋ฐ”๊พผ ๋’ค์—๋Š” ๊ธฐ์กด ๋ธŒ๋žœ์น˜์— ๋”ฐ๋กœ ๋„ฃ๋Š”๋‹ค(`wrangler preview secret put <KEY> --name <๋ธŒ๋žœ์น˜>`). ์ฆ์ƒ์€ GitHub ๋กœ `client_id=undefined` ๊ฐ€ ๊ฐ€๋Š” ๊ฒƒ.
- ์ด๋ฏธ ์„ค์ •๋œ ๊ฒƒ: D1 `feedback`ยท`feedback-preview`(APAC, id ๋Š” `wrangler.jsonc`), ์‹œํฌ๋ฆฟ `GITHUB_CLIENT_ID`ยท`GITHUB_CLIENT_SECRET`, ์ปค์Šคํ…€ ๋„๋ฉ”์ธ(`wrangler.jsonc` ์˜ `routes`), ๋นŒ๋“œ ๋ณ€์ˆ˜ `BUN_VERSION=1.4.0`(๋นŒ๋“œ ์ด๋ฏธ์ง€ ๊ธฐ๋ณธ bun ์€ lockfile v2 ๋ฅผ ๋ชป ์ฝ๋Š”๋‹ค. ๋กœ์ปฌ bun ์„ ์˜ฌ๋ฆฌ๋ฉด ๊ฐ™์ด ์˜ฌ๋ฆฐ๋‹ค).
- ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์‹œํฌ๋ฆฟ๋„ ๋ฐฐํฌํ•  ๋•Œ๋งˆ๋‹ค `--secrets-file` ๋กœ ๋„ฃ์œผ๋ฏ€๋กœ Previews base config ์—๋Š” ์‹œํฌ๋ฆฟ์„ ๋‘์ง€ ์•Š๋Š”๋‹ค.
- ์ด๋ฏธ ์„ค์ •๋œ ๊ฒƒ: D1 `feedback`ยท`feedback-preview`(APAC, id ๋Š” `wrangler.jsonc`), org secret `CLOUDFLARE_API_TOKEN`ยท`APP_CLIENT_SECRET`, ์ปค์Šคํ…€ ๋„๋ฉ”์ธ(`wrangler.jsonc` ์˜ `routes`).
- GitHub App ์˜ Callback URL ์€ localhost, `https://feedback.dalestudy.com/auth/callback`, `https://feedback-preview.dalestudy.com/auth/callback`(์™€์ผ๋“œ์นด๋“œ) ์…‹. ์•ฑ์€ **๊ณต๊ฐœ(public)** ์—ฌ์•ผ ํ•œ๋‹ค โ€” ๋น„๊ณต๊ฐœ ์•ฑ์€ ์†Œ์œ  ์กฐ์ง ๋ฉค๋ฒ„๋งŒ authorize ํ•  ์ˆ˜ ์žˆ์–ด์„œ ์กฐ์ง ๋ฐ– ์ฐธ๊ฐ€์ž๊ฐ€ GitHub ์˜ authorize ํŽ˜์ด์ง€์—์„œ 404 ๋ฅผ ๋ณธ๋‹ค(์šฐ๋ฆฌ ๋กœ๊ทธ์—๋Š” ์•ˆ ๋‚จ๋Š”๋‹ค).
- ์ €์žฅ์†Œ๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“ค๋ฉด Workers Builds ์—ฐ๊ฒฐ์ด ๋Š๊ธด๋‹ค(๋Œ€์‹œ๋ณด๋“œ์—๋Š” ์ด๋ฆ„์ด ๊ทธ๋Œ€๋กœ ๋ณด์—ฌ๋„). ๋Œ€์‹œ๋ณด๋“œ์—์„œ Disconnect โ†’ Connect ๋กœ ๋‹ค์‹œ ์ž‡๋Š”๋‹ค.

## Architecture

Expand Down
3 changes: 1 addition & 2 deletions src/env.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// wrangler secret put ์œผ๋กœ ๋“ฑ๋กํ•˜๋Š” ๊ฐ’๋“ค. wrangler types ๋Š” ์‹œํฌ๋ฆฟ์„ ๋ชจ๋ฅด๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ ๋ณด๊ฐ•ํ•œ๋‹ค.
// ๋ฐฐํฌํ•  ๋•Œ GitHub secret์—์„œ ๋„ฃ๋Š” ๊ฐ’. wrangler types ๋Š” ์‹œํฌ๋ฆฟ์„ ๋ชจ๋ฅด๋ฏ€๋กœ ์—ฌ๊ธฐ์„œ ๋ณด๊ฐ•ํ•œ๋‹ค.
declare namespace Cloudflare {
interface Env {
GITHUB_CLIENT_ID: string
GITHUB_CLIENT_SECRET: string
}
}
4 changes: 4 additions & 0 deletions wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"main": "@tanstack/react-start/server-entry",
"observability": { "enabled": true },
"upload_source_maps": true,
// GitHub App(dalestudy)์˜ Client ID. ๋กœ๊ทธ์ธ URL์— ๊ทธ๋Œ€๋กœ ๋“œ๋Ÿฌ๋‚˜๋Š” ๊ณต๊ฐœ ๊ฐ’์ด๋‹ค.
"vars": { "GITHUB_CLIENT_ID": "Iv23liwu6Ob75w8NcAX9" },
"routes": [
{ "pattern": "feedback.dalestudy.com", "custom_domain": true },
// ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ „์šฉ ๋„๋ฉ”์ธ: ํ”„๋กœ๋•์…˜ ํŠธ๋ž˜ํ”ฝ์€ ๋ฐ›์ง€ ์•Š๋Š”๋‹ค(enabled: false)
Expand All @@ -23,6 +25,7 @@
// PR ๋ฏธ๋ฆฌ๋ณด๊ธฐ(Workers Previews). ๋ธŒ๋žœ์น˜๋งˆ๋‹ค <๋ธŒ๋žœ์น˜>.feedback-preview.dalestudy.com ์— ๋œฌ๋‹ค.
// D1 ์€ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ผ๋ฆฌ ํ•จ๊ป˜ ์“ฐ๋Š” feedback-preview ๋กœ โ€” ํ”„๋กœ๋•์…˜ ์‘๋‹ต์„ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ฒŒ.
"previews": {
"vars": { "GITHUB_CLIENT_ID": "Iv23liwu6Ob75w8NcAX9" },
"d1_databases": [
{
"binding": "DB",
Expand All @@ -36,6 +39,7 @@
// `preview_database_id` ๋กœ ํ•˜๋ฉด ๋กœ์ปฌ ๊ฐœ๋ฐœ๋„ ๊ทธ id ์˜ ๋นˆ DB ๋ฅผ ์“ฐ๊ฒŒ ๋ผ์„œ ์ด๋ ‡๊ฒŒ ๋‘”๋‹ค. ๋ฐฐํฌ์—๋Š” ์“ฐ์ง€ ์•Š๋Š”๋‹ค.
"env": {
"migrate-preview": {
"vars": { "GITHUB_CLIENT_ID": "Iv23liwu6Ob75w8NcAX9" },
"d1_databases": [
{
"binding": "DB",
Expand Down
Loading