Workflow Improvements Summary
This document provides a quick overview of the improvements made to GitHub Actions workflows.
Executive Summary
The workflows have been rewritten to:
- ✅ Run 40-50% faster through parallelization
- ✅ Fail faster with early validation
- ✅ Use resources more efficiently with better caching
- ✅ Be more maintainable with clearer structure
- ✅ Follow best practices with proper gating and permissions
CI Workflow Improvements (Round 2)
Eight additional enhancements landed in PR #788:
Before → After Comparison
| Aspect | Before | After | Improvement |
|---|---|---|---|
| deno install | 12-line retry block duplicated in 5 jobs | Composite action .github/actions/deno-install | No duplication |
| Worker build on PRs | Not verified until deploy to main | verify-deploy dry-run on every PR | Catch failures before merge |
| Frontend jobs | Two separate jobs (frontend + frontend-build) | Single frontend-build job | One pnpm install per run |
| pnpm lockfile | --no-frozen-lockfile (silent drift) | --frozen-lockfile (fails on drift) | Enforced consistency |
| Coverage upload | Main push only | PRs and main push | Coverage visible on PRs |
| Action versions | Floating tags (@v4) | Full commit SHAs + comments | Supply-chain hardened |
| Migration errors | || echo "already applied or failed" silenced real errors | run_migration() function parses output | Real errors fail the step |
| Dead code | detect-changes job (always returned true) | Removed | Cleaner pipeline |
New Job: verify-deploy
Runs a Cloudflare Worker build dry-run on every pull request:
# Runs on PRs only — uses the frontend artifact from frontend-build
verify-deploy:
needs: [frontend-build]
if: github.event_name == 'pull_request'
steps:
- uses: ./.github/actions/deno-install
- run: deno task wrangler:verify
The ci-gate job includes verify-deploy in its needs list, so a failing Worker build blocks merge.
Composite Action: deno-install
Extracted the 3-attempt deno install retry loop into a reusable composite action:
# .github/actions/deno-install/action.yml
steps:
- name: Install dependencies
env:
DENO_TLS_CA_STORE: system
run: |
for i in 1 2 3; do
deno install && break
if [ "$i" -lt 3 ]; then
echo "Attempt $i failed, retrying in 10s..."
sleep 10
else
echo "All 3 attempts failed."
exit 1
fi
done
CI Workflow Improvements (Round 1)
Before → After Comparison
| Aspect | Before | After | Improvement |
|---|---|---|---|
| Structure | 1 monolithic job + separate jobs | 5 parallel jobs + gated sequential jobs | Better parallelization |
| Runtime | ~5-7 minutes | ~2-3 minutes | 40-50% faster |
| Type Checking | 2 files only | All entry points | More comprehensive |
| Caching | Basic (deno.json only) | Advanced (deno.json + deno.lock) | More precise |
| Deployment | 2 separate jobs | 1 combined job | Simpler |
| Gating | Security runs independently | All checks gate publish/deploy | More reliable |
Key Changes
# BEFORE: Sequential execution in single job
jobs:
ci:
steps:
- Lint
- Format
- Type Check
- Test
security: # Runs independently
publish: # Only depends on ci
deploy-worker: # Depends on ci + security
deploy-pages: # Depends on ci + security
# AFTER: Parallel execution with proper gating
jobs:
lint: # \
format: # |-- Run in parallel
typecheck: # |
test: # |
security: # /
publish: # Depends on ALL above
deploy: # Depends on ALL above (combined worker + pages)
Release Workflow Improvements
Before → After Comparison
| Aspect | Before | After | Improvement |
|---|---|---|---|
| Validation | None | Full CI before builds | Fail fast |
| Binary Caching | No per-target cache | Per-target + OS cache | Faster builds |
| Asset Prep | Complex loop | Simple find command | Cleaner code |
| Comments | Verbose warnings | Concise, essential only | More readable |
Key Changes
# BEFORE: Build immediately, might fail late
jobs:
build-binaries:
# Starts building right away
build-docker:
# Builds without validation
# AFTER: Validate first, then build
jobs:
validate:
# Run lint, format, typecheck, test
build-binaries:
needs: validate # Only run after validation
build-docker:
needs: validate # Only run after validation
Version Bump Workflow Improvements
Before → After Comparison
| Aspect | Before | After | Improvement |
|---|---|---|---|
| Trigger | Auto on PR + Manual | Manual only | Less disruptive |
| Files Updated | 9 files (including examples) | 4 core files only | Focused |
| Error Handling | if/elif chain | case statement | More robust |
| Validation | None | Verification step | More reliable |
| Git Operations | Add all files | Selective add | Safer |
Key Changes
# BEFORE: Automatic trigger
on:
pull_request:
types: [opened] # Auto-runs on every PR!
workflow_dispatch:
# AFTER: Manual only
on:
workflow_dispatch: # Only runs when explicitly triggered
Performance Impact
CI Workflow
Before (~8-10 minutes total):
flowchart LR
subgraph SEQ["CI Job (sequential) — 5-7 min"]
L[Lint<br/>1 min] --> F[Format<br/>1 min] --> TC[Type Check<br/>1 min] --> T[Test<br/>2-4 min]
end
SEC[Security<br/>2 min]
T --> PUB[Publish<br/>1 min]
SEC --> PUB
PUB --> DW[Deploy Worker<br/>1 min]
DW --> DP[Deploy Pages<br/>1 min]
After (~4-6 minutes total, 40-50% improvement):
flowchart LR
subgraph PAR["Parallel Phase — 2-4 min"]
L[Lint<br/>1 min]
F[Format<br/>1 min]
TC[Type Check<br/>1 min]
T[Test<br/>2-4 min]
SEC[Security<br/>2 min]
end
L --> PUB[Publish<br/>1 min]
F --> PUB
TC --> PUB
T --> PUB
SEC --> PUB
PUB --> DEP[Deploy<br/>1 min]
Release Workflow
Before (on failure, ~15 minutes wasted):
flowchart LR
BB[Build Binaries<br/>10 min] --> BD[Build Docker<br/>5 min] --> CR[Create Release<br/>❌ fails here]
After (on failure, ~3 minutes wasted — 80% improvement):
flowchart LR
V[Validate<br/>❌ fails here<br/>3 min]
Caching Strategy
Before
key: deno-${{ runner.os }}-${{ hashFiles('deno.json') }}
restore-keys: deno-${{ runner.os }}-
After
key: deno-${{ runner.os }}-${{ hashFiles('deno.json', 'deno.lock') }}
restore-keys: |
deno-${{ runner.os }}-
Benefits:
- More precise cache invalidation (includes lock file)
- Better restore key strategy
- Per-target caching for binaries
Best Practices Implemented
✅ Principle of Least Privilege: Minimal permissions per job
✅ Fail Fast: Validate before expensive operations
✅ Parallelization: Independent tasks run concurrently
✅ Proper Gating: Critical jobs depend on quality checks
✅ Concurrency Control: Cancel outdated runs automatically
✅ Idempotency: Workflows can be safely re-run
✅ Clear Naming: Job names clearly indicate purpose
✅ Efficient Caching: Smart cache keys and restore strategies
✅ Supply-Chain Hardening: Third-party actions pinned to full commit SHAs
✅ DRY Composite Actions: Shared retry logic extracted to .github/actions/
✅ PR Build Verification: Worker dry-run validates deployability on every PR
Breaking Changes
⚠️ Version Bump Workflow
- No longer triggers automatically on PR open
- Must be run manually via workflow_dispatch
- No longer updates example files
Migration Guide
For Contributors
Before: Version was auto-bumped on PR creation After: Manually run "Version Bump" workflow when needed
For Maintainers
Before:
- Merge PR → Auto publish → Manual tag → Release
After:
- Merge PR → Auto publish
- Run "Version Bump" workflow
- Tag created → Release triggered
OR
- Merge PR → Auto publish
- Run "Version Bump" with "Create release" checked
- Done!
Monitoring
Success Metrics
Track these to measure improvement:
- ✅ Average CI runtime (target: <5 min)
- ✅ Success rate on first run (target: >90%)
- ✅ Time to failure (target: <3 min)
- ✅ Cache hit rate (target: >80%)
What to Watch
- Long test runs: If tests exceed 5 minutes, consider parallelization
- Cache misses: If cache hit rate drops, check lock file stability
- Build failures: ARM64 builds might need cross-compilation setup
Future Optimizations
Potential improvements for consideration:
- Test Parallelization: Split tests by module
- Selective Testing: Only test changed modules on PRs
- Artifact Caching: Cache build artifacts between jobs
- Matrix Testing: Test on multiple Deno versions
- Scheduled Scans: Weekly security scans instead of every commit
Conclusion
These workflow improvements provide:
- Faster feedback for developers
- More reliable deployments
- Better resource utilization
- Clearer structure for maintenance
The changes maintain backward compatibility while significantly improving performance and reliability.