Interactive GitHub release pipeline CLI with multi-environment releases, meta-repo submodules, and branch sync via gh CLI


Keywords
github, release, deploy, xeploy, semver, staging, production, monorepo, meta-repo, gh
License
MIT
Install
npm install xeploy@1.4.2

Documentation

__  _______ ____  _     _____   __
\ \/ / ____|  _ \| |   / _ \ \ / /
 \  /|  _| | |_) | |  | | | \ V /
 /  \| |___|  __/| |__| |_| || |
/_/\_\_____|_|   |_____\___/ |_|

xeploy

Interactive GitHub release pipeline CLI with multi-environment releases, meta-repo submodules, and branch sync via gh CLI.

npm version license node


Overview

xeploy automates the GitHub release lifecycle through a guided interactive CLI:

  • Multi-environment releases — select staging, uat, sandbox, and/or production in one run; RC envs share one pre-release tag, final envs share one production tag.
  • Production without RC — create a final production release even when no -rc.N tag exists.
  • Release branches and PRs — optionally create release/X.Y.Z branches and open PRs per environment instead of direct merges.
  • Meta-repo support — parallel releases across git submodules, then a serial umbrella release.
  • Old release management — list past RC releases and promote or re-publish them.

Release notes are auto-generated by the GitHub CLI (when enabled), diffed against the previous release tag.


Prerequisites

Tool Version
Node.js ≥ 18
GitHub CLI (gh) any recent
Git any

The gh CLI must be authenticated (gh auth login) before running.


Installation

npm install --save-dev xeploy
# or
yarn add -D xeploy
# or
pnpm add -D xeploy
# or
bun add -D xeploy

Usage

npx xeploy
# or
bunx xeploy

Or add to your package.json scripts:

{
  "scripts": {
    "deploy": "xeploy"
  }
}

Then run:

npm run deploy
# or
bun run deploy

Makefile integration

deploy:
	npx xeploy

Interactive flow

🚀  xeploy

? What would you like to do?
  ▶ Deploy new release
    Deploy old release
    Config

  → Deploy new release
    ? Select repos to bump  (mono/meta only; multiselect, umbrella + subprojects all checked by default)
      ▶ Umbrella (this repo)
        frontend
        backend

    ? Select release environment  (single choice; only envs with a branch in `environments`)
      ▶ staging release   (RC)
        production release (final)

    ? Select bump type  (current latest: 1.2.3-rc.4)
      ▶ Release Candidate  →  1.2.3-rc.5
        Bug Fix            →  1.2.4-rc.1
        Minor              →  1.3.0-rc.1
        Major              →  2.0.0-rc.1
        Custom

  → Config
    Edit .xeploy.json settings interactively

  → Deploy old release
    Lists all previous RC tags
    ? What to do with 1.2.1-rc.2?
      ▶ Promote to production  →  1.2.1  (final)
        Re-publish as same RC  →  1.2.1-rc.2

On first run, if .xeploy.json is missing, you are prompted to create one with auto-detected settings.


Semver conventions

Tag format Meaning
X.Y.Z-rc.N Staging / UAT pre-release
X.Y.Z Final release (sandbox / production)

No v prefix is used. RC tags sort lower than final tags for the same X.Y.Z.

Empty-repo behaviour

When no tags exist yet, the first release defaults to 0.1.0-rc.1 (or 1.0.0-rc.1 for a major bump). Release notes cover the full commit history.


Configuration

Create .xeploy.json in your project root (or let the CLI create it on first run):

{
  "type": "default",
  "subprojectsDir": null,
  "tag_prefix": "v",
  "generate_release_notes": true,
  "create_production_release_branch": true,
  "create_tag": true,
  "create_pr": {
    "staging": false,
    "uat": false,
    "sandbox": false,
    "production": false
  },
  "environments": {
    "develop": "develop",
    "staging": "staging",
    "uat": "uat",
    "sandbox": "sandbox",
    "production": "main"
  }
}

| Option | Type | Default | Description | | ---------------------------------- | ----------- | -------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------- | ---------------------- | | type | "default" | "mono" | "meta" | auto-detected | Repository layout type | | subprojectsDir | string | null | auto-detected | Parent directory for mono/meta sub-projects | | tag_prefix | string | auto-detected | Prefix for git/GitHub tags (e.g. "v"v1.0.0); package.json stays unprefixed | | generate_release_notes | boolean | true | Generate GitHub release notes vs previous tag | | create_production_release_branch | boolean | true | Create release/X.Y.Z before production merge/PR | | create_tag | boolean | true | Create git tag and GitHub release; when false, only bump package.json | | create_pr | object | all false | Open PR instead of direct merge per environment | | environments | object | branch-matched | Maps env names to git branch names (null if missing); only non-null release envs appear in "Select release environments" | | subprojects | array | auto-detected | Per-subproject config when type is "mono" or "meta" — see below |

The version field of each subproject's own package.json is always what gets bumped — there's no separate versionFiles list to maintain.

Repository types

Type Detection Behaviour
default single package.json standard single-repo release
mono multiple package.json files version-bumps all configured packages
meta .gitmodules present parallel submodule releases, then umbrella

Subproject config

When type is "mono" or "meta", each subproject can be disabled from release/bump entirely with "enabled": false — disabled subprojects are skipped and don't show up in the "Select repos to bump" step. "meta" submodules (separate repos) can additionally override create_tag, create_pr, and environments; "mono" subprojects share the umbrella's create_tag/create_pr/environments since they release together as one repo.

{
  "type": "mono",
  "subprojectsDir": "apps",
  "subprojects": [
    { "repo": "frontend", "enabled": true },
    { "repo": "internal-tool", "enabled": false }
  ]
}
{
  "type": "meta",
  "subprojectsDir": "apps",
  "subprojects": [
    {
      "repo": "frontend",
      "enabled": true,
      "create_tag": true,
      "create_pr": {
        "staging": true,
        "uat": true,
        "sandbox": true,
        "production": true
      },
      "environments": {
        "develop": "develop",
        "staging": "staging",
        "uat": "uat",
        "sandbox": "sandbox",
        "production": "main"
      }
    }
  ]
}

Version consistency (mono/meta)

When you deploy a mono or meta release and the selected repos are on different latest versions, xeploy shows an extra step after the bump type is chosen:

The latest versions of selected repos are different:
Umbrella (this repo): 1.2.3
frontend: 1.2.2
backend: 1.2.2

How would you like to bump?
- Bump each version separately (Umbrella: 1.2.4, frontend: 1.2.3, backend: 1.2.3)
- Unify them to 1.2.4
  • Bump each version separately — every repo is bumped from its own current version (so repos stay on independent version lines).
  • Unify them to X.Y.Z — all repos are set to the same version, computed from the highest current version among selected repos.

For meta, the "latest version" is each repo's latest git tag. For mono, it's the version field of each package.json. This step is skipped when versions already match or when a custom version was entered.


Programmatic API

All internals are exported for use in custom scripts:

import {
  loadConfig,
  createDefaultConfig,
  flowNewRelease,
  flowOldRelease,
  executeReleasePlan,
  getTags,
  getLatestTag,
  parseSemVer,
  formatSemVer,
  bumpVersion,
} from "xeploy";

const config = loadConfig(process.cwd()) ?? createDefaultConfig(process.cwd());
const tags = getTags();
await flowNewRelease(tags, config, process.cwd());

Development

git clone https://github.com/Hussain7Abbas/xeploy.git
cd xeploy
make install
make build
Command Description
make build Compile TypeScript → dist/
make dev Watch mode
make clean Remove dist/
make typecheck Type-check without emitting
make lint Biome lint
make format Biome format
make check Biome check + format
make publish-dry Preview npm publish
make publish Build + publish to npm
make deploy Run xeploy CLI, then optional npm publish

Contributing

Pull requests are welcome. Please open an issue first to discuss significant changes.


License

MIT © Hussain Abbas