Skip to content

bunx: support anonymous URL packages - #37283

Open
kjanat wants to merge 7 commits into
oven-sh:mainfrom
kjanat:fix/bunx-url-package
Open

bunx: support anonymous URL packages#37283
kjanat wants to merge 7 commits into
oven-sh:mainfrom
kjanat:fix/bunx-url-package

Conversation

@kjanat

@kjanat kjanat commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

bunx <tarball-url> fails with unrecognised dependency format: @https://... because a remote tarball provides no package or bin name before installation. This PR installs URL specifiers under a deterministic internal alias and then uses package-owned bin discovery to select and link the executable from the installed package's package.json.

Fixes #3675

Stacked on #37150. The last two commits are new here; the first five belong to that PR, whose bin-resolution machinery this builds on. It rebases onto main once #37150 lands.

  • bunx: support anonymous URL packages (currently 9244332)
  • bunx: log the binary selected after install (currently 256f152) — the four post-install execution paths ran without a scoped_log!, so a BUN_DEBUG_bunx trace ended at the install and never showed which executable was chosen.

How did you verify your code works?

  • test/cli/install/bunx.test.ts covers bun x and bunx cold installs, warm cache reuse, and --no-install errors against a hermetic opaque-URL fixture (no package name derivable from the URL).
  • bun bd test test/cli/install/bunx.test.ts: 50 pass, 1 skip, 0 fail.
  • New tests fail with USE_SYSTEM_BUN=1.
  • A tarball URL carrying a query string (?x=y, the shape from Bun isolated linker misresolves packages installed from tarball URLs with query strings #36987) resolves and runs: the alias hashes the whole URL, so the query never becomes a path component and keeps its own cache entry.

kjanat added 6 commits August 7, 2026 17:38
Give concurrent bunx tests independent registry handlers, request counters,
and package directories so one case cannot consume another case's fixture
state. Dispose temporary install/cache directories after the suite instead
of leaving debug binaries behind.

Also make the user-agent assertion independent of inherited npm config and
the debug-only version suffix.
When multiple packages expose the same bin, the shared node_modules/.bin
winner may not belong to the package selected by bunx. Resolve requested
and default bins from the named package's package.json for local and bunx
cache installs, then use the installer's validated cross-platform linker.

Keep explicit package selection from falling through to unrelated bins.
Preserve native-binlink redirects only when the installed target matches
the declaring optional dependency's name, version, and platform, and share
the installer's fallback policy through Linker.

Cover explicit and implicit bunx forms, warm and cold caches, hoisted and
isolated linkers, unsafe or missing bins, and native redirect fallback.
- validate package name before building node_modules paths; make
  alias_is_safe_install_target pub for the bunx-side guard
- return PathTooLong instead of panicking when a formatted path exactly
  fills its buffer; same for three pre-existing expect("unreachable") sites
- skip a native-binlink candidate on path overflow instead of aborting
  the whole search
- capture abs_dest_buf pointer before writes, matching link()
- tests: drain piped stdout, parameterize bin name, assert bin output and
  exit codes, chmodSync over shell chmod
- rebuild my-special-pkg/actual-package/runner-pkg fixture tarballs:
  oven-sh#21517 shipped them with a literal '#\!' shebang (shell history-escaping
  artifact) plus macOS ._ AppleDouble entries, so executing their bin
  always failed with ENOEXEC; its assertions never checked the run, so
  the three package-selection tests passed while exercising nothing
- resolve_installed_native_binlink_target derived the real node_modules
  dir with a single dirname; for a scoped package that lands on the
  @scope directory, so the realpath candidate never matched and the
  isolated linker fell back to the main package bin. Strip one more
  component for scoped names. Covered by new scoped fixtures
  (`@binlink-scope/test-native-binlink`) and a per-linker bunx test
  that fails on the isolated linker without the fix.
- a probe failure (unreadable node_modules, malformed package.json,
  failed symlink) aborted bunx before the install fallback could repair
  it; probes now degrade to PackageNotFound with a debug log
- log 'refusing untrusted cached binary' in the post-install lookups,
  matching every other trust check in exec()
- cold-cache test: assert exactly one bunx cache entry instead of
  taking the first match
- the isolated installer's retry-without-native-binlink kept the first
  attempt's err; link_bin_or_create_shim treats a pre-set err as its own
  failure and unlinks the retry's symlink, so the retry was a no-op
  whenever err (not skipped_due_to_missing_bin) triggered it. Reset both
  fields in both retry blocks, matching bin.rs link_package_bin. The
  hoisted installer rebuilds its Linker per iteration and was unaffected.
- dummy registry: extract the duplicated version-map construction into
  buildVersions, note that 'latest' falls back to insertion order
- scoped-fixture generator: clean up temp dirs in a finally block
Remote tarball URLs do not provide a package or bin name before
installation. Install them under a deterministic internal alias, then
use package-owned bin discovery to select and link the executable from
package.json.

Cover `bun x` and `bunx` cold installs, warm cache reuse, and
`--no-install` errors with a hermetic opaque URL fixture.

Fixes oven-sh#3675
The post-install execution paths ran without a scoped log, so a
BUN_DEBUG_bunx trace ended at the install and never showed which
executable was chosen. Only the rejecting branches logged.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@kjanat

kjanat commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

Conflicts with #39403 (claude/install-mega), 8 hunks:

  • bin.rs (2): their seen.remove() before the early return, my return false
  • PackageInstaller.rs: they rewrote the guard in alias_is_safe_install_target, I only widened it to pub
  • postinstall_optimizer.rs: their added libc check, my extracted is_native_binlink_replacement
  • bunx_command.rs: their directories.bin validation from bunx: resolve directories.bin relative to the package #39096 and my wanted-bin fast path in the same block
  • dummy.registry.ts (2): their manifestVersionInfo() inside the version loop I replaced with buildVersions()
  • bun-install-native-binlink.test.ts: their added case, my dropped as const

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.

bun x throws error: unrecognised dependency format

1 participant