From 26250490d35af8beda6fc2114ba1ab9e5e6ce485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20D=C3=ADaz?= Date: Thu, 30 Apr 2026 14:06:29 +0200 Subject: [PATCH 1/9] Change release wheels workflow to only push on new tags --- .github/workflows/wheels-release.yml | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/.github/workflows/wheels-release.yml b/.github/workflows/wheels-release.yml index b3a43ee..5ec20bb 100644 --- a/.github/workflows/wheels-release.yml +++ b/.github/workflows/wheels-release.yml @@ -2,11 +2,8 @@ name: Build Wheels and Release on: push: - branches: - - "main" - paths: - - "ooniauth-core/**" - - "ooniauth-py/**" + tags: + - "*" workflow_dispatch: {} permissions: @@ -68,18 +65,11 @@ jobs: echo "Wheel files in dist:" ls -la dist - - name: Compute release tag name - id: tag - run: | - SHORT_SHA="${GITHUB_SHA::7}" - TAG="ooniauth-py-${SHORT_SHA}-${GITHUB_RUN_NUMBER}" - echo "tag=$TAG" >> "$GITHUB_OUTPUT" - - name: Upload wheels to GitHub Release uses: softprops/action-gh-release@v2 with: - tag_name: ${{ steps.tag.outputs.tag }} - name: ${{ steps.tag.outputs.tag }} + tag_name: ${{ github.ref_name }} + name: ${{ github.ref_name }} target_commitish: ${{ github.sha }} generate_release_notes: true files: | From bc2686951f8210954b393d9aaafb5338e109dbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20D=C3=ADaz?= Date: Thu, 30 Apr 2026 14:33:39 +0200 Subject: [PATCH 2/9] Use testing variables in workflow --- .github/workflows/wheels-release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/wheels-release.yml b/.github/workflows/wheels-release.yml index 5ec20bb..4ca9b12 100644 --- a/.github/workflows/wheels-release.yml +++ b/.github/workflows/wheels-release.yml @@ -80,7 +80,7 @@ jobs: - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: - # password: ${{ secrets.TEST_PYPI_API_TOKEN }} # use this for testing the workflow - password: ${{ secrets.PYPI_API_TOKEN }} + password: ${{ secrets.TEST_PYPI_API_TOKEN }} # use this for testing the workflow + # password: ${{ secrets.PYPI_API_TOKEN }} packages-dir: dist - # repository-url: https://test.pypi.org/legacy/ # uncomment this to use the test api token + repository-url: https://test.pypi.org/legacy/ # uncomment this to use the test api token From bb82a84c72fd5599a1b3ffd24746830dfdd83215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20D=C3=ADaz?= Date: Thu, 30 Apr 2026 14:35:27 +0200 Subject: [PATCH 3/9] add release tag helper script --- scripts/create-release-tag.sh | 56 +++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100755 scripts/create-release-tag.sh diff --git a/scripts/create-release-tag.sh b/scripts/create-release-tag.sh new file mode 100755 index 0000000..1114114 --- /dev/null +++ b/scripts/create-release-tag.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash + +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +TAG_PREFIX="userauth" + +if ! command -v git >/dev/null 2>&1; then + echo "Error: git is required but was not found in PATH" >&2 + exit 1 +fi + +CURRENT_BRANCH="$(git -C "$ROOT_DIR" rev-parse --abbrev-ref HEAD)" +COMMIT_SHA="$(git -C "$ROOT_DIR" rev-parse --short HEAD)" +MAX_RELEASE_NUM="$( + git -C "$ROOT_DIR" tag --list "${TAG_PREFIX}-*" \ + | awk -F '-' ' + $1 "-" $2 == "ooniauth-py" && $NF ~ /^[0-9]+$/ { + if ($NF > max) max = $NF + } + END { print max + 0 } + ' +)" +NEXT_RELEASE_NUM=$((MAX_RELEASE_NUM + 1)) +TAG_NAME="${TAG_PREFIX}-${COMMIT_SHA}-${NEXT_RELEASE_NUM}" + +if git -C "$ROOT_DIR" rev-parse "$TAG_NAME" >/dev/null 2>&1; then + echo "Error: tag $TAG_NAME already exists locally." >&2 + exit 1 +fi + +if git -C "$ROOT_DIR" ls-remote --tags origin "refs/tags/$TAG_NAME" | grep -q "$TAG_NAME"; then + echo "Error: tag $TAG_NAME already exists on origin." >&2 + exit 1 +fi + +echo "Release tag preview:" +echo " Tag scheme: ${TAG_PREFIX}--" +echo " Release number: $NEXT_RELEASE_NUM" +echo " Tag: $TAG_NAME" +echo " Branch: $CURRENT_BRANCH" +echo " Commit: $COMMIT_SHA" +echo +read -r -p "Create and push tag $TAG_NAME to origin? [y/N] " answer + +case "$answer" in + y|Y|yes|YES) + git -C "$ROOT_DIR" tag -a "$TAG_NAME" -m "Release $TAG_NAME" + git -C "$ROOT_DIR" push origin "$TAG_NAME" + echo "Done: pushed $TAG_NAME" + ;; + *) + echo "Aborted." + exit 0 + ;; +esac From 176f66b1aa0caa84d33a20509c86cc1bac73f328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20D=C3=ADaz?= Date: Thu, 30 Apr 2026 14:38:05 +0200 Subject: [PATCH 4/9] Print final release name in yellow --- scripts/create-release-tag.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/create-release-tag.sh b/scripts/create-release-tag.sh index 1114114..792fd90 100755 --- a/scripts/create-release-tag.sh +++ b/scripts/create-release-tag.sh @@ -4,6 +4,8 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" TAG_PREFIX="userauth" +YELLOW=$'\033[33m' +RESET=$'\033[0m' if ! command -v git >/dev/null 2>&1; then echo "Error: git is required but was not found in PATH" >&2 @@ -37,7 +39,7 @@ fi echo "Release tag preview:" echo " Tag scheme: ${TAG_PREFIX}--" echo " Release number: $NEXT_RELEASE_NUM" -echo " Tag: $TAG_NAME" +echo " Tag: ${YELLOW}${TAG_NAME}${RESET}" echo " Branch: $CURRENT_BRANCH" echo " Commit: $COMMIT_SHA" echo From f851fa519ba38b410d67c87ad71d408599ac49c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20D=C3=ADaz?= Date: Thu, 30 Apr 2026 15:03:38 +0200 Subject: [PATCH 5/9] Print useful messages when tagging a commit not on main --- .github/workflows/wheels-release.yml | 2 +- scripts/create-release-tag.sh | 23 +++++++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/.github/workflows/wheels-release.yml b/.github/workflows/wheels-release.yml index 4ca9b12..1d08528 100644 --- a/.github/workflows/wheels-release.yml +++ b/.github/workflows/wheels-release.yml @@ -3,7 +3,7 @@ name: Build Wheels and Release on: push: tags: - - "*" + - "userauth-*" workflow_dispatch: {} permissions: diff --git a/scripts/create-release-tag.sh b/scripts/create-release-tag.sh index 792fd90..c8aaf37 100755 --- a/scripts/create-release-tag.sh +++ b/scripts/create-release-tag.sh @@ -4,7 +4,9 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" TAG_PREFIX="userauth" +CYAN=$'\033[36m' YELLOW=$'\033[33m' +RED=$'\033[31m' RESET=$'\033[0m' if ! command -v git >/dev/null 2>&1; then @@ -16,9 +18,10 @@ CURRENT_BRANCH="$(git -C "$ROOT_DIR" rev-parse --abbrev-ref HEAD)" COMMIT_SHA="$(git -C "$ROOT_DIR" rev-parse --short HEAD)" MAX_RELEASE_NUM="$( git -C "$ROOT_DIR" tag --list "${TAG_PREFIX}-*" \ - | awk -F '-' ' - $1 "-" $2 == "ooniauth-py" && $NF ~ /^[0-9]+$/ { - if ($NF > max) max = $NF + | awk -v prefix="${TAG_PREFIX}-" ' + index($0, prefix) == 1 && match($0, /-([0-9]+)$/, m) { + release_num = m[1] + 0 + if (release_num > max) max = release_num } END { print max + 0 } ' @@ -39,11 +42,19 @@ fi echo "Release tag preview:" echo " Tag scheme: ${TAG_PREFIX}--" echo " Release number: $NEXT_RELEASE_NUM" -echo " Tag: ${YELLOW}${TAG_NAME}${RESET}" -echo " Branch: $CURRENT_BRANCH" +echo " Tag: ${CYAN}${TAG_NAME}${RESET}" +if [[ "$CURRENT_BRANCH" == "main" ]]; then + echo " Branch: $CURRENT_BRANCH" +else + echo " Branch: ${RED}${CURRENT_BRANCH} (WARNING: not main)${RESET}" +fi echo " Commit: $COMMIT_SHA" echo -read -r -p "Create and push tag $TAG_NAME to origin? [y/N] " answer +if [[ "$CURRENT_BRANCH" != "main" ]]; then + printf "%b\n" " ${YELLOW}WARNING: current branch is '$CURRENT_BRANCH' (not main): commit $COMMIT_SHA may not be on main${RESET}" +fi + +read -r -p "Create and push tag ${CYAN} $TAG_NAME ${RESET} to origin? [y/N] " answer case "$answer" in y|Y|yes|YES) From e145f9f26ec3b060e56be1dc26a35381af027462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20D=C3=ADaz?= Date: Thu, 30 Apr 2026 15:55:51 +0200 Subject: [PATCH 6/9] Add workflow to check for version bump before releasing to pypi --- .github/workflows/wheels-release.yml | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/.github/workflows/wheels-release.yml b/.github/workflows/wheels-release.yml index 1d08528..85d72a6 100644 --- a/.github/workflows/wheels-release.yml +++ b/.github/workflows/wheels-release.yml @@ -53,6 +53,9 @@ jobs: runs-on: ubuntu-latest needs: build-wheels steps: + - name: Checkout + uses: actions/checkout@v5 + - name: Download wheel artifacts uses: actions/download-artifact@v4 with: @@ -60,6 +63,38 @@ jobs: path: dist merge-multiple: true + # pulls the current version from pypi and compares with the current version on ooniauth-py + - name: Verify version is greater than PyPI + shell: bash + run: | + set -euo pipefail + + LOCAL_VERSION="$(awk -F '"' '/^version = "/ { print $2; exit }' ooniauth-py/Cargo.toml)" + PYPI_VERSION="$(python3 - <<'PY' + import json + import urllib.request + + url = "https://pypi.org/pypi/ooniauth-py/json" + with urllib.request.urlopen(url, timeout=20) as response: + data = json.load(response) + print(data["info"]["version"]) + PY + )" + + if [[ -z "$LOCAL_VERSION" || -z "$PYPI_VERSION" ]]; then + echo "Failed to parse local or PyPI version." >&2 + exit 1 + fi + + echo "Local version: $LOCAL_VERSION" + echo "PyPI version: $PYPI_VERSION" + + HIGHEST="$(printf '%s\n%s\n' "$PYPI_VERSION" "$LOCAL_VERSION" | sort -V | tail -n 1)" + if [[ "$LOCAL_VERSION" == "$PYPI_VERSION" || "$HIGHEST" != "$LOCAL_VERSION" ]]; then + echo "Local version must be greater than PyPI version." >&2 + exit 1 + fi + - name: Check staged wheels run: | echo "Wheel files in dist:" From 25fb76d77a821b908dda829d18991e5c667e11c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20D=C3=ADaz?= Date: Thu, 30 Apr 2026 15:57:39 +0200 Subject: [PATCH 7/9] Remove workflow file, no longer needed --- .../check-ooniauth-py-version-bump.yml | 54 ------------------- 1 file changed, 54 deletions(-) delete mode 100644 .github/workflows/check-ooniauth-py-version-bump.yml diff --git a/.github/workflows/check-ooniauth-py-version-bump.yml b/.github/workflows/check-ooniauth-py-version-bump.yml deleted file mode 100644 index d54a349..0000000 --- a/.github/workflows/check-ooniauth-py-version-bump.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: Check ooniauth-py version bump -# This workflow checks that we're bumping the library version before publishing to PyPi. -# If you don't bump it, the upload will fail after merge since you can only upload new versions -on: - pull_request: - branches: - - main - paths: - - "ooniauth-py/**" - - "ooniauth-core/**" - -jobs: - check-version-bump: - runs-on: ubuntu-latest - steps: - - name: Checkout PR branch - uses: actions/checkout@v5 - with: - fetch-depth: 0 - - - name: Check version bumped vs main - shell: bash - run: | - set -euo pipefail - - FILE="ooniauth-py/Cargo.toml" - - if [ ! -f "$FILE" ]; then - echo "Missing $FILE in PR branch" - exit 1 - fi - - git fetch origin main - - MAIN_VERSION="$(git show origin/main:$FILE | sed -n 's/^version = "\(.*\)"/\1/p' | head -n1 || true)" - PR_VERSION="$(sed -n 's/^version = "\(.*\)"/\1/p' "$FILE" | head -n1 || true)" - - if [ -z "$MAIN_VERSION" ] || [ -z "$PR_VERSION" ]; then - echo "Could not parse version from $FILE" - echo "main='$MAIN_VERSION' pr='$PR_VERSION'" - exit 1 - fi - - echo "main version: $MAIN_VERSION" - echo "PR version: $PR_VERSION" - - # Check that the new version is higher than the version in Main - HIGHEST="$(printf '%s\n%s\n' "$MAIN_VERSION" "$PR_VERSION" | sort -V | tail -n1)" - if [ "$PR_VERSION" = "$MAIN_VERSION" ] || [ "$HIGHEST" != "$PR_VERSION" ]; then - echo "::error::ooniauth-py version in $FILE must be strictly higher than main (main=$MAIN_VERSION, PR=$PR_VERSION). Otherwise the PyPi upload will fail after merge" - exit 1 - fi - - echo "[OK] Version bump detected (main=$MAIN_VERSION -> PR=$PR_VERSION)" From e8de2bd8ec392e81d5ae6293f6ab7bbeb3956256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20D=C3=ADaz?= Date: Mon, 4 May 2026 13:08:55 +0200 Subject: [PATCH 8/9] Simplify release creation workflow by removing python version check --- .github/workflows/wheels-release.yml | 32 -------------------------- scripts/create-release-tag.sh | 34 +++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/.github/workflows/wheels-release.yml b/.github/workflows/wheels-release.yml index 85d72a6..4321b17 100644 --- a/.github/workflows/wheels-release.yml +++ b/.github/workflows/wheels-release.yml @@ -63,38 +63,6 @@ jobs: path: dist merge-multiple: true - # pulls the current version from pypi and compares with the current version on ooniauth-py - - name: Verify version is greater than PyPI - shell: bash - run: | - set -euo pipefail - - LOCAL_VERSION="$(awk -F '"' '/^version = "/ { print $2; exit }' ooniauth-py/Cargo.toml)" - PYPI_VERSION="$(python3 - <<'PY' - import json - import urllib.request - - url = "https://pypi.org/pypi/ooniauth-py/json" - with urllib.request.urlopen(url, timeout=20) as response: - data = json.load(response) - print(data["info"]["version"]) - PY - )" - - if [[ -z "$LOCAL_VERSION" || -z "$PYPI_VERSION" ]]; then - echo "Failed to parse local or PyPI version." >&2 - exit 1 - fi - - echo "Local version: $LOCAL_VERSION" - echo "PyPI version: $PYPI_VERSION" - - HIGHEST="$(printf '%s\n%s\n' "$PYPI_VERSION" "$LOCAL_VERSION" | sort -V | tail -n 1)" - if [[ "$LOCAL_VERSION" == "$PYPI_VERSION" || "$HIGHEST" != "$LOCAL_VERSION" ]]; then - echo "Local version must be greater than PyPI version." >&2 - exit 1 - fi - - name: Check staged wheels run: | echo "Wheel files in dist:" diff --git a/scripts/create-release-tag.sh b/scripts/create-release-tag.sh index c8aaf37..41d6ff6 100755 --- a/scripts/create-release-tag.sh +++ b/scripts/create-release-tag.sh @@ -39,6 +39,37 @@ if git -C "$ROOT_DIR" ls-remote --tags origin "refs/tags/$TAG_NAME" | grep -q "$ exit 1 fi +CARGO_TOML="$ROOT_DIR/ooniauth-py/Cargo.toml" +if [[ ! -f "$CARGO_TOML" ]]; then + echo "Error: expected $CARGO_TOML" >&2 + exit 1 +fi + +LOCAL_VERSION="$(awk -F '"' '/^version = "/ { print $2; exit }' "$CARGO_TOML")" +PYPI_VERSION="$(python3 - <<'PY' +import json +import urllib.request + +url = "https://pypi.org/pypi/ooniauth-py/json" +with urllib.request.urlopen(url, timeout=20) as response: + data = json.load(response) +print(data["info"]["version"]) +PY +)" + +if [[ -z "$LOCAL_VERSION" || -z "$PYPI_VERSION" ]]; then + echo "Failed to parse local or PyPI version." >&2 + exit 1 +fi + +HIGHEST="$(printf '%s\n%s\n' "$PYPI_VERSION" "$LOCAL_VERSION" | sort -V | tail -n 1)" +if [[ "$LOCAL_VERSION" == "$PYPI_VERSION" || "$HIGHEST" != "$LOCAL_VERSION" ]]; then + echo "Local ooniauth-py version must be greater than PyPI (bump ooniauth-py/Cargo.toml before releasing)." >&2 + echo " Local: $LOCAL_VERSION" >&2 + echo " PyPI: $PYPI_VERSION" >&2 + exit 1 +fi + echo "Release tag preview:" echo " Tag scheme: ${TAG_PREFIX}--" echo " Release number: $NEXT_RELEASE_NUM" @@ -49,9 +80,10 @@ else echo " Branch: ${RED}${CURRENT_BRANCH} (WARNING: not main)${RESET}" fi echo " Commit: $COMMIT_SHA" +echo " ooniauth-py: $LOCAL_VERSION (PyPI: $PYPI_VERSION)" echo if [[ "$CURRENT_BRANCH" != "main" ]]; then - printf "%b\n" " ${YELLOW}WARNING: current branch is '$CURRENT_BRANCH' (not main): commit $COMMIT_SHA may not be on main${RESET}" + printf "%b\n" " ${YELLOW}WARNING: current branch is '$CURRENT_BRANCH' (not main): commit $COMMIT_SHA is not in main${RESET}" fi read -r -p "Create and push tag ${CYAN} $TAG_NAME ${RESET} to origin? [y/N] " answer From 72bb66d9df4d52e34ecb3714c8a0d967c95d999a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20D=C3=ADaz?= Date: Mon, 4 May 2026 17:02:42 +0200 Subject: [PATCH 9/9] CReate release tag script in python, still WIP --- scripts/create-release-tag.py | 161 ++++++++++++++++++++++++++++++++++ scripts/create-release-tag.sh | 101 --------------------- 2 files changed, 161 insertions(+), 101 deletions(-) create mode 100755 scripts/create-release-tag.py delete mode 100755 scripts/create-release-tag.sh diff --git a/scripts/create-release-tag.py b/scripts/create-release-tag.py new file mode 100755 index 0000000..c40e926 --- /dev/null +++ b/scripts/create-release-tag.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +""" +create and push userauth-- tags +Checks that the version in ooniauth-py is consistent before creating the new tag +""" + +import json +import re +import subprocess +import sys +import urllib.request +from pathlib import Path + +TAG_PREFIX = "userauth" +PYPI_JSON_URL = "https://pypi.org/pypi/ooniauth-py/json" + +CYAN = "\033[36m" +YELLOW = "\033[33m" +RED = "\033[31m" +RESET = "\033[0m" + +REPO_ROOT = Path(__file__).resolve().parent.parent + +def eprint(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + +def git(*args: str, check: bool = True) -> subprocess.CompletedProcess[str]: + return subprocess.run( + ["git", "-C", str(REPO_ROOT), *args], + check=check, + capture_output=True, + text=True, + ) + +def version_tuple(v: str) -> tuple[int, ...]: + """Numeric dotted version -> tuple for lexicographic compare""" + parts = v.strip().split(".") + if not parts or any(not p.isdigit() for p in parts): + raise ValueError(f"expected dotted numeric version, got {v}") + return tuple(int(p) for p in parts) + + +def parse_cargo_version(cargo_toml: Path) -> str: + text = cargo_toml.read_text(encoding="utf-8") + m = re.search(r'^version\s*=\s*"([^"]+)"', text, re.MULTILINE) + if not m: + raise SystemExit(f"Error: could not parse version from {cargo_toml}") + return m.group(1) + + +def fetch_pypi_version(url: str = PYPI_JSON_URL) -> str: + req = urllib.request.Request(url, headers={"User-Agent": "create-release-tag.py"}) + with urllib.request.urlopen(req, timeout=20) as response: + data = json.load(response) + return str(data["info"]["version"]) + + +def max_release_number(tags: list[str]) -> int: + pat = re.compile(rf"^{re.escape(TAG_PREFIX)}-[^-]+-(\d+)$") + best = 0 + for line in tags: + t = line.strip() + m = pat.match(t) + if m: + best = max(best, int(m.group(1))) + return best + + +def tag_exists_remote(tag: str) -> bool: + # ls-remote exits 0 with empty output when the ref is missing. + out = git( + "ls-remote", + "--tags", + "origin", + f"refs/tags/{tag}", + ) + return bool(out.stdout.strip()) + + +def main() -> None: + current_branch = git("rev-parse", "--abbrev-ref", "HEAD").stdout + commit_sha = git("rev-parse", "--short", "HEAD").stdout + + tags_out = git("tag", "--list", f"{TAG_PREFIX}-*").stdout + tags = [ln for ln in tags_out.splitlines() if ln.strip()] + max_n = max_release_number(tags) + next_n = max_n + 1 + tag_name = f"{TAG_PREFIX}-{commit_sha}-{next_n}" + + if git("rev-parse", tag_name, check = False).returncode != 0: + eprint(f"Error: tag {tag_name} already exists locally.") + sys.exit(1) + + if tag_exists_remote(tag_name): + eprint(f"Error: tag {tag_name} already exists on origin.") + sys.exit(1) + + cargo_toml = REPO_ROOT / "ooniauth-py" / "Cargo.toml" + if not cargo_toml.is_file(): + eprint(f"Error: Missing ooniauth-py cargo.toml file :{cargo_toml}") + sys.exit(1) + + local_version = parse_cargo_version(cargo_toml) + + print("Checking last version from PyPi...") + try: + pypi_version = fetch_pypi_version() + except Exception as e: + eprint(f"Error: failed to fetch PyPI version: {e}") + sys.exit(1) + + try: + local_version = version_tuple(local_version) + pypi_version = version_tuple(pypi_version) + except ValueError as e: + eprint(f"Error: {e}") + sys.exit(1) + + if local_version <= pypi_version: + eprint( + f"{RED}ERROR{RESET}: Local ooniauth-py version must be greater than PyPI, " + "bump ooniauth-py/Cargo.toml before releasing.", + ) + eprint(f" Local: {local_version}") + eprint(f" PyPI: {pypi_version}") + sys.exit(1) + + print("Version number OK!") + + print("Release tag preview:") + print(f" Tag scheme: {TAG_PREFIX}--") + print(f" Release number: {next_n}") + print(f" Tag: {CYAN}{tag_name}{RESET}") + if current_branch == "main": + print(f" Branch: {current_branch}") + else: + print(f" Branch: {RED}{current_branch} (WARNING: not main){RESET}") + print(f" Commit: {commit_sha}") + print(f" ooniauth-py: {local_version} (PyPI: {pypi_version})") + print() + if current_branch != "main": + print( + f" {YELLOW}WARNING: current branch is '{current_branch}' (not main): " + f"commit {commit_sha} is not in main{RESET}" + ) + + prompt = ( + f"Create and push tag {CYAN} {tag_name} {RESET} to origin? [y/N] " + ) + answer = input(prompt).strip().lower() + if answer not in ("y", "yes"): + print("Aborted.") + return + + git("tag", "-a", tag_name, "-m", f"Release {tag_name}") + git("push", "origin", tag_name) + print(f"Done: pushed {CYAN}{tag_name}{RESET}") + + +if __name__ == "__main__": + main() diff --git a/scripts/create-release-tag.sh b/scripts/create-release-tag.sh deleted file mode 100755 index 41d6ff6..0000000 --- a/scripts/create-release-tag.sh +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -TAG_PREFIX="userauth" -CYAN=$'\033[36m' -YELLOW=$'\033[33m' -RED=$'\033[31m' -RESET=$'\033[0m' - -if ! command -v git >/dev/null 2>&1; then - echo "Error: git is required but was not found in PATH" >&2 - exit 1 -fi - -CURRENT_BRANCH="$(git -C "$ROOT_DIR" rev-parse --abbrev-ref HEAD)" -COMMIT_SHA="$(git -C "$ROOT_DIR" rev-parse --short HEAD)" -MAX_RELEASE_NUM="$( - git -C "$ROOT_DIR" tag --list "${TAG_PREFIX}-*" \ - | awk -v prefix="${TAG_PREFIX}-" ' - index($0, prefix) == 1 && match($0, /-([0-9]+)$/, m) { - release_num = m[1] + 0 - if (release_num > max) max = release_num - } - END { print max + 0 } - ' -)" -NEXT_RELEASE_NUM=$((MAX_RELEASE_NUM + 1)) -TAG_NAME="${TAG_PREFIX}-${COMMIT_SHA}-${NEXT_RELEASE_NUM}" - -if git -C "$ROOT_DIR" rev-parse "$TAG_NAME" >/dev/null 2>&1; then - echo "Error: tag $TAG_NAME already exists locally." >&2 - exit 1 -fi - -if git -C "$ROOT_DIR" ls-remote --tags origin "refs/tags/$TAG_NAME" | grep -q "$TAG_NAME"; then - echo "Error: tag $TAG_NAME already exists on origin." >&2 - exit 1 -fi - -CARGO_TOML="$ROOT_DIR/ooniauth-py/Cargo.toml" -if [[ ! -f "$CARGO_TOML" ]]; then - echo "Error: expected $CARGO_TOML" >&2 - exit 1 -fi - -LOCAL_VERSION="$(awk -F '"' '/^version = "/ { print $2; exit }' "$CARGO_TOML")" -PYPI_VERSION="$(python3 - <<'PY' -import json -import urllib.request - -url = "https://pypi.org/pypi/ooniauth-py/json" -with urllib.request.urlopen(url, timeout=20) as response: - data = json.load(response) -print(data["info"]["version"]) -PY -)" - -if [[ -z "$LOCAL_VERSION" || -z "$PYPI_VERSION" ]]; then - echo "Failed to parse local or PyPI version." >&2 - exit 1 -fi - -HIGHEST="$(printf '%s\n%s\n' "$PYPI_VERSION" "$LOCAL_VERSION" | sort -V | tail -n 1)" -if [[ "$LOCAL_VERSION" == "$PYPI_VERSION" || "$HIGHEST" != "$LOCAL_VERSION" ]]; then - echo "Local ooniauth-py version must be greater than PyPI (bump ooniauth-py/Cargo.toml before releasing)." >&2 - echo " Local: $LOCAL_VERSION" >&2 - echo " PyPI: $PYPI_VERSION" >&2 - exit 1 -fi - -echo "Release tag preview:" -echo " Tag scheme: ${TAG_PREFIX}--" -echo " Release number: $NEXT_RELEASE_NUM" -echo " Tag: ${CYAN}${TAG_NAME}${RESET}" -if [[ "$CURRENT_BRANCH" == "main" ]]; then - echo " Branch: $CURRENT_BRANCH" -else - echo " Branch: ${RED}${CURRENT_BRANCH} (WARNING: not main)${RESET}" -fi -echo " Commit: $COMMIT_SHA" -echo " ooniauth-py: $LOCAL_VERSION (PyPI: $PYPI_VERSION)" -echo -if [[ "$CURRENT_BRANCH" != "main" ]]; then - printf "%b\n" " ${YELLOW}WARNING: current branch is '$CURRENT_BRANCH' (not main): commit $COMMIT_SHA is not in main${RESET}" -fi - -read -r -p "Create and push tag ${CYAN} $TAG_NAME ${RESET} to origin? [y/N] " answer - -case "$answer" in - y|Y|yes|YES) - git -C "$ROOT_DIR" tag -a "$TAG_NAME" -m "Release $TAG_NAME" - git -C "$ROOT_DIR" push origin "$TAG_NAME" - echo "Done: pushed $TAG_NAME" - ;; - *) - echo "Aborted." - exit 0 - ;; -esac