From acd69a1165591a2e1a8469a6c188e643aec7ef51 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Sun, 6 Sep 2026 15:07:26 -0400 Subject: [PATCH] ci: publish canjv Homebrew formula after PyPI release --- .github/workflows/release-pypi.yml | 51 ++++++++++++++++++++++++++ README.md | 12 ++++++ packaging/homebrew/generate_formula.sh | 48 ++++++++++++++++++++++++ 3 files changed, 111 insertions(+) create mode 100644 packaging/homebrew/generate_formula.sh diff --git a/.github/workflows/release-pypi.yml b/.github/workflows/release-pypi.yml index 97bf7d95..5d4344d9 100644 --- a/.github/workflows/release-pypi.yml +++ b/.github/workflows/release-pypi.yml @@ -24,6 +24,8 @@ jobs: pypi: if: github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest + outputs: + version: ${{ steps.ver.outputs.version }} environment: name: pypi url: https://pypi.org/p/codeanalyzer-java @@ -73,3 +75,52 @@ jobs: packages-dir: packaging/python/dist # Re-runs stay idempotent: a version already on PyPI is skipped, not a 400. skip-existing: true + + # Publish only after PyPI succeeds, so the pinned canjv package is available. + # A tap failure can be retried without rebuilding or republishing the wheel. + homebrew: + needs: pypi + runs-on: ubuntu-latest + env: + VERSION: ${{ needs.pypi.outputs.version }} + steps: + - name: Check out release automation + uses: actions/checkout@v5 + # Use the default branch so workflow_dispatch can backfill older releases. + with: + ref: ${{ github.event.repository.default_branch }} + + - name: Generate Homebrew formula from the published wheel + run: | + curl --fail --silent --show-error --location --retry 12 --retry-all-errors --retry-delay 5 \ + "https://pypi.org/pypi/codeanalyzer-java/${VERSION}/json" -o pypi.json + wheel="$(jq -er --arg version "$VERSION" \ + '[.urls[] | select(.filename == ("codeanalyzer_java-" + $version + "-py3-none-any.whl") and .yanked == false)] | if length == 1 then .[0] else error("Expected one non-yanked universal wheel") end' pypi.json)" + WHEEL_URL="$(jq -er '.url' <<< "$wheel")" + SHA256="$(jq -er '.digests.sha256' <<< "$wheel")" + export WHEEL_URL SHA256 + bash packaging/homebrew/generate_formula.sh > codeanalyzer-java.rb + ruby -c codeanalyzer-java.rb + + - name: Check out the shared tap + uses: actions/checkout@v5 + with: + repository: codellm-devkit/homebrew-tap + token: ${{ secrets.HOMEBREW_TAP_TOKEN }} + path: tap + + - name: Update the formula + run: | + mkdir -p tap/Formula + cp codeanalyzer-java.rb tap/Formula/codeanalyzer-java.rb + cd tap + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add Formula/codeanalyzer-java.rb + if git diff --cached --quiet; then + echo "Formula already up to date" + exit 0 + fi + git commit -m "codeanalyzer-java ${VERSION}" + git pull --rebase + git push diff --git a/README.md b/README.md index 933b7eb9..ea54a3c5 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,18 @@ pip install codeanalyzer-java canjv -i /path/to/project -a 2 -o ./out ``` +Or with Homebrew: + +```sh +brew install codellm-devkit/homebrew-tap/codeanalyzer-java +canjv -i /path/to/project -a 2 -o ./out +``` + +The Homebrew launcher uses `uv` to download the pinned PyPI package and its bundled +JVM on first use, which requires internet access. No system Java is needed. +The release workflow updates the tap after PyPI publication using the repository's +`HOMEBREW_TAP_TOKEN` secret (write access to `codellm-devkit/homebrew-tap`). + Or grab the latest release jar and a `codeanalyzer` launcher (requires a Java 11+ runtime): ```sh diff --git a/packaging/homebrew/generate_formula.sh b/packaging/homebrew/generate_formula.sh new file mode 100644 index 00000000..433094fb --- /dev/null +++ b/packaging/homebrew/generate_formula.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Generate the canjv formula from the published PyPI wheel's URL and checksum. +# Usage: VERSION=3.0.2 WHEEL_URL=... SHA256=... bash generate_formula.sh +set -euo pipefail + +: "${VERSION:?set VERSION}" +: "${WHEEL_URL:?set WHEEL_URL from PyPI release metadata}" +: "${SHA256:?set SHA256 from PyPI release metadata}" + +[[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "Expected a stable release version" >&2; exit 1; } +[[ "$WHEEL_URL" =~ ^https://files\.pythonhosted\.org/[a-zA-Z0-9/_.-]+\.whl$ ]] || { echo "Expected a PyPI wheel URL" >&2; exit 1; } +[[ "$SHA256" =~ ^[a-f0-9]{64}$ ]] || { echo "Expected a SHA256 digest" >&2; exit 1; } + +cat <