diff --git a/scripts/release.sh b/scripts/release.sh index 05a7876..13e7fe9 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -12,93 +12,103 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" # --- Helpers --- -die() { echo "error: $*" >&2; exit 1; } +die() { + echo "error: $*" >&2 + exit 1 +} + +sedi() { + local expr="$1" + local file="$2" + sed -i.bak -e "$expr" "$file" + rm -f "${file}.bak" +} get_current_version() { - grep '^version = ' "$PROJECT_ROOT/pyproject.toml" | sed 's/version = "\(.*\)"/\1/' + grep '^version = ' "$PROJECT_ROOT/pyproject.toml" | sed 's/version = "\(.*\)"/\1/' } # Convert Python version (0.1.0rc1) to npm semver (0.1.0-rc.1) to_npm_version() { - local v="$1" - if [[ "$v" =~ ^([0-9]+\.[0-9]+\.[0-9]+)rc([0-9]+)$ ]]; then - echo "${BASH_REMATCH[1]}-rc.${BASH_REMATCH[2]}" - else - echo "$v" - fi + local v="$1" + if [[ "$v" =~ ^([0-9]+\.[0-9]+\.[0-9]+)rc([0-9]+)$ ]]; then + echo "${BASH_REMATCH[1]}-rc.${BASH_REMATCH[2]}" + else + echo "$v" + fi } calculate_next_version() { - local current="$1" - local bump_type="$2" - - case "$bump_type" in - rc) - if [[ "$current" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)rc([0-9]+)$ ]]; then - # Already an RC: increment RC number - local major="${BASH_REMATCH[1]}" - local minor="${BASH_REMATCH[2]}" - local patch="${BASH_REMATCH[3]}" - local rc="${BASH_REMATCH[4]}" - echo "${major}.${minor}.${patch}rc$((rc + 1))" - elif [[ "$current" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then - # Stable: bump minor, start RC1 - local major="${BASH_REMATCH[1]}" - local minor="${BASH_REMATCH[2]}" - local patch="${BASH_REMATCH[3]}" - echo "${major}.$((minor + 1)).${patch}rc1" - else - die "cannot parse version: $current" - fi - ;; - stable) - if [[ "$current" =~ ^([0-9]+\.[0-9]+\.[0-9]+)rc[0-9]+$ ]]; then - echo "${BASH_REMATCH[1]}" - else - die "current version ($current) is not an RC — nothing to promote" - fi - ;; - *) - # Explicit version provided - if [[ "$bump_type" =~ ^[0-9]+\.[0-9]+\.[0-9]+(rc[0-9]+)?$ ]]; then - echo "$bump_type" - else - die "invalid version format: $bump_type (expected X.Y.Z or X.Y.Zrc#)" - fi - ;; - esac + local current="$1" + local bump_type="$2" + + case "$bump_type" in + rc) + if [[ "$current" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)rc([0-9]+)$ ]]; then + # Already an RC: increment RC number + local major="${BASH_REMATCH[1]}" + local minor="${BASH_REMATCH[2]}" + local patch="${BASH_REMATCH[3]}" + local rc="${BASH_REMATCH[4]}" + echo "${major}.${minor}.${patch}rc$((rc + 1))" + elif [[ "$current" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then + # Stable: bump minor, start RC1 + local major="${BASH_REMATCH[1]}" + local minor="${BASH_REMATCH[2]}" + local patch="${BASH_REMATCH[3]}" + echo "${major}.$((minor + 1)).${patch}rc1" + else + die "cannot parse version: $current" + fi + ;; + stable) + if [[ "$current" =~ ^([0-9]+\.[0-9]+\.[0-9]+)rc[0-9]+$ ]]; then + echo "${BASH_REMATCH[1]}" + else + die "current version ($current) is not an RC — nothing to promote" + fi + ;; + *) + # Explicit version provided + if [[ "$bump_type" =~ ^[0-9]+\.[0-9]+\.[0-9]+(rc[0-9]+)?$ ]]; then + echo "$bump_type" + else + die "invalid version format: $bump_type (expected X.Y.Z or X.Y.Zrc#)" + fi + ;; + esac } update_version_files() { - local new_version="$1" - local npm_version - npm_version="$(to_npm_version "$new_version")" + local new_version="$1" + local npm_version + npm_version="$(to_npm_version "$new_version")" - echo "updating versions to $new_version (npm: $npm_version)" + echo "updating versions to $new_version (npm: $npm_version)" - # 1. pyproject.toml - sed -i '' "s/^version = \".*\"/version = \"$new_version\"/" "$PROJECT_ROOT/pyproject.toml" + # 1. pyproject.toml + sedi "s/^version = \".*\"/version = \"$new_version\"/" "$PROJECT_ROOT/pyproject.toml" - # 2. parallel_web_tools/__init__.py - sed -i '' "s/__version__ = \".*\"/__version__ = \"$new_version\"/" "$PROJECT_ROOT/parallel_web_tools/__init__.py" + # 2. parallel_web_tools/__init__.py + sedi "s/__version__ = \".*\"/__version__ = \"$new_version\"/" "$PROJECT_ROOT/parallel_web_tools/__init__.py" - # 3. bigquery cloud function requirements.txt - sed -i '' "s/parallel-web-tools>=.*/parallel-web-tools>=$new_version/" \ - "$PROJECT_ROOT/parallel_web_tools/integrations/bigquery/cloud_function/requirements.txt" + # 3. bigquery cloud function requirements.txt + sedi "s/parallel-web-tools>=.*/parallel-web-tools>=$new_version/" \ + "$PROJECT_ROOT/parallel_web_tools/integrations/bigquery/cloud_function/requirements.txt" - # 5. npm/package.json - sed -i '' "s/\"version\": \".*\"/\"version\": \"$npm_version\"/" "$PROJECT_ROOT/npm/package.json" + # 5. npm/package.json + sedi "s/\"version\": \".*\"/\"version\": \"$npm_version\"/" "$PROJECT_ROOT/npm/package.json" } # --- Main --- if [[ $# -lt 1 ]]; then - echo "usage: ./scripts/release.sh " - echo "" - echo " rc bump to next release candidate" - echo " stable promote current RC to stable" - echo " X.Y.Z set explicit version" - exit 1 + echo "usage: ./scripts/release.sh " + echo "" + echo " rc bump to next release candidate" + echo " stable promote current RC to stable" + echo " X.Y.Z set explicit version" + exit 1 fi BUMP_TYPE="$1" @@ -109,7 +119,7 @@ NPM_VERSION="$(to_npm_version "$NEW_VERSION")" # Determine if this is a prerelease IS_PRERELEASE=false if [[ "$NEW_VERSION" =~ rc ]]; then - IS_PRERELEASE=true + IS_PRERELEASE=true fi echo "" @@ -121,24 +131,24 @@ echo "" # Safety checks if [[ -n "$(git status --porcelain)" ]]; then - die "working tree is not clean — commit or stash changes first" + die "working tree is not clean — commit or stash changes first" fi CURRENT_BRANCH="$(git branch --show-current)" if [[ "$CURRENT_BRANCH" != "main" ]]; then - die "must be on main branch (currently on $CURRENT_BRANCH)" + die "must be on main branch (currently on $CURRENT_BRANCH)" fi # Check if tag already exists if git rev-parse "v$NEW_VERSION" >/dev/null 2>&1; then - die "tag v$NEW_VERSION already exists" + die "tag v$NEW_VERSION already exists" fi # Confirm read -r -p "proceed? [y/N] " confirm if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then - echo "aborted." - exit 0 + echo "aborted." + exit 0 fi # Update files @@ -148,23 +158,23 @@ update_version_files "$NEW_VERSION" BRANCH="release/v$NEW_VERSION" git checkout -b "$BRANCH" git add \ - pyproject.toml \ - parallel_web_tools/__init__.py \ - parallel_web_tools/integrations/bigquery/cloud_function/requirements.txt \ - npm/package.json + pyproject.toml \ + parallel_web_tools/__init__.py \ + parallel_web_tools/integrations/bigquery/cloud_function/requirements.txt \ + npm/package.json # Commit — if pre-commit hooks modify files (e.g. uv.lock), re-stage and retry if ! git commit -m "chore: bump version to $NEW_VERSION"; then - echo "pre-commit hooks modified files, re-staging and retrying..." - git add \ - pyproject.toml \ - parallel_web_tools/__init__.py \ - tests/test_cli.py \ - parallel_web_tools/integrations/bigquery/cloud_function/requirements.txt \ - npm/package.json - # Also stage any lock files updated by hooks - git diff --name-only | xargs -r git add - git commit -m "chore: bump version to $NEW_VERSION" + echo "pre-commit hooks modified files, re-staging and retrying..." + git add \ + pyproject.toml \ + parallel_web_tools/__init__.py \ + tests/test_cli.py \ + parallel_web_tools/integrations/bigquery/cloud_function/requirements.txt \ + npm/package.json + # Also stage any lock files updated by hooks + git diff --name-only | xargs -r git add + git commit -m "chore: bump version to $NEW_VERSION" fi echo "" @@ -173,12 +183,13 @@ git push -u origin "$BRANCH" PRERELEASE_NOTE="" if [[ "$IS_PRERELEASE" == "true" ]]; then - PRERELEASE_NOTE=" (pre-release)" + PRERELEASE_NOTE=" (pre-release)" fi gh pr create \ - --title "chore: bump version to $NEW_VERSION" \ - --body "$(cat <