Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .github/workflows/release-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
48 changes: 48 additions & 0 deletions packaging/homebrew/generate_formula.sh
Original file line number Diff line number Diff line change
@@ -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 <<EOF
# Auto-generated by codeanalyzer-java's release workflow; do not edit by hand.
class CodeanalyzerJava < Formula
desc "CLDK Java analyzer (canjv), with a bundled JVM"
homepage "https://github.com/codellm-devkit/codeanalyzer-java"
url "${WHEEL_URL}", using: :nounzip
sha256 "${SHA256}"
version "${VERSION}"
license "Apache-2.0"

depends_on "uv"

# Resolve the wheel and its platform-specific jdk4py runtime on first use,
# outside Homebrew's build-time network sandbox (same pattern as canpy).
def install
(bin/"canjv").write <<~SH
#!/bin/bash
exec "#{Formula["uv"].opt_bin}/uvx" --from "codeanalyzer-java==#{version}" canjv "\$@"
SH
chmod 0755, bin/"canjv"
end

def caveats
<<~EOS
The first canjv invocation downloads the pinned PyPI package and its
bundled JVM through uv. Internet access is required on first use.
EOS
end

test do
assert_match "codeanalyzer-java==#{version}", File.read(bin/"canjv")
assert_predicate bin/"canjv", :executable?
end
end
EOF