Version Management Tutorial
This tutorial explains how the version management system works in the Proxy Smart project. The system is designed to keep all package.json files synchronized across the monorepo and automate versioning for different release types.
Overview
The version management system consists of:
- Central Script:
scripts/version.js- Main version management logic - Git Hooks:
.githooks/pre-commit- Automatic version synchronization - GitHub Actions: Automated version validation and release workflows
- NPM Scripts: Convenient commands for version operations
Architecture
Multi-Package Structure
The project is a monorepo with multiple packages:
├── package.json # Root package (master version)
├── backend/package.json # Backend service
├── ui/package.json # Frontend React application
├── infra/package.json # CDK infrastructure
└── scripts/package.json # Build & CI scriptsAll packages must maintain version consistency, with the root package.json serving as the source of truth.
Version Format
The system supports semantic versioning with pre-release identifiers:
X.Y.Z[-suffix[.build[.sha]]]- X.Y.Z: Standard semantic version (major.minor.patch)
- suffix: Release type (alpha, beta, or RELEASE for production)
- build: Build number for non-production releases (YYYYMMDDHHMM format)
- sha: Short commit SHA for non-production releases
Examples:
1.2.3-RELEASE- Production release1.2.3-alpha.202508031914.823869b- Alpha release1.2.3-beta.202508031915.a1b2c3d- Beta release
Core Script: scripts/version.js
The central script provides all version management functionality:
Key Functions
- findPackageFiles(): Automatically discovers all package.json files
- updateVersion(): Updates version across all packages
- checkConsistency(): Validates version consistency
- incrementVersion(): Handles semantic version bumping
- getBaseVersion(): Extracts base version without suffixes
Commands
| Command | Description | Example |
|---|---|---|
sync | Synchronize all packages to root version | node scripts/version.js sync |
bump [type] | Increment version (patch/minor/major) | node scripts/version.js bump minor |
set <version> | Set specific version | node scripts/version.js set 1.2.3 |
check | Validate version consistency | node scripts/version.js check |
base | Get base version (no suffixes) | node scripts/version.js base |
NPM Scripts Integration
The project provides convenient NPM scripts in the root package.json:
{
"scripts": {
"version:sync": "node scripts/version.js sync",
"version:bump": "node scripts/version.js bump",
"version:bump:minor": "node scripts/version.js bump minor",
"version:bump:major": "node scripts/version.js bump major",
"version:set": "node scripts/version.js set",
"version:check": "node scripts/version.js check",
"version:base": "node scripts/version.js base",
"precommit": "bun run version:sync"
}
}Usage Examples
# Check current version consistency
bun run version:check
# Sync all packages to root version
bun run version:sync
# Bump patch version (1.2.3 → 1.2.4)
bun run version:bump
# Bump minor version (1.2.3 → 1.3.0)
bun run version:bump:minor
# Bump major version (1.2.3 → 2.0.0)
bun run version:bump:major
# Set specific version
bun run version:set 2.1.0
# Get base version without suffixes
bun run version:baseGit Integration
Pre-commit Hook
The .githooks/pre-commit hook automatically runs before each commit:
#!/bin/bash
echo "Checking version consistency..."
node scripts/version.js sync
git add **/package.json
echo "✅ Version consistency ensured"This ensures that all commits maintain version consistency across packages.
Setup Git Hooks
To enable git hooks:
# Run the setup script
node scripts/setup-hooks.js
# Or manually configure
git config core.hooksPath .githooksGitHub Actions Workflows
1. Version Validation (version-check.yml)
Automatically validates version consistency on pull requests:
on:
pull_request:
branches: [main, develop, test]What it does:
- Checks that all package.json files have consistent versions
- Fails the PR if versions are inconsistent
- Provides clear feedback on version mismatches
2. Version Operations (version-operations.yml)
Reusable workflow for version operations:
Inputs:
operation: 'validate' or 'update'release_type: 'alpha', 'beta', or 'production'should_bump_version: Whether to increment versionversion_suffix: Version suffix to apply
Outputs:
current_version: Version before changesnew_version: Version after changesbase_version: Base version without suffixesbuild_number: Build number for non-productionshort_sha: Commit SHAis_consistent: Whether versions are consistent
3. Manual Version Bump (manual-version-bump.yml)
Allows manual version bumping through GitHub Actions:
Triggers:
- Manual workflow dispatch
- Configurable version type (minor/major)
- Optional custom version
- Target branch selection
Process:
- Validates inputs
- Calculates new version
- Updates all package.json files
- Updates README badge
- Commits and pushes changes
Release Process
Branch Flow
The project uses a three-stage promotion model:
develop (alpha) ──auto-PR──▶ test (beta) ──manual-PR──▶ main (production)Each branch has its own version suffix. When code is pushed, the release workflow creates a version-stamped tag and GitHub Release automatically.
Alpha Releases (develop)
Alpha releases are created automatically on every push to develop:
- Trigger: Push to
developbranch (excluding bot commits) - Workflow:
release-alpha.yml→release-orchestrator.yml→version-operations.yml - Version Logic: Current base version + alpha suffix with build number + short SHA
- Format:
X.Y.Z-alpha.YYYYMMDDHHMM.SHA - Example:
0.0.2-alpha.202603231607.00c7934f
Beta Releases (test)
Beta releases are created when code merges from develop into test:
- Trigger: Push to
testbranch (excluding bot commits) - Workflow:
release-beta.yml→release-orchestrator.yml→version-operations.yml - Version Logic: Current base version + beta suffix with build number + short SHA
- Format:
X.Y.Z-beta.YYYYMMDDHHMM.SHA - Deploy: Beta releases trigger deployment to the staging VPS via
deploy-beta.yml
Production Releases (main)
Production releases are created when code merges from test into main:
- Trigger: Push to
mainbranch - Workflow:
release-production.yml→release-orchestrator.yml→version-operations.yml - Version Logic: Base version with RELEASE suffix
- Format:
X.Y.Z-RELEASE
Automated PR Promotion
Auto-PR Creation (create-pr.yml)
When code is pushed to develop, the create-pr.yml workflow automatically:
- Creates (or updates) a PR from
develop→test - Waits for
Alpha Releaseto finish on that commit - Enables GitHub auto-merge, so the PR merges once all required checks pass
Similarly, when code merges to test, a PR from test → main is created -- but this one requires manual review before merge.
The PR is created on every push to develop and test, including bot commits. Only the steps that push a branch (version-conflict resolution) skip themselves on [proxy-smart-releaser] commits, which is what prevents the workflow re-triggering itself. Guarding the whole job instead used to mean a real change followed by a version-sync commit produced no PR at all, because the sync commit was the last push. The Alpha Release wait is likewise skipped for those commits -- they never produce an alpha release, so waiting on one only burned the timeout.
Version Conflict Auto-Resolution
Because each branch has different version suffixes (alpha vs beta vs production), the 5 package.json files always conflict between branches. The create-pr.yml workflow resolves this automatically:
- Detection: The workflow checks if the source branch is behind the target (
commits_behind != 0) - Merge: It merges the target branch into the source using
git merge -X ours(keeping the source's versions) - Rationale: The source's version strings are ephemeral anyway --
version-operations.ymlwill immediately re-stamp the correct stage version after merge - Bot commit: The merge commit includes
[proxy-smart-releaser]to prevent re-triggering release workflows
Example flow for develop → test:
1. develop has: 0.0.2-alpha.202603231607.00c7934f
2. test has: 0.0.2-beta.202603231553.5d372300
3. Workflow merges origin/test into develop with -X ours
4. Push → PR becomes conflict-free → auto-merge
5. version-operations re-stamps test as 0.0.2-beta.{new-build}.{new-sha}Skip Conditions
Bot-generated commits are filtered to prevent infinite loops:
| Commit message contains | Alpha Release | Create PR | Compliance Tests |
|---|---|---|---|
proxy-smart-releaser | Skipped | Skipped | Skipped (push only) |
🔄 Update version | Skipped | Skipped | Skipped (push only) |
🤖 Update client APIs | Skipped | Skipped | Skipped (push only) |
These workflows still run when triggered via workflow_dispatch, workflow_call, or schedule.
Working with versions
Let the pre-commit hook do the synchronizing, and use bun run version:check to confirm the tree is consistent before you commit rather than after CI tells you it was not. Bumps follow semantic versioning: patch for bug fixes, minor for backwards-compatible additions, major for breaking changes.
bun run version:set exists for hotfixes and for pinning a specific version. Because it bypasses the normal bump, run version:check afterwards and say in the commit message why the version was set by hand.
Versions flow through the branches automatically. Feature branches merge into develop, which produces alpha versions; an auto-PR promotes develop to test, which auto-merges and produces beta versions deployed to the VPS; a reviewed PR promotes test to main for production versions. Hotfixes go straight to main and are the one case where the version bump is manual.
Troubleshooting
Common Issues
Version Inconsistency Error
bash❌ backend/package.json: 1.2.3 (expected: 1.2.4)Solution: Run
bun run version:syncGit Hook Not Working
bash# Re-setup hooks node scripts/setup-hooks.jsCI/CD Version Validation Failure
- Check all package.json files manually
- Run
bun run version:checklocally - Sync versions and commit changes
Debugging Commands
# Check which packages exist
node scripts/version.js sync --dry-run
# Manually inspect versions
find . -name "package.json" -not -path "*/node_modules/*" -exec echo {} \; -exec jq -r '.version' {} \;
# Reset all versions to root
bun run version:syncAdvanced Usage
Custom Version Formats
For special releases, you can set custom versions:
# Release candidate
bun run version:set 2.0.0-rc.1
# Custom suffix
bun run version:set 1.5.0-hotfix.1
# Production release
bun run version:set 1.2.3-RELEASEProgrammatic Usage
You can import and use the version script functions:
import {
getCurrentVersion,
updateVersion,
checkConsistency
} from './scripts/version.js';
const currentVersion = getCurrentVersion();
const isConsistent = checkConsistency();Integration with CI/CD
The version management system is deeply integrated with the CI/CD pipeline:
- Pre-commit Hook: Synchronizes all package.json files before every commit
- PR Validation:
version-check.ymlensures version consistency on pull requests - Auto-Release: Push to
develop/test/maintriggers stage-specific release workflows - Auto-PR:
create-pr.ymlpromotes code through branches with conflict resolution - Auto-Merge:
develop→testPRs merge automatically after checks pass - Changelog: AI-powered changelog generation from commit history (OpenAI)
- Compliance: SMART Inferno compliance tests run on real code merges
- Deploy: Beta releases deploy to the staging VPS automatically
The point of all this is that no package version is ever set by hand in normal operation. The pre-commit hook keeps the monorepo synchronized, CI validation catches the cases it cannot, and the branch a commit lands on determines the release type. What is left to decide is the size of the bump.