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
113 changes: 113 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: Deploy to Production

on:
push:
branches: [prod]

Comment on lines +3 to +6
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a concurrency group for this workflow (or at least for the deploy job) to prevent overlapping production deploys. Without concurrency/cancel-in-progress, two quick pushes to prod can run two deploy jobs in parallel and the older run may finish last, effectively redeploying an older commit after a newer one.

Copilot uses AI. Check for mistakes.
jobs:
test:
runs-on: ubuntu-24.04

steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Enable corepack
run: corepack enable

- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version-file: ".node-version"
cache: "pnpm"
cache-dependency-path: "pnpm-lock.yaml"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Run tests
run: pnpm test:run

- name: Run linter (without build artifacts)
run: rm -rf dist && rm -f hono/static/*.js hono/static/*.js.map && pnpm lint

- name: Run build
run: pnpm build

e2e:
runs-on: ubuntu-24.04

steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Enable corepack
run: corepack enable

- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version-file: ".node-version"
cache: "pnpm"
cache-dependency-path: "pnpm-lock.yaml"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Install Playwright Browsers
run: pnpm exec playwright install --with-deps chromium

- name: Build JavaScript files
run: pnpm build

- name: Run database migrations
run: pnpm db:migrate

- name: Seed database
run: pnpm db:seed

- name: Run Playwright tests
env:
SESSION_SECRET: ${{ secrets.SESSION_SECRET }}
E2E_GMAIL_ACCOUNT: ${{ secrets.E2E_GMAIL_ACCOUNT }}
E2E_GMAIL_PASSWORD: ${{ secrets.E2E_GMAIL_PASSWORD }}
run: pnpm test:e2e

- name: Upload test artifacts
uses: actions/upload-artifact@v5
if: failure()
with:
name: playwright-report
path: playwright-report/
retention-days: 30

deploy:
needs: [test, e2e]
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deploy job uses a repo-level secret (CLOUDFLARE_API_TOKEN) and runs on every push to prod. If you intend to gate production deployments, consider using a GitHub Actions environment: production on the deploy job so you can enforce required reviewers / deployment protection rules and scope secrets to that environment.

Suggested change
needs: [test, e2e]
needs: [test, e2e]
environment: production

Copilot uses AI. Check for mistakes.
runs-on: ubuntu-24.04

steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Enable corepack
run: corepack enable

- name: Setup Node.js
uses: actions/setup-node@v5
with:
node-version-file: ".node-version"
cache: "pnpm"
cache-dependency-path: "pnpm-lock.yaml"

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Apply D1 database migrations
run: pnpm db:migrate:prod
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}

- name: Deploy to Cloudflare Workers
run: pnpm run deploy
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
35 changes: 33 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,40 @@ pnpm lint:fix
pnpm run deploy
```

### Subsequent Deployments
### Automated Deployment (CI/CD)

After the initial setup, deploy with:
Production deployments are automated via GitHub Actions. Pushing to the `prod` branch triggers the deployment pipeline defined in `.github/workflows/deploy.yml`.

**Pipeline overview:**

1. **`test` job** — Runs unit tests, linter, and full build
2. **`e2e` job** — Runs Playwright E2E tests (in parallel with `test`)
3. **`deploy` job** — Applies D1 migrations and deploys to Cloudflare Workers (only runs after both `test` and `e2e` pass)

**Typical workflow:**

```bash
# Merge main into prod to trigger a deployment
git checkout prod
git merge main
git push origin prod
```

**Required GitHub secret:**

The deploy job authenticates with Cloudflare using the `CLOUDFLARE_API_TOKEN` repository secret. To set it up:

1. Go to [Cloudflare Dashboard > API Tokens](https://dash.cloudflare.com/profile/api-tokens) and create a token with these permissions:
- **Workers Scripts: Edit**
- **D1: Edit**
- **Account Settings: Read**
2. Add the token as `CLOUDFLARE_API_TOKEN` in your GitHub repository under **Settings > Secrets and variables > Actions > New repository secret**

The E2E job reuses the existing `SESSION_SECRET`, `E2E_GMAIL_ACCOUNT`, and `E2E_GMAIL_PASSWORD` secrets already configured for CI.

### Manual Deployment

You can still deploy manually if needed:

```bash
pnpm run deploy
Expand Down
Loading