Skip to content
Merged
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
456 changes: 456 additions & 0 deletions .github/workflows/python-cli-wheels.yml

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,13 @@ big-code-analysis-py/python/big_code_analysis/_native*.so
# tracking policy and the regenerate workflow (`make py-relock`).
*.pyc
abuild-keys/

# CLI pip-wheel build artefacts. `python-cli-wheels.yml` stages the
# generated TPL, a copy of the workspace LICENSE, and the `man/` pages
# into the CLI crate directory and builds wheels under `dist/` there;
# none of those staged copies are source and must not be checked in
# (the canonical originals live at the repo root / `man/`).
big-code-analysis-cli/dist/
big-code-analysis-cli/LICENSE
big-code-analysis-cli/THIRD-PARTY-LICENSES-bca.md
big-code-analysis-cli/man/
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@ for historical reference.

### Added

- The `bca` CLI is now pip-installable. `pip install big-code-analysis-cli`
drops the compiled `bca` binary onto your `PATH` (no Rust toolchain
required), the way `pip install ruff` installs the `ruff` command. The
PyPI **distribution name is `big-code-analysis-cli`** while the installed
**command stays `bca`** — distinct from the importable library bindings
published as `big-code-analysis`. A new
[`python-cli-wheels.yml`](.github/workflows/python-cli-wheels.yml)
workflow builds `-b bin` wheels for Linux (`manylinux_2_28` `x86_64` /
`aarch64`), macOS (`x86_64` / `arm64`), and Windows (`x86_64`),
smoke-tests each, and publishes to PyPI via Trusted Publishing in
lockstep with the workspace version. Each wheel carries the full
`all-languages` grammar set and bundles the per-binary
`THIRD-PARTY-LICENSES-bca.md` + `LICENSE` (in `.dist-info/licenses/`)
and the `bca` man pages. (#408)
- `bca report markdown|html` now honors in-source suppression markers
(`bca: suppress`, `bca: suppress-file`, `#lizard forgives`) **by
default**, omitting a function from a metric's hotspot table when that
Expand Down
153 changes: 149 additions & 4 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,23 @@ If any stage fails, nothing downstream runs. `publish` and
repo; they run in parallel so a crates.io failure does not block the
GitHub Release's `verify` step (and vice versa).

The **same `v*` tag push also triggers two independent PyPI wheel
workflows** that are *not* part of `release.yml` and run fully in
parallel with it (a failure in one does not block the others):

- **`python-wheels.yml`** publishes the importable library bindings
(`big-code-analysis`) — an abi3 extension wheel. See
[Python wheels (PyPI)](#python-wheels-pypi).
- **`python-cli-wheels.yml`** publishes the `bca` command-line tool
(`big-code-analysis-cli`) — a `-b bin` wheel that drops `bca` onto
`PATH`. See [CLI wheels (PyPI)](#cli-wheels-pypi).

Both read the workspace version (`dynamic = ["version"]`), so they
publish in lockstep with the crates above on every bump — no separate
version step. Their one-time Trusted-Publisher setup is in the
[Post-public-release checklist](#post-public-release-checklist); after
that they fire automatically on each tag.

## Defer-and-gate state for public publication

The repository is staging for a future public release. Until the
Expand Down Expand Up @@ -550,15 +567,25 @@ git tag -a v1.2.0 -m "v1.2.0"
git push origin v1.2.0
```

That's it — the push of the tag triggers `release.yml`. Watch it in
the Actions tab:
That's it — the push of the tag triggers `release.yml` **and** the two
PyPI wheel workflows (`python-wheels.yml` for the library bindings,
`python-cli-wheels.yml` for the `bca` CLI), all in parallel. Watch all
three in the Actions tab:

```bash
gh run watch
# or
# or, per workflow:
gh run list --workflow=Release
gh run list --workflow="Python wheels"
gh run list --workflow="Python CLI wheels"
```

The wheel workflows publish to PyPI automatically once their one-time
Trusted Publishers are registered (see the
[Post-public-release checklist](#post-public-release-checklist)); no
per-release action beyond the tag is needed. Confirm both wheels landed
in [Post-release verification](#post-release-verification).

## Cutting a pre-release

Pre-release tags match `vX.Y.Z-<suffix>` where `<suffix>` is
Expand All @@ -579,6 +606,16 @@ Signed artefacts, SBOMs, and SLSA provenance still publish normally,
so a pre-release is a full test of everything except the external
pushes.

Both PyPI wheel workflows still **build and smoke-test** every wheel on
a pre-release tag but **skip the PyPI publish step**, so a pre-release
never lands a wheel on PyPI. The CLI wheel (`python-cli-wheels.yml`)
skips publish for *any* hyphenated suffix — `!contains(github.ref,
'-')`, matching `release.yml`'s `*-*` prerelease rule exactly; the
library wheel (`python-wheels.yml`) skips the recognised `-rc` /
`-beta` / `-alpha` suffixes. For the suffixes this project actually
uses (above), all three pipelines stay aligned — one tag cannot publish
a prerelease to one registry while skipping another.

## Post-release verification

The pipeline's own `verify` job downloads the musl tarball from the
Expand Down Expand Up @@ -612,6 +649,24 @@ once the corresponding gating variable is on):
- Scoop bucket: new commit on `dekobon/scoop-bucket` touching
`bucket/big-code-analysis.json`.

Confirm both PyPI wheels published at the new version (these ship on
every tag once their Trusted Publishers are registered). Either check
the project pages — <https://pypi.org/project/big-code-analysis/> and
<https://pypi.org/project/big-code-analysis-cli/> — or verify the CLI
end-to-end from a clean environment:

```bash
VERSION=0.1.0
python -m venv /tmp/bca-rel && . /tmp/bca-rel/bin/activate
# Library bindings (importable module):
pip install "big-code-analysis==${VERSION}"
python -c "import big_code_analysis as bca; print(bca.__version__)"
# CLI tool (drops `bca` on PATH):
pip install "big-code-analysis-cli==${VERSION}"
bca --version # must print the tagged version
deactivate
```

## Post-public-release checklist

The first time the repository goes public and a stable release is
Expand Down Expand Up @@ -639,6 +694,15 @@ turns into a foot-gun on the *next* release.
- [ ] **`python-wheels` PR label.** Create the label (see the
Python wheels section) so contributors can opt PRs into the
wheel matrix.
- [ ] **PyPI Trusted Publisher and `pypi-cli` GH environment (CLI
wheel).** Claim `big-code-analysis-cli` on PyPI via the
pending-publisher flow, registering a TP for workflow
`python-cli-wheels.yml` + environment `pypi-cli`, and create the
`pypi-cli` GitHub environment. See [CLI wheels
(PyPI)](#cli-wheels-pypi).
- [ ] **`python-cli-wheels` PR label.** Create the label (see the CLI
wheels section) so contributors can opt PRs into the CLI wheel
matrix.
- [ ] **Shared Homebrew tap reachable.** Confirm
`dekobon/homebrew-tap` exists and the configured PAT can push to
it. The release workflow appends `Formula/big-code-analysis.rb`
Expand Down Expand Up @@ -799,7 +863,88 @@ into a production-shaped flow.
The wheel pipeline ships Linux only (x86_64 + aarch64). macOS and
Windows wheels are tracked separately under
[#103](https://github.com/dekobon/big-code-analysis/issues/103)'s
"Out of scope" section.
"Out of scope" section. (This Linux-only scope is for the *library*
bindings wheel above; the **CLI** `bca` wheel — see below — does ship
macOS and Windows.)

## CLI wheels (PyPI)

The `bca` command-line tool ships as its own pip-installable
distribution via `.github/workflows/python-cli-wheels.yml`, separate
from both `release.yml` (crates.io / native packages) and
`python-wheels.yml` (the library bindings). All three trigger on the
same `v[0-9]*` tag push and run in parallel; a failure in one does not
block the others.

This is a `maturin -b bin` wheel: the compiled `bca` binary is packaged
as a console script, so `pip install big-code-analysis-cli` drops `bca`
onto the user's `PATH`. Key differences from the library wheel:

- **No abi3 / no per-Python matrix.** A bin wheel is tagged
`py3-none-<platform>`; one wheel per (OS, arch) covers every CPython
3.x and PyPy. The matrix is per-platform, not per-Python-version.
- **Distribution name `big-code-analysis-cli`, command `bca`.** The
installed command intentionally differs from the dist name (the `bca`
name on PyPI is taken; `big-code-analysis` is the library bindings).
- **Full grammar set is inherited from the crate** (`all-languages`, via
#252) — no `[tool.maturin] features` wiring.
- **Wider platform matrix:** Linux `manylinux_2_28` (`x86_64` /
`aarch64`), macOS (`x86_64` / `arm64`), Windows (`x86_64`).
- **Compliance artefacts ride in the wheel:** the per-binary
`THIRD-PARTY-LICENSES-bca.md` (cargo-about) and `LICENSE` land in
`.dist-info/licenses/`; the `bca` man pages are bundled; maturin emits
a CycloneDX SBOM into `.dist-info/sboms/`.

### One-time PyPI setup

Mirror the library-wheel setup above, with CLI-specific values:

1. **Claim the project name** at
`https://pypi.org/project/big-code-analysis-cli/` (the
pending-publisher flow reserves the name in the same step as the TP
registration). The name was confirmed available when #408 was filed.

2. **Register a Trusted Publisher** at
`https://pypi.org/manage/account/publishing/` with:

- PyPI Project Name: `big-code-analysis-cli`.
- Owner: `dekobon`.
- Repository name: `big-code-analysis`.
- Workflow filename: `python-cli-wheels.yml` (basename only).
- Environment name: `pypi-cli`.

`pypi-cli` is intentionally distinct from the library's `pypi`
environment and the crates.io `release` environment so each
registry/project's OIDC `environment` claim is unambiguous.

3. **Create the `pypi-cli` GitHub Environment** (Settings →
Environments → New environment → `pypi-cli`) before the first `v*`
tag if you want a manual approval gate on the first publish — GitHub
auto-creates a referenced-but-undefined environment with no
protection rules otherwise.

4. **Create the `python-cli-wheels` PR label** so contributors can opt a
PR into the wheel matrix (Rust-only PRs that merely brush a
path-filter neighbour skip it):

```bash
gh label create python-cli-wheels \
--color 1d76db \
--description "PR opts in to the bca CLI wheel CI matrix"
```

Tag pushes and `workflow_dispatch` runs ignore the label.

5. **First tagged release validates the path**, exactly as for the
library wheel — Trusted Publishing cannot be rehearsed via
`workflow_dispatch`.

### Version coupling

`big-code-analysis-cli` inherits its version from
`[workspace.package] version` (`version.workspace = true`), and its
`pyproject.toml` reads the same value at build time (`dynamic =
["version"]`). No separate version field to maintain.

## Rotating the minisign key

Expand Down
33 changes: 33 additions & 0 deletions big-code-analysis-book/src/commands/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,39 @@ information from source code. Each command **may** include parameters
specific to the task it performs. Below, we describe the core types
of commands available in **bca**.

## Installation

The `bca` command-line tool is available as a pip-installable wheel.
The **distribution name is `big-code-analysis-cli`** and the installed
**command is `bca`** — the two differ deliberately (the `bca` name on
PyPI belongs to an unrelated project, and `big-code-analysis` is this
project's importable *library* bindings):

```bash
pip install big-code-analysis-cli # installs the `bca` command on PATH
bca --version
```

This drops the compiled `bca` binary onto your `PATH` the way
`pip install ruff` gives you the `ruff` command — no Rust toolchain
required. The wheel carries the full `all-languages` grammar set, so
every [supported language](../languages.md) works out of the box. A
single `py3-none-<platform>` wheel covers every CPython 3.x (and PyPy)
on that platform; prebuilt wheels ship for Linux (`manylinux_2_28`
`x86_64` / `aarch64`), macOS (`x86_64` / `arm64`), and Windows
(`x86_64`). On any other platform `pip` falls back to a source build,
which needs a Rust toolchain.

This is the binary CLI, distinct from the importable
[Python bindings](../python/installation.md)
(`pip install big-code-analysis`). Other install paths — Homebrew,
`.deb` / `.rpm` / `.apk` packages, prebuilt release archives, or
`cargo install big-code-analysis-cli` — are described in the
repository README.

The wheel build and publish matrix is defined in
[`.github/workflows/python-cli-wheels.yml`](https://github.com/dekobon/big-code-analysis/blob/main/.github/workflows/python-cli-wheels.yml).

## Metrics

Metrics provide quantitative measures about source code, which can help in:
Expand Down
31 changes: 30 additions & 1 deletion big-code-analysis-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,38 @@ aggregated reports, AST dumps, node lookups, and more.

## Installation

### From PyPI (pip)

The fastest path on Linux, macOS, and Windows — no Rust toolchain
required:

```sh
pip install big-code-analysis-cli # installs the `bca` command on PATH
bca --version
```

Note the deliberate split between the **distribution name** and the
**command name**: you `pip install big-code-analysis-cli`, but the
installed executable is `bca`. The `bca` name on PyPI belongs to an
unrelated project, and `big-code-analysis` is this project's importable
*library* bindings (`pip install big-code-analysis` — a different
deliverable). The wheel ships the full `all-languages` grammar set; a
single `py3-none-<platform>` wheel covers every CPython 3.x (and PyPy)
on that platform. Prebuilt wheels are published for Linux
(`manylinux_2_28` `x86_64` / `aarch64`), macOS (`x86_64` / `arm64`), and
Windows (`x86_64`); other platforms fall back to a source build.

### From crates.io (cargo)

```sh
cargo install big-code-analysis-cli
```

### From source

```sh
cd big-code-analysis-cli/
cargo build
cargo build --release
```

## Usage
Expand Down
60 changes: 60 additions & 0 deletions big-code-analysis-cli/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
[build-system]
requires = ["maturin>=1.7,<2.0"]
build-backend = "maturin"

[project]
name = "big-code-analysis-cli"
description = "The bca command-line tool: compute and export code metrics for many languages"
readme = "README.md"
# A `-b bin` wheel carries a compiled binary launched as a console
# script, not a CPython extension module — there is no Python ABI to
# match, so a single `py3-none-<platform>` wheel serves every CPython
# 3.x and PyPy on that platform. The floor is therefore set low and
# generously (3.8) rather than tracking the library bindings' 3.12
# requirement; nothing here imports Python.
requires-python = ">=3.8"
license = "MPL-2.0"
license-files = ["LICENSE", "THIRD-PARTY-LICENSES-bca.md"]
authors = [
{ name = "Calixte Denizet", email = "cdenizet@mozilla.com" },
{ name = "Elijah Zupancic", email = "elijah@zupancic.name" },
]
keywords = ["metrics", "tree-sitter", "static-analysis", "cli"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: Developers",
"Programming Language :: Rust",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Quality Assurance",
]
# Version is read from the Rust workspace's Cargo.toml at build time by
# maturin (the package inherits `version.workspace = true`), keeping the
# wheel version in lockstep with the crate / library / web versions.
dynamic = ["version"]

[project.urls]
Repository = "https://github.com/dekobon/big-code-analysis"
Documentation = "https://dekobon.github.io/big-code-analysis/"

[tool.maturin]
# `bin` bindings package the compiled `bca` binary into the wheel as a
# console script so `pip install big-code-analysis-cli` drops `bca` onto
# the user's PATH. maturin auto-detects this mode for a single-binary
# crate with no pyo3 / cdylib target (which this crate is), but it is
# spelled out so the contract is explicit and a future PyO3 addition to
# the crate cannot silently flip the build into extension-module mode.
bindings = "bin"
# The `bca` man pages (workspace `man/`, generated by `cargo xtask`) are
# copied into this crate directory at wheel-build time by
# `.github/workflows/python-cli-wheels.yml` and bundled for reference.
# They are listed as a glob — not literal paths — so a clean local
# `maturin build` / `maturin sdist`, where the staged copies are absent,
# silently omits them instead of failing. `format = "wheel"` keeps them
# out of the sdist (a source snapshot; a downstream rebuild regenerates
# them from the clap definitions via `cargo xtask`). The compliance
# artefacts (LICENSE + per-binary THIRD-PARTY-LICENSES-bca.md) are NOT
# bundled here — they ride in `[project].license-files`, which places
# them in the wheel's standard `.dist-info/licenses/` directory and
# records `License-File:` metadata.
include = [{ path = "man/*.1", format = "wheel" }]
Loading