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.
This commit is contained in:
AJ ONeal
2026-03-10 18:26:41 -06:00
parent ada10ed43a
commit 05abb1ffd2
3 changed files with 74 additions and 3 deletions

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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
}
}
}