repo-version-manager
v1.1.1
Published
Interactive CLI to manage the global version of a repo or monorepo and the individual version of each of its services, with git integration (branches, tags, rebase)
Downloads
127
Maintainers
Readme
Repo Version Manager (rpvm)
Interactive CLI to version a repository — or every service inside a monorepo — from your terminal, with git integration (branches, tags, rebase). You stay in control: rpvm never pushes.

Why rpvm?
Tools like semantic-release or changesets automate versioning from commit conventions and CI pipelines. rpvm takes the opposite approach: a short interactive session where you decide every bump, review a summary of exactly what will happen, and nothing leaves your machine until you push it yourself.
- 🎛️ Interactive and explicit — pick every bump from a prompt; no commit-message conventions to learn.
- 🧩 Monorepo-aware — one global version for the repo, plus an individual version per service.
- 🚦 Guard rails — a service never bumps above the global bump, and from the main branch only
patch(hotfix) releases are allowed. - 🔄 Git taken care of — syncs with
origin(fetch + rebase), then creates the release commit and the annotated tag for you. - 🙅 Never pushes — commits and tags stay local; the CLI prints the exact push command for you to run.
- 🧪 Dry run —
rpvm release --dry-runwalks the whole flow without touching a single file.
Quick start
npm install -g repo-version-manager
cd your-repo
rpvm init # one-time setup: mode, services, versions and branches
rpvm release # every time you want to cut a new versionRequires Node.js ≥ 18.19. The package installs a single binary: rpvm.
Commands
rpvm init — set up the repository (once)
Run it from the repository root. It asks you, with sensible defaults auto-detected from your project:
- Whether the repo is a monorepo with services (detected from your folder structure).
- In monorepo mode, which first-level folders are services (folders with a
package.jsoncome preselected) and each service's initial version. - The global version (defaults to the root
package.jsonversion when available). - For services that are Expo/React Native apps with committed
ios//android/folders (or the repo root itself, in single-repo mode), whetherrpvmshould also manage their native versions on every release (see Expo / React Native apps). - The main branch (
main/master) and the development branch (develop/development).
It then writes the config and version files (see Generated files), updates the package.json versions, commits everything (🔖 RPVM init vX.Y.Z) and, optionally, creates the initial vX.Y.Z tag.
rpvm release — cut a new version
The everyday command — the one recorded in the demo above. It checks that the working tree is clean and that you are on the main or the development branch, rebases onto origin, and then walks you through the bumps: first the global one, then (in monorepo mode) one per service. It ends with a summary like this, and nothing is written until you confirm it:
◇ Release v1.3.0 summary ────╮
│ │
│ global 1.2.3 → 1.3.0 │
│ api 0.9.1 → 0.10.0 │
│ web 2.1.0 (unchanged) │
│ │
├─────────────────────────────╯On confirmation it writes the version files — including the native files of managed Expo apps — creates the release commit and the annotated tag, and prints the exact push command for when you're ready. Add --dry-run to walk the whole flow and see the list of actions without modifying anything.
rpvm status — where am I?
Shows the global version (and each service's version in monorepo mode) and warns when a package.json is out of sync with its .version file:
┌ rpvm status
│
◇ Versions ───────────────────────────────╮
│ │
│ global v1.2.3 ✔ package.json in sync │
│ │
├──────────────────────────────────────────╯
│
└ main: main · develop: develop · current: developFor managed Expo apps it also shows one line per native target (android, ios, app.json) with its version, build number and sync state.
How a release works
flowchart TD
start(["rpvm release"]) --> checks{"Working tree clean?<br/>On main or develop?"}
checks -- no --> abort(["Abort — nothing modified"])
checks -- yes --> sync["Sync with origin (fetch + rebase)<br/>develop: onto origin/develop, then origin/main<br/>main: onto origin/main"]
sync --> bump["Choose the global bump<br/>(from main: patch only)"]
bump --> services["Monorepo: choose each service's bump<br/>(never above the global one)"]
services --> summary{"Summary — confirm?"}
summary -- no --> abort
summary -- yes --> write["Write .version, package.json<br/>and native (Expo) version files"]
write --> tag["Commit 🔖 RPVM release vX.Y.Z<br/>+ annotated tag vX.Y.Z"]
tag --> push(["You push when ready —<br/>rpvm prints the exact command"])The rules behind the flow:
- A service bump never exceeds the global one (
patch<minor<major); a service may also stay unchanged. - From the main branch only
patchreleases are generated — features ship through the development branch; main is for hotfixes. - Git tags (
vX.Y.Z) track the global version. - On the development branch the suggested push uses
--force-with-lease(the branch was just rebased); on main it never suggests forcing.
Expo / React Native apps
For Expo apps whose native folders are committed (ejected / expo prebuild), bumping the version means touching several native files. When native management is enabled for a service during rpvm init, every release that bumps that service also updates:
| Platform | File | Fields |
|---|---|---|
| Android | android/app/build.gradle | versionName, versionCode |
| iOS | ios/<App>.xcodeproj/project.pbxproj | MARKETING_VERSION, CURRENT_PROJECT_VERSION (all build configurations) |
| iOS | ios/<App>/Info.plist | CFBundleShortVersionString, CFBundleVersion — only when the values are literals; entries using $(MARKETING_VERSION)-style variables are left to the pbxproj |
| Expo | app.json | expo.version, plus expo.ios.buildNumber / expo.android.versionCode when the project already declares them |
How it works:
- The semver version written to the native files is the service version (or the global version in single-repo mode).
- Build numbers (
versionCode,CFBundleVersion/CURRENT_PROJECT_VERSION) are not semver:rpvmuses a single shared build number per app and auto-increments it on every release — one past the highest build number found on any platform, so store uploads stay monotonic. - All edits are surgical text replacements: the rest of the file (formatting, comments, other settings) is untouched.
- If a value cannot be updated safely (
versionCodecomputed from a Gradle variable, config inapp.config.js/tsinstead ofapp.json),rpvmwarns and asks you to update it manually instead of guessing. --dry-runlists the exact native files that would be written.
The selection is stored in .rpvmrc.json under the expo key ("." refers to the repository root):
{
"monorepo": true,
"services": ["api", "mobile"],
"expo": {
"mobile": { "ios": true, "android": true, "syncAppJson": true }
}
}Generated files
| File | Where | Content |
|---|---|---|
| .rpvmrc.json | root | mode (monorepo or not), branches, service list and Expo native targets |
| .version | root | global repository version |
| .version | each service (monorepo mode) | service version — the source of truth, package.json follows it |
FAQ
The rebase hit conflicts. Is my repo in a weird state?
No. rpvm aborts the rebase and exits without modifying anything. Resolve the conflicts manually (git rebase origin/<branch>) and run rpvm release again.
rpvm status warns that a package.json is out of sync.
.version is the source of truth. Someone edited a package.json version by hand — set it back to the .version value (the next release will rewrite it anyway).
Can I use it without a remote? Without git at all?
Yes. With no origin remote, the sync step is skipped (you'll see a warning). With no git repository, rpvm only manages the version files — no commits, no tags.
I cut a release locally and regret it. How do I undo it?
As long as you haven't pushed: git tag -d vX.Y.Z && git reset --hard HEAD~1. That's the point of never pushing automatically.
Why only patch from main?
Main represents what's in production. New features go through the development branch and reach main via a regular release; anything released directly from main is by definition a hotfix.
Development
git clone https://github.com/Gcuencam/repo-version-manager.git
cd repo-version-manager
npm install
npm run dev # tsup in watch mode
npm run typecheck
npm test
npm run build
npm link # try `rpvm` locallyTo regenerate the README demo GIF (requires vhs):
bash demo/setup.sh && vhs demo/demo.tapeFound a bug or have an idea? Open an issue.
