Skip to content

feat: distribute becwright on npm/pnpm (no Python required)#16

Merged
DataDave-Dev merged 4 commits into
mainfrom
feat/npm-distribution
Jun 30, 2026
Merged

feat: distribute becwright on npm/pnpm (no Python required)#16
DataDave-Dev merged 4 commits into
mainfrom
feat/npm-distribution

Conversation

@DataDave-Dev

@DataDave-Dev DataDave-Dev commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Summary

Lets JS/web/backend developers — anyone without Python — install becwright via npm/pnpm, while keeping pip/pipx working. Distribution uses the esbuild/ruff model: a thin launcher package plus os/cpu-gated optional packages that each carry a self-contained binary (no Python at runtime).

Delivered in four atomic phases:

Phase 0 — Decouple checks from python3 (59d3323)

The blocker: generated .bec/rules.yaml hardcoded python3 -m becwright.checks.X, so a binary alone wouldn't help. Now a new becwright run <check> [args] subcommand dispatches to built-in checks, and init/materialize emit becwright run …. The same command works whether becwright comes from pip, pipx or a binary. Legacy python3 -m … is still recognized on import (back-compat).

Phase 1 — Standalone binary (8faf322)

PyInstaller one-file spec (uses collect_submodules because checks load dynamically), a build extra, python -m becwright entry point, and a CI matrix building + smoke-testing the binary on linux x64/arm64, darwin x64/arm64, win32 x64.

Phase 2 — npm packaging + local hook (98099a8)

Launcher becwright + five @becwright/<target> packages. The launcher prepends the binary's dir to PATH so the engine's becwright run subprocess resolves even on a local devDependency install (where node_modules/.bin isn't on the hook's PATH). The git hook now prefers ./node_modules/.bin/becwright before a global one.

Phase 3 — Automated release (c1ea31d)

One published GitHub release → builds binaries, stages them, derives every npm version from the tag, publishes the 5 platform packages then the launcher, and publishes to PyPI (Trusted Publishing). build-binaries.yml became a reusable workflow; publish.yml folded into release.yml.

Test plan

  • pytest — 110 passing throughout (107 + 3 new for run; back-compat tests added)
  • Dogfood: becwright check --all passes with the new becwright run form
  • Frozen binary (linux-x64): version / list / run forbid / run debug_remnants work; full init → check blocks a debugger; with only the binary on PATH (no Python)
  • Simulated npm graph: --version via launcher; init + check + a real git commit blocked with no Python and no global becwright (local devDependency only)
  • Workflow YAML validated; JS shim node --check; set-version.mjs deterministic
  • Real macOS/Windows/arm64 builds and npm publish — only run in CI on release

Before the first real release (maintainer)

Documented in documentation/releasing.md:

  1. Add NPM_TOKEN repo secret.
  2. Create the @becwright npm scope and own the unscoped becwright name.
  3. PyPI Trusted Publishing (already configured).

Note: the linux/arm64 binary builds on the public ubuntu-24.04-arm runner.

Summary by CodeRabbit

  • New Features

    • Added a new becwright run command for running built-in checks directly.
    • Added packaged binaries and npm distribution support for common platforms.
    • Added release workflows to publish binaries and packages automatically.
  • Bug Fixes

    • Built-in checks now support both the new becwright run ... form and the legacy command style for compatibility.
  • Documentation

    • Updated setup, usage, portability, and release docs in English and Spanish to match the new command and distribution options.

…ardcode python3

Generated rules and exported BECs now use 'becwright run <check>' instead of
'python3 -m becwright.checks.<check>'. The same command works whether becwright
is installed via pip, pipx or a standalone binary, which is the prerequisite for
distributing it on npm to users without Python.

- New 'becwright run <check> [args]' subcommand dispatching to built-in checks.
- materialize() and init's starter rules emit the new form.
- classify_check() still recognizes the legacy 'python3 -m ...' form on import.
- Migrate dogfood .bec/rules.yaml, READMEs and documentation; add back-compat note.
- Tests for the run subcommand and legacy-form back-compat.
Adds the tooling to ship becwright as a self-contained binary so users without
Python can install it (the npm packaging in the next phase consumes these
artifacts).

- packaging/becwright.spec: one-file PyInstaller build. Uses collect_submodules
  on becwright.checks because checks are imported dynamically by 'becwright run'
  and would otherwise be missed by static analysis.
- packaging/becwright_entry.py: absolute-import entry point for the freezer.
- src/becwright/__main__.py: enables 'python -m becwright'.
- pyproject: 'build' extra pinning pyinstaller.
- .github/workflows/build-binaries.yml: builds and smoke-tests the binary on
  linux x64/arm64, macOS x64/arm64 and Windows x64, uploading one artifact each.
Lets JS/web/backend developers install becwright without Python, using the
esbuild/ruff model: a thin launcher package plus os/cpu-gated optional
dependencies that each carry one prebuilt binary.

- npm/becwright: launcher package. bin/becwright.js resolves the platform binary
  via require.resolve and execs it. It prepends the binary's own directory to
  PATH so the engine's 'becwright run <check>' subprocess resolves even on a
  local-only (devDependency) install, where node_modules/.bin is not on PATH.
- npm/platforms/*: five per-platform packages (linux x64/arm64, darwin x64/arm64,
  win32 x64) with os/cpu fields; the binary is staged in by CI.
- npm/stage.mjs: copies a built binary into its platform package.
- git hook now prefers ./node_modules/.bin/becwright before a global becwright,
  so local installs work inside git hooks.

Verified locally end to end: with no Python and no global becwright, a
devDependency install runs init/check and a real 'git commit' is blocked.
A published release (tag vX.Y.Z) now builds the platform binaries, publishes the
npm packages and publishes to PyPI, all from one workflow.

- release.yml: on release, calls the build matrix (now a reusable workflow),
  stages each binary into its platform package, derives every npm version from
  the tag, publishes the platform packages then the launcher, and publishes to
  PyPI via Trusted Publishing.
- build-binaries.yml: switched to workflow_call (+ workflow_dispatch) so the
  release reuses it instead of double-building on the tag; publish.yml removed
  (folded into release.yml).
- npm/set-version.mjs: syncs the version across launcher + platform packages and
  the launcher's optionalDependencies. Platform package.json normalized to its
  canonical (stable) form.
- Document the release process and required NPM_TOKEN/scope setup; document
  npm/pnpm install in both READMEs.
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 83da1335-6941-4ba1-99fd-2daa320aca38

📥 Commits

Reviewing files that changed from the base of the PR and between f25c60f and c1ea31d.

📒 Files selected for processing (34)
  • .bec/rules.yaml
  • .github/workflows/build-binaries.yml
  • .github/workflows/publish.yml
  • .github/workflows/release.yml
  • .gitignore
  • README.es.md
  • README.md
  • documentation/portability.es.md
  • documentation/portability.md
  • documentation/releasing.es.md
  • documentation/releasing.md
  • documentation/usage.es.md
  • documentation/usage.md
  • documentation/writing-checks.es.md
  • documentation/writing-checks.md
  • npm/becwright/README.md
  • npm/becwright/bin/becwright.js
  • npm/becwright/package.json
  • npm/platforms/darwin-arm64/package.json
  • npm/platforms/darwin-x64/package.json
  • npm/platforms/linux-arm64/package.json
  • npm/platforms/linux-x64/package.json
  • npm/platforms/win32-x64/package.json
  • npm/set-version.mjs
  • npm/stage.mjs
  • packaging/becwright.spec
  • packaging/becwright_entry.py
  • pyproject.toml
  • src/becwright/__main__.py
  • src/becwright/bundle.py
  • src/becwright/cli.py
  • src/becwright/git.py
  • tests/test_bundle.py
  • tests/test_cli_and_git.py

📝 Walkthrough

Walkthrough

Adds a becwright run <check> CLI subcommand and migrates all built-in check invocations from python3 -m becwright.checks.* to it. Introduces PyInstaller binary packaging, five platform-scoped npm packages with a Node.js launcher, CI workflows for multi-platform binary builds and coordinated npm/PyPI releases, and updates the pre-commit hook to prefer a local npm-installed binary.

Changes

becwright run subcommand, bundle migration, hook update, and docs

Layer / File(s) Summary
CLI run subcommand and starter rule updates
src/becwright/cli.py, src/becwright/__main__.py
Adds _cmd_run function, wires a run subparser accepting module and trailing args, updates list text, and changes _starter_rules to emit becwright run ... commands. __main__.py is filled in as a sys.exit(main()) entrypoint.
Bundle regex and materialization migration
src/becwright/bundle.py
Expands _BUILTIN regex to match both becwright run <module> and legacy python3 -m becwright.checks.<module> forms, updates classify_check to extract module from either capture group, and changes materialize to emit becwright run <module>.
Pre-commit hook binary resolution
src/becwright/git.py
Updates _HOOK_CONTENT to check for ./node_modules/.bin/becwright first, fall back to a global becwright, and print an install error with exit 1 if neither is found.
Config, docs, and self-dogfooding rule updates
.bec/rules.yaml, README.md, README.es.md, documentation/portability*.md, documentation/usage*.md, documentation/writing-checks*.md, documentation/releasing*.md
Migrates all check: commands in rules and documentation examples from python3 -m becwright.checks.* to becwright run *; adds backward-compat notes for legacy form on import; adds npm/pipx install instructions; adds new release documentation (EN and ES).
Bundle and CLI tests
tests/test_bundle.py, tests/test_cli_and_git.py
Updates bundle classification and materialization assertions to the new command form; adds legacy-form tests; adds run subcommand tests for pattern matching, no-pattern, and unknown module (exit code 2).

npm binary distribution and CI release pipeline

Layer / File(s) Summary
PyInstaller binary build configuration
packaging/becwright.spec, packaging/becwright_entry.py, pyproject.toml
Adds a PyInstaller spec collecting becwright.checks submodules as hidden imports, an entry script calling main(), and pyinstaller>=6 as an optional build dependency.
npm launcher and platform packages
npm/becwright/..., npm/platforms/*/package.json, .gitignore
Adds the becwright npm launcher (package.json, bin/becwright.js, README) resolving the platform binary from @becwright/<os>-<arch>, and five platform-scoped package manifests (darwin-arm64, darwin-x64, linux-arm64, linux-x64, win32-x64). Adds .gitignore entries for staged binaries and node_modules/.
Release CI scripts and workflows
npm/stage.mjs, npm/set-version.mjs, .github/workflows/build-binaries.yml, .github/workflows/release.yml
Adds stage.mjs (copies PyInstaller binary into platform package directory) and set-version.mjs (patches all package.json versions); adds a matrix build-binaries.yml workflow; adds release.yml orchestrating binary builds, npm publishing, and PyPI OIDC publishing; removes the old publish.yml.

Sequence Diagram

sequenceDiagram
  participant Release as GitHub Release (published)
  participant BuildBinaries as build-binaries.yml
  participant NpmJob as release / npm job
  participant PyPIJob as release / pypi job
  participant NpmRegistry as npm registry
  participant PyPI as PyPI

  Release->>BuildBinaries: workflow_call (matrix: 5 platforms)
  BuildBinaries-->>NpmJob: upload dist/becwright[.exe] artifacts
  NpmJob->>NpmJob: stage.mjs → copy binary to npm/platforms/<target>/
  NpmJob->>NpmJob: set-version.mjs → patch package.json versions from release tag
  NpmJob->>NpmRegistry: npm publish `@becwright/`<platform> (×5)
  NpmJob->>NpmRegistry: npm publish becwright launcher
  Release->>PyPIJob: parallel trigger
  PyPIJob->>PyPI: pypa/gh-action-pypi-publish (OIDC)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • DataDave-Dev/becwright#1: Both PRs touch .github/workflows/publish.yml for PyPI Trusted Publishing—this PR removes it and folds the logic into release.yml.
  • DataDave-Dev/becwright#4: Both PRs modify .bec/rules.yaml self-dogfooding rules; the retrieved PR adds them and this PR migrates their check: commands to becwright run.
  • DataDave-Dev/becwright#12: Both PRs extend cli.py's argparse subcommand wiring—the retrieved PR adds becwright list, this PR adds becwright run.

Poem

🐇 Hoppity hop, the binary's neat,
No Python module to manually beat!
becwright run forbid — one command, done right,
npm bundles the binary, day and night.
From PyInstaller spec to npm land,
This rabbit ships releases, perfectly planned! 🚀

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/npm-distribution

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant