$ show release-it/release-it
release-it is a production-grade CLI tool that automates the entire software release lifecycle: determining the next semantic version, bumping manifests (package.json via npm version), staging/committing/tagging/pushing to git, publishing to npm (including staged publishing queues, OTP flows, custom registries, and pnpm/bun publishers), and creating GitHub or GitLab releases with changelogs and binary assets. It is distributed as an npm package with a bin entry point (bin/release-it.js) and is used by major projects including Axios, jQuery, Redux, Swagger, and n8n. In production it operates as a shell-out orchestrator rather than a library-first tool — it drives git and npm CLI commands directly and talks to GitHub via Octokit and GitLab via undici fetch against their v4 REST API.
The LLM identified 4 files worth highlighting for reuse:
A strict CLI argument parser built on node:util.parseArgs and substantially extended. It discovers dynamic options for hooks.* and plugins.* dotted paths, supports mixed boolean/string values (e.g. --preRelease vs --preRelease=rc vs --preRelease=false), negation via --no- prefixes, short aliases, repeated array values, collapsible arrays, and rejects unknown options with clean ERR_PARSE_ARGS_* errors. It also blocks prototype-pollution attack paths (__proto__, constructor, prototype) and detects conflicts between parent and nested options. The module is dependency-free and exhaustively unit-tested including a schema-driven test that walks every built-in option; it can be lifted into any Node.js CLI project as a drop-in strict parser.
A shell execution layer with three notable patterns: (1) dry-run semantics that print '$ cmd' for read-only commands and '! cmd' for write commands while skipping execution of writes; (2) an execution cache keyed by command string with cache eviction on rejection, so repeated read-only git introspection (e.g. latest tag resolution) runs once per process; (3) cross-platform handling for npm/yarn/pnpm on Windows where spawn() must degrade to a shell-string invocation. It also supports interactive spawns with inherited stdio (required for npm login and passkey 2FA flows) and env-scoped execution. It depends only on a small template formatter from lib/util.js plus a minimal { log, config } container contract, making the caching/dry-run/spawn pattern directly portable to other tooling.
The base class that defines release-it's pluggable lifecycle contract. It declares the full sequence of release-cycle hooks (init, getName, getLatestVersion, getChangelog, getIncrement, getIncrementedVersionCI, getIncrementedVersion, beforeBump, bump, beforeRelease, release, afterRelease) with no-op defaults, and injects a container with helpers: exec() for templated shell commands, registerPrompts() for interactive confirmation points, and step() which transparently chooses between an interactive prompt and a CI spinner based on config. Static isEnabled()/disablePlugin() gates let plugins self-select and replace core plugins. This is a clean reference design for any lifecycle-driven extensible tool.
A small, nearly dependency-free utility set that converts a release changelog into actionable metadata: getSearchQueries batches commit SHAs into GitHub search queries while respecting the 256-character URL-encoded length limit; getCommitsFromChangelog extracts 7+ char hex SHAs from changelog markers; getResolvedIssuesFromChangelog parses issue references via issue-parser while ignoring HTML comments and deduplicating repeated references across entries. These are pure, isolated functions (only import issue-parser) usable by any changelog automation or release tooling that must map commits to merged PRs and closed issues.