Files
cpp-httplib/scripts/release.sh
yhirose fe56a07da5 Wait for in-progress CI runs before releasing
The release check treated runs with an empty conclusion as failures.
Now it inspects each run's status and aborts with an error if any CI
check is still running, so releases wait until CI completes.
2026-06-06 13:38:36 -04:00

192 lines
5.6 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Release a new version of cpp-httplib.
#
# Usage: ./release.sh [--run] [--minor]
#
# By default, runs in dry-run mode (no changes made).
# Pass --run to actually update files, commit, tag, and push.
# Pass --minor to force a minor bump even when ABI is unchanged
# (use this for behavioral breaking changes that don't break ABI).
#
# This script:
# 1. Reads the current version from httplib.h
# 2. Checks that the working directory is clean
# 3. Verifies CI status of the latest commit (all must pass except abidiff)
# 4. Determines the next version automatically:
# - abidiff passed → patch bump (e.g., 0.38.0 → 0.38.1)
# - abidiff failed → minor bump (e.g., 0.38.1 → 0.39.0)
# - --minor passed → forces minor bump regardless of abidiff
# 5. Updates httplib.h and docs-src/config.toml
# 6. Commits, tags (vX.Y.Z), and pushes
set -euo pipefail
DRY_RUN=1
FORCE_MINOR=0
while [ $# -gt 0 ]; do
case "$1" in
--run)
DRY_RUN=0
shift
;;
--minor)
FORCE_MINOR=1
shift
;;
*)
echo "Usage: $0 [--run] [--minor]"
exit 1
;;
esac
done
# --- Step 1: Read current version from httplib.h ---
CURRENT_VERSION=$(sed -n 's/^#define CPPHTTPLIB_VERSION "\([^"]*\)"/\1/p' httplib.h)
IFS='.' read -r V_MAJOR V_MINOR V_PATCH <<< "$CURRENT_VERSION"
echo "==> Current version: $CURRENT_VERSION"
# --- Step 2: Check working directory is clean ---
if [ -n "$(git status --porcelain)" ]; then
echo "Error: working directory is not clean"
exit 1
fi
# --- Step 3: Check CI status of the latest commit ---
echo ""
echo "==> Checking CI status of the latest commit..."
HEAD_SHA=$(git rev-parse HEAD)
HEAD_SHORT=$(git rev-parse --short HEAD)
echo " Latest commit: $HEAD_SHORT"
# Fetch all workflow runs for the HEAD commit
RUNS=$(gh run list --commit "$HEAD_SHA" --json name,status,conclusion,headSha)
NUM_RUNS=$(echo "$RUNS" | jq 'length')
if [ "$NUM_RUNS" -eq 0 ]; then
echo "Error: No CI runs found for commit $HEAD_SHORT."
echo " Wait for CI to complete before releasing."
exit 1
fi
echo " Found $NUM_RUNS workflow run(s):"
FAILED=0
RUNNING=0
ABIDIFF_PASSED=0
while IFS=$'\t' read -r name status conclusion; do
# A run that hasn't completed yet has an empty conclusion; don't treat it
# as a failure — the release should wait until CI finishes.
if [ "$status" != "completed" ]; then
echo " [ .. ] $name (still running)"
RUNNING=1
continue
fi
if [[ "$name" == *abidiff* ]] || [[ "$name" == *abi* && "$name" != *stability* ]]; then
if [ "$conclusion" = "success" ]; then
echo " [ OK ] $name"
ABIDIFF_PASSED=1
else
echo " [FAIL] $name ($conclusion) → ABI break detected, minor bump"
ABIDIFF_PASSED=0
fi
continue
fi
if [ "$conclusion" = "success" ]; then
echo " [ OK ] $name"
else
echo " [FAIL] $name ($conclusion)"
FAILED=1
fi
done < <(echo "$RUNS" | jq -r '.[] | [.name, .status, .conclusion] | @tsv')
if [ "$RUNNING" -eq 1 ]; then
echo ""
echo "Error: Some CI checks are still running. Wait for them to complete before releasing."
exit 1
fi
if [ "$FAILED" -eq 1 ]; then
echo ""
echo "Error: Some CI checks failed. Fix them before releasing."
exit 1
fi
echo " All non-abidiff CI checks passed."
# --- Step 4: Determine new version ---
if [ "$FORCE_MINOR" -eq 1 ] && [ "$ABIDIFF_PASSED" -eq 1 ]; then
NEW_MINOR=$((V_MINOR + 1))
NEW_VERSION="$V_MAJOR.$NEW_MINOR.0"
echo ""
echo "==> abidiff passed but --minor specified → forced minor bump"
elif [ "$ABIDIFF_PASSED" -eq 1 ]; then
NEW_PATCH=$((V_PATCH + 1))
NEW_VERSION="$V_MAJOR.$V_MINOR.$NEW_PATCH"
echo ""
echo "==> abidiff passed → patch bump"
else
NEW_MINOR=$((V_MINOR + 1))
NEW_VERSION="$V_MAJOR.$NEW_MINOR.0"
echo ""
if [ "$FORCE_MINOR" -eq 1 ]; then
echo "==> abidiff failed → minor bump (--minor also specified)"
else
echo "==> abidiff failed → minor bump"
fi
fi
VERSION_HEX=$(printf "0x%02x%02x%02x" "${NEW_VERSION%%.*}" "$(echo "$NEW_VERSION" | cut -d. -f2)" "${NEW_VERSION##*.}")
if [ "$DRY_RUN" -eq 1 ]; then
echo "==> [DRY RUN] New version: $NEW_VERSION ($VERSION_HEX)"
else
echo "==> New version: $NEW_VERSION ($VERSION_HEX)"
fi
# --- Step 5: Update files ---
echo ""
if [ "$DRY_RUN" -eq 1 ]; then
echo "==> [DRY RUN] Would update httplib.h:"
echo " CPPHTTPLIB_VERSION = \"$NEW_VERSION\""
echo " CPPHTTPLIB_VERSION_NUM = \"$VERSION_HEX\""
echo ""
echo "==> [DRY RUN] Would update docs-src/config.toml:"
echo " version = \"$NEW_VERSION\""
echo ""
echo "==> [DRY RUN] Would commit, tag v$NEW_VERSION and latest, and push."
echo ""
echo "==> Dry run complete. No changes were made."
else
echo "==> Updating httplib.h..."
sed -i '' "s/#define CPPHTTPLIB_VERSION \"[^\"]*\"/#define CPPHTTPLIB_VERSION \"$NEW_VERSION\"/" httplib.h
sed -i '' "s/#define CPPHTTPLIB_VERSION_NUM \"0x[0-9a-fA-F]*\"/#define CPPHTTPLIB_VERSION_NUM \"$VERSION_HEX\"/" httplib.h
echo " CPPHTTPLIB_VERSION = \"$NEW_VERSION\""
echo " CPPHTTPLIB_VERSION_NUM = \"$VERSION_HEX\""
echo ""
echo "==> Updating docs-src/config.toml..."
sed -i '' "s/^version = \"[^\"]*\"/version = \"$NEW_VERSION\"/" docs-src/config.toml
echo " version = \"$NEW_VERSION\""
# --- Step 6: Commit, tag, and push ---
echo ""
echo "==> Committing and tagging..."
git add httplib.h docs-src/config.toml
git commit -m "Release v$NEW_VERSION"
git tag "v$NEW_VERSION"
git tag -f "latest"
echo ""
echo "==> Pushing..."
git push && git push --tags --force
echo ""
echo "==> Released v$NEW_VERSION"
fi