From 05abb1ffd27c52be521337e8da353ea4bb9029f5 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Tue, 10 Mar 2026 18:26:41 -0600 Subject: [PATCH] fix(git): normalize .windows.N version suffix Git for Windows uses tags like v2.53.0.windows.1. Node.js strips ".windows.1" and replaces ".windows.N" (N>1) with ".N". Add NormalizeVersions to the git package and wire it into the classify pipeline. Also add version normalization to comparecache so the comparison uses canonical versions for both caches. Remaining git diffs: data freshness (.windows.2 releases Go hasn't fetched) and RC versions in Go that live doesn't have. --- cmd/comparecache/main.go | 34 ++++++++++++++++++++++++++--- internal/classifypkg/classifypkg.go | 10 +++++++++ internal/releases/git/versions.go | 33 ++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 internal/releases/git/versions.go diff --git a/cmd/comparecache/main.go b/cmd/comparecache/main.go index c141b88..cf36b33 100644 --- a/cmd/comparecache/main.go +++ b/cmd/comparecache/main.go @@ -248,6 +248,8 @@ func compare(livePath, goPath, pkg string, latestOnly, windowed bool) packageDif return d } + normVersion := normalizeVersionFunc(pkg) + // Collect filenames by version. If filter is non-nil, skip filenames it rejects. extractVersionFiles := func(ce *cacheEntry, filter func(string) bool) (map[string]map[string]bool, []string) { vf := make(map[string]map[string]bool) @@ -256,10 +258,11 @@ func compare(livePath, goPath, pkg string, latestOnly, windowed bool) packageDif if filter != nil && !filter(name) { continue } - if vf[r.Version] == nil { - vf[r.Version] = make(map[string]bool) + ver := normVersion(r.Version) + if vf[ver] == nil { + vf[ver] = make(map[string]bool) } - vf[r.Version][name] = true + vf[ver][name] = true } var versions []string for v := range vf { @@ -519,6 +522,31 @@ func isLiveNoise(name string) bool { return false } +// normalizeVersionFunc returns a version normalizer for a given package. +// Most packages return the identity function. Some (like git) need +// version string normalization to match across Go and Node.js caches. +func normalizeVersionFunc(pkg string) func(string) string { + switch pkg { + case "git": + return func(v string) string { + // Git for Windows: v2.53.0.windows.1 → v2.53.0 + // v2.53.0.windows.2 → v2.53.0.2 + idx := strings.Index(v, ".windows.") + if idx < 0 { + return v + } + suffix := v[idx+len(".windows."):] + base := v[:idx] + if suffix == "1" { + return base + } + return base + "." + suffix + } + default: + return func(v string) string { return v } + } +} + func printSummary(diffs []packageDiff) { // Count by category. categoryCounts := make(map[string]int) diff --git a/internal/classifypkg/classifypkg.go b/internal/classifypkg/classifypkg.go index 7e6aa21..8316d4e 100644 --- a/internal/classifypkg/classifypkg.go +++ b/internal/classifypkg/classifypkg.go @@ -47,6 +47,7 @@ func Package(pkg string, conf *installerconf.Conf, d *rawcache.Dir) ([]storage.A } TagVariants(pkg, assets) + NormalizeVersions(pkg, assets) assets = ApplyConfig(assets, conf) return assets, nil } @@ -85,6 +86,15 @@ func classifySource(pkg string, conf *installerconf.Conf, d *rawcache.Dir) ([]st } } +// NormalizeVersions applies package-specific version normalization. +// For example, Git for Windows strips ".windows.N" from version strings. +func NormalizeVersions(pkg string, assets []storage.Asset) { + switch pkg { + case "git": + git.NormalizeVersions(assets) + } +} + // TagVariants applies package-specific variant tags to classified assets. // Each case delegates to a per-installer package under internal/releases/. func TagVariants(pkg string, assets []storage.Asset) { diff --git a/internal/releases/git/versions.go b/internal/releases/git/versions.go new file mode 100644 index 0000000..ae6047f --- /dev/null +++ b/internal/releases/git/versions.go @@ -0,0 +1,33 @@ +package git + +import ( + "strings" + + "github.com/webinstall/webi-installers/internal/storage" +) + +// NormalizeVersions strips the ".windows.N" suffix from Git for Windows +// version strings to match the upstream Git version scheme. +// +// Git for Windows tags are like "v2.53.0.windows.1" or "v2.53.0.windows.2". +// Node.js strips ".windows.1" entirely and replaces ".windows.N" (N>1) +// with ".N": +// +// v2.53.0.windows.1 → v2.53.0 +// v2.53.0.windows.2 → v2.53.0.2 +func NormalizeVersions(assets []storage.Asset) { + for i := range assets { + v := assets[i].Version + idx := strings.Index(v, ".windows.") + if idx < 0 { + continue + } + suffix := v[idx+len(".windows."):] + base := v[:idx] + if suffix == "1" { + assets[i].Version = base + } else { + assets[i].Version = base + "." + suffix + } + } +}