docs: document --call-graph-provider and Jelly backend; fix analysis.… #7
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 | |
| # Tolerate a repo with no test files (bun test exits 1 when it finds none), | |
| # but still fail the gate on real test failures when tests DO exist. | |
| if find . -path ./node_modules -prune -o \ | |
| \( -name '*.test.ts' -o -name '*.test.tsx' -o -name '*.test.js' -o -name '*.test.jsx' \ | |
| -o -name '*.spec.ts' -o -name '*.spec.tsx' -o -name '*.spec.js' -o -name '*.spec.jsx' \) \ | |
| -print | grep -q .; then | |
| bun test | |
| else | |
| echo "No test files found — skipping bun test." | |
| fi | |
| - 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 | |
| # Make re-runs idempotent: a version already on PyPI is skipped rather | |
| # than failing the whole job with a 400 (PyPI rejects duplicate uploads). | |
| skip-existing: true | |
| # Hand the raw binaries to the separate `homebrew` job below. Passing them | |
| # as an artifact (rather than re-downloading the GitHub Release) keeps the | |
| # formula checksums byte-identical to what was just published. | |
| - name: Upload release binaries for the homebrew job | |
| if: startsWith(github.ref, 'refs/tags/') | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-bins | |
| path: release-bins/ | |
| # ----- Homebrew: regenerate the formula from the just-built binaries and push | |
| # it to the shared tap. Split into its own job (needs: release) so a tap-push | |
| # failure -- e.g. a missing HOMEBREW_TAP_TOKEN -- is isolated from the PyPI and | |
| # GitHub Release steps above, and can be re-run on its own without re-uploading | |
| # wheels. This is the non-Rust equivalent of what cargo-dist does for you. | |
| homebrew: | |
| needs: release | |
| if: startsWith(github.ref, 'refs/tags/') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check out code | |
| uses: actions/checkout@v4 | |
| - name: Derive version from tag | |
| id: ver | |
| run: echo "version=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT" | |
| - name: Download release binaries | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: release-bins | |
| path: release-bins | |
| - name: Generate Homebrew formula | |
| 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 | |
| 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 |