diff --git a/bun/releases.conf b/bun/releases.conf index 6a23b5b..2995afb 100644 --- a/bun/releases.conf +++ b/bun/releases.conf @@ -1,3 +1,7 @@ source = github owner = oven-sh repo = bun +# non-baseline=amd64v3, -baseline=amd64 +default_x86_64 = x86_64_v3 +x86_64_v2 = baseline +variants = profile diff --git a/cmd/webicached/main.go b/cmd/webicached/main.go index 2f1ad87..21611f7 100644 --- a/cmd/webicached/main.go +++ b/cmd/webicached/main.go @@ -199,6 +199,9 @@ func (wc *WebiCache) refreshPackage(ctx context.Context, pkg pkgConf) error { return fmt.Errorf("classify: %w", err) } + // Step 2.5: Tag build variants. + tagVariants(name, conf, assets) + // Step 3: Apply config transforms. assets = applyConfig(assets, conf) @@ -1449,3 +1452,135 @@ func isMetaAsset(name string) bool { return false } +// tagVariants sets Asset.Variants for known build variants. +// Detection logic is per-package; the releases.conf "variants" key +// is documentation only — actual pattern matching lives here. +func tagVariants(pkg string, conf *installerconf.Conf, assets []storage.Asset) { + switch pkg { + case "bun": + tagVariantsBun(assets) + case "pwsh": + tagVariantsPwsh(assets) + case "ollama": + tagVariantsOllama(assets) + case "git": + tagVariantsGit(assets) + case "node": + tagVariantsNode(assets) + case "lsd": + tagVariantsLsd(assets) + case "fish": + tagVariantsFish(assets) + case "xcaddy": + tagVariantsXcaddy(assets) + } +} + +// tagVariantsBun: -profile is a debug build, -baseline is actually amd64 +// (non-baseline is amd64v3). Arch remapping + variant tagging. +func tagVariantsBun(assets []storage.Asset) { + for i := range assets { + lower := strings.ToLower(assets[i].Filename) + if strings.Contains(lower, "-profile") { + assets[i].Variants = append(assets[i].Variants, "profile") + } + // Non-baseline x86_64 is actually amd64v3; baseline is plain amd64. + if assets[i].Arch == "amd64" { + if strings.Contains(lower, "-baseline") { + // baseline stays amd64, no variant needed + } else { + // non-baseline is the v3 microarchitecture + assets[i].Arch = "amd64v3" + } + } + } +} + +// tagVariantsPwsh: -fxdependent and -fxdependentWinDesktop are +// .NET framework-dependent builds (smaller, require .NET runtime). +func tagVariantsPwsh(assets []storage.Asset) { + for i := range assets { + lower := strings.ToLower(assets[i].Filename) + if strings.Contains(lower, "-fxdependentwindesktop") { + assets[i].Variants = append(assets[i].Variants, "fxdependentWinDesktop") + } else if strings.Contains(lower, "-fxdependent") { + assets[i].Variants = append(assets[i].Variants, "fxdependent") + } + } +} + +// tagVariantsOllama: GPU accelerator builds (-rocm, -jetpack5, -jetpack6). +func tagVariantsOllama(assets []storage.Asset) { + for i := range assets { + lower := strings.ToLower(assets[i].Filename) + for _, v := range []string{"rocm", "jetpack5", "jetpack6"} { + if strings.Contains(lower, "-"+v) { + assets[i].Variants = append(assets[i].Variants, v) + } + } + } +} + +// tagVariantsGit: GUI installer .exe files (Git-*-bit.exe, PortableGit, etc.) +// vs MinGit .zip which is the actual portable binary. +func tagVariantsGit(assets []storage.Asset) { + for i := range assets { + name := assets[i].Filename + lower := strings.ToLower(name) + // Git-2.48.1-64-bit.exe and similar are GUI installers + if assets[i].Format == ".exe" { + assets[i].Variants = append(assets[i].Variants, "installer") + } + // PortableGit is a self-extracting installer + if strings.Contains(lower, "portablegit") { + assets[i].Variants = append(assets[i].Variants, "installer") + } + // .pdb archives are debug symbols + if strings.Contains(lower, "-pdb") { + assets[i].Variants = append(assets[i].Variants, "pdb") + } + } +} + +// tagVariantsNode: .exe files for Windows are the bare binary, but +// node-v*-x64.msi and similar are GUI installers. +func tagVariantsNode(assets []storage.Asset) { + for i := range assets { + if assets[i].Format == ".msi" { + assets[i].Variants = append(assets[i].Variants, "installer") + } + // node-v25.8.0-win-x64.exe is a bare binary, not an installer. + // Only .msi is the installer for node. + } +} + +// tagVariantsLsd: .deb packages and windows-msvc builds. +func tagVariantsLsd(assets []storage.Asset) { + for i := range assets { + if assets[i].Format == ".deb" { + assets[i].Variants = append(assets[i].Variants, "deb") + } + if strings.Contains(strings.ToLower(assets[i].Filename), "-msvc") { + assets[i].Variants = append(assets[i].Variants, "msvc") + } + } +} + +// tagVariantsFish: .pkg installers and source tarballs. +func tagVariantsFish(assets []storage.Asset) { + for i := range assets { + if assets[i].Format == ".pkg" { + assets[i].Variants = append(assets[i].Variants, "installer") + } + } +} + +// tagVariantsXcaddy: .deb packages. +func tagVariantsXcaddy(assets []storage.Asset) { + for i := range assets { + if assets[i].Format == ".deb" { + assets[i].Variants = append(assets[i].Variants, "deb") + } + } +} + diff --git a/git/releases.conf b/git/releases.conf index 4839b3f..8077896 100644 --- a/git/releases.conf +++ b/git/releases.conf @@ -1,3 +1,4 @@ source = github owner = git-for-windows repo = git +variants = installer diff --git a/hugo/releases.conf b/hugo/releases.conf index f56daed..0e26d81 100644 --- a/hugo/releases.conf +++ b/hugo/releases.conf @@ -1,4 +1,4 @@ source = github owner = gohugoio repo = hugo -asset_exclude = extended +exclude = extended diff --git a/internal/installerconf/installerconf.go b/internal/installerconf/installerconf.go index 94085b0..d9841db 100644 --- a/internal/installerconf/installerconf.go +++ b/internal/installerconf/installerconf.go @@ -2,6 +2,7 @@ // // The format is simple key=value, one per line. Blank lines and lines // starting with # are ignored. Keys and values are trimmed of whitespace. +// Multi-value keys are whitespace-delimited. // // Minimal example (covers ~60% of packages): // @@ -16,12 +17,13 @@ // repo = jq // version_prefixes = jq- // -// With filename exclusions (hugo publishes _extended_ variants): +// With filename exclusions and variant documentation: // // source = github // owner = gohugoio // repo = hugo -// exclude = _extended_, Linux-64bit +// exclude = _extended_ Linux-64bit +// variants = extended extended_withdeploy // // Monorepo with tag prefix: // @@ -43,6 +45,8 @@ // Complex packages that need custom logic beyond what the classifier // auto-detects (e.g. ollama's universal binaries, ffmpeg's non-standard // naming) should put that logic in Go code, not in the config. +// The variants key documents known build variants for human readers; +// actual variant detection logic lives in Go. package installerconf import ( @@ -74,17 +78,21 @@ type Conf struct { TagPrefix string // VersionPrefixes are stripped from version/tag strings. - // Comma-separated. Each release tag is checked against these in order; - // the first match is stripped. Projects may change tag conventions across - // versions (e.g. "jq-1.7.1" in older releases, bare "1.8.0" later). - // Example: "jq-, cli-" + // Whitespace-delimited. Each release tag is checked against these + // in order; the first match is stripped. Projects may change tag + // conventions across versions (e.g. "jq-1.7.1" older, "1.8.0" later). VersionPrefixes []string // Exclude lists filename substrings to filter out. - // Assets whose name contains any of these are skipped. - // Example: ["_extended_", "-gogit-", "-docs-"] + // Whitespace-delimited. Assets whose name contains any of these + // are skipped entirely (not stored). Exclude []string + // Variants documents known build variant names for this package. + // Whitespace-delimited. This is a human-readable cue — actual + // variant detection logic lives in Go code per-package. + Variants []string + // Extra holds any unrecognized keys for forward compatibility. Extra map[string]string } @@ -121,15 +129,9 @@ func Read(path string) (*Conf, error) { c.TagPrefix = raw["tag_prefix"] if v := raw["version_prefixes"]; v != "" { - for _, p := range strings.Split(v, ",") { - p = strings.TrimSpace(p) - if p != "" { - c.VersionPrefixes = append(c.VersionPrefixes, p) - } - } + c.VersionPrefixes = strings.Fields(v) } else if v := raw["version_prefix"]; v != "" { - // Back-compat with singular form. - c.VersionPrefixes = []string{v} + c.VersionPrefixes = strings.Fields(v) } if v := raw["base_url"]; v != "" { @@ -138,13 +140,15 @@ func Read(path string) (*Conf, error) { c.BaseURL = raw["url"] } + // Accept both "exclude" and "asset_exclude" (back-compat). if v := raw["exclude"]; v != "" { - for _, p := range strings.Split(v, ",") { - p = strings.TrimSpace(p) - if p != "" { - c.Exclude = append(c.Exclude, p) - } - } + c.Exclude = strings.Fields(v) + } else if v := raw["asset_exclude"]; v != "" { + c.Exclude = strings.Fields(v) + } + + if v := raw["variants"]; v != "" { + c.Variants = strings.Fields(v) } // Collect unrecognized keys. @@ -152,7 +156,7 @@ func Read(path string) (*Conf, error) { "source": true, "owner": true, "repo": true, "base_url": true, "url": true, "tag_prefix": true, "version_prefix": true, "version_prefixes": true, - "exclude": true, + "exclude": true, "asset_exclude": true, "variants": true, } for k, v := range raw { if !known[k] { diff --git a/internal/installerconf/installerconf_test.go b/internal/installerconf/installerconf_test.go index 1426ccc..b52249c 100644 --- a/internal/installerconf/installerconf_test.go +++ b/internal/installerconf/installerconf_test.go @@ -32,7 +32,7 @@ func TestVersionPrefixes(t *testing.T) { source = github owner = jqlang repo = jq -version_prefixes = jq-, cli- +version_prefixes = jq- cli- `) if len(c.VersionPrefixes) != 2 { t.Fatalf("VersionPrefixes has %d items, want 2: %v", len(c.VersionPrefixes), c.VersionPrefixes) @@ -46,7 +46,7 @@ func TestExclude(t *testing.T) { source = github owner = gohugoio repo = hugo -exclude = _extended_, Linux-64bit +exclude = _extended_ Linux-64bit `) if len(c.Exclude) != 2 { t.Fatalf("Exclude has %d items, want 2: %v", len(c.Exclude), c.Exclude) @@ -110,6 +110,34 @@ custom_thing = hello } } +func TestAssetExcludeAlias(t *testing.T) { + c := confFromString(t, ` +source = github +owner = gohugoio +repo = hugo +asset_exclude = extended +`) + if len(c.Exclude) != 1 { + t.Fatalf("Exclude has %d items, want 1: %v", len(c.Exclude), c.Exclude) + } + assertEqual(t, "Exclude[0]", c.Exclude[0], "extended") +} + +func TestVariants(t *testing.T) { + c := confFromString(t, ` +source = github +owner = jmorganca +repo = ollama +variants = rocm jetpack5 jetpack6 +`) + if len(c.Variants) != 3 { + t.Fatalf("Variants has %d items, want 3: %v", len(c.Variants), c.Variants) + } + assertEqual(t, "Variants[0]", c.Variants[0], "rocm") + assertEqual(t, "Variants[1]", c.Variants[1], "jetpack5") + assertEqual(t, "Variants[2]", c.Variants[2], "jetpack6") +} + func TestEmptyExclude(t *testing.T) { c := confFromString(t, "source = github\n") if c.Exclude != nil { diff --git a/ollama/releases.conf b/ollama/releases.conf index 8437206..7f44d22 100644 --- a/ollama/releases.conf +++ b/ollama/releases.conf @@ -1,3 +1,4 @@ source = github owner = jmorganca repo = ollama +variants = rocm jetpack5 jetpack6 diff --git a/pwsh/releases.conf b/pwsh/releases.conf index 0d87b7f..e5554f5 100644 --- a/pwsh/releases.conf +++ b/pwsh/releases.conf @@ -1,3 +1,4 @@ source = github owner = powershell repo = powershell +variants = fxdependent fxdependentWinDesktop