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
- Validation - Tests, typecheck, lint, coverage
- Preview Deployment - Deploy to Cloudflare Workers preview URL
- PR Comment - Automatic comment with preview URL
For Merges to Main
- Validation - Full quality checks
- Release Please - Creates/updates Release PR (if releasable commits)
- Review Release PR - Check version bump and changelog (manual step)
- Merge Release PR - Creates GitHub Release
- Manual Deployment Approval - Wait for deployment approval
- 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):
actions/[email protected]actions/[email protected]pnpm/[email protected]cloudflare/[email protected]actions/[email protected]actions/[email protected]softprops/[email protected]
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:
- Code is built and deployed to Cloudflare Workers
- Preview URL is generated:
pr-<number>-asset360.<subdomain>.workers.dev - PR is automatically commented with the preview URL
- 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:
- Release Please analyzes commits since the last release
- 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!:orBREAKING CHANGE:→ Major version (0.1.0 → 1.0.0)
- Creates/updates a Release PR with:
- Version bump in
package.json - Auto-generated
CHANGELOG.mdentries - Summary of all changes since last release
- Version bump in
- You review the Release PR and verify the proposed changes
- Merge the Release PR to create the GitHub Release
- 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 Type | Example | Version Impact | Current | New |
|---|---|---|---|---|
fix: | fix: resolve login timeout | Patch | 1.2.3 | 1.2.4 |
feat: | feat: add dark mode | Minor | 1.2.3 | 1.3.0 |
feat!: | feat!: redesign API | Major | 1.2.3 | 2.0.0 |
docs: | docs: update README | No release | 1.2.3 | 1.2.3 |
chore: | chore: update deps | No release | 1.2.3 | 1.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 featuresfix:- Bug fixesperf:- Performance improvementsfeat!:,fix!:- Breaking changes
Don't trigger release:
docs:- Documentation onlystyle:- Formatting changesrefactor:- Code refactoringtest:- Test additions/changeschore:- Maintenance tasksci:- CI/CD changesbuild:- 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:
simpletype = 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:
| Aspect | Old (run_number) | New (release-please) |
|---|---|---|
| Versioning | v0.1.1, v0.1.2, v0.1.3 | v0.1.0 → v0.2.0 → v1.0.0 |
| Changelog | Manual | Automated from commits |
| Version meaning | Just a counter | Semantic (major.minor.patch) |
| Oversight | None | Review PR before release |
| Breaking changes | Not communicated | Major version bump |
| Release timing | Every merge | Only for releasable commits |
Production Deployment
Manual Approval Requirement
Production deployments require manual approval via GitHub Environments:
- Merge to main triggers release creation
- Deploy job waits for approval
- GitHub sends notification to reviewers
- Reviewer clicks "Approve deployment" or "Reject deployment"
- 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:
- Tests - Full test suite with coverage
- Type Checking -
pnpm typecheck - Linting -
pnpm lint - 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
- Check for breaking changes in release notes
- Review test results in PR
- Merge if tests pass
- Monitor deployment after merge
Secrets Management
Required secrets in GitHub repository settings:
CLOUDFLARE_API_TOKEN- Cloudflare API token for deploymentsCLOUDFLARE_ACCOUNT_ID- Cloudflare account IDNPM_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:
- Go to repository Settings → Environments
- Click New environment
- Name it
production(exactly, case-sensitive) - Click Configure environment
Configure Protection Rules
- ✅ Check Required reviewers
- Add yourself (or team members) as reviewers
- Only one approval needed from the list
- ✅ (Optional) Check Prevent self-review
Configure Deployment Branches
- Under Deployment branches, select Selected branches and tags
- Click Add deployment branch or tag rule
- Select Branch as Ref type
- Enter:
main - Click Add rule
Optional: Wait Timer
Add a cooling-off period before deployments:
- Check Wait timer
- Enter minutes (e.g., 5)
- Deployment will wait this long before becoming available for approval
Verifying Setup
- Create a test PR to verify preview deployments
- 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: trueis set inwrangler.jsonc - Verify
CLOUDFLARE_API_TOKENandCLOUDFLARE_ACCOUNT_IDsecrets
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
!orBREAKING 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:
| Check | Pre-Commit Hook | CI Pipeline | Why Both? |
|---|---|---|---|
| Format | ✅ Auto-fixes with Prettier | ✅ Validates | Ensures all code is formatted |
| Lint | ✅ Auto-fixes with ESLint | ✅ Validates | Catches issues before push |
| Typecheck | ✅ Full codebase | ✅ Full codebase | Early feedback + CI gate |
| Tests | ❌ Too slow | ✅ With coverage | Tests run in CI only |
| Build | ❌ Too slow | ✅ Full build | Build validation in CI |
| Branch protection | ✅ Blocks main | ❌ N/A | Local enforcement |
| Commit format | ✅ Validates | ❌ N/A | Ensures 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
| Metric | Target | Current |
|---|---|---|
| Build Time (cached) | < 2 min | Varies |
| Build Time (uncached) | < 5 min | Varies |
| Test Coverage | > 80% | Check artifacts |
| PR Preview Time | < 5 min | Varies |
| Deployment Time | < 3 min | After approval |
Future Improvements
Consider implementing:
- Semantic Versioning - Use conventional commits + release-please
- Staging Environment - Add pre-production environment
- Rollback Automation - One-click rollback to previous release
- Performance Monitoring - Integrate APM for deployment tracking
- Slack/Discord Notifications - Real-time deployment notifications
- Canary Deployments - Gradual rollout to production
- Load Testing - Automated performance tests in CI