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