Skip to content

feat(downloader): rank asset variants for the host - #136

Merged
roxblnfk merged 14 commits into
1.xfrom
feat/asset-ranking
Oct 6, 2026
Merged

roxblnfk merged 14 commits into
1.xfrom
feat/asset-ranking

Conversation

@roxblnfk

@roxblnfk roxblnfk commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

🔍 What was changed

dload now ranks the assets that fit the host instead of taking the first one the API lists, so a broad asset-pattern picks the right build among variants:

  • Android builds are never picked on Linux. On Android, Linux builds stay a fallback.
  • ARM macOS and Windows fall back to x86-64 builds, which Rosetta 2 and the Windows emulation run, with a warning that the build needs an emulator.
  • libc: on a musl host (detected by the musl loader, or --os alpine) and on Android musl builds come first; elsewhere they come last but stay available, since some tools ship only a static musl build. The host is probed only when a release offers both kinds.
  • Plain build first: the asset with the fewest name tokens beyond the platform, version and extension wins, so -baseline, -profile and -debug builds become fallbacks without a list of variant names. This also stops TigerBeetle from installing its debug build.
  • Checksums, signatures and SBOMs are never selected.
  • The Bun registry entry uses /^bun-.*/ instead of a pattern spelling out its asset names.

How it works

  • Selection is a pipeline of rules (src/Module/Downloader/Internal/AssetSelection/Rule). A rule either removes an asset, judging it alone, or gives every asset a named rank.
  • Ranks compare in pipeline order, so a later rule only orders assets the earlier ones left equal: OS → arch → libc → extra tokens → archive → release order.
  • Strict and gradual selection are one pipeline: without a binary config, OS and arch rank instead of remove, which keeps the old fallback order. -vvv logs every asset's ranks.

Why?

Variant suffixes were invisible: with /^bun-.*/ every host got a -profile build and Linux got the Android one.

Checklist

Review notes

  • OperatingSystem::Alpine is gone: it was never detected (PHP_OS is Linux on Alpine), and as an OS it would have excluded every plain Linux build. --os alpine / unknown-musl now mean Linux with musl.
  • The pipeline is Testo's interceptor chain copied into src/Module/Common/Pipeline without ordering and conflict options: AssetSelector lists the rules, and that order is the priority of their ranks.
  • Known gaps (universal macOS builds, platform words in tool names, amd64_v3) are in Asset ranking: platform words in tool names, universal macOS builds, amd64 levels #137.

@github-actions github-actions Bot added enhancement New feature or request tests labels Oct 5, 2026
@codecov

codecov Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
src/Bootstrap.php 100.00% <100.00%> (ø)
src/Command/Get.php 42.69% <100.00%> (+1.99%) ⬆️
src/Module/Common/Libc.php 100.00% <100.00%> (ø)
src/Module/Common/OperatingSystem.php 94.73% <100.00%> (+8.07%) ⬆️
src/Module/Common/Pipeline/Pipeline.php 100.00% <100.00%> (ø)
src/Module/Downloader/Downloader.php 100.00% <100.00%> (ø)
...e/Downloader/Internal/AssetSelection/AssetName.php 100.00% <100.00%> (ø)
...wnloader/Internal/AssetSelection/AssetSelector.php 100.00% <100.00%> (ø)
...e/Downloader/Internal/AssetSelection/Candidate.php 100.00% <100.00%> (ø)
.../Internal/AssetSelection/Rule/ArchitectureRule.php 100.00% <100.00%> (ø)
... and 8 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Rules either remove an asset or rank it, and ranks compare in pipeline order, so a decision that depends on the other assets never removes anything. The gradual search becomes the same pipeline with OS and architecture ranked instead of required, which keeps its old order and tries each asset once. The pipeline is the interceptor chain from Testo without its ordering and conflict options: the rules run in the order the selector lists them, which is the priority of their ranks.

Assisted-By: Claude Opus 5.5
An `android` token in an asset name now wins over `linux`, so Android builds are no longer picked on a Linux host. On an Android host (detected by `ANDROID_ROOT`) Linux builds stay a fallback, since static Linux binaries run there.

Assisted-By: Claude Opus 5.5
A musl host is detected by the presence of the musl dynamic loader, or set with `--os alpine`. musl builds rank first there and last elsewhere, but are never removed: some tools publish only a static musl build for Linux. Alpine is no longer a separate OS: it was never detected, and treating it as one would have excluded every plain Linux build.

Assisted-By: Claude Opus 5.5
feat(registry): select Bun assets with a broad pattern

Among assets that fit the host, the one with the fewest name tokens beyond the platform, version and extension wins, so `-baseline`, `-profile` and `-debug` builds become fallbacks without a list of variant names. This also stops TigerBeetle from installing its debug build.

Closes #134

Assisted-By: Claude Opus 5.5
@roxblnfk
roxblnfk force-pushed the feat/asset-ranking branch from f1ca3e4 to 426fe1b Compare October 5, 2026 18:54

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Coverage metadata references two nonexistent test-namespace classes, and the new automatic host-detection branches remain untested.

Review effort: Balanced
Findings: 1 High severity · 2 Low severity

Open (3)
What changed in this PR

Introduces host-aware asset ranking so the downloader selects the best OS, architecture, libc, and build variant.

Changes:

  • Adds a reusable ranking pipeline and selection rules.
  • Detects Android and musl hosts and broadens Bun asset matching.
  • Adds selection tests, diagnostics, and documentation.
File Description
tests/​.../​Stub/​RuleRunner.php Adds rule test helper.
tests/​.../​Stub/​NamedAssets.php Creates named asset fixtures.
tests/​.../​SelectionTest.php Tests ranking and removal.
tests/​.../​Rule/​OperatingSystemRuleTest.php Tests OS selection.
tests/​.../​Rule/​NamePatternRuleTest.php Tests pattern filtering.
tests/​.../​Rule/​LibcRuleTest.php Tests libc ranking.
tests/​.../​Rule/​FormatRuleTest.php Tests format filtering.
tests/​.../​Rule/​ExtrasRuleTest.php Tests variant ranking.
tests/​.../​Rule/​ArchiveRuleTest.php Tests archive preference.
tests/​.../​Rule/​ArchitectureRuleTest.php Tests architecture selection.
tests/​.../​AssetSelectorTest.php Tests the complete pipeline.
tests/​.../​AssetNameTest.php Tests filename parsing.
tests/​Unit/​Module/​Downloader/​DownloaderTest.php Injects the selector in fixtures.
tests/​Unit/​Module/​Downloader/​AssetSelectionTest.php Adds Bun, Mago, and TigerBeetle cases.
tests/​.../​Pipeline/​Stub/​TraceInterceptor.php Adds tracing interceptor fixture.
tests/​.../​Pipeline/​Stub/​HaltInterceptor.php Adds halting interceptor fixture.
tests/​.../​Pipeline/​PipelineTest.php Tests pipeline behavior.
tests/​.../​OperatingSystemTest.php Tests Android and musl OS parsing.
tests/​.../​LibcTest.php Tests libc parsing and configuration.
src/​.../​AssetSelection/​Selection.php Stores and sorts ranked candidates.
src/​.../​Rule/​OperatingSystemRule.php Ranks compatible operating systems.
src/​.../​Rule/​NamePatternRule.php Filters names by pattern.
src/​.../​Rule/​LibcRule.php Ranks libc compatibility.
src/​.../​Rule/​FormatRule.php Filters accepted formats.
src/​.../​Rule/​ExtrasRule.php Prefers fewer variant tokens.
src/​.../​Rule/​ArchiveRule.php Prefers supported archives.
src/​.../​Rule/​ArchitectureRule.php Ranks host architecture.
src/​.../​AssetSelection/​Candidate.php Represents ranked assets.
src/​.../​AssetSelection/​AssetSelector.php Defines rule priority.
src/​.../​AssetSelection/​AssetRule.php Defines the rule contract.
src/​.../​AssetSelection/​AssetName.php Parses asset traits.
src/​Module/​Downloader/​Downloader.php Integrates ranked selection.
src/​.../​Pipeline/​Pipeline.php Implements interceptor execution.
src/​.../​Pipeline/​Interceptor.php Defines the interceptor base.
src/​Module/​Common/​OperatingSystem.php Adds Android and Alpine mapping.
src/​Module/​Common/​Libc.php Adds libc detection.
src/​Command/​Get.php Displays musl host information.
src/​Bootstrap.php Registers libc dependency.
skills/​.../​troubleshooting.md Documents ranking diagnostics.
skills/​.../​registry-entry.md Documents asset variant rules.
resources/​software.json Broadens Bun’s asset pattern.
README.md Documents new OS options.
README-zh.md Updates Chinese OS options.
README-ru.md Updates Russian OS options.
README-es.md Updates Spanish OS options.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/Module/Common/Libc.php Outdated
Comment thread src/Module/Common/OperatingSystem.php Outdated
fix(downloader): do not detect musl when the loader lookup fails

Host detection takes the OS family and the probed facts as arguments, so the Android and musl branches are tested on any host. `glob()` reports an error as `false`, which the old check took for a found loader.

Assisted-By: Claude Opus 5.5
@roxblnfk
roxblnfk force-pushed the feat/asset-ranking branch 2 times, most recently from 20f6585 to c7f8c71 Compare October 6, 2026 06:53
The libc rule takes the host libc from the container only when the remaining assets differ in libc, so selecting assets through the API does not probe the host for nothing.

Assisted-By: Claude Opus 5.5
Rosetta 2 and the Windows emulation run x86-64 binaries on ARM, so such a build is no longer discarded when a release has no ARM build for the host. Linux is left out: it needs qemu-user set up to do the same.

Assisted-By: Claude Opus 5.5
@roxblnfk
roxblnfk force-pushed the feat/asset-ranking branch from c7f8c71 to f8b6823 Compare October 6, 2026 06:55
A companion file carries the platform of the build it describes, so it could outrank a build of another libc or variant, and downloading it succeeds, which ended the search with a file that is not the tool.

Assisted-By: Claude Opus 5.5
Android has its own libc, which runs no glibc build, while musl builds are usually static. A configured OS other than Linux now also yields glibc instead of probing the host for musl.

Assisted-By: Claude Opus 5.5
docs(skill): describe when Android builds are dropped

`PREG_NO_ERROR` is an error code, not a flag; the `@` already silences an invalid pattern. Android builds are only dropped on Linux in a strict selection, and Android hosts now prefer musl builds.

Assisted-By: Claude Opus 5.5
Rosetta 2 is optional on macOS and Windows 10 on ARM has no x64 emulation, so a fallback build may not run.

Assisted-By: Claude Opus 5.5
feat(get): show the libc of Linux and Android hosts

Android gets its own libc instead of posing as musl, and Windows, macOS and BSD get a system libc that ranks no build above another. Which builds a libc prefers now lives in the enum, so the rule only asks it.

Assisted-By: Claude Opus 5.5
@roxblnfk
roxblnfk merged commit 2e08478 into 1.x Oct 6, 2026
21 checks passed
@roxblnfk
roxblnfk deleted the feat/asset-ranking branch October 6, 2026 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prefer the plain build among asset variants (musl, android, baseline)

2 participants