mirror of
https://github.com/webinstall/webi-installers.git
synced 2026-04-06 18:36:50 +00:00
ref: remove universal fallback chains from buildmeta and platlatest
Arch and libc fallbacks are not universal — they depend on the OS, the package, and even the version. ARM64 on macOS/Windows can run x64 (Rosetta/emulation) but not on Linux. Musl can be static or dynamically linked depending on the package version. Windows GNU may or may not need mingw. These rules belong in per-installer config, not in shared types. platlatest stays as a simple fact store (triplet → version). Resolution with fallbacks will be the caller's job.
This commit is contained in:
@@ -102,40 +102,3 @@ func (t Target) Triplet() string {
|
||||
return string(t.OS) + "-" + string(t.Arch) + "-" + string(t.Libc)
|
||||
}
|
||||
|
||||
// ArchFallbacks returns the architectures that a machine with the given
|
||||
// arch can run, ordered from most specific to least. The input arch is
|
||||
// always first. Returns nil for unknown architectures.
|
||||
//
|
||||
// For example, an amd64v4 machine can run v4, v3, v2, and baseline (v1)
|
||||
// binaries. An armv7 machine can run armv7 and armv6 binaries.
|
||||
func ArchFallbacks(arch Arch) []Arch {
|
||||
switch arch {
|
||||
case ArchAMD64v4:
|
||||
return []Arch{ArchAMD64v4, ArchAMD64v3, ArchAMD64v2, ArchAMD64}
|
||||
case ArchAMD64v3:
|
||||
return []Arch{ArchAMD64v3, ArchAMD64v2, ArchAMD64}
|
||||
case ArchAMD64v2:
|
||||
return []Arch{ArchAMD64v2, ArchAMD64}
|
||||
case ArchARMv7:
|
||||
return []Arch{ArchARMv7, ArchARMv6}
|
||||
default:
|
||||
// No fallback chain — exact match only.
|
||||
return []Arch{arch}
|
||||
}
|
||||
}
|
||||
|
||||
// LibcFallbacks returns the libc variants a machine can use, ordered
|
||||
// by preference. A musl system can only run musl or static binaries.
|
||||
// A glibc system can only run glibc or static binaries.
|
||||
func LibcFallbacks(libc Libc) []Libc {
|
||||
switch libc {
|
||||
case LibcGNU:
|
||||
return []Libc{LibcGNU, LibcNone}
|
||||
case LibcMusl:
|
||||
return []Libc{LibcMusl, LibcNone}
|
||||
case LibcMSVC:
|
||||
return []Libc{LibcMSVC, LibcNone}
|
||||
default:
|
||||
return []Libc{libc}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/webinstall/webi-installers/internal/buildmeta"
|
||||
"github.com/webinstall/webi-installers/internal/lexver"
|
||||
)
|
||||
|
||||
// Index tracks the latest version for each build target of a package.
|
||||
@@ -79,41 +78,6 @@ func (idx *Index) All() map[string]string {
|
||||
return out
|
||||
}
|
||||
|
||||
// Resolve finds the latest compatible version for a target by walking
|
||||
// the arch and libc fallback chains. It prefers the newest version over
|
||||
// the most specific arch match.
|
||||
//
|
||||
// For example, on an amd64v4 machine with glibc:
|
||||
// - v2.0.0 has amd64 (baseline) → candidate "v2.0.0"
|
||||
// - v1.0.0 has amd64v4 → candidate "v1.0.0"
|
||||
// - Returns "v2.0.0" because it's newer, even though v1.0.0
|
||||
// is a more specific arch match.
|
||||
//
|
||||
// Returns the best version and the exact target it matched, or "" if
|
||||
// no compatible version exists.
|
||||
func (idx *Index) Resolve(t buildmeta.Target) (version string, matched buildmeta.Target) {
|
||||
idx.mu.RLock()
|
||||
defer idx.mu.RUnlock()
|
||||
|
||||
arches := buildmeta.ArchFallbacks(t.Arch)
|
||||
libcs := buildmeta.LibcFallbacks(t.Libc)
|
||||
|
||||
for _, arch := range arches {
|
||||
for _, libc := range libcs {
|
||||
candidate := buildmeta.Target{OS: t.OS, Arch: arch, Libc: libc}
|
||||
v := idx.m[candidate.Triplet()]
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
if version == "" || lexver.Compare(lexver.Parse(v), lexver.Parse(version)) > 0 {
|
||||
version = v
|
||||
matched = candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
return version, matched
|
||||
}
|
||||
|
||||
// Save persists the index to disk (atomic write).
|
||||
func (idx *Index) Save() error {
|
||||
idx.mu.RLock()
|
||||
|
||||
@@ -91,113 +91,6 @@ func TestAll(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveArchFallback(t *testing.T) {
|
||||
p := filepath.Join(t.TempDir(), "latest.json")
|
||||
idx, err := platlatest.Open(p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// v1.0.0 had per-microarch builds.
|
||||
idx.Set(buildmeta.Target{OS: buildmeta.OSLinux, Arch: buildmeta.ArchAMD64v4, Libc: buildmeta.LibcGNU}, "v1.0.0")
|
||||
idx.Set(buildmeta.Target{OS: buildmeta.OSLinux, Arch: buildmeta.ArchAMD64v3, Libc: buildmeta.LibcGNU}, "v1.0.0")
|
||||
idx.Set(buildmeta.Target{OS: buildmeta.OSLinux, Arch: buildmeta.ArchAMD64v2, Libc: buildmeta.LibcGNU}, "v1.0.0")
|
||||
idx.Set(buildmeta.Target{OS: buildmeta.OSLinux, Arch: buildmeta.ArchAMD64, Libc: buildmeta.LibcGNU}, "v1.0.0")
|
||||
|
||||
// v2.0.0 dropped microarch, ships only baseline amd64.
|
||||
idx.Set(buildmeta.Target{OS: buildmeta.OSLinux, Arch: buildmeta.ArchAMD64, Libc: buildmeta.LibcGNU}, "v2.0.0")
|
||||
|
||||
// An amd64v4 machine should get v2.0.0 (latest) via baseline fallback,
|
||||
// not v1.0.0 (which had a specific v4 build).
|
||||
want := buildmeta.Target{OS: buildmeta.OSLinux, Arch: buildmeta.ArchAMD64v4, Libc: buildmeta.LibcGNU}
|
||||
ver, matched := idx.Resolve(want)
|
||||
if ver != "v2.0.0" {
|
||||
t.Errorf("Resolve(amd64v4) version = %q, want v2.0.0", ver)
|
||||
}
|
||||
if matched.Arch != buildmeta.ArchAMD64 {
|
||||
t.Errorf("Resolve(amd64v4) matched arch = %q, want %q", matched.Arch, buildmeta.ArchAMD64)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveExactMatchPreferred(t *testing.T) {
|
||||
p := filepath.Join(t.TempDir(), "latest.json")
|
||||
idx, err := platlatest.Open(p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Both amd64v3 and baseline have the same latest version.
|
||||
idx.Set(buildmeta.Target{OS: buildmeta.OSLinux, Arch: buildmeta.ArchAMD64v3, Libc: buildmeta.LibcGNU}, "v2.0.0")
|
||||
idx.Set(buildmeta.Target{OS: buildmeta.OSLinux, Arch: buildmeta.ArchAMD64, Libc: buildmeta.LibcGNU}, "v2.0.0")
|
||||
|
||||
// When versions are equal, the more specific arch should win
|
||||
// (it appears first in the fallback chain).
|
||||
want := buildmeta.Target{OS: buildmeta.OSLinux, Arch: buildmeta.ArchAMD64v3, Libc: buildmeta.LibcGNU}
|
||||
ver, matched := idx.Resolve(want)
|
||||
if ver != "v2.0.0" {
|
||||
t.Errorf("version = %q, want v2.0.0", ver)
|
||||
}
|
||||
if matched.Arch != buildmeta.ArchAMD64v3 {
|
||||
t.Errorf("matched arch = %q, want %q (more specific)", matched.Arch, buildmeta.ArchAMD64v3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveLibcFallback(t *testing.T) {
|
||||
p := filepath.Join(t.TempDir(), "latest.json")
|
||||
idx, err := platlatest.Open(p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Only a static (LibcNone) build exists.
|
||||
idx.Set(buildmeta.Target{OS: buildmeta.OSLinux, Arch: buildmeta.ArchAMD64, Libc: buildmeta.LibcNone}, "v1.0.0")
|
||||
|
||||
// A glibc machine should find it via libc fallback.
|
||||
want := buildmeta.Target{OS: buildmeta.OSLinux, Arch: buildmeta.ArchAMD64, Libc: buildmeta.LibcGNU}
|
||||
ver, matched := idx.Resolve(want)
|
||||
if ver != "v1.0.0" {
|
||||
t.Errorf("version = %q, want v1.0.0", ver)
|
||||
}
|
||||
if matched.Libc != buildmeta.LibcNone {
|
||||
t.Errorf("matched libc = %q, want %q", matched.Libc, buildmeta.LibcNone)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveNoMatch(t *testing.T) {
|
||||
p := filepath.Join(t.TempDir(), "latest.json")
|
||||
idx, err := platlatest.Open(p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
idx.Set(linuxAMD64, "v1.0.0")
|
||||
|
||||
// Darwin target should not match a Linux entry.
|
||||
ver, _ := idx.Resolve(darwinARM64)
|
||||
if ver != "" {
|
||||
t.Errorf("Resolve(darwin) = %q, want empty (no match)", ver)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveBaselineOnly(t *testing.T) {
|
||||
p := filepath.Join(t.TempDir(), "latest.json")
|
||||
idx, err := platlatest.Open(p)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// amd64v1 machine can't run v2+ binaries.
|
||||
idx.Set(buildmeta.Target{OS: buildmeta.OSLinux, Arch: buildmeta.ArchAMD64v2, Libc: buildmeta.LibcGNU}, "v2.0.0")
|
||||
idx.Set(buildmeta.Target{OS: buildmeta.OSLinux, Arch: buildmeta.ArchAMD64, Libc: buildmeta.LibcGNU}, "v1.0.0")
|
||||
|
||||
// Baseline machine gets v1.0.0 — it can't run v2's amd64v2 binary.
|
||||
want := buildmeta.Target{OS: buildmeta.OSLinux, Arch: buildmeta.ArchAMD64, Libc: buildmeta.LibcGNU}
|
||||
ver, _ := idx.Resolve(want)
|
||||
if ver != "v1.0.0" {
|
||||
t.Errorf("Resolve(amd64 baseline) = %q, want v1.0.0", ver)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpenNonexistent(t *testing.T) {
|
||||
p := filepath.Join(t.TempDir(), "does-not-exist.json")
|
||||
idx, err := platlatest.Open(p)
|
||||
|
||||
Reference in New Issue
Block a user