# 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: 1. **Triggers automatically** after successful production deployments (via `workflow_run`) 2. **Updates only** the `package.json` version in the test deployment directory 3. **Restarts PM2** with `--update-env` to refresh version metadata 4. **Completes in ~30 seconds** instead of 5-7 minutes ### Architecture ```yaml # .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.yml` remains **unchanged** (runs normally for code changes) - `deploy-to-prod.yml` remains **unchanged** (no explicit trigger needed) - `workflow_run` automatically 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: 1. **Production deployment** completes and bumps version 2. **Version sync workflow** triggers automatically within seconds 3. **Test PM2 processes** restart with updated version metadata 4. **PM2 list** shows matching versions across environments ```bash # Verify version sync worked pm2 jlist | jq '.[] | select(.name | endsWith("-test")) | {name, version: .pm2_env.version}' ``` Expected output: ```json { "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.yml` duration after prod deploys (~5-7 min) - After: `sync-test-version.yml` duration (~30 sec) ## Rollback Plan If version sync fails or causes issues: 1. Remove `sync-test-version.yml` 2. Previous behavior automatically resumes (full test deployment on version bumps) 3. No data loss or configuration changes needed ## References - Inspired by similar optimization in `stock-alert` project (commit `021f9c8`) - Related: ADR-061 for PM2 process isolation and deployment safety - Workflow pattern: GitHub Actions `workflow_run` trigger 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.