@orderly.network/release-tag
v1.0.6
Published
Shared Orderly release tag CLI for creating `dev`, `qa`, and `prod` deployment tags.
Keywords
Readme
@orderly.network/release-tag
Shared Orderly release tag CLI for creating dev, qa, and prod deployment tags.
Install
Requires Node.js 18 or newer.
npm install -D @orderly.network/release-tagUsage
orderly-release-tag --env dev
orderly-release-tag --env qa
orderly-release-tag --env prodLocal mode pushes to origin by default. Repeat --remote to publish the same
tag to selected Git remotes:
orderly-release-tag --env qa --remote origin --remote gitlabRemote selection uses this precedence: repeated --remote options,
RELEASE_TAG_REMOTES, tagRemotes in the config file, then origin. Before
choosing the next tag, local mode first verifies that a normal, non-force push
would succeed for every selected branch remote, then validates the release tag
plan.
Only after all preflight checks pass does it push the current branch, verify each
branch remote tip, and create the tag. branchRemotes defaults to the selected
tag remotes for backward compatibility. Set it to a subset when some remotes
only need the tag, or to [] to skip branch network operations. Set
pushBranchBeforeTag: false to disable the
automatic branch push and require every configured branch remote to be
synchronized before running the release command. Dry runs use
git push --dry-run and do not modify remote branches. CLI and environment
remote overrides also limit configured branch remotes to the selected tag
remotes. When a selected Git remote is not configured in the local repository,
the CLI uses origin instead.
Tags with the same name must point to the same Git object on every selected remote. The CLI rejects conflicts instead of force-updating local tag refs.
Worktree cleanliness is not checked by default. Set requireCleanWorktree: true
to reject staged, unstaged, or untracked changes before any branch or tag push.
Pushes to multiple remotes are not atomic. If every push fails, the CLI deletes the new local tag as before. If only some pushes fail, it keeps the local tag and reports both the successful and failed remotes. Retry only the failed remotes with the existing local tag:
orderly-release-tag --retry-tag v1.0.0-qa-0 --remote gitlabRetry mode skips remotes that already contain the same tag and rejects a remote tag with the same name but a different Git object. It does not push branches or calculate a new tag number.
Use --config to load project-specific tag rules:
orderly-release-tag --env qa --config ./release-tag.config.mjsUse --dry-run to validate the release path without creating a tag or triggering GitLab:
orderly-release-tag --env dev --dry-run--dry-run still performs validation, but it does not push a branch, create a
local tag, push a tag, or trigger a GitLab pipeline.
Modes
RELEASE_TAG_MODE supports:
auto: use trigger mode when trigger configuration and token are available; otherwise use local tag mode.trigger: require GitLab trigger configuration and trigger the release pipeline.local: create and push the Git tag from the local machine.
The CLI loads .env.local from the current working directory when present. Existing shell environment variables take precedence over .env.local values.
Trigger mode
Trigger mode creates a GitLab trigger pipeline instead of pushing the tag from your local machine.
RELEASE_TAG_MODE=trigger \
GITLAB_TRIGGER_TOKEN=<gitlab_pipeline_trigger_token> \
orderly-release-tag --env dev --project-id 52443474GITLAB_TRIGGER_TOKEN must be a GitLab Pipeline Trigger Token. Do not use
CI_JOB_TOKEN.
The project id can also be provided through GitLab's predefined CI_PROJECT_ID:
RELEASE_TAG_MODE=trigger \
CI_PROJECT_ID=52443474 \
GITLAB_TRIGGER_TOKEN=<gitlab_pipeline_trigger_token> \
orderly-release-tag --env devOr configure it in release-tag.config.mjs:
export default {
gitlabProjectId: 52443474,
};The resolution order is --project-id, CI_PROJECT_ID, then
gitlabProjectId from release-tag.config.mjs.
RELEASE_TAG_BRANCH is optional for dev and qa. When omitted, the
current branch is used. By default, prod triggers main; this can be changed
with prodEnv / prodBranch. --config is passed to the triggered pipeline
as RELEASE_TAG_CONFIG. In trigger mode, the config path must be relative to
the repository so the remote pipeline can load the same file.
Local mode
Local mode creates and pushes the Git tag from the current machine.
RELEASE_TAG_MODE=local orderly-release-tag --env devBefore creating a tag, the CLI validates that:
- the current checkout is on a branch, not detached
HEAD; - the prod environment releases from the configured prod branch;
- the current branch can be pushed to every configured branch remote, or already
matches each branch remote when
pushBranchBeforeTagis disabled; - a base release tag can be resolved from the latest tag matching the configured release tag rule.
Configuration
The CLI uses the built-in strategy by default. To customize tag rules, add
release-tag.config.mjs in the current working directory, or pass a config path
with --config / RELEASE_TAG_CONFIG.
export default {
gitlabProjectId: 52443474,
tagRemotes: ["origin", "gitlab"],
branchRemotes: ["origin"],
pushBranchBeforeTag: true,
requireCleanWorktree: false,
releaseVersionMode: "legacy",
environments: ["dev", "qa", "prod"],
prodEnv: "prod",
prodBranch: "main",
releaseTagRule: {
pattern: /^sdk-v(\d+)\.(\d+)\.(\d+)$/,
description: "sdk-vX.Y.Z",
example: "sdk-v1.0.0",
format({ major, minor, patch }) {
return `sdk-v${major}.${minor}.${patch}`;
},
},
formatPrereleaseTag({ releaseTag, branchPart, env, nextNumber }) {
return branchPart
? `${releaseTag}-${branchPart}-${env}-${nextNumber}`
: `${releaseTag}-${env}-${nextNumber}`;
},
};releaseVersionMode controls which stable version prerelease tags target:
legacy(the default) preserves the original behavior: prerelease tags use the latest stable tag, while prod increments to the next stable tag.nextmakes prerelease and prod tags share the next stable version. For example, a base tag ofv1.2.3producesv1.2.4-rc.0and thenv1.2.4.
Three-part rules increment patch; four-part rules increment build.
Values other than legacy and next are rejected.
releaseTagRule.pattern must use its first three capture groups for
major, minor, and patch. These values must be numeric safe integers, and
the pattern must not use the stateful g or y flag. A fourth capture group is
treated as build only when it contains digits; a missing or non-numeric fourth
group is ignored. Every matching tag is normalized through
releaseTagRule.format, so a rule can accept legacy tag variants while still
returning one canonical base release tag. When that canonical tag also matches
the rule, its normalized version parts determine sorting and prod increments;
for example, formatting legacy v1.2.3 as v1.2.3.0 makes prod increment the
build to v1.2.3.1. formatPrereleaseTag must append
nextNumber as the final tag suffix so the CLI can find and increment existing
prerelease tags.
resolveProdTag can override the built-in prod version increment for projects
that promote a prerelease tag to the same stable version or use another release
policy. It receives all remote tags, the normalized base releaseTag,
baseReleaseTag, targetReleaseTag, releaseVersionMode, the parsed version
(major, minor, patch, and optional build), and the validated production
branchName. releaseTag remains an alias for the existing base tag for
backward compatibility. It must synchronously return a non-empty Git tag name.
The CLI rejects invalid names and tags that already exist.
For example, a project using vX.Y.Z-rc.N can promote its highest RC version to
vX.Y.Z:
export default {
releaseVersionMode: "next",
resolveProdTag({ tags, targetReleaseTag }) {
const candidates = tags
.map((tag) => tag.match(/^v(\d+)\.(\d+)\.(\d+)-rc\.(\d+)$/))
.filter((match) => {
if (!match) return false;
const [, major, minor, patch] = match;
return `v${major}.${minor}.${patch}` === targetReleaseTag;
})
.sort((a, b) => Number(b[4]) - Number(a[4]));
if (!candidates.length) {
throw new Error("No RC tag found.");
}
return targetReleaseTag;
},
};For example, a four-part project can accept both a canonical release tag and a legacy numeric-suffix tag:
export default {
releaseTagRule: {
pattern: /^v(\d+)\.(\d+)\.(\d+)\.(\d+)-demo(?:-\d+)?$/,
description: "vX.Y.Z.W-demo or vX.Y.Z.W-demo-N",
example: "v3.1.4.0-demo",
format({ major, minor, patch, build = 0 }) {
return `v${major}.${minor}.${patch}.${build}-demo`;
},
},
};With v3.0.4.1-demo and v3.1.4.0-demo-7 present, the latest normalized
base tag is v3.1.4.0-demo. Three-part rules remain unchanged because their
formatters can ignore the optional build value. When otherwise equal, a
four-part release sorts after the corresponding three-part release, so
v1.2.3.0 is newer than v1.2.3.
Tag format
The next tag is based on the latest release tag matching releaseTagRule.
Examples:
main+dev:v1.0.0-dev-0feature/order-entry+qa:v1.0.0-order-entry-qa-0main+prod:v1.0.1
With releaseVersionMode: "next", the prerelease examples use the next stable
version instead, such as v1.0.1-dev-0; prod remains v1.0.1.
If a matching prerelease tag already exists, the numeric suffix is incremented.
For three-part release rules, prod increments patch. For four-part release
rules, prod increments build, for example v1.2.3.10 becomes v1.2.3.11.
Environment variables
| Name | Description |
| ---------------------- | --------------------------------------------------------------------------------------------- |
| RELEASE_TAG_MODE | auto, trigger, or local. Defaults to auto. |
| RELEASE_TAG_ENV | Optional release environment. --env takes precedence. |
| RELEASE_TAG_CONFIG | Optional path to release-tag.config.mjs. --config takes precedence. |
| RELEASE_TAG_REMOTES | Optional comma-separated Git remotes for local mode. --remote takes precedence. |
| RELEASE_TAG_BRANCH | Optional branch override for dev and qa trigger pipelines. Must match the current branch. |
| CI_PROJECT_ID | Optional GitLab project id used by trigger mode. Overrides configured gitlabProjectId; --project-id takes precedence. |
| GITLAB_TRIGGER_TOKEN | GitLab Pipeline Trigger Token used by trigger mode. |
Forwarding trigger variables
Trigger mode only forwards release-tag variables by default. To pass additional
environment variables to the triggered GitLab pipeline, add triggerVariables to
release-tag.config.mjs:
export default {
triggerVariables: ["VITE_APP_TARGET"],
};Only variables that are present in the local environment are forwarded.
Troubleshooting
No release tag found. Expected at least one tag like v1.0.0.
Create or fetch a base release tag first. The CLI only increments tags that
match the configured releaseTagRule; the built-in rule matches vX.Y.Z.
Local HEAD does not match <remote>/<branch>.
Push or pull the current branch before running the release command again.
RELEASE_TAG_MODE=trigger requires trigger project id and GITLAB_TRIGGER_TOKEN.
