semalease
v0.2.2
Published
Semantic version commit message based release utilities. Simple, flexible, designed for monorepo (polyci).
Downloads
1,308
Readme
semalease
Finds the latest git tag matching a pattern, analyzes commits since that tag by conventional commits, calculates next version from commit messages, and writes LATEST_VERSION, NEXT_VERSION, LATEST_INCREMENT, and NEXT_INCREMENT.
Version bump (major/minor/patch) is determined by --major, --minor, --patch regex patterns matched against each commit message. Highest match wins.
Version format
semalease extends semver by using a flexible dotted-numeric pattern: \d+(\.\d+)*. This matches any sequence of integers separated by dots (e.g. 1, 1.0, 1.0.0, 1.2.3.4). The default version pattern parses ?<major>, ?<minor>, ?<patch> with minor and patch optional.
Why this helps:
- Strict semver – Use
1.0.0-style tags. The default pattern matches standard semver. - Pre-release / branch workflows – Add an optional
?<increment>group to the tag pattern. The version stays stable; the increment bumps on each release (e.g.v1.0.0-develop-0.0.1→v1.0.0-develop-0.0.2). - Monorepos – Use
api-v1.0.0,ui-v2.1.0with pattern^(\w+)-v(?<version>...)for per-package versions. - Custom schemes – Use
--version-patternto parse any dotted-numeric format.
Examples
Basic usage
Write version variables to a file. Uses default tag pattern ^v(?<version>\d+(\.\d+)*)$ (e.g. v1.2.3).
npx semalease --output .env.releaseCustom working directory
Run from a subdirectory (e.g. monorepo module). Git operations use --cwd; output path is resolved from the current directory.
npx semalease --cwd modules/my-package --output modules/my-package/semalease.envSimple semver tags
Default pattern matches tags like v1.0.0, v2.3.1. Commits since the latest tag determine the bump: feat: → minor, fix: → patch, BREAKING CHANGE → major.
npx semalease -o release.env -t '^v(?<version>\d+(\.\d+)*)$'Branch-based tags with increment
For pre-release or branch workflows, use ?<increment> in the tag pattern. Tags are sorted by version, then increment. The increment is bumped instead of the version.
Example tags: my-module-v1.0.0-develop-0.0.1, my-module-v1.0.0-develop-0.0.2
npx semalease -o semalease.env -t '^my-module-v(?<version>\d+(\.\d+)*)(-develop-(?<increment>\d+(\.\d+)*))?$'Custom commit patterns
Override which commit messages trigger major/minor/patch bumps. Defaults: feat: → minor, fix:/chore: → patch, BREAKING CHANGE or !: → major.
npx semalease -o .env.release \
--major '^break:' \
--minor '^feat:' \
--patch '^fix:'Options
| Option | Description |
|--------|-------------|
| -o, --output | Output file path (required) |
| -t, --tag-pattern | Tag regex with ?<version> and optionally ?<increment>. Default: ^v(?<version>\d+(\.\d+)*)$. If ?<increment> present: sorts by version then increment, bumps increment instead of version. |
| -v, --version-pattern | Regex to parse version/increment into ?<major>, ?<minor>, ?<patch>. Default: ^(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)$ |
| --cwd | Working directory (default: current directory) |
| --major | Regex for major bump |
| --minor | Regex for minor bump |
| --patch | Regex for patch bump |
GitLab CI
Git strategy and depth
semalease uses git tag -l and git log to find tags and analyze commits. In GitLab CI, shallow clones (GIT_DEPTH < full history) can omit tags whose commits were not fetched. Jobs that run semalease should use a full clone so all tags are available.
variables:
GIT_DEPTH: 0 # Full clone; required for semalease to see all tags
GIT_STRATEGY: clone # Prefer clone over fetch to avoid stale tag referencesIf you cannot use a full clone, run git fetch --tags before semalease to pull tags from the remote. Tags pointing to commits outside a shallow fetch may still be missing.
Examples
Minimal release job
Run semalease, source the output, and use the version variables. Requires git for tag/commit analysis.
release:
stage: release
image: node:alpine
before_script:
- apk add --no-cache git
script:
- npx semalease -o semalease.env
- source semalease.env
- echo "Next version: $NEXT_VERSION"
- |
if [ -n "${NEXT_VERSION:-}" ] && [ "${NEXT_VERSION}" != "${LATEST_VERSION:-}" ]; then
npm version $NEXT_VERSION --no-git-tag-version
# git tag, npm publish, etc.
fiMonorepo: loop over modules
Use --cwd when packages live in subdirectories. Loop over modules and run semalease for each with a module-specific tag pattern (e.g. api-v1.0.0, ui-v2.1.0).
release:
stage: release
image: node:alpine
before_script:
- apk add --no-cache git
script:
- |
for MODULE in api ui shared; do
MODULE_PATH="modules/$MODULE"
npx semalease --cwd "$MODULE_PATH" --tag-pattern "^${MODULE}-v(?<version>\\d+(\\.\\d+)*)$" -o "$MODULE_PATH/semalease.env"
if [ -f "$MODULE_PATH/semalease.env" ]; then
source "$MODULE_PATH/semalease.env"
echo "$MODULE: LATEST=$LATEST_VERSION NEXT=$NEXT_VERSION"
fi
doneBranch-based pre-release (main vs develop)
Use different tag patterns per branch. Main branch: simple v1.0.0. Develop: v1.0.0-develop-0.0.1 with increment.
release:
stage: release
image: node:alpine
before_script:
- apk add --no-cache git
script:
- |
if [ "$CI_COMMIT_BRANCH" = "main" ]; then
npx semalease -o semalease.env -t '^v(?<version>\d+(\.\d+)*)$'
else
npx semalease -o semalease.env -t '^v(?<version>\d+(\.\d+)*)(-'"$CI_COMMIT_BRANCH"'-(?<increment>\d+(\.\d+)*))?$'
fi
- source semalease.env
- echo "NEXT_VERSION=$NEXT_VERSION NEXT_INCREMENT=$NEXT_INCREMENT"