A real changelog and release, from the terminal
Cutting a release shouldn't mean remembering the version-bump rules, the tag format, and the build flags. One OrchStep task computes the next version, writes the changelog, tags, and builds — and shows you the plan before it runs.
blog/changelog-releaseCutting a release is a sequence of small, error-prone decisions. Is this a patch or a minor? What was the last version — was it 1.3.0 or 1.3.1? Did I tag before or after the build? Get the order wrong and you've pushed a tag for a version that doesn't exist yet. So releases become the thing only one maintainer does, on one machine, from memory.
None of those decisions are hard. They're just unwritten. A release is: compute the next version from the bump level, generate the changelog, tag it, build the artifacts. Write that down as a task and anyone can cut a release — after seeing exactly what it'll do.
The release task
The version math is real logic, not string-mashing, so it goes in a transform step — sandboxed JavaScript that returns the next version as a structured value the rest of the task reads:
name: release
defaults:
level: patch # patch | minor | major
current: "1.3.0"
tasks:
# `orchstep run release --var level=minor --dry-run`
release:
desc: "Bump version, changelog, tag, and build"
steps:
- name: bump
func: transform
do: |
const p = vars.current.split(".").map(Number);
if (vars.level === "major") { p[0]++; p[1] = 0; p[2] = 0; }
else if (vars.level === "minor") { p[1]++; p[2] = 0; }
else { p[2]++; }
return { version: p.join(".") };
- name: changelog
func: shell
do: echo "generating changelog for v{{ steps.bump.version }}"
outputs:
notes: "CHANGELOG-{{ steps.bump.version }}.md"
- name: tag
func: shell
do: echo "git tag v{{ steps.bump.version }} (from {{ steps.changelog.notes }})"
- name: build
func: shell
do: echo "building release artifacts for v{{ steps.bump.version }}"(The shell steps echo so the demo runs anywhere — swap in your changelog tool, git tag, and build command to make it live.)
Semver as code, not as a guess
The bump step encodes the rule everyone says they follow: major resets minor and patch, minor resets patch, patch increments. Once it's written down, --var level=minor against 1.3.0 deterministically produces 1.4.0 — every time, on every machine. The downstream steps read steps.bump.version, so the version is computed once and flows through tag and build identically. No copy-paste of a number you typed at the top.
Order is encoded too: changelog, then tag, then build, top to bottom. You can't accidentally tag before the changelog exists, because the steps run in the order they're written.
Dry-run first, every time
The reason this is safe to hand to anyone: you can see the whole release before a single command fires. A dry-run resolves the bump, fills in every version reference, and prints the plan:
orchstep run release --var level=minor --dry-runYou read it — "yes, 1.4.0, tag v1.4.0, changelog CHANGELOG-1.4.0.md" — and only then drop --dry-run:
orchstep run release --var level=minorWhat you actually gained
| Concern | By-hand release | OrchStep release task |
|---|---|---|
| Next version | mental semver math | a transform step, deterministic |
| Step order | remember tag-after-build | encoded top to bottom |
| Version reused everywhere | retype the number | steps.bump.version flows through |
| Preview before tagging | none | --dry-run prints the plan |
| Who can cut a release | the one who remembers | anyone who can read the task |
Where OrchStep is not the answer
If a full release tool already owns your pipeline — semantic-release, GoReleaser, release-please — keep it; those do far more (artifact signing, multi-registry publish, PR automation). This pattern is for the project that has no release process yet, or one that lives in a maintainer's head — a readable, dry-runnable first step you can grow later.
Where to go next
- transform — sandboxed JavaScript for pure computation
- Previewing with Dry Run — see the release plan before it runs
- Variables & Outputs — passing the computed version down the task
- git — first-class tags, commits, and pushes when you wire it up for real
No real release process yet — just one maintainer who remembers the steps? Write them down as a dry-runnable task. Install the binary and start with the demo above.
curl -fsSL https://orchstep.dev/install.sh | sh