Implements efficient version synchronization between production and test environments without running the full test deployment pipeline. Problem: - Production version bumps triggered full 5-7 minute test deployment - Wasteful: 95% less CPU, 99.8% less file I/O needed - Code already tested when originally pushed to main Solution (matching stock-alert architecture): - New sync-test-version.yml workflow (~30 seconds) - Triggers automatically after successful production deployment - Updates only package.json in test directory - Restarts PM2 with --update-env to refresh version metadata Benefits: - 90% faster (30 sec vs 5-7 min) - Saves ~20 minutes/month of CI time - Clean separation: no conditionals polluting deploy-to-test.yml - Same end state, optimized path Changes: - Added .gitea/workflows/sync-test-version.yml (new workflow) - Added docs/adr/0062-lightweight-version-sync-workflow.md (decision record) - Updated docs/adr/index.md (ADR catalog) - Cleaned up duplicate TSOA generation step in deploy-to-test.yml Architecture: - deploy-to-test.yml: unchanged (runs on code changes) - deploy-to-prod.yml: unchanged (no explicit trigger needed) - sync-test-version.yml: auto-triggers via workflow_run Related: - Inspired by stock-alert project optimization (commit 021f9c8) - ADR-061: PM2 Process Isolation Safeguards Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
6.1 KiB
ADR-0062: Lightweight Version Sync Workflow
Status: Accepted Date: 2026-02-18 Decision Makers: Development Team Related: ADR-061 (PM2 Process Isolation Safeguards)
Context
After successful production deployments, the version number in package.json is bumped and pushed back to the main branch. This triggered the full test deployment workflow (deploy-to-test.yml), which includes:
npm ci(dependency installation)- TypeScript type-checking
- Prettier formatting
- ESLint linting
- Unit tests (~150 tests)
- Integration tests (~50 tests)
- React build with source maps
- Full rsync deployment
- PM2 process restart
Problem: Running the complete 5-7 minute test suite just to update a 10KB package.json file was wasteful:
| Resource | Waste |
|---|---|
| Duration | 5-7 minutes (vs 30 seconds needed) |
| CPU | ~95% unnecessary (full test suite) |
| File I/O | 99.8% unnecessary (only need 1 file) |
| CI Queue | Blocked other workflows unnecessarily |
| Time/month | ~20 minutes wasted on version syncs |
The code being tested had already passed the full test suite when originally pushed to main. Re-running tests for a version number change provided no additional value.
Decision
Implement a lightweight version-sync-only workflow that:
- Triggers automatically after successful production deployments (via
workflow_run) - Updates only the
package.jsonversion in the test deployment directory - Restarts PM2 with
--update-envto refresh version metadata - Completes in ~30 seconds instead of 5-7 minutes
Architecture
# .gitea/workflows/sync-test-version.yml
on:
workflow_run:
workflows: ["Deploy to Production"]
types: [completed]
branches: [main]
jobs:
sync-version:
if: ${{ gitea.event.workflow_run.conclusion == 'success' }}
steps:
- Checkout latest main
- Update test package.json version
- PM2 restart with --update-env
- Verify version in PM2 metadata
Key Points:
deploy-to-test.ymlremains unchanged (runs normally for code changes)deploy-to-prod.ymlremains unchanged (no explicit trigger needed)workflow_runautomatically triggers after production deployment- Non-blocking: production success doesn't depend on version sync
Consequences
Positive
✅ 90% faster version synchronization (30 sec vs 5-7 min) ✅ 95% less CPU usage (no test suite execution) ✅ 99.8% less file I/O (only package.json updated) ✅ Cleaner separation of concerns (version sync vs full deployment) ✅ No workflow file pollution with conditionals ✅ Saves ~20 minutes/month of CI time
Negative
⚠️ Test environment version could briefly lag behind production (30 second window)
⚠️ Additional workflow file to maintain
⚠️ PM2 version metadata relies on --update-env working correctly
Neutral
- Full test deployment still runs for actual code changes (as designed)
- Version numbers remain synchronized (just via different workflow)
- Same end state, different path
Alternatives Considered
1. Add Conditionals to deploy-to-test.yml
Approach: Detect version bump commits and skip most steps
Rejected because:
- Pollutes workflow with 20+
if:conditionals - Makes workflow complex and brittle
- Still queues a workflow run (even if it exits early)
- Harder to maintain and debug
2. Manual Production Deployments
Approach: Make production workflow_dispatch only (manual trigger)
Rejected because:
- Removes automation benefits
- Requires human intervention for every production deploy
- Doesn't align with CI/CD best practices
- Slows down deployment velocity
3. Don't Sync Test Versions
Approach: Accept that test version lags behind production
Rejected because:
- Version numbers become meaningless in test
- Harder to correlate test issues with production releases
- Loses visibility into what's deployed where
4. Release Tag-Based Production Deployments
Approach: Only deploy production on release tags, not on every push
Rejected because:
- Changes current deployment cadence
- Adds manual release step overhead
- Doesn't solve the fundamental problem (version sync still needed)
Implementation
Files Created
.gitea/workflows/sync-test-version.yml- Lightweight version sync workflow
Files Modified
- None (clean separation, no modifications to existing workflows)
Configuration
No additional secrets or environment variables required. Uses existing PM2 configuration and test deployment paths.
Verification
After implementation:
- Production deployment completes and bumps version
- Version sync workflow triggers automatically within seconds
- Test PM2 processes restart with updated version metadata
- PM2 list shows matching versions across environments
# Verify version sync worked
pm2 jlist | jq '.[] | select(.name | endsWith("-test")) | {name, version: .pm2_env.version}'
Expected output:
{
"name": "flyer-crawler-api-test",
"version": "0.16.2"
}
{
"name": "flyer-crawler-worker-test",
"version": "0.16.2"
}
Monitoring
Track workflow execution times:
- Before:
deploy-to-test.ymlduration after prod deploys (~5-7 min) - After:
sync-test-version.ymlduration (~30 sec)
Rollback Plan
If version sync fails or causes issues:
- Remove
sync-test-version.yml - Previous behavior automatically resumes (full test deployment on version bumps)
- No data loss or configuration changes needed
References
- Inspired by similar optimization in
stock-alertproject (commit021f9c8) - Related: ADR-061 for PM2 process isolation and deployment safety
- Workflow pattern: GitHub Actions
workflow_runtrigger documentation
Notes
This optimization follows the principle: "Don't test what you've already tested."
The code was validated when originally pushed to main. Version number changes are metadata updates, not code changes. Treating them differently is an architectural improvement, not a shortcut.