@edcalderon/versioning
v1.5.13
Published
A comprehensive versioning and changelog management tool for monorepos
Maintainers
Readme
@edcalderon/versioning
A comprehensive versioning and changelog management tool designed for monorepos and single repositories. It provides automated version bumping, changelog generation using conventional commits, and version synchronization across multiple packages.
📋 Latest Changes (v1.5.13)
Bug Fixes
- release: require a current README release entry and matching package version before tagged publication
- release: block accidental public npm publishes outside the tagged GitHub Actions workflow
- release: skip an already-published version instead of failing a rerun or recovery workflow
For full version history, see CHANGELOG.md and GitHub releases
Features
- 🚀 Automated version bumping (patch, minor, major, prerelease)
- 🌿 Optional branch-aware versioning (
main,develop,feature/*,hotfix/*) - 📝 Conventional commit-based changelog generation
- 🔄 Version synchronization across monorepo packages
- 🎯 Works with both monorepos and single repositories
- 📦 NPM publishable
- 🏷️ Git tagging and committing
- ✅ Validation of version sync
- 🧭 Changelog validation before release
- 🔌 Extensible plugin system for subdirectory-based extensions
- 🔒 Security Checks with automatic Husky integration
- 🧹 Repository Cleanup to keep root directory organized
- 🗂️
.agents/task tracking with reentry snapshot sync
Installation
npm install -g @edcalderon/versioning
# or
pnpm add -g @edcalderon/versioning
# or
yarn global add @edcalderon/versioningExtensions
The versioning tool supports a composable extension system that allows you to add custom business logic and commands. Extensions can:
- Add new CLI commands
- Hook into existing workflows (pre/post version bumps, releases, etc.)
- Integrate with external services
- Implement custom versioning strategies
Extensions are loaded automatically from:
- Built-in extensions in subdirectories of
src/extensions/(e.g.src/extensions/changelog-guard/index.ts,src/extensions/reentry-status/index.ts,src/extensions/tasks/index.ts) - External packages listed in
versioning.config.json
Creating Extensions
Extensions are TypeScript modules that implement the VersioningExtension interface:
import { Command } from 'commander';
import { VersioningExtension } from '@edcalderon/versioning';
const extension: VersioningExtension = {
name: 'my-extension',
description: 'My custom extension',
version: '1.0.0',
register: async (program: Command, config: any) => {
// Add custom commands here
program
.command('my-command')
.description('My custom command')
.action(async () => {
console.log('Custom command executed!');
});
},
hooks: {
preVersion: async (type: string, options: any) => {
console.log(`About to bump ${type} version...`);
},
postVersion: async (type: string, version: string, options: any) => {
console.log(`Version bumped to ${version}`);
}
}
};
export default extension;Extension Hooks
Extensions can hook into the versioning lifecycle:
preVersion: Called before version bumppostVersion: Called after version bumppreRelease: Called before release creationpostRelease: Called after release creationpreChangelog: Called before changelog generationpostChangelog: Called after changelog generationpreSync: Called before version syncpostSync: Called after version sync
Built-in Extensions
Workspace Env Extension
Adds first-class workspace environment orchestration for monorepos:
versioning env sync
versioning env doctor
versioning env validate --target landingKey capabilities:
- Canonical root env sources:
.envthen.env.local - Alias resolution for legacy variable names
- Minimal per-target runtime env generation
- Root and per-target example file generation from one manifest
- Deterministic output with generated headers and no rewrites when content is unchanged
- CI-friendly validation and dry-run sync checks
Manifest default lookup order:
config/env/manifest.tsconfig/env/manifest.cjsconfig/env/manifest.jsconfig/env/manifest.json
Supported manifest styles:
- Legacy map-based manifest:
{
sources: ['.env', '.env.local'],
variables: {
SUPABASE_URL: {
description: 'Supabase URL',
required: true,
targets: {
landing: 'NEXT_PUBLIC_SUPABASE_URL'
}
}
},
targets: {
landing: {
path: 'apps/landing'
}
}
}- Spec-style manifest:
{
rootExampleFile: '.env.example',
variables: [
{
key: 'SUPABASE_URL',
description: 'Supabase URL'
},
{
key: 'OPENAI_API_KEY',
aliases: ['LEGACY_OPENAI_KEY'],
secret: true
}
],
targets: [
{
id: 'landing',
outputFile: 'apps/landing/.env.local',
exampleFile: 'apps/landing/.env.example',
entries: [
{
source: 'SUPABASE_URL',
target: 'NEXT_PUBLIC_SUPABASE_URL',
required: true
},
{
source: 'OPENAI_API_KEY'
}
]
}
]
}CLI options:
versioning env sync --target landing --check
versioning env sync --json
versioning env doctor --target landing --json
versioning env validate --all --strict-unusedBasic config (versioning.config.json):
{
"extensionConfig": {
"workspace-env": {
"enabled": true,
"manifestPath": "config/env/manifest.cjs"
}
}
}Lifecycle Hooks Extension
Demonstrates all available hooks with example business logic:
versioning hooks list # List available hooks
versioning hooks run pre-deploy # Manually run a hookNPM Publish Extension
Handles NPM publishing with custom logic:
versioning publish-package --tag latest
versioning publish-local --registry http://localhost:4873Features:
- Automatic package building
- Prepublish checks
- Publication verification
- 2FA/OTP support
- Local registry support
- Dry-run mode
Re-entry Status + Roadmap Extension
Maintains a fast re-entry layer (current state + next micro-step) and a slow roadmap/backlog layer (long-term plan).
Canonical files:
Single-project (default):
.versioning/reentry.status.json(machine).versioning/REENTRY.md(generated, minimal diffs).versioning/ROADMAP.md(human-first; only a small managed header block is auto-updated)
Multi-project (scoped by --project <name>):
.versioning/projects/<project>/reentry.status.json.versioning/projects/<project>/REENTRY.md.versioning/projects/<project>/ROADMAP.md
Smart Features:
- Auto-Update:
versioning reentry updateinfers the project phase and next step from your last git commit.feat: ...→ Phase: developmentfix: ...→ Phase: maintenance
- Git Context: Automatically links status to the latest branch, commit, and author.
Commands:
# Fast layer
versioning reentry init
versioning reentry update # Auto-update status from git commit
versioning reentry show # Show current status summary
versioning reentry sync
# Fast layer (scoped)
versioning reentry init --project trader
versioning reentry update --project trader
versioning reentry show --project trader
# Slow layer
versioning roadmap init --title "My Project"
versioning roadmap list
versioning roadmap set-milestone --id "now-01" --title "Ship X"Cleanup Repo Extension
Keeps your repository root clean by organizing stray files into appropriate directories (docs, scripts, config, archive).
Features:
- Smart Scanning: Identifies files that don't belong in the root.
- Configurable Routes: Map extensions to folders (e.g.
.sh→scripts/). - Safety: Allowlist for root files and Denylist for forced moves.
- Husky Integration: Auto-scan or auto-move on commit.
Commands:
versioning cleanup scan # Dry-run scan of root
versioning cleanup move # Move files to configured destinations
versioning cleanup restore # Restore a moved file
versioning cleanup config # View/manage configuration
versioning cleanup husky # Setup git hook (scan-only by default)Configuration (versioning.config.json):
{
"extensionConfig": {
"cleanup-repo": {
"enabled": true,
"defaultDestination": "docs",
"allowlist": ["CHANGELOG.md"],
"routes": {
".sh": "scripts",
".json": "config"
}
}
}
}Secrets Check Extension
Prevents sensitive data (private keys, tokens, mnemonics) from being committed to the repository.
Features:
- Pre-defined Patterns: Detects AWS, GitHub, NPM tokens, private keys, and EVM mnemonics.
- Allowlist: Ignore false positives or specific test keys.
- Husky Integration: Easy CLI setup to block commits containing secrets.
Commands:
versioning check-secrets # Scan staged files for secrets
versioning check-secrets husky # Add blocking secrets check to pre-commit hookConfiguration (versioning.config.json):
{
"extensionConfig": {
"secrets-check": {
"enabled": true,
"patterns": [
"CUSTOM_API_KEY=[0-9a-f]{32}"
],
"allowlist": [
"ETHERSCAN_API_KEY=YOUR_KEY"
]
}
}
}README Maintainer Extension
Keeps README.md files aligned with the latest changelog entry and resolves the release-history link from the target project when possible.
Behavior:
- Uses
package.json.repositoryfirst - Falls back to the git
originremote, thenGITHUB_REPOSITORY - Omits the releases link when no project repository can be resolved
Override the release link with extensionConfig["readme-maintainer"].releasesUrl or repositoryUrl in versioning.config.json.
External Extensions
To use external extensions, add them to your versioning.config.json:
{
"extensions": [
"my-versioning-extension",
{
"name": "another-extension",
"path": "./local-extensions/another-extension"
}
]
}External extensions should be published as NPM packages with the naming convention *-versioning-extension or implement the VersioningExtension interface.
Extension Development
- Create a new TypeScript file in
src/extensions/ - Implement the
VersioningExtensioninterface - Export the extension as default
- The extension will be loaded automatically
For external extensions:
- Create a separate NPM package
- Export the extension as the main module
- Publish to NPM
- Install and configure in target projects
Extension API Reference
VersioningExtension Interface
interface VersioningExtension {
name: string; // Extension name
description: string; // Extension description
version: string; // Extension version
register: (program: Command, config: any) => void | Promise<void>;
hooks?: ExtensionHooks; // Optional lifecycle hooks
}ExtensionHooks Interface
interface ExtensionHooks {
preVersion?: (type: string, options: any) => void | Promise<void>;
postVersion?: (type: string, version: string, options: any) => void | Promise<void>;
preRelease?: (version: string, options: any) => void | Promise<void>;
postRelease?: (version: string, options: any) => void | Promise<void>;
preChangelog?: (options: any) => void | Promise<void>;
postChangelog?: (options: any) => void | Promise<void>;
preSync?: (options: any) => void | Promise<void>;
postSync?: (options: any) => void | Promise<void>;
}Extension Context
Extensions can access the versioning context:
import { getExtensionContext } from '@edcalderon/versioning';
const context = getExtensionContext();
if (context) {
// Access versionManager, changelogManager, etc.
}Quick Start
- Initialize configuration:
versioning init- Bump version and generate changelog:
versioning bump patch- Sync versions across packages (for monorepos):
versioning syncEdward's Monorepo Example
For this specific monorepo with dashboard app:
# Initialize config
versioning init
# Edit versioning.config.json to:
{
"packages": ["apps/dashboard"],
"ignorePackages": ["packages/versioning"]
}
# Sync dashboard with main version
versioning patch --packages "apps/dashboard"
# Versioning package maintains its own version
cd packages/versioning && versioning patch --skip-syncInternal Versioning
This versioning package uses its own versioning system internally for development and releases:
# Bump version internally
npm run version:patch # Bump patch version
npm run version:minor # Bump minor version
npm run version:major # Bump major version
# Generate changelog
npm run changelog
# Publish to a local registry for testing only
npm run publish:local
# Prepare a production release: bump and sync README
npm run release
# Commit and merge the release to main, then push the tag from main
npm run create-tagThe pushed versioning-v* tag is the only production publishing trigger for this package. Do not run npm publish or npm run publish:npm for a public release.
NPM Publish Extension
The package includes an NPM publishing extension for consumer projects. This repository's own public package release remains GitHub Actions-only.
# Publish to a local registry for testing
npm run publish:local
# Publish to custom local registry
npm run publish:local -- --registry http://localhost:4873The extension automatically:
- Builds the package if needed
- Runs prepublish checks
- Verifies publication
- Handles 2FA/OTP if required
- Supports dry-run mode
Configuration
Create a versioning.config.json file in your project root:
For single repositories:
{
"rootPackageJson": "package.json",
"packages": [],
"changelogFile": "CHANGELOG.md",
"conventionalCommits": true,
"syncDependencies": false,
"ignorePackages": [],
"branchAwareness": {
"enabled": false,
"defaultBranch": "main",
"branches": {
"main": {
"versionFormat": "semantic",
"tagFormat": "v{version}",
"syncFiles": ["package.json", "version.production.json"],
"environment": "production",
"bumpStrategy": "semantic"
},
"develop": {
"versionFormat": "dev",
"tagFormat": "v{version}",
"syncFiles": ["version.development.json"],
"environment": "development",
"bumpStrategy": "dev-build"
},
"feature/*": {
"versionFormat": "feature",
"tagFormat": "v{version}",
"syncFiles": ["version.development.json"],
"environment": "development",
"bumpStrategy": "feature-branch"
},
"hotfix/*": {
"versionFormat": "hotfix",
"tagFormat": "v{version}",
"syncFiles": ["version.development.json"],
"environment": "development",
"bumpStrategy": "hotfix"
}
}
}
}For monorepos:
{
"rootPackageJson": "package.json",
"packages": ["packages/*", "apps/*"],
"changelogFile": "CHANGELOG.md",
"conventionalCommits": true,
"syncDependencies": true,
"ignorePackages": []
}Configuration Options
rootPackageJson: Path to the root package.json filepackages: Array of glob patterns for packages to syncchangelogFile: Path to the changelog fileconventionalCommits: Whether to use conventional commits for changelogsyncDependencies: Whether to sync internal dependenciesignorePackages: Array of package names to ignore during syncbranchAwareness: Optional branch-aware release rules and file sync targetsbranchAwareness.branches.<pattern>.versionFormat:semantic,dev,feature,hotfix, or custombranchAwareness.branches.<pattern>.syncFiles: Only these files are updated when branch-aware mode is enabled
Branch-Aware Releases
Enable branch-aware mode per command:
versioning patch --branch-aware
versioning patch --branch-aware --target-branch develop
versioning patch --branch-aware --format dev --build 396
versioning minor --branch-aware
versioning major --branch-awareBehavior summary:
- Exact branch names are checked first (e.g.
main,develop) - Wildcard patterns are checked next (e.g.
feature/*,hotfix/*) - If no match is found, the
defaultBranchrule is used --force-branch-awareenables branch-aware behavior even whenbranchAwareness.enabledisfalse- Full config template:
packages/versioning/examples/versioning.config.branch-aware.json
Commands
Release Commands
versioning patch [options]
Create a patch release (bumps 1.0.0 → 1.0.1)
versioning patch
versioning patch --packages "packages/app1,packages/app2" --message "Fix critical bug"versioning minor [options]
Create a minor release (bumps 1.0.0 → 1.1.0)
versioning minor
versioning minor --packages "apps/dashboard" --message "Add new features"versioning major [options]
Create a major release (bumps 1.0.0 → 2.0.0)
versioning major --message "Breaking changes"versioning release <version> [options]
Create a custom release with specific version
versioning release 1.2.3 --message "Custom release"
versioning release 2.0.0-beta.1 --skip-syncOptions for patch, minor, and major:
-p, --packages <packages>: Comma-separated list of packages to sync-m, --message <message>: Release commit message-c, --config <file>: Config file path (default: versioning.config.json)--branch-aware: Enable branch-aware release behavior--force-branch-aware: Force branch-aware mode even if disabled in config--target-branch <branch>: Explicit branch to resolve branch rules--format <format>: Override the configured branch version format--build <number>: Override build number for non-semantic branch formats--no-tag: Do not create git tag--no-commit: Do not commit changes
Options for release <version>:
-p, --packages <packages>: Comma-separated list of packages to sync-m, --message <message>: Release commit message-c, --config <file>: Config file path (default: versioning.config.json)--no-tag: Do not create git tag--no-commit: Do not commit changes--skip-sync: Skip version synchronization
Other Commands
Bump the version and generate changelog.
Types: patch, minor, major, prerelease
Options:
-p, --pre-release <identifier>: Prerelease identifier-c, --config <file>: Config file path (default: versioning.config.json)--no-commit: Don't commit changes--no-tag: Don't create git tag
versioning changelog [options]
Generate changelog from commits.
Options:
-f, --from <commit>: From commit-t, --to <commit>: To commit-c, --config <file>: Config file path
versioning sync [options]
Sync versions across all packages.
Options:
-v, --version <version>: Target version to sync to-c, --config <file>: Config file path
versioning validate [options]
Validate that all packages have the correct version.
Options:
-c, --config <file>: Config file path
versioning init [options]
Initialize a new versioning config file.
Options:
-f, --force: Overwrite existing config
Workflow Integration
GitHub Actions
Add to your release workflow:
- name: Bump version
run: npx @edcalderon/versioning bump patch
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}Pre-commit Hooks
Use with husky for automated versioning:
npx husky add .husky/pre-commit "versioning validate"Conventional Commits
This tool works best with conventional commits. Examples:
feat: add new feature(minor bump)fix: resolve bug(patch bump)BREAKING CHANGE: change API(major bump)
Documentation
- Usage Guide - Comprehensive examples and advanced usage patterns
- Configuration Examples - Real-world configuration examples
- CHANGELOG - Version history and changes
Publishing & Releases
Automated NPM Publishing
This package uses GitHub Actions as the sole production publisher. Pushing a versioning-v<version> tag starts the publish workflow.
Release Process
Prepare the release commit: Bump the version, generate the changelog, and sync the README.
npm run version:patch npm run update-readmeCommit and merge the release: The version, CHANGELOG, and README must be committed on
main.Publish locally (optional): Test only against a local registry.
npm run publish:localCreate the release tag: Use the create-tag script to create and push the matching tag.
npm run create-tagMonitor GitHub Actions: The workflow verifies the tag, package version, and generated README before publishing. It skips a version already present in npm instead of attempting a duplicate publish.
Quick Release Commands
# Prepare a production release, then commit and merge it to main
npm run release
# After the release commit is on main, create the publishing tag
npm run create-tag
# Local testing release
npm run release:local # Bump version, changelog, publish locallyPublic npm versions are immutable. A direct publish or rerun after a version is already published returns npm E403. The package lifecycle blocks public manual publishes by default. An emergency manual publish requires explicit maintainer approval and both ALLOW_MANUAL_NPM_PUBLISH=1 and MANUAL_PUBLISH_CONFIRM=@edcalderon/versioning@<version>.
NPM Token Setup
To enable automated publishing:
- Go to NPM → Access Tokens → Generate New Token
- Create a token with Automation scope
- Add it only to GitHub repository secrets as
NPM_TOKEN; do not keep a production publishing token in local developer configuration.
Version Tags
Tags should follow the format v{major}.{minor}.{patch} for the CLI release flow and versioning-v{major}.{minor}.{patch} for this package's own publish flow (for example, v1.0.0, v1.1.0, versioning-v1.5.7).
The create-tag script will:
- Read the version from
package.json - Create an annotated git tag
- Push the tag to trigger the publish workflow
- Validate the tag against the current package version and the latest release floor before pushing
Release Guard
Enable release/tag consistency checks in versioning.config.json:
{
"rootPackageJson": "package.json",
"packages": ["packages/*", "apps/*"],
"releaseGuard": {
"enabled": true,
"tagPrefix": "v",
"allowBuildMetadata": true,
"checkReleaseFloor": true,
"metadataFiles": ["version.production.json"]
}
}This will validate the root package, all configured package directories, and any extra release metadata files before a release tag is published.
