diff --git a/compare.go b/compare.go index 3913ef6..0abe541 100644 --- a/compare.go +++ b/compare.go @@ -72,6 +72,66 @@ func platformVector(platform specs.Platform) []specs.Platform { if variant == "" { variant = "v8" } + + majorVariant, minorVariant, hasMinor := strings.Cut(variant, ".") + if armMajor, err := strconv.Atoi(strings.TrimPrefix(majorVariant, "v")); err == nil && armMajor >= 8 { + armMinor := 0 + if len(variant) == 4 { + if minor, err := strconv.Atoi(minorVariant); err == nil && hasMinor { + armMinor = minor + } + } + + if armMajor == 9 { + for minor := armMinor - 1; minor >= 0; minor-- { + arm64Variant := "v" + strconv.Itoa(armMajor) + "." + strconv.Itoa(minor) + if minor == 0 { + arm64Variant = "v" + strconv.Itoa(armMajor) + } + vector = append(vector, specs.Platform{ + Architecture: platform.Architecture, + OS: platform.OS, + OSVersion: platform.OSVersion, + OSFeatures: platform.OSFeatures, + Variant: arm64Variant, + }) + } + + // v9.0 diverged from v8.5, meaning that v9.x is compatible with v8.{x+5} until v9.4/v8.9 + armMinor = armMinor + 5 + if armMinor > 9 { + armMinor = 9 + } + armMajor = 8 + vector = append(vector, specs.Platform{ + Architecture: platform.Architecture, + OS: platform.OS, + OSVersion: platform.OSVersion, + OSFeatures: platform.OSFeatures, + Variant: "v8." + strconv.Itoa(armMinor), + }) + } + + for minor := armMinor - 1; minor >= 0; minor-- { + arm64Variant := "v" + strconv.Itoa(armMajor) + "." + strconv.Itoa(minor) + if minor == 0 { + arm64Variant = "v" + strconv.Itoa(armMajor) + } + vector = append(vector, specs.Platform{ + Architecture: platform.Architecture, + OS: platform.OS, + OSVersion: platform.OSVersion, + OSFeatures: platform.OSFeatures, + Variant: arm64Variant, + }) + } + } + + // All arm64/v8.x and arm64/v9.x are compatible with arm/v8 (32-bits) and below. + // There's no arm64 v9 variant, so it's normalized to v8. + if strings.HasPrefix(variant, "v8.") || strings.HasPrefix(variant, "v9.") { + variant = "v8" + } vector = append(vector, platformVector(specs.Platform{ Architecture: "arm", OS: platform.OS, @@ -87,6 +147,8 @@ func platformVector(platform specs.Platform) []specs.Platform { // Only returns a match comparer for a single platform // using default resolution logic for the platform. // +// For arm64/v9.x, will also match arm64/v9.{0..x-1} and arm64/v8.{0..x+5} +// For arm64/v8.x, will also match arm64/v8.{0..x-1} // For arm/v8, will also match arm/v7, arm/v6 and arm/v5 // For arm/v7, will also match arm/v6 and arm/v5 // For arm/v6, will also match arm/v5 diff --git a/compare_test.go b/compare_test.go index 2c276d0..ef5c2a1 100644 --- a/compare_test.go +++ b/compare_test.go @@ -185,6 +185,95 @@ func TestOnly(t *testing.T) { }, }, }, + { + platform: "linux/arm64/v9.6", + matches: map[bool][]string{ + true: { + "linux/arm", + "linux/arm/v5", + "linux/arm/v6", + "linux/arm/v7", + "linux/arm/v8", + "linux/arm64", + "linux/arm64/v8", + "linux/arm64/v8.1", + "linux/arm64/v8.2", + "linux/arm64/v8.3", + "linux/arm64/v8.4", + "linux/arm64/v8.5", + "linux/arm64/v8.6", + "linux/arm64/v8.7", + "linux/arm64/v8.8", + "linux/arm64/v8.9", + "linux/arm64/v9", + "linux/arm64/v9.0", + "linux/arm64/v9.1", + "linux/arm64/v9.2", + "linux/arm64/v9.3", + "linux/arm64/v9.4", + "linux/arm64/v9.5", + "linux/arm64/v9.6", + }, + false: { + "linux/amd64", + "linux/arm/v4", + "windows/amd64", + "windows/arm", + "linux/arm64/v8.10", // there's no v8.10 + }, + }, + }, + { + platform: "linux/arm64/v9", + matches: map[bool][]string{ + true: { + "linux/arm", + "linux/arm/v5", + "linux/arm/v6", + "linux/arm/v7", + "linux/arm/v8", + "linux/arm64", + "linux/arm64/v8", + "linux/arm64/v8.1", + "linux/arm64/v8.2", + "linux/arm64/v8.3", + "linux/arm64/v8.4", + "linux/arm64/v8.5", + "linux/arm64/v9", + "linux/arm64/v9.0", + }, + false: { + "linux/amd64", + "linux/arm/v4", + "windows/amd64", + "windows/arm", + "linux/arm64/v8.6", + }, + }, + }, + { + platform: "linux/arm64/v8.1", + matches: map[bool][]string{ + true: { + "linux/arm", + "linux/arm/v5", + "linux/arm/v6", + "linux/arm/v7", + "linux/arm/v8", + "linux/arm64", + "linux/arm64/v8", + "linux/arm64/v8.1", + }, + false: { + "linux/amd64", + "linux/arm/v4", + "linux/arm/v9", + "linux/arm64/v9", + "windows/amd64", + "windows/arm", + }, + }, + }, { platform: "linux/arm64", matches: map[bool][]string{ @@ -390,6 +479,75 @@ func TestOnlyStrict(t *testing.T) { }, }, }, + { + platform: "linux/arm64/v9.5", + matches: map[bool][]string{ + true: { + "linux/arm64/v9.5", + }, + false: { + "linux/arm", + "linux/arm/v5", + "linux/arm/v6", + "linux/arm/v7", + "linux/arm/v8", + "linux/amd64", + "linux/arm/v4", + "linux/arm64/v8", + "linux/arm64/v8.1", + "linux/arm64/v9", + "linux/arm64/v9.1", + "linux/arm64/v9.4", + "windows/amd64", + "windows/arm", + }, + }, + }, + { + platform: "linux/arm64/v9", + matches: map[bool][]string{ + true: { + "linux/arm64/v9", + "linux/arm64/v9.0", + }, + false: { + "linux/arm", + "linux/arm/v5", + "linux/arm/v6", + "linux/arm/v7", + "linux/arm/v8", + "linux/amd64", + "linux/arm/v4", + "linux/arm/v9", + "linux/arm64/v8", + "linux/arm64/v8.1", + "windows/amd64", + "windows/arm", + }, + }, + }, + { + platform: "linux/arm64/v8.1", + matches: map[bool][]string{ + true: { + "linux/arm64/v8.1", + }, + false: { + "linux/arm", + "linux/arm/v5", + "linux/arm/v6", + "linux/arm/v7", + "linux/arm/v8", + "linux/amd64", + "linux/arm/v4", + "linux/arm/v9", + "linux/arm64/v8", + "linux/arm64/v9", + "windows/amd64", + "windows/arm", + }, + }, + }, { platform: "linux/arm64", matches: map[bool][]string{ diff --git a/database.go b/database.go index 2e26fd3..4c7c669 100644 --- a/database.go +++ b/database.go @@ -86,9 +86,22 @@ func normalizeArch(arch, variant string) (string, string) { } case "aarch64", "arm64": arch = "arm64" - switch variant { - case "8", "v8": + majorVariant, minorVariant, hasMinor := strings.Cut(variant, ".") + majorVariant = strings.TrimPrefix(majorVariant, "v") + if minorVariant == "0" { + minorVariant = "" + hasMinor = false + } + + if (majorVariant == "" || majorVariant == "8") && !hasMinor { + // normalize v8 to empty string variant = "" + } else { + // otherwise to v8.x or v9 or v9.x + variant = "v" + majorVariant + if hasMinor { + variant = variant + "." + minorVariant + } } case "armhf": arch = "arm" diff --git a/platforms.go b/platforms.go index 1bbbdb9..7a84449 100644 --- a/platforms.go +++ b/platforms.go @@ -121,7 +121,7 @@ import ( ) var ( - specifierRe = regexp.MustCompile(`^[A-Za-z0-9_-]+$`) + specifierRe = regexp.MustCompile(`^[A-Za-z0-9_.-]+$`) osAndVersionRe = regexp.MustCompile(`^([A-Za-z0-9_-]+)(?:\(([A-Za-z0-9_.-]*)\))?$`) )