Files
flyer-crawler.projectium.com/docs/adr/0062-lightweight-version-sync-workflow.md
2026-02-19 00:36:02 +05:00

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:

  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

# .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
# 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.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.