mirror of
https://github.com/webinstall/webi-installers.git
synced 2026-04-06 18:36:50 +00:00
fix(classifypkg): tag Rust static musl builds as libc='none'
Rust *-unknown-linux-musl builds are statically linked with zero runtime libc dependency. Detect this pattern in classifyGitHub and override libc from 'musl' to 'none'. Hard-musl packages (pwsh, bun, node) use different filename patterns and keep libc='musl'.
This commit is contained in:
@@ -14,6 +14,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"regexp"
|
||||
|
||||
"github.com/webinstall/webi-installers/internal/buildmeta"
|
||||
"github.com/webinstall/webi-installers/internal/classify"
|
||||
"github.com/webinstall/webi-installers/internal/installerconf"
|
||||
"github.com/webinstall/webi-installers/internal/rawcache"
|
||||
@@ -305,6 +308,13 @@ type ghAsset struct {
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
// reRustMuslStatic matches Rust target triples that indicate a statically-linked
|
||||
// musl build. Rust's *-unknown-linux-musl targets are always static — they have
|
||||
// zero runtime libc dependency. This is distinct from packages like pwsh
|
||||
// (-linux-musl-x64), bun (-linux-x64-musl), and node (-linux-x64-musl) which
|
||||
// dynamically link against musl and require it at runtime.
|
||||
var reRustMuslStatic = regexp.MustCompile(`(?i)-unknown-linux-musl`)
|
||||
|
||||
func classifyGitHub(pkg string, conf *installerconf.Conf, d *rawcache.Dir) ([]storage.Asset, error) {
|
||||
tagPrefix := conf.TagPrefix
|
||||
releases, err := ReadAllRaw(d)
|
||||
@@ -356,13 +366,19 @@ func classifyGitHub(pkg string, conf *installerconf.Conf, d *rawcache.Dir) ([]st
|
||||
name = name[:len(name)-4] + ".tar.gz"
|
||||
}
|
||||
|
||||
libc := r.Libc
|
||||
// Rust static musl builds have zero runtime libc dependency.
|
||||
if libc == buildmeta.LibcMusl && reRustMuslStatic.MatchString(a.Name) {
|
||||
libc = buildmeta.LibcNone
|
||||
}
|
||||
|
||||
assets = append(assets, storage.Asset{
|
||||
Filename: name,
|
||||
Version: version,
|
||||
Channel: channel,
|
||||
OS: string(r.OS),
|
||||
Arch: string(r.Arch),
|
||||
Libc: string(r.Libc),
|
||||
Libc: string(libc),
|
||||
Format: string(r.Format),
|
||||
Download: a.BrowserDownloadURL,
|
||||
Date: date,
|
||||
|
||||
@@ -320,26 +320,29 @@ func TestResolvePosixPackages(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestResolveLibcPreference tests musl vs gnu selection.
|
||||
// TestResolveLibcPreference tests libc selection.
|
||||
// bat is a Rust project — its musl builds are static (libc='none').
|
||||
// pwsh has hard musl dependencies (libc='musl').
|
||||
func TestResolveLibcPreference(t *testing.T) {
|
||||
dists := loadCacheDists(t, "bat")
|
||||
batDists := loadCacheDists(t, "bat")
|
||||
|
||||
// Explicit musl request.
|
||||
m := resolve.Best(dists, resolve.Query{
|
||||
// Musl host requesting bat: gets the static musl build (tagged 'none').
|
||||
m := resolve.Best(batDists, resolve.Query{
|
||||
OS: buildmeta.OSLinux,
|
||||
Arch: buildmeta.ArchAMD64,
|
||||
Libc: buildmeta.LibcMusl,
|
||||
Formats: []string{".tar.gz"},
|
||||
})
|
||||
if m == nil {
|
||||
t.Fatal("expected musl match")
|
||||
t.Fatal("expected match for musl host")
|
||||
}
|
||||
if m.Libc != "musl" {
|
||||
t.Errorf("Libc = %q, want musl", m.Libc)
|
||||
// Rust musl builds are static — tagged as 'none', not 'musl'.
|
||||
if m.Libc != "none" {
|
||||
t.Errorf("bat musl request: Libc = %q, want none (static musl)", m.Libc)
|
||||
}
|
||||
|
||||
// Explicit gnu request.
|
||||
m = resolve.Best(dists, resolve.Query{
|
||||
m = resolve.Best(batDists, resolve.Query{
|
||||
OS: buildmeta.OSLinux,
|
||||
Arch: buildmeta.ArchAMD64,
|
||||
Libc: buildmeta.LibcGNU,
|
||||
@@ -352,8 +355,8 @@ func TestResolveLibcPreference(t *testing.T) {
|
||||
t.Errorf("Libc = %q, want gnu", m.Libc)
|
||||
}
|
||||
|
||||
// No preference — should still match (accepts either).
|
||||
m = resolve.Best(dists, resolve.Query{
|
||||
// No preference — should still match (accepts any).
|
||||
m = resolve.Best(batDists, resolve.Query{
|
||||
OS: buildmeta.OSLinux,
|
||||
Arch: buildmeta.ArchAMD64,
|
||||
Formats: []string{".tar.gz"},
|
||||
@@ -361,6 +364,21 @@ func TestResolveLibcPreference(t *testing.T) {
|
||||
if m == nil {
|
||||
t.Fatal("expected match with no libc preference")
|
||||
}
|
||||
|
||||
// pwsh has hard musl builds (dynamically linked, requires musl runtime).
|
||||
pwshDists := loadCacheDists(t, "pwsh")
|
||||
m = resolve.Best(pwshDists, resolve.Query{
|
||||
OS: buildmeta.OSLinux,
|
||||
Arch: buildmeta.ArchAMD64,
|
||||
Libc: buildmeta.LibcMusl,
|
||||
Formats: []string{".tar.gz"},
|
||||
})
|
||||
if m == nil {
|
||||
t.Fatal("expected pwsh musl match")
|
||||
}
|
||||
if m.Libc != "musl" {
|
||||
t.Errorf("pwsh musl request: Libc = %q, want musl", m.Libc)
|
||||
}
|
||||
}
|
||||
|
||||
// TestResolveFormatFallback tests format preference cascading.
|
||||
|
||||
@@ -250,20 +250,21 @@ func TestCacheGitPackages(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestCacheLibcPreference tests explicit libc selection.
|
||||
// bat is Rust — its musl builds are static (tagged 'none').
|
||||
func TestCacheLibcPreference(t *testing.T) {
|
||||
assets := loadAssets(t, "bat")
|
||||
|
||||
// Explicit musl.
|
||||
// Musl host requesting bat: gets static musl build (tagged 'none').
|
||||
res, err := resolver.Resolve(assets, resolver.Request{
|
||||
OS: "linux",
|
||||
Arch: "x86_64",
|
||||
Libc: "musl",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal("expected musl match")
|
||||
t.Fatal("expected match for musl host")
|
||||
}
|
||||
if res.Asset.Libc != "musl" {
|
||||
t.Errorf("Libc = %q, want musl", res.Asset.Libc)
|
||||
if res.Asset.Libc != "none" {
|
||||
t.Errorf("Libc = %q, want none (static musl)", res.Asset.Libc)
|
||||
}
|
||||
|
||||
// Explicit gnu.
|
||||
|
||||
Reference in New Issue
Block a user