
Security News
/Research
Popular node-ipc npm Package Infected with Credential Stealer
Socket detected malicious node-ipc versions with obfuscated stealer/backdoor behavior in a developing npm supply chain attack.
versionary
Advanced tools
Automatic release framework based on conventional commits and semantic versioning
Versionary is a software-agnostic automated release tool focused on SemVer, conventional commits, release PR workflows, and extensibility.
Versionary is designed as a practical middle ground between semantic-release
and release-please.
semantic-release, it supports direct release execution.release-please, it supports a release PR workflow so maintainers can
preview and review changes before publication.The core idea is to keep versioning, changelog generation, tagging, and SCM release metadata in one tool, while leaving package publication (npm, crates.io, etc.) to dedicated CI workflows triggered by tags or releases.
Versionary is being built to:
In scope:
Out of scope (intentional):
Use your CI/CD platform for registry publishing, triggered from a created release/tag.
Current implementation focuses on:
simple, node, rust, r, latex,
python)github provider today)Planned/harder areas include deeper monorepo ergonomics, broader SCM coverage, and stronger failure recovery around release steps.
Versionary is set up so new language strategies can be added internally without
changing release orchestration. A new strategy should implement the
VersionStrategy contract in src/strategy/types.ts and be wired in
src/strategy/resolve.ts.
Checklist for new strategies:
namegetVersionFile(config) defaults and config override behaviorreadVersion(cwd, config) with explicit malformed-file errorswriteVersion(cwd, config, version) returning deterministic updated
file pathsreadPackageName(cwd, config) so monorepo release tags
can derive from language metadata (similar to Node/Rust/R)propagateDependentPatchImpacts(cwd, packages) if
dependency updates in this ecosystem should trigger dependent package patch
bumpsfinalizeVersionWrites(cwd, writes, context) for
ecosystem post-processing after all target version files are writtentests/strategy-contract.test.tsrelease-type behavior and defaultsCurrent ecosystem policy defaults:
changelog-filepackages.<path>.changelog-file when configuredNEWS.md for R targets or CHANGELOG.md for other targets at
<package-path>/<default-file>package-lock.json/npm-shrinkwrap.json when
presentCargo.lock filespoetry.lock/uv.lock/pdm.lock at the
package root by shelling out to the matching tool (poetry lock --no-update, uv lock, pdm lock --update-reuse); the corresponding
binary must be on PATHversion.workspace = true via [workspace.package].versionCurrent runtime code uses a flat src/ layout with clear module boundaries:
src/cli/: command router (run, verify, plan, changelog, pr, release)src/release/: release orchestration (plan/changelog/PR/release/state/recovery)src/strategy/: strategy contracts, resolver, and built-in implementations
(simple, node, rust, r, latex, python)src/scm/: SCM client contracts and provider implementation(s)src/config/: config loading and schema validationsrc/git/: git commit/range and repository URL helperssrc/verify/: repository/config verificationsrc/types/: shared config/plugin-facing typesConfiguration is loaded from versionary.jsonc by default (or
versionary.json).
Schema URL for editor support:
https://raw.githubusercontent.com/jolars/versionary/main/schemas/config.jsonFor a quick trial, use:
version-file (default version.txt) as version sourcechangelog-file (default CHANGELOG.md) as release notes outputrelease-type: "node" uses package.json as version source and updates it
during release PR preprelease-type: "r" uses DESCRIPTION as version source and updates the
Version: fieldrelease-type: "rust" uses Cargo manifests (Cargo.toml) as version source;
version-file must point to a Cargo.toml (default: Cargo.toml)release-type: "latex" uses build.lua as version source and updates LaTeX
\ProvidesPackage{...}[YYYY-MM-DD vX.Y.Z ...] metadata in src/**/*.dtx
using the release commit daterelease-type: "python" uses pyproject.toml (default) and updates
[project].version and/or [tool.poetry].version; point version-file at a
Python source file (e.g. src/<pkg>/__init__.py) to update a __version__
assignment instead. Refreshes poetry.lock/uv.lock/pdm.lock at the
package root if presentrelease-type can also be an array of strategy names to compose them across
manifests, e.g. ["python", "rust"] for a PyO3/maturin project: the first
entry is the primary (drives readVersion, readPackageName, and consumes
any version-file override); each secondary writes its default manifest
with the same target version. Common combinations:
["python", "rust"] ā PyO3/maturin (pyproject.toml + Cargo.toml +
Cargo.lock)["node", "rust"] ā napi-rs (package.json + Cargo.toml + Cargo.lock)["r", "rust"] ā R packages with embedded Rust crates (note: nested
src/rust/Cargo.toml layouts are not supported by the array form yet ā
use a single strategy until per-strategy version-file overrides land)version.txt as source of truth and does not
update package.jsonrelease-branch, default: versionary/release) so
release PRs are updated in-placebaseline-file (default .versionary-manifest.json) tracks baseline SHA for
deterministic commit ranges independent of tags0.y.z, breaking
changes bump to 0.(y+1).0; set allow-stable-major: true to allow explicit
auto-transition to 1.0.0 on a breaking releasereview-mode): pr (PR/MR style) or direct (no review
request)release-draft (default false) publishes GitHub releases as drafts when
enabledrelease-reference-comments controls release comments on linked issues/PRs:
off (default): do not post commentsbest-effort: post comments and continue on API/permission failuresstrict: fail release if comment posting failsmonorepo-mode and packages:
independent computes package bumps per pathfixed computes one shared bump across configured package pathspackage-name can override release identity (labels + tag base)changelog-file writes package release notes to
<package-path>/<changelog-file>follows declares an asymmetric version link to one or more
source packages: when any source bumps, the follower releases too, with
bump = max(own bump, max(source bumps)). The follower's changelog gets a
### Dependencies section listing the followed sources. Use it when one
package bundles another's artifact (e.g. an editor extension that ships
the CLI binary). Cycles, self-references, unknown source paths, and
combining follows with monorepo-mode: "fixed" are config errors.
follows is non-transitive: A follows B does not imply A follows what B
follows.// Editor extension that bundles the root CLI artifact
{
"version": 1,
"release-type": "rust",
"monorepo-mode": "independent",
"packages": {
".": { "exclude-paths": ["editors"] },
"editors/code": {
"release-type": "node",
"package-name": "panache-code",
"follows": ["."]
}
}
}
Rust strategy examples:
// Single crate
{
"release-type": "rust",
"version-file": "Cargo.toml"
}
// Workspace root (virtual or root crate + members)
{
"release-type": "rust",
"version-file": "Cargo.toml"
}
Current rust auto-update behavior (phase scope):
[package].versionversion.workspace = true by updating
[workspace.package].version in the owning workspace manifestCargo.lock via cargo generate-lockfile when Cargo.lock exists
in repo root[dependencies], [dev-dependencies], [build-dependencies][target.*.dependencies], [target.*.dev-dependencies],
[target.*.build-dependencies]Current rust non-goals/limits:
workspace.dependenciesversion = ... fields to dependency inline tablesIf Cargo.lock exists, cargo must be available in PATH during PR preparation.
For independent monorepo targets, Versionary derives release tags as:
"."): v<version><release-name>-v<version>release-name precedence is:
packages.<path>.package-name (explicit override)package.json nameCargo.toml [package].nameDESCRIPTION Package:When multiple packages resolve to the same <release-name> and version, the run
fails fast with a duplicate-tag error and suggests setting unique
package-name values.
Release planning is based on Conventional Commit parsing semantics:
header, body, footer, type, scope,
description, notes, references, mentions, revert)inferReleaseType*)! and BREAKING CHANGE / BREAKING-CHANGE
footersfeat => minor, fix|perf => patch, breaking => majorrevert: commits as patch-releasable by default (and major if marked
breaking, e.g. revert!: or BREAKING CHANGE)Commands:
pnpm verifypnpm run (default orchestration: no-op, create/update release PR, or publish
release based on context)pnpm run -- --json (machine-readable orchestration result)pnpm planpnpm changelog -- --writepnpm prpnpm releasepnpm pr prepares release commit + branch and opens/updates a review request
through the SCM client. pnpm run is the recommended CI entrypoint and
auto-dispatches between PR/update and release publish.
For LaTeX projects like moloch, use release-type: "latex" so Versionary:
build.lua versionsrc/**/*.dtx \ProvidesPackage{...}[YYYY-MM-DD vX.Y.Z ...] entries
with the release commit date (git show --format=%cs <sha>)Example versionary.jsonc for moloch:
{
"version": 1,
"review-mode": "pr",
"release-type": "latex",
"version-file": "build.lua",
"changelog-file": "CHANGELOG.md",
"release-branch": "versionary/release"
}
For first-run bootstrapping, set bootstrap-sha (similar to release-please).
Subsequent runs use the baseline state file.
Release publish (pnpm release or the publish path in pnpm run) is idempotent
by target tag:
Versionary fails fast when recovery is unsafe (for example, local and remote tags with the same name point to different SHAs). In these cases, the error message includes remediation guidance so CI logs are actionable.
Versionary currently uses a static internal SCM client model:
src/scm/types.ts defines the ScmClient contractsrc/scm/client.ts returns the active provider clientgithub via src/scm/github-plugin.tsThere is no runtime discovery/loading of external SCM providers in the release
flow. Adding another provider is an internal extension: implement ScmClient
and wire provider selection in src/scm/client.ts.
Required environment for the GitHub SCM provider:
GITHUB_REPOSITORY (format: owner/repo)VERSIONARY_PR_TOKEN or GH_TOKEN or GITHUB_TOKENToken precedence is:
VERSIONARY_PR_TOKEN > GH_TOKEN > GITHUB_TOKENMinimum GitHub token/repo permissions for Versionary-managed metadata:
contents: write, pull-requests: writecontents: writereview-mode behavior:
pr (preferred; review is a backward-compatible alias): pnpm run run
prepares/updates the release branch and creates or
updates a release PRdirect: pnpm run run prepares/updates the release branch and skips review
request creationConcise GitHub Actions examples:
# 1) Release PR / update flow (run on push to default branch)
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
fetch-tags: true
- id: versionary
uses: jolars/versionary@v1
with:
token: ${{ secrets.RELEASE_TOKEN }}
# 2) Release publish flow after merge (release commit context)
permissions:
contents: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
fetch-tags: true
- id: versionary
uses: jolars/versionary@v1
with:
token: ${{ secrets.RELEASE_TOKEN }}
- if: ${{ steps.versionary.outputs.release_created == 'true' }}
run: echo "Released ${{ steps.versionary.outputs.tag_name }}"
token is used for both GitHub API calls and git push authentication in
the composite action. This means release-branch force-pushes are attributed to
that token and can trigger downstream workflows when using a PAT/App token.
(github-token remains as a deprecated alias for backward compatibility.)
Action outputs:
action: noop, pr-prepared, release-published, release-skippedmessage: human-readable summaryrelease_created: "true" when at least one release was publishedtag_name: first published tag (for single-target flows)tag_names: JSON array of published tagsreview_url: review request URL when PR flow runsFor GitHub Action consumers, publish immutable tags (for example v1.2.3) and
maintain a moving major tag (v1, v2, ...). A small release-triggered
workflow should update v<major> to the latest release tag so uses: jolars/versionary@v1 stays current without breaking major compatibility.
Package publication is intentionally out of scope in the current release flow. Use separate CI workflows for publishing after Versionary has prepared/tagged the release.
You can install directly from a git ref:
{
"devDependencies": {
"versionary": "github:jolars/versionary#<commit-or-tag>"
}
}
The package runs a prepare build during git installation so the versionary
CLI binary is available after pnpm install.
FAQs
Automatic release framework based on conventional commits and semantic versioning
The npm package versionary receives a total of 300 weekly downloads. As such, versionary popularity was classified as not popular.
We found that versionary demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Ā It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
/Research
Socket detected malicious node-ipc versions with obfuscated stealer/backdoor behavior in a developing npm supply chain attack.

Security News
TeamPCP and BreachForums are promoting a Shai-Hulud supply chain attack contest with a $1,000 prize for the biggest package compromise.

Security News
Packagist urges PHP projects to update Composer after a GitHub token format change exposed some GitHub Actions tokens in CI logs.