#!/usr/bin/env bash set -euo pipefail require() { command -v "$1" &>/dev/null || { echo "error: $1 not found" exit 1 }; } require git require git-cliff require jq # --- pre-flight --- if [ "$#" -gt 1 ]; then echo "usage: hack/release [version]" exit 1 fi manual=${1:-} if [ -n "$manual" ]; then manual=${manual#v} [[ $manual =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "error: invalid version '$manual' (expected e.g. 3.4.0)" exit 1 } fi if [ -n "$(git status --porcelain)" ]; then echo "error: working tree is dirty" exit 1 fi if [ -n "$manual" ]; then next=$manual else next=$(git-cliff --bumped-version 2>/dev/null | sed 's/^v//') fi if git tag | grep -qx "v$next"; then if [ -n "$manual" ]; then echo "error: tag v$next already exists" exit 1 fi echo "no releasable commits since v$next" exit 0 fi echo "releasing -> v$next" # --- update files --- jq --arg v "$next" '.version = $v' package.json >package.json.tmp mv package.json.tmp package.json # --- run git-cliff --- git-cliff --tag "v$next" --output CHANGELOG.md # --- remove newlines --- sed -i -e :a -e '/^\s*$/{$d;N;ba' -e '}' CHANGELOG.md # --- commit, tag, push --- git add package.json pnpm-lock.yaml CHANGELOG.md flake.nix git commit --no-verify -m "chore: release v$next" git tag -a "v$next" -m "release v$next" git push git push --tags echo "released v$next"