| #!/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..." | |
| # `-i.bak` is the in-place form GNU and BSD sed both accept (`-i ''` is | |
| # BSD-only: GNU sed reads the '' as the script). | |
| sed -i.bak "s/#define CPPHTTPLIB_VERSION \"[^\"]*\"/#define CPPHTTPLIB_VERSION \"$NEW_VERSION\"/" httplib.h | |
| sed -i.bak "s/#define CPPHTTPLIB_VERSION_NUM \"0x[0-9a-fA-F]*\"/#define CPPHTTPLIB_VERSION_NUM \"$VERSION_HEX\"/" httplib.h | |
| rm -f httplib.h.bak | |
| echo " CPPHTTPLIB_VERSION = \"$NEW_VERSION\"" | |
| echo " CPPHTTPLIB_VERSION_NUM = \"$VERSION_HEX\"" | |
| echo "" | |
| echo "==> Updating docs-src/config.toml..." | |
| sed -i.bak "s/^version = \"[^\"]*\"/version = \"$NEW_VERSION\"/" docs-src/config.toml | |
| rm -f docs-src/config.toml.bak | |
| 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 |