Skip to content

cli: add aubr/aubx multicall shims for run and dlx#6

Merged
jdx merged 3 commits intomainfrom
claude/elated-vaughan-b0009a
Apr 18, 2026
Merged

cli: add aubr/aubx multicall shims for run and dlx#6
jdx merged 3 commits intomainfrom
claude/elated-vaughan-b0009a

Conversation

@jdx
Copy link
Copy Markdown
Contributor

@jdx jdx commented Apr 18, 2026

Summary

  • aubr … dispatches to aube run … and aubx … to aube dlx …, purely via argv[0] basename detection in crates/aube-cli/src/main.rs — no new clap subcommands or second compile pass.
  • Shims are hardlinks to the same aube executable: zero disk cost, macOS codesign carries through via the shared inode, and basename dispatch stays transparent for flags (aubr --help shows run's help, etc.).
  • The mise build task now recreates target/debug/aubr / aubx after cargo build, and BATS common_setup refreshes them at the start of every test so the suite exercises the real shim path.
  • Unit tests cover the argv rewrite (including absolute paths and .exe suffixes); a new test/multicall_shims.bats file drives end-to-end smoke tests against the live binary.

Follow-up (not in this PR)

Release packaging and installer integration still need to ship the shims:

  • .github/workflows/release.yml currently uploads only aube — needs a post-step that hardlinks aubr/aubx into the archive (and copies on Windows, where hardlinks are impractical).
  • Homebrew formula, install.sh, cargo-binstall, winget/scoop manifests each need a two-line update to create the shims at install time.

Until those land, users enable the shims with a local ln as documented in the new README section.

Test plan

  • cargo test --workspace — 5 new multicall unit tests plus the existing suite green.
  • cargo clippy --all-targets -- -D warnings clean.
  • cargo fmt --check clean.
  • ./test/bats/bin/bats test/multicall_shims.bats — 4/4 pass.
  • ./test/bats/bin/bats test/run.bats test/dlx.bats — 21/21 still green after the common_setup change.

🤖 Generated with Claude Code


Note

Medium Risk
Introduces new entrypoints and rewrites CLI argv parsing based on argv[0], which could affect command dispatch/help output and release packaging across platforms if edge cases slip through.

Overview
Adds multicall CLI shims aubr and aubx that invoke the existing binary but auto-dispatch to aube run … and aube dlx … by rewriting argv based on argv[0].

Updates build/release and tests to ship and validate the shims: Cargo now builds three bins (with aubr/aubx stubs including main.rs), the release workflow uploads a single archive containing all three binaries per target, and the BATS harness creates hardlinked shims plus a new end-to-end multicall_shims.bats suite; docs/README are updated to document the shortcuts.

Reviewed by Cursor Bugbot for commit dc7a870. Bugbot is set up for automated code reviews on this repo. Configure here.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 18, 2026

Greptile Summary

This PR adds aubr and aubx as multicall shims for aube run and aube dlx respectively, implemented via argv[0] basename detection in main.rs — no new clap subcommands or second compile pass needed. Shims are hardlinks to the same aube binary, keeping disk cost and codesign implications zero.

Key changes:

  • rewrite_multicall_argv in main.rs intercepts argv[0], handles absolute paths, .exe suffixes, and case normalization, then splices the appropriate subcommand into args before clap parses them.
  • 5 new unit tests in multicall_tests cover the full dispatch matrix (passthrough, aubr→run, aubx→dlx, absolute path, .exe, bare invocation).
  • mise.toml build task hardlinks aubr/aubx after cargo build for local dev runs.
  • common_setup.bash refreshes the hardlinks at the start of every BATS test so the suite always exercises the real shim path.
  • A new test/multicall_shims.bats file provides 4 end-to-end smoke tests tagged serial (correct, since aubx hits the fixture registry).
  • README documents the feature and the manual ln workaround until installer integration lands.

Confidence Score: 4/5

Safe to merge; the core dispatch logic is correct and well-tested, with one non-blocking style note in the test helper.

The rewrite_multicall_argv implementation is correct (NLL borrow handling is sound, all edge cases covered by 5 unit tests + 4 BATS smoke tests). The only non-blocking concern is that 2>/dev/null in common_setup.bash silences ln errors that could surface as confusing 'command not found' failures. Release packaging is acknowledged as a follow-up and doesn't block this PR.

test/test_helper/common_setup.bash — the silent error suppression on ln -f is the only item worth a second look.

Important Files Changed

Filename Overview
crates/aube-cli/src/main.rs Adds rewrite_multicall_argv function and multicall_tests module; the dispatch logic is correct, NLL borrow handling is sound, and all key edge cases (absolute paths, .exe suffix, uppercase, bare invocation) are covered by unit tests.
test/multicall_shims.bats New BATS smoke-test file with 4 end-to-end tests covering aubr script execution, aubr no-arg error, aubx binary run, and aubx flag forwarding; tagged serial which is correct since aubx hits the fixture registry.
test/test_helper/common_setup.bash Adds hardlink refresh of aubr/aubx before every test; silences ln errors with `2>/dev/null
mise.toml Adds ln -f steps to the build task to create aubr/aubx hardlinks; Unix/macOS only but Windows limitations are acknowledged in the PR description as a follow-up.
README.md Adds 'Shortcuts: aubr and aubx' section with a clear explanation and manual ln instructions; documentation accurately reflects the current state of the feature.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Process starts\nstd::env::args_os()"] --> B["rewrite_multicall_argv(args)"]
    B --> C{argv0 basename\nafter file_stem +\nto_ascii_lowercase}
    C -- "aubr" --> D["Insert 'run' at args[1]\nargs[0] = 'aube'"]
    C -- "aubx" --> E["Insert 'dlx' at args[1]\nargs[0] = 'aube'"]
    C -- "aube / other" --> F["Pass args unchanged"]
    D --> G["Cli::parse_from(args)\n→ Commands::Run(...)"]
    E --> H["Cli::parse_from(args)\n→ Commands::Dlx(...)"]
    F --> I["Cli::parse_from(args)\n→ normal dispatch"]
Loading

Fix All in Claude Code

Reviews (2): Last reviewed commit: "cli: add aubr/aubx multicall shims for `..." | Re-trigger Greptile

jdx and others added 2 commits April 18, 2026 15:08
Dispatch happens in `main.rs` via `argv[0]` basename: invocations as
`aubr …` rewrite to `aube run …`, `aubx …` to `aube dlx …`. Shims are
hardlinks to the same `aube` executable (so size cost is zero and
macOS codesign carries through) created by the `mise build` task and
refreshed in BATS `common_setup` on every run.

Follow-up: teach the release workflow and each install channel
(Homebrew formula, install.sh, cargo-binstall, winget) to materialize
the shims at distribution time — until then users enable them with a
local `ln` as documented in the README.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…archives

Switch from the hardlink-at-install approach to real Cargo bin targets:
`aubr` and `aubx` get `[[bin]]` entries pointing at tiny `src/bin/*.rs`
stubs that `include!("../main.rs")`, so they share the argv[0] dispatch
with `aube` while keeping Cargo from warning about duplicate source
paths. `test = false` on the shim bins keeps `cargo test` from running
the shared suite three times.

Release workflow now calls `taiki-e/upload-rust-binary-action` in
multi-bin mode (`bin: aube,aubr,aubx`, `archive: aube-$tag-$target`),
so every target's single tarball carries all three binaries. macOS
codesign applies uniformly via `codesign_prefix: dev.jdx.`.

Drop the mise/BATS hardlink plumbing: since Cargo now produces the
shim binaries natively at `target/debug/{aubr,aubx}`, nothing needs to
be faked in either the `mise build` task or the BATS `common_setup`.
Document the shortcut in `docs/package-manager/scripts.md` alongside
the existing `run` / `exec` / `dlx` coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jdx jdx force-pushed the claude/elated-vaughan-b0009a branch from 5443aa1 to 45660c9 Compare April 18, 2026 20:09
Local `cargo build` produces all three multicall binaries natively, but
CI only uploads `target/debug/aube` as a Buildkite artifact — the bats
shards then download just `aube` and call `aubr`/`aubx` through PATH,
which fails with command-not-found (seen on
https://buildkite.com/endev/aube/builds/17).

Restore the idempotent `ln -f` fallback in common_setup.bash that I
dropped in the previous commit. It's a no-op when the shims already
exist (e.g. after a local `cargo build`) and reconstructs them from
the downloaded `aube` artifact in CI. Cheaper than teaching every
artifact-download step in `.buildkite/pipeline.sh` to fetch three
identical 72 MB binaries.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jdx jdx merged commit 3219e21 into main Apr 18, 2026
16 checks passed
@jdx jdx deleted the claude/elated-vaughan-b0009a branch April 18, 2026 20:19
jdx added a commit that referenced this pull request Apr 18, 2026
## Summary

- Add a preinstall-based npm distribution alongside the existing
mise/cargo/curl paths. Root package is `@endevco/aube`; six per-platform
subs are `@endevco/aube-<os>-<arch>` (darwin-arm64, darwin-x64,
linux-x64, linux-arm64, win32-x64, win32-arm64).
- New standalone workflow
[`publish-npm.yml`](.github/workflows/publish-npm.yml) triggers on
`release: published` (and `workflow_dispatch` for reruns). Decoupled
from `release-plz.yml` so an npm hiccup never blocks crates.io or the
GitHub release.
- Auth via npm **Trusted Publishing (OIDC)** — no `NPM_TOKEN` secret.

## How it works

At install time, `@endevco/aube`'s [`preinstall`
script](npm/installArchSpecificPackage.js) spawns `npm install --no-save
@endevco/aube-<os>-<arch>@<version>` and hardlinks (falling back to
copy) the three binaries (`aube`, `aubr`, `aubx`) from the sub-package's
`bin/` into the root's `./bin/`. Shape mirrors
[`@jdxcode/mise`](https://www.npmjs.com/package/@jdxcode/mise) — no
runtime JS shim and no `optionalDependencies` sprawl in
`package-lock.json`. Note this means `--ignore-scripts` and fully
offline caches won't work; those users keep the mise/cargo paths.

The multicall dispatch from [#6](#6)
works through npm because the preinstall creates three named files, so
`aubr` invoked via npm's bin wrapper sees `argv[0]` ending in `aubr` and
routes to `run`.

At release time, [`npm/scripts/publish.mjs`](npm/scripts/publish.mjs)
downloads each `aube-<tag>-<target>.{tar.gz,zip}` from the
just-published GitHub release, extracts the binaries, stages a
platform-scoped `package.json` with correct `os`/`cpu`/`bin`, and `npm
publish`es each sub-package. Root publishes last so its preinstall can
resolve every sub. Auto-picks the `next` dist-tag for pre-releases
(`1.0.0-beta.1` → `next`) and `latest` for stable. `DRY_RUN=1`,
`SKIP_ROOT=1`, and `SKIP_PLATFORMS=1` env flags exist for manual
recovery.

## Why OIDC

npm's Trusted Publishing (GA mid-2025) exchanges a short-lived GitHub
OIDC token for a one-shot npm publish token. No long-lived secret to
rotate or leak, and the publish is provenance-signed. Requires npm ≥
11.5.1 — the workflow upgrades to `npm@latest` before publishing to
avoid drift with the version Node 24 ships.

## Pre-merge setup

On [npmjs.com/org/endevco](https://www.npmjs.com/org/endevco) → Settings
→ Trusted Publishers, add an org-level trusted publisher:
- Repo: `endevco/aube`
- Workflow: `.github/workflows/publish-npm.yml`
- Environment: (blank)

Org-level config covers all new `@endevco/*` packages, so the first
release auto-covers root + 6 subs.

## Limitations / follow-ups

- `aube` (unscoped) is taken on npm by a year-old placeholder
(`estjs/aube`, 1 version, never updated). If you want the unscoped name,
file an [npm dispute](https://docs.npmjs.com/policies/disputes) —
unrelated to this PR.
- No Alpine / musl package yet. Linux users on glibc distros get
`linux-<arch>`; muslc users will hit the glibc binary and fail. Adding
`linux-x64-musl` / `linux-arm64-musl` needs corresponding Rust release
targets first.
- Retrying after partial publish failure: same-version republishes
return 403. Recovery is running the workflow manually with
`SKIP_PLATFORMS=1` (or vice versa) to publish only what hasn't shipped.

## Test plan

- [x] Built `aube`, `aubr`, `aubx` locally; staged a fake
`@endevco/aube-darwin-arm64` sub-package under `node_modules/`; ran the
link logic and confirmed each bin dispatches to the right subcommand via
`argv[0]` basename (`aubr --help` → `run`'s help, `aubx --help` →
`dlx`'s help).
- [x] `npm pack --dry-run` on the root package — tarball contains only
`installArchSpecificPackage.js` + `package.json` (README copied in by
publish script at release time).
- [x] `npm pack --dry-run` on a hand-staged platform package — contains
`bin/{aube,aubr,aubx}`, `package.json` with correct `os`/`cpu`,
`README.md`.
- [ ] End-to-end first publish under `1.0.0-beta.X` with `next`
dist-tag, then `npm install -g @endevco/aube@next` from a clean machine.
Runs for real on the next release.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Adds a new release-triggered GitHub Actions workflow and
`preinstall`-driven npm packaging/publishing logic, which can impact
release automation and end-user installation behavior if misconfigured.
> 
> **Overview**
> Adds npm distribution for `aube` by introducing a root `@endevco/aube`
package that installs a platform-specific `@endevco/aube-<os>-<arch>`
subpackage at `preinstall` time and links/copies the native
`aube`/`aubr`/`aubx` binaries into `./bin`.
> 
> Introduces a new `publish-npm` GitHub Actions workflow that runs on
`release: published` (or manual tag input) and uses npm Trusted
Publishing (OIDC) to download release artifacts, stage per-platform npm
packages, and publish them before publishing the root package.
Documentation is updated to mention `npm install -g @endevco/aube` as an
install option.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
cf71251. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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