|
| 1 | +#!/usr/bin/env bash |
| 2 | +# |
| 3 | +# Controlled release for ES-Runtime. |
| 4 | +# |
| 5 | +# scripts/release.sh |
| 6 | +# |
| 7 | +# Asks for a major / minor / patch bump, then: |
| 8 | +# 1. bumps the version (workspace Cargo.toml + Cargo.lock). The site |
| 9 | +# (site/package.json) is versioned independently — docs change far more |
| 10 | +# often than the runtime — so it is intentionally left alone. |
| 11 | +# 2. promotes CHANGELOG.md's [Unreleased] section to the new version + date, |
| 12 | +# 3. shows the diff and asks you to confirm, |
| 13 | +# 4. commits + creates an annotated git tag, |
| 14 | +# 5. asks whether to push the branch and tag. |
| 15 | +# |
| 16 | +# Pushing the tag triggers .github/workflows/release.yml, which runs the tests |
| 17 | +# and only then builds and publishes the artifacts. Nothing here is automatic — |
| 18 | +# every step waits for you. |
| 19 | +set -euo pipefail |
| 20 | + |
| 21 | +ROOT="$(git rev-parse --show-toplevel)" |
| 22 | +cd "$ROOT" |
| 23 | + |
| 24 | +CARGO_TOML="Cargo.toml" |
| 25 | +CHANGELOG="CHANGELOG.md" |
| 26 | + |
| 27 | +red() { printf '\033[31m%s\033[0m\n' "$*" >&2; } |
| 28 | +bold() { printf '\033[1m%s\033[0m\n' "$*"; } |
| 29 | +err() { red "error: $*"; exit 1; } |
| 30 | + |
| 31 | +# --- preconditions ---------------------------------------------------------- |
| 32 | +[ -f "$CARGO_TOML" ] || err "must run inside the ES-Runtime repo" |
| 33 | +git diff --quiet && git diff --cached --quiet || err "working tree is dirty — commit or stash first" |
| 34 | + |
| 35 | +# --- local quality gate (before touching anything) ------------------------- |
| 36 | +# CI re-runs all of this after the tag is pushed, but verify locally first so a |
| 37 | +# release is never started on code that doesn't format, lint, or test clean. |
| 38 | +# `cargo test --workspace` includes the CLI end-to-end test |
| 39 | +# (crates/runtime-cli/tests) that spawns the real `esrun` binary. |
| 40 | +# Reuses the existing target/ cache (no clean) — incremental on a warm tree. |
| 41 | +# A cold tree is slow once (downloads/links the prebuilt V8 static lib). |
| 42 | +bold "Running fmt, clippy, and the full test suite…" |
| 43 | +cargo fmt --all --check || err "formatting check failed — run 'cargo fmt' and retry" |
| 44 | +cargo clippy --workspace --all-targets --locked -- -D warnings || |
| 45 | + err "clippy failed — fix the warnings and retry" |
| 46 | +cargo test --workspace --locked || err "tests failed — fix them and retry" |
| 47 | + |
| 48 | +branch="$(git rev-parse --abbrev-ref HEAD)" |
| 49 | + |
| 50 | +# Current version = the workspace.package.version (first `version = "x"` line). |
| 51 | +current="$(grep -m1 -E '^version = "' "$CARGO_TOML" | sed -E 's/^version = "([^"]+)".*/\1/')" |
| 52 | +[ -n "$current" ] || err "could not read the workspace version from $CARGO_TOML" |
| 53 | +IFS=. read -r MA MI PA <<<"${current%%-*}" |
| 54 | + |
| 55 | +bold "ES-Runtime release" |
| 56 | +echo " current version: $current" |
| 57 | +echo " branch: $branch" |
| 58 | +echo |
| 59 | + |
| 60 | +# --- choose the bump -------------------------------------------------------- |
| 61 | +patch="$MA.$MI.$((PA + 1))" |
| 62 | +minor="$MA.$((MI + 1)).0" |
| 63 | +major="$((MA + 1)).0.0" |
| 64 | + |
| 65 | +echo "Release type:" |
| 66 | +echo " 1) patch -> $patch" |
| 67 | +echo " 2) minor -> $minor" |
| 68 | +echo " 3) major -> $major" |
| 69 | +asis="" |
| 70 | +if ! git rev-parse -q --verify "refs/tags/v$current" >/dev/null; then |
| 71 | + asis="$current" |
| 72 | + echo " 0) current -> $current (release the current version as-is; not yet tagged)" |
| 73 | +fi |
| 74 | +printf "Choice: " |
| 75 | +read -r choice |
| 76 | +case "$choice" in |
| 77 | + 1) new="$patch" ;; |
| 78 | + 2) new="$minor" ;; |
| 79 | + 3) new="$major" ;; |
| 80 | + 0) [ -n "$asis" ] && new="$asis" || err "invalid choice" ;; |
| 81 | + *) err "invalid choice" ;; |
| 82 | +esac |
| 83 | + |
| 84 | +tag="v$new" |
| 85 | +git rev-parse -q --verify "refs/tags/$tag" >/dev/null && err "tag $tag already exists" |
| 86 | +date="$(date -u +%Y-%m-%d)" |
| 87 | +echo |
| 88 | +bold "Preparing $tag ($date)" |
| 89 | + |
| 90 | +# --- 1. bump versions ------------------------------------------------------- |
| 91 | +# workspace.package.version — the first `version = "..."` line in the root manifest. |
| 92 | +NEW="$new" perl -0777 -i -pe 'BEGIN{$v=$ENV{NEW}} s/^(version = ")[^"]+(")/${1}$v${2}/m' "$CARGO_TOML" |
| 93 | +# Cargo.lock — refresh only the workspace members' recorded versions (no build). |
| 94 | +cargo update --workspace --offline >/dev/null 2>&1 || |
| 95 | + cargo update --workspace >/dev/null 2>&1 || |
| 96 | + err "could not update Cargo.lock (run 'cargo update --workspace' and retry)" |
| 97 | + |
| 98 | +# --- 2. changelog: promote [Unreleased] -> [new] - date, keep a fresh one --- |
| 99 | +grep -q '^## \[Unreleased\]' "$CHANGELOG" || err "no '## [Unreleased]' section in $CHANGELOG" |
| 100 | +NEW="$new" DATE="$date" perl -0777 -i -pe \ |
| 101 | + 'BEGIN{$v=$ENV{NEW};$d=$ENV{DATE}} s/^## \[Unreleased\]/## [Unreleased]\n\n## [$v] - $d/m' \ |
| 102 | + "$CHANGELOG" |
| 103 | + |
| 104 | +# --- 3. review + confirm ---------------------------------------------------- |
| 105 | +echo |
| 106 | +bold "Changes for $tag:" |
| 107 | +git --no-pager diff -- "$CARGO_TOML" "$CHANGELOG" |
| 108 | +echo |
| 109 | +printf "Commit these and tag %s? [y/N] " "$tag" |
| 110 | +read -r confirm |
| 111 | +case "$confirm" in |
| 112 | + y | Y) ;; |
| 113 | + *) |
| 114 | + bold "Aborted — reverting changes." |
| 115 | + git checkout -- "$CARGO_TOML" "$CHANGELOG" Cargo.lock 2>/dev/null || true |
| 116 | + exit 1 |
| 117 | + ;; |
| 118 | +esac |
| 119 | + |
| 120 | +# --- 4. commit + tag -------------------------------------------------------- |
| 121 | +git add "$CARGO_TOML" "$CHANGELOG" Cargo.lock |
| 122 | +git commit -m "release: $tag" |
| 123 | +git tag -a "$tag" -m "$tag" |
| 124 | +bold "Committed and tagged $tag." |
| 125 | + |
| 126 | +# --- 5. push ---------------------------------------------------------------- |
| 127 | +echo |
| 128 | +printf "Push '%s' and tag '%s' to origin now? [y/N] " "$branch" "$tag" |
| 129 | +read -r dopush |
| 130 | +case "$dopush" in |
| 131 | + y | Y) |
| 132 | + git push origin "$branch" "$tag" |
| 133 | + bold "Pushed. The Release workflow will run tests, build, and publish $tag." |
| 134 | + ;; |
| 135 | + *) |
| 136 | + bold "Not pushed. When ready:" |
| 137 | + echo " git push origin $branch $tag" |
| 138 | + ;; |
| 139 | +esac |
0 commit comments