Skip to main content

CI/CD and Deployment

Our CI/CD pipeline uses GitHub Actions to automate testing, building, and deploying to Cloudflare Workers with security best practices and manual approval gates.

Pipeline Overview

For Pull Requests

  1. Validation - Tests, typecheck, lint, coverage
  2. Preview Deployment - Deploy to Cloudflare Workers preview URL
  3. PR Comment - Automatic comment with preview URL

For Merges to Main

  1. Validation - Full quality checks
  2. Release Please - Creates/updates Release PR (if releasable commits)
  3. Review Release PR - Check version bump and changelog (manual step)
  4. Merge Release PR - Creates GitHub Release
  5. Manual Deployment Approval - Wait for deployment approval
  6. Production Deployment - Migrations → Deploy → Health check

Security Features

Pinned Actions

All GitHub Actions are pinned to exact version tags to prevent unexpected changes:

# ✅ Good - pinned to exact version
uses: actions/[email protected]

# ❌ Bad - floating version tag
uses: actions/checkout@v4

Note: For maximum security, you can pin to commit SHAs instead of version tags, but exact versions provide a good balance between security and readability.

Current pinned actions (exact versions):

Least Privilege Permissions

Each job has explicit, minimal permissions:

# Validate job
permissions:
contents: read
pull-requests: write

# Deploy job
permissions:
contents: read
deployments: write

Preview Deployments

Every pull request automatically gets a preview deployment:

  1. Code is built and deployed to Cloudflare Workers
  2. Preview URL is generated: pr-<number>-asset360.<subdomain>.workers.dev
  3. PR is automatically commented with the preview URL
  4. Preview remains active until PR is closed

Benefits

  • Test changes in production-like environment before merging
  • Stakeholders can review without running code locally
  • Catch deployment issues early
  • No configuration needed - automatic for all PRs

Automated Semantic Releases

We use Release Please for automated semantic versioning based on conventional commit messages.

How It Works

When you push conventional commits to main:

  1. Release Please analyzes commits since the last release
  2. Determines version bump based on commit types:
    • fix: → Patch version (0.1.0 → 0.1.1)
    • feat: → Minor version (0.1.0 → 0.2.0)
    • feat!: or BREAKING CHANGE: → Major version (0.1.0 → 1.0.0)
  3. Creates/updates a Release PR with:
    • Version bump in package.json
    • Auto-generated CHANGELOG.md entries
    • Summary of all changes since last release
  4. You review the Release PR and verify the proposed changes
  5. Merge the Release PR to create the GitHub Release
  6. Deploy workflow triggers and waits for manual approval

Example Workflow

# 1. Make commits using conventional format
git commit -m "feat: add investor portfolio view"
git commit -m "fix: correct NAV calculation for zero units"
git push origin main

# 2. Release Please creates/updates PR titled:
# "chore(main): release 0.2.0"
#
# Contents:
# - package.json version: 0.1.0 → 0.2.0
# - CHANGELOG.md with new entries
#
# 3. Review the release PR
# - Check version bump is correct
# - Verify changelog entries
# - Ensure all changes are included
#
# 4. Merge the release PR
# - Creates GitHub Release v0.2.0
# - Triggers deploy workflow
# - Deploy waits for your approval
#
# 5. Approve deployment
# - Migrations run
# - Deploy to production
# - Health check passes

Commit Message Impact on Versioning

Commit TypeExampleVersion ImpactCurrentNew
fix:fix: resolve login timeoutPatch1.2.31.2.4
feat:feat: add dark modeMinor1.2.31.3.0
feat!:feat!: redesign APIMajor1.2.32.0.0
docs:docs: update READMENo release1.2.31.2.3
chore:chore: update depsNo release1.2.31.2.3

Release PR Workflow

Release Please maintains a single "release PR" that accumulates changes:

First commit:

feat: add preview deployments

→ Creates PR: "chore(main): release 0.2.0"

Second commit (before merging release PR):

fix: correct health check timeout

→ Updates same PR: "chore(main): release 0.2.0"
→ Adds fix to changelog

Third commit:

feat!: change authentication method

→ Updates PR: "chore(main): release 1.0.0" # Now major!
→ Version bump recalculated

This gives you full visibility and control over what gets released.

What Commits Trigger Releases?

Only certain commit types trigger releases:

Trigger release:

  • feat: - New features
  • fix: - Bug fixes
  • perf: - Performance improvements
  • feat!:, fix!: - Breaking changes

Don't trigger release:

  • docs: - Documentation only
  • style: - Formatting changes
  • refactor: - Code refactoring
  • test: - Test additions/changes
  • chore: - Maintenance tasks
  • ci: - CI/CD changes
  • build: - Build system changes

If you only have non-releasable commits (like docs: or chore:), Release Please won't create a PR. The release PR only appears when there are actual code changes worth releasing.

Configuration Files

.release-please-manifest.json

{
".": "0.1.0"
}

Tracks the current version. Release Please updates this automatically.

release-please-config.json

{
"packages": {
".": {
"changelog-path": "CHANGELOG.md",
"release-type": "simple",
"bump-minor-pre-major": true,
"bump-patch-for-minor-pre-major": false
}
}
}

Configures release behavior:

  • simple type = non-NPM project (perfect for Cloudflare Workers)
  • bump-minor-pre-major = Allow minor bumps before 1.0.0
  • Changelog automatically generated and maintained

Benefits of Semantic Versioning

Clear Communication:

  • Version numbers communicate the impact of changes
  • Breaking changes are immediately obvious (major bump)
  • Stakeholders understand release significance at a glance

Automated Changelog:

  • No manual changelog maintenance
  • Organized by change type (Features, Bug Fixes, Breaking Changes)
  • Links to commits and PRs automatically

Human Oversight:

  • Review release PR before creating release
  • Verify version bump is appropriate
  • Catch issues before they're released
  • Merge when ready (not immediately on push)

Better Than Current Approach:

AspectOld (run_number)New (release-please)
Versioningv0.1.1, v0.1.2, v0.1.3v0.1.0 → v0.2.0 → v1.0.0
ChangelogManualAutomated from commits
Version meaningJust a counterSemantic (major.minor.patch)
OversightNoneReview PR before release
Breaking changesNot communicatedMajor version bump
Release timingEvery mergeOnly for releasable commits

Production Deployment

Manual Approval Requirement

Production deployments require manual approval via GitHub Environments:

  1. Merge to main triggers release creation
  2. Deploy job waits for approval
  3. GitHub sends notification to reviewers
  4. Reviewer clicks "Approve deployment" or "Reject deployment"
  5. After approval, deployment proceeds automatically

Deployment Steps

1. Apply database migrations (D1)

2. Deploy to Cloudflare Workers

3. Wait 30 seconds for propagation

4. Health check (verify HTTP 200)

5. Success or failure notification

Database Migrations

Migrations run automatically before each deployment:

pnpm wrangler d1 migrations apply asset360 --remote

Important: Never create migrations manually. Use Drizzle Kit:

# After changing DB schema
pnpm drizzle-kit generate

Performance Optimizations

Caching

pnpm dependencies are automatically cached using actions/setup-node with cache: 'pnpm':

  • Cache keyed to pnpm-lock.yaml
  • Reduces build time by 2-3x on cache hits
  • Automatic cache invalidation on lockfile changes

Parallel Execution

Jobs run in parallel when possible:

  • Validation and Preview jobs run simultaneously for PRs
  • Only deployment waits for validation + release

Quality Gates

All code must pass before deployment:

  1. Tests - Full test suite with coverage
  2. Type Checking - pnpm typecheck
  3. Linting - pnpm lint
  4. Build - Successful compilation

Coverage reports are uploaded as artifacts and retained for 30 days.

Dependency Management

Dependabot

Automatic dependency updates configured for:

GitHub Actions:

  • Checks weekly (Mondays)
  • Creates PRs for action updates
  • Limit: 10 open PRs

npm Packages:

  • Checks weekly (Mondays)
  • Groups minor and patch updates (reduces noise)
  • Major updates get separate PRs
  • Limit: 10 open PRs

Reviewing Dependabot PRs

  1. Check for breaking changes in release notes
  2. Review test results in PR
  3. Merge if tests pass
  4. Monitor deployment after merge

Secrets Management

Required secrets in GitHub repository settings:

  • CLOUDFLARE_API_TOKEN - Cloudflare API token for deployments
  • CLOUDFLARE_ACCOUNT_ID - Cloudflare account ID
  • NPM_TOKEN - GitHub Packages token for private npm packages

Never commit secrets to code!

Setup Instructions

Creating Production Environment

To enable manual approval for production deployments:

  1. Go to repository SettingsEnvironments
  2. Click New environment
  3. Name it production (exactly, case-sensitive)
  4. Click Configure environment

Configure Protection Rules

  1. ✅ Check Required reviewers
  2. Add yourself (or team members) as reviewers
  3. Only one approval needed from the list
  4. ✅ (Optional) Check Prevent self-review

Configure Deployment Branches

  1. Under Deployment branches, select Selected branches and tags
  2. Click Add deployment branch or tag rule
  3. Select Branch as Ref type
  4. Enter: main
  5. Click Add rule

Optional: Wait Timer

Add a cooling-off period before deployments:

  1. Check Wait timer
  2. Enter minutes (e.g., 5)
  3. Deployment will wait this long before becoming available for approval

Verifying Setup

  1. Create a test PR to verify preview deployments
  2. Merge to main and verify:
    • Release is created automatically
    • Deploy job waits for approval
    • Migrations run after approval
    • Health check passes

Troubleshooting

Preview Deployment Fails

Problem: Preview job fails with deployment error

Solutions:

  • Verify preview URLs are enabled in Cloudflare dashboard
  • Check preview_urls: true is set in wrangler.jsonc
  • Verify CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID secrets

Deploy Job Doesn't Wait for Approval

Problem: Deployment happens without approval prompt

Solutions:

  • Verify environment is named exactly production (case-sensitive)
  • Check required reviewers are configured in environment settings
  • Ensure deployment branches include main

Migrations Fail

Problem: Database migration step fails during deployment

Solutions:

  • Check D1 database connection in Cloudflare
  • Verify migration SQL syntax is valid
  • Check for schema conflicts with existing data
  • Review Drizzle Kit generated migrations

Health Check Fails

Problem: Deployment succeeds but health check reports failure

Solutions:

  • Worker may need more than 30s to propagate globally
  • Verify URL is accessible manually
  • Check worker logs in Cloudflare dashboard
  • Verify no runtime errors in deployed worker

Tests Pass Locally but Fail in CI

Problem: Tests work locally but fail in GitHub Actions

Solutions:

  • Check for environment-specific issues
  • Verify all dependencies are in package.json
  • Check for timezone or locale differences
  • Review test logs in GitHub Actions

Dependabot PRs Breaking Build

Problem: Dependabot update causes tests to fail

Solutions:

  • Review breaking changes in dependency release notes
  • Update code to accommodate breaking changes
  • Pin dependency to previous version temporarily
  • Report issues to dependency maintainers

Release PR Not Created

Problem: Pushed commits to main but no Release PR appears

Solutions:

  • Check if commits use releasable types (feat:, fix:, perf:)
  • Non-releasable commits (docs:, chore:, ci:) don't trigger releases
  • Verify Release Please workflow ran successfully
  • Check workflow logs for errors

Wrong Version Bump

Problem: Release PR has incorrect version bump

Solutions:

  • Verify commit messages follow conventional format
  • Check for ! or BREAKING CHANGE: in commit body
  • If needed, close Release PR and make corrective commit
  • Release Please will recalculate on next push

Release PR Has Merge Conflicts

Problem: Release PR has conflicts with main branch

Solutions:

  • Release Please will automatically rebase and resolve
  • Wait for bot to update the PR
  • If issue persists, close PR and Release Please will recreate it

Best Practices

Commits

  • Write clear, descriptive commit messages
  • Use conventional commit format (enforced by commit-msg hook)
  • Reference issue numbers when applicable
  • Keep commits focused and atomic
  • Never skip pre-commit hooks (they catch errors before CI)
  • Never commit directly to main (enforced by pre-commit hook)
  • Always work in feature branches

Pre-Commit vs CI Checks

Your pre-commit hooks and CI pipeline work together to ensure code quality:

CheckPre-Commit HookCI PipelineWhy Both?
Format✅ Auto-fixes with Prettier✅ ValidatesEnsures all code is formatted
Lint✅ Auto-fixes with ESLint✅ ValidatesCatches issues before push
Typecheck✅ Full codebase✅ Full codebaseEarly feedback + CI gate
Tests❌ Too slow✅ With coverageTests run in CI only
Build❌ Too slow✅ Full buildBuild validation in CI
Branch protection✅ Blocks main❌ N/ALocal enforcement
Commit format✅ Validates❌ N/AEnsures semantic versioning

Philosophy:

  • Pre-commit hooks = Fast feedback on code quality (format, lint, types)
  • CI pipeline = Comprehensive validation (tests, build, deploy)

Pull Requests

  • Test using preview deployment before requesting review
  • Keep PRs small and focused
  • Write clear PR descriptions
  • Respond to review feedback promptly
  • Ensure all checks pass before requesting review

Release PRs

  • Review release PRs created by Release Please promptly
  • Verify the version bump is appropriate for the changes
  • Check that all changes are reflected in the changelog
  • Don't modify Release PRs manually (Release Please manages them)
  • Merge when ready to create the release
  • Release PR merges trigger deployment workflow

Deployments

  • Always review release notes before approving deployment
  • Monitor application after deployment
  • Keep an eye on error rates and performance metrics
  • Have rollback plan ready if issues occur

Security

  • Rotate secrets regularly
  • Never share or expose secrets
  • Review Dependabot security advisories promptly
  • Keep all dependencies up to date

Workflow Files

Main Workflow

Location: .github/workflows/github-flow.yml

Contains all CI/CD logic:

  • Validation job
  • Preview deployment job
  • Release creation job
  • Production deployment job

Dependabot Configuration

Location: .github/dependabot.yml

Configures automatic dependency updates for:

  • GitHub Actions (weekly)
  • npm packages (weekly)

Monitoring and Alerts

GitHub Actions

Monitor workflow runs:

  • Go to repository Actions tab
  • Filter by workflow, branch, or status
  • Review logs for failed jobs
  • Download coverage artifacts

Cloudflare Workers

Monitor deployments:

  • Cloudflare dashboard → Workers & Pages
  • View deployment history
  • Check worker logs
  • Monitor analytics and metrics

Notifications

You'll receive notifications for:

  • Failed workflow runs
  • Deployment approval requests
  • Dependabot PRs
  • Security advisories

Configure notification preferences in GitHub settings.

Key Metrics

MetricTargetCurrent
Build Time (cached)< 2 minVaries
Build Time (uncached)< 5 minVaries
Test Coverage> 80%Check artifacts
PR Preview Time< 5 minVaries
Deployment Time< 3 minAfter approval

Future Improvements

Consider implementing:

  1. Semantic Versioning - Use conventional commits + release-please
  2. Staging Environment - Add pre-production environment
  3. Rollback Automation - One-click rollback to previous release
  4. Performance Monitoring - Integrate APM for deployment tracking
  5. Slack/Discord Notifications - Real-time deployment notifications
  6. Canary Deployments - Gradual rollout to production
  7. Load Testing - Automated performance tests in CI

Additional Resources