From 1253fcd671a6ac5da31ed0dcc804ee999ed61c19 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Mon, 9 Mar 2026 21:50:10 -0600 Subject: [PATCH] ref: remove universal fallback chains from buildmeta and platlatest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- internal/buildmeta/buildmeta.go | 37 --------- internal/platlatest/platlatest.go | 36 --------- internal/platlatest/platlatest_test.go | 107 ------------------------- 3 files changed, 180 deletions(-) diff --git a/internal/buildmeta/buildmeta.go b/internal/buildmeta/buildmeta.go index 28f7821..533ae1f 100644 --- a/internal/buildmeta/buildmeta.go +++ b/internal/buildmeta/buildmeta.go @@ -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} - } -} diff --git a/internal/platlatest/platlatest.go b/internal/platlatest/platlatest.go index d72c0e1..7e28d90 100644 --- a/internal/platlatest/platlatest.go +++ b/internal/platlatest/platlatest.go @@ -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() diff --git a/internal/platlatest/platlatest_test.go b/internal/platlatest/platlatest_test.go index 92f8d49..82f2539 100644 --- a/internal/platlatest/platlatest_test.go +++ b/internal/platlatest/platlatest_test.go @@ -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)