docs: document pip/Homebrew install of the cants command #6
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |