semantic-release-script

Semantic release in one bash script. No npm tree. Just git, bash and gh.

Requires git, bash 4+ and the GitHub CLI (gh), logged in when you run it locally.

latest bash 4+ GPL-3.0
release.sh --dry-run
$ ./scripts/release/release.sh --dry-run

== Step 1 — evaluate the new version ==

Repository      : JeremieLitzler/semantic-release-script
Commit range    : v0.4.0..HEAD (last tag: v0.4.0)
Commits scanned : 17

  feature   3ae97e8 feat: refuse a name-forced level below what the commits imply (#79)
  feature   08ffc72 feat: force the release level from a branch name with --level-from (#78)
  fix       1e09fa0 fix: restore the line continuations of the level check (#79)
  other     df6b77c ci: pin the release workflow's release.sh to v0.4.0 (#74)

Bump            : minor
Version         : 0.4.0 -> 0.5.0
-> Continue to step 2 and build the release notes for v0.5.0?

== Step 2 — release notes for v0.5.0 ==

### Features

- Refuse a name-forced level below what the commits imply (#79)
- Force the release level from a branch name with --level-from (#78)

-> Continue to step 3 and create the tag v0.5.0?

== Step 3 — create and push v0.5.0 ==

[dry-run] git tag -a v0.5.0 -m v0.5.0 HEAD
[dry-run] git push origin v0.5.0
-> Continue to step 4 and publish the GitHub release v0.5.0?

A real run on this repository, trimmed for length. Without --dry-run, a human gate sits before each step.

Features

Conventional commits

The version bump and the release notes come from the commit messages since the last version tag.

A human gate before each step

Evaluate, write the notes, tag, publish: you answer before each one, or pass --yes to run unattended.

--dry-run

See the version and the notes without pushing a tag or creating a release. It reaches no remote.

Resume a half-published release

A run that died after pushing the tag and before creating the release is finished by the next run, not redone.

--level-from

Force the bump from a release/<major|minor|patch>/… branch name. A level below what the commits imply is refused.

--check

A setup report: every check and guard a release would weigh, each with its reason and its fix, before anything is released.

No npm

One file you vendor into your repository. No node_modules, no plugin tree to keep patched.

How it works

A run is four steps, and it can stop at the gate before any of them.

1

Evaluate the version

Reads the commits since the last version tag and decides major, minor or patch.

2

Write the release notes

Builds the Markdown notes, grouped by type, with issue and pull request titles resolved through gh.

3

Create and push the version tag

Creates an annotated vX.Y.Z tag and pushes it to origin.

4

Publish the release

Creates the GitHub release on that tag, with the notes as its body.

Quick start

Vendor release.sh at the latest version tag, then run it from your trunk.

curl -fsSL https://raw.githubusercontent.com/JeremieLitzler/semantic-release-script/<latest-tag>/scripts/release/release.sh -o release.sh && chmod +x release.sh

The latest version could not be fetched. Replace <latest-tag> with the tag on the releases page.

./release.sh --dry-run   # preview the version and the notes
./release.sh             # the real thing, one gate per step

The command pins a version tag: pin it, don't track a branch.

Options

FlagWhat it does
-y, --yesSkip every human gate (unattended run).
-n, --dry-runDo everything but push the tag and create the release.
-l, --localCreate the tag locally, but neither push nor publish.
--since <ref>Read commits since a ref instead of the last version tag.
--to <ref>Read commits up to a ref instead of HEAD, and tag that ref.
--trunk <name>The branch releases are cut from. Defaults to the repository's default branch.
--level <level>Force the bump: major, minor or patch.
--level-from <ref>Force the bump from a release branch name.
--notes <file>Also write the release notes to a file.
--changelog <file>Prepend the release to a changelog, newest first.
--summary <file>Write what the run decided, as key=value lines.
--checkPrint the setup report instead of releasing.
-h, --helpShow the help.

In CI

A dry run reaches no remote, so a runner can preview a release without a terminal. A real run on a runner passes --yes.

# preview the pending release
GH_TOKEN="$GITHUB_TOKEN" ./release.sh --dry-run

# cut it
GH_TOKEN="$GITHUB_TOKEN" ./release.sh --yes