Skip to content

docs: document pip/Homebrew install of the cants command #6

docs: document pip/Homebrew install of the cants command

docs: document pip/Homebrew install of the cants command #6

Workflow file for this run

name: Release (binaries + Python wheels)
# Builds the self-contained cants binary for every supported
# platform and publishes:
# 1. the raw binaries as GitHub Release assets (for analysis_backend_path /
# $CODEANALYZER_TS_BIN / download-on-first-use use cases), and
# 2. platform-tagged Python wheels to PyPI as `codeanalyzer-typescript`.
#
# Bun cross-compiles all targets from one host, so a single Linux job suffices.
on:
push:
tags:
- "v*.*.*"
workflow_dispatch: {}
permissions:
contents: write # create GitHub Release + delete tag on failure
id-token: write # PyPI Trusted Publishing (OIDC) -- no API token needed
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
# Derive the release version from the tag (vX.Y.Z -> X.Y.Z) and require it
# to match package.json. This keeps the PyPI wheel version, the GitHub
# Release tag, and the npm version in lockstep -- a mismatch fails fast
# rather than silently publishing the wrong version to PyPI.
- name: Determine and verify version
id: ver
run: |
pkgjson="$(node -p "require('./package.json').version")"
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
version="${GITHUB_REF#refs/tags/v}"
if [[ "$version" != "$pkgjson" ]]; then
echo "::error::Tag version ($version) != package.json version ($pkgjson). Bump package.json or fix the tag."
exit 1
fi
else
version="$pkgjson" # workflow_dispatch: no tag, fall back to package.json
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
echo ">>> Releasing version $version"
- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install analyzer dependencies
run: bun install
# ----- test gate: a broken build must not produce a release -----
- name: Typecheck and test
id: test
continue-on-error: true
run: |
bun run typecheck
bun test
- name: Delete tag on failure
if: steps.test.conclusion == 'failure' && startsWith(github.ref, 'refs/tags/')
run: |
echo "Tests failed. Deleting tag ${GITHUB_REF#refs/tags/}..."
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git push --delete origin "${GITHUB_REF#refs/tags/}"
exit 1
- name: Fail if tests failed (non-tag runs)
if: steps.test.conclusion == 'failure'
run: exit 1
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install Python build tooling
# hatchling is the build backend; build_wheels.sh runs `python -m build
# --no-isolation`, so it must be present in this env (no auto-install).
run: python -m pip install --upgrade build wheel hatchling
- name: Build platform wheels (cross-compiles every target via Bun)
working-directory: packaging/python
env:
PKG_VERSION: ${{ steps.ver.outputs.version }}
run: ./build_wheels.sh
- name: Extract raw binaries from wheels (for GitHub Release)
working-directory: packaging/python
run: |
mkdir -p ../../release-bins
for whl in dist/*.whl; do
plat="$(basename "$whl" .whl | sed 's/.*-py3-none-//')"
tmp="$(mktemp -d)"
python -m zipfile -e "$whl" "$tmp"
bin="$(find "$tmp/codeanalyzer_typescript/_bin" -type f ! -name '.gitignore')"
ext=""; [[ "$bin" == *.exe ]] && ext=".exe"
cp "$bin" "../../release-bins/cants-${plat}${ext}"
done
ls -lh ../../release-bins
- name: Build changelog (auto-generated from commits/PRs)
id: changelog
if: startsWith(github.ref, 'refs/tags/')
uses: mikepenz/release-changelog-builder-action@v5
with:
failOnError: "false"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish GitHub Release (raw binaries)
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
files: release-bins/*
body: ${{ steps.changelog.outputs.changelog }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Publish wheels to PyPI (Trusted Publishing / OIDC)
if: startsWith(github.ref, 'refs/tags/')
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: packaging/python/dist
# ----- Homebrew: regenerate the formula from the just-built binaries and
# push it to the shared tap. This is the non-Rust equivalent of what
# cargo-dist does for you; it is independent of the PyPI path above and
# only consumes the raw bun binaries in release-bins/.
- name: Generate Homebrew formula
if: startsWith(github.ref, 'refs/tags/')
env:
REPO: ${{ github.repository }}
VERSION: ${{ steps.ver.outputs.version }}
run: |
./packaging/homebrew/generate_formula.sh release-bins > codeanalyzer-typescript.rb
cat codeanalyzer-typescript.rb
- name: Push formula to codellm-devkit/homebrew-tap
if: startsWith(github.ref, 'refs/tags/')
env:
TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} # PAT with write access to homebrew-tap
VERSION: ${{ steps.ver.outputs.version }}
run: |
git clone "https://x-access-token:${TAP_TOKEN}@github.com/codellm-devkit/homebrew-tap.git" tap
mkdir -p tap/Formula
cp codeanalyzer-typescript.rb tap/Formula/codeanalyzer-typescript.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-typescript.rb
git commit -m "codeanalyzer-typescript ${VERSION}" || { echo "no formula change"; exit 0; }
git push