Phase 7: CI/CD & Deployment¶
Mục tiêu¶
Setup automated CI/CD pipeline với GitHub Actions cho multi-environment deployment.
Thời gian ước tính: 2 giờ
Environment Strategy¶
Environments¶
| Environment | Branch | Supabase Project | Cloudflare Worker |
|---|---|---|---|
| Development | - | Local (supabase start) | wrangler dev |
| Staging | staging |
taskflow-staging | taskflow-staging |
| Production | main |
taskflow-prod | taskflow |
Branch Strategy¶
Step 1: GitHub Repository Setup¶
1.1 Create GitHub Environments¶
- Go to Repository → Settings → Environments
- Create
stagingenvironment: - No protection rules (auto-deploy)
- Create
productionenvironment: - Required reviewers: 1+
- Deployment branches:
mainonly
1.2 Add Secrets¶
For each environment, add:
# Supabase
SUPABASE_PROJECT_REF
SUPABASE_ACCESS_TOKEN
SUPABASE_DB_PASSWORD
# Cloudflare
CLOUDFLARE_API_TOKEN
CLOUDFLARE_ACCOUNT_ID
# App secrets
SUPABASE_SERVICE_ROLE_KEY
SENDGRID_API_KEY
1.3 Add Variables¶
# Staging
SUPABASE_URL=https://staging.supabase.co
SUPABASE_ANON_KEY=eyJ...
# Production
SUPABASE_URL=https://production.supabase.co
SUPABASE_ANON_KEY=eyJ...
Step 2: CI Workflow¶
# .github/workflows/ci.yml
name: CI
on:
pull_request:
branches: [main, staging]
push:
branches: [main, staging]
jobs:
lint-and-type-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Install dependencies
run: pnpm install
- name: Lint
run: pnpm lint
- name: Type check
run: pnpm tsc --noEmit
build:
runs-on: ubuntu-latest
needs: lint-and-type-check
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Install dependencies
run: pnpm install
- name: Build
run: pnpm build
env:
NEXT_PUBLIC_SUPABASE_URL: ${{ vars.SUPABASE_URL }}
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ vars.SUPABASE_ANON_KEY }}
- name: Build for Cloudflare
run: pnpm build:worker
test:
runs-on: ubuntu-latest
needs: lint-and-type-check
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Install dependencies
run: pnpm install
- name: Run tests
run: pnpm test
Step 3: Staging Deployment¶
# .github/workflows/deploy-staging.yml
name: Deploy Staging
on:
push:
branches: [staging]
jobs:
deploy-database:
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- name: Setup Supabase CLI
uses: supabase/setup-cli@v1
with:
version: latest
- name: Link Supabase project
run: supabase link --project-ref ${{ vars.SUPABASE_PROJECT_REF }}
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
- name: Run migrations
run: supabase db push
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
- name: Verify migrations
run: supabase migration list
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
deploy-app:
runs-on: ubuntu-latest
needs: deploy-database
environment: staging
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Install dependencies
run: pnpm install
- name: Build for Cloudflare
run: pnpm build:worker
env:
NEXT_PUBLIC_SUPABASE_URL: ${{ vars.SUPABASE_URL }}
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ vars.SUPABASE_ANON_KEY }}
- name: Deploy to Cloudflare (Staging)
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --env staging
- name: Set Cloudflare secrets
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: secret put SUPABASE_SERVICE_KEY --env staging
secrets: |
SUPABASE_SERVICE_KEY=${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}
deploy-workers:
runs-on: ubuntu-latest
needs: deploy-database
environment: staging
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Deploy Queue Consumer
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: workers/notification-consumer
command: deploy --env staging
- name: Deploy Cron Worker
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: workers/cron
command: deploy --env staging
Step 4: Production Deployment¶
# .github/workflows/deploy-production.yml
name: Deploy Production
on:
push:
branches: [main]
jobs:
deploy-database:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Setup Supabase CLI
uses: supabase/setup-cli@v1
with:
version: latest
- name: Link Supabase project
run: supabase link --project-ref ${{ vars.SUPABASE_PROJECT_REF }}
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
- name: Backup before migration
run: |
echo "Creating backup checkpoint..."
# For Pro plans, this triggers a backup
# supabase db dump -f backup-$(date +%Y%m%d%H%M%S).sql
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
- name: Run migrations
run: supabase db push
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
- name: Verify migrations
run: supabase migration list
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
deploy-app:
runs-on: ubuntu-latest
needs: deploy-database
environment: production
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: 8
- name: Install dependencies
run: pnpm install
- name: Build for Cloudflare
run: pnpm build:worker
env:
NEXT_PUBLIC_SUPABASE_URL: ${{ vars.SUPABASE_URL }}
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ vars.SUPABASE_ANON_KEY }}
- name: Deploy to Cloudflare (Production)
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy
deploy-workers:
runs-on: ubuntu-latest
needs: deploy-database
environment: production
steps:
- uses: actions/checkout@v4
- name: Deploy Queue Consumer
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: workers/notification-consumer
command: deploy
- name: Deploy Cron Worker
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: workers/cron
command: deploy
notify:
runs-on: ubuntu-latest
needs: [deploy-app, deploy-workers]
if: always()
steps:
- name: Notify Slack on success
if: ${{ needs.deploy-app.result == 'success' }}
run: |
curl -X POST ${{ secrets.SLACK_WEBHOOK_URL }} \
-H 'Content-Type: application/json' \
-d '{"text": "TaskFlow deployed to production successfully!"}'
- name: Notify Slack on failure
if: ${{ needs.deploy-app.result == 'failure' }}
run: |
curl -X POST ${{ secrets.SLACK_WEBHOOK_URL }} \
-H 'Content-Type: application/json' \
-d '{"text": "TaskFlow production deployment failed!"}'
Step 5: Wrangler Configuration¶
Main App (multi-environment)¶
# wrangler.toml
name = "taskflow"
main = ".open-next/worker.js"
compatibility_date = "2024-01-01"
compatibility_flags = ["nodejs_compat"]
[site]
bucket = ".open-next/assets"
# Staging environment
[env.staging]
name = "taskflow-staging"
vars = { APP_ENV = "staging" }
[[env.staging.queues.producers]]
queue = "taskflow-notifications-staging"
binding = "NOTIFICATION_QUEUE"
# Production (default)
[vars]
APP_ENV = "production"
[[queues.producers]]
queue = "taskflow-notifications"
binding = "NOTIFICATION_QUEUE"
Workers (multi-environment)¶
# workers/notification-consumer/wrangler.toml
name = "taskflow-notification-consumer"
main = "src/index.ts"
[env.staging]
name = "taskflow-notification-consumer-staging"
[[env.staging.queues.consumers]]
queue = "taskflow-notifications-staging"
max_batch_size = 10
max_retries = 3
[[queues.consumers]]
queue = "taskflow-notifications"
max_batch_size = 10
max_retries = 3
Step 6: Local Development¶
# Start Supabase locally
supabase start
# Run Next.js dev server
pnpm dev
# In another terminal, run worker locally
pnpm preview
Rollback Procedures¶
Database Rollback¶
-- Option 1: Create reverse migration
supabase migration new rollback_xxx
-- In the migration file, write reverse operations
-- Option 2: Point-in-time recovery (Pro plan)
-- Dashboard → Database → Backups → Restore
Cloudflare Rollback¶
# Rollback to previous version
wrangler rollback
# Or deploy specific version
wrangler deploy --version-id <version-id>
GitHub Actions Rollback¶
Verification Checklist¶
- [ ] CI workflow passing (lint, type-check, build, test)
- [ ] Staging deployment working
- [ ] Production deployment with approval
- [ ] Database migrations running in CI
- [ ] Cloudflare Workers deploying
- [ ] Secrets properly configured
- [ ] Rollback procedure tested
Final Project Checklist¶
Features¶
- [ ] User registration and login
- [ ] Organization/workspace management
- [ ] Project CRUD
- [ ] Kanban board with drag-and-drop
- [ ] Task management with comments
- [ ] File attachments
- [ ] Real-time updates (optional)
- [ ] Email notifications
- [ ] Activity logging
Technical¶
- [ ] Database schema with RLS
- [ ] Type-safe Supabase client
- [ ] Queue processing (pgmq or Cloudflare)
- [ ] Scheduled jobs (pg_cron + Cron Triggers)
- [ ] CI/CD pipeline
- [ ] Multi-environment deployment
Quality¶
- [ ] Code passes linting
- [ ] TypeScript strict mode
- [ ] Error handling
- [ ] Loading states
- [ ] Responsive design
Congratulations!¶
Bạn đã hoàn thành TaskFlow project với đầy đủ các tính năng của Light Development Stack:
- Next.js on Cloudflare Workers
- Supabase PostgreSQL, Auth, Storage, Realtime
- Cloudflare Workers, Queues, Cron, KV
- CI/CD với GitHub Actions
Next Steps¶
- Thêm more features (notifications UI, search, etc.)
- Optimize performance (caching, pagination)
- Add monitoring and analytics
- Scale to production