From ade535b21e3347892661bed815c0eed3e3e8c2da Mon Sep 17 00:00:00 2001 From: radiantjade Date: Fri, 15 May 2026 17:09:07 +0900 Subject: [PATCH] Preserve exact Gradle wrapper versions Railpack should pass the version pinned in gradle-wrapper.properties through to mise instead of truncating it to the major version. Major-only resolution can select newer milestones or previews that the project did not request. Constraint: Gradle wrapper distribution URLs are the authoritative version source for Gradle wrapper projects. Rejected: Keep major-only parsing | allows mise latest-major resolution to drift to prerelease versions. Confidence: high Scope-risk: narrow Tested: go test ./core/providers/java Tested: go test ./core -run 'TestGenerateBuildPlanForExamples/java-gradle' Tested: git diff --cached --check --- core/providers/java/gradle.go | 20 ++------ core/providers/java/gradle_test.go | 79 ++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 16 deletions(-) create mode 100644 core/providers/java/gradle_test.go diff --git a/core/providers/java/gradle.go b/core/providers/java/gradle.go index 8aae09857..f77bd29cc 100644 --- a/core/providers/java/gradle.go +++ b/core/providers/java/gradle.go @@ -34,29 +34,17 @@ func (p *JavaProvider) setGradleVersion(ctx *generate.GenerateContext) { return } - versionRegex, err := regexp.Compile(`(distributionUrl[\S].*[gradle])(-)([0-9|\.]*)`) + versionRegex, err := regexp.Compile(`gradle-([0-9][0-9A-Za-z.-]*)-(?:bin|all)\.zip`) if err != nil { return } - if !versionRegex.Match([]byte(wrapperProps)) { + matches := versionRegex.FindStringSubmatch(wrapperProps) + if len(matches) < 2 { return } - customVersion := string(versionRegex.FindSubmatch([]byte(wrapperProps))[3]) - - parseVersionRegex, err := regexp.Compile(`^(?:[\sa-zA-Z-"']*)(\d*)(?:\.*)(\d*)(?:\.*\d*)(?:["']?)$`) - if err != nil { - return - } - - if !parseVersionRegex.Match([]byte(customVersion)) { - return - } - - parsedVersion := string(parseVersionRegex.FindSubmatch([]byte(customVersion))[1]) - - miseStep.Version(gradle, parsedVersion, "gradle-wrapper.properties") + miseStep.Version(gradle, matches[1], "gradle-wrapper.properties") } func (p *JavaProvider) gradleCache(ctx *generate.GenerateContext) string { diff --git a/core/providers/java/gradle_test.go b/core/providers/java/gradle_test.go new file mode 100644 index 000000000..83e9fffdc --- /dev/null +++ b/core/providers/java/gradle_test.go @@ -0,0 +1,79 @@ +package java + +import ( + "os" + "path/filepath" + "testing" + + "github.com/railwayapp/railpack/core/app" + "github.com/railwayapp/railpack/core/config" + "github.com/railwayapp/railpack/core/generate" + "github.com/railwayapp/railpack/core/logger" + "github.com/stretchr/testify/require" +) + +func TestSetGradleVersionUsesFullWrapperVersion(t *testing.T) { + tests := []struct { + name string + distributionURL string + expectedVersion string + }{ + { + name: "minor version", + distributionURL: `https\://services.gradle.org/distributions/gradle-8.13-bin.zip`, + expectedVersion: "8.13", + }, + { + name: "patch version", + distributionURL: `https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip`, + expectedVersion: "9.5.0", + }, + { + name: "prerelease version", + distributionURL: `https\://services.gradle.org/distributions/gradle-9.6.0-rc-1-all.zip`, + expectedVersion: "9.6.0-rc-1", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := createGradleWrapperContext(t, tt.distributionURL) + + provider := JavaProvider{} + provider.setGradleVersion(ctx) + + gradle := ctx.Resolver.Get("gradle") + require.NotNil(t, gradle) + require.Equal(t, tt.expectedVersion, gradle.Version) + require.Equal(t, "gradle-wrapper.properties", gradle.Source) + }) + } +} + +func createGradleWrapperContext(t *testing.T, distributionURL string) *generate.GenerateContext { + t.Helper() + + appDir := t.TempDir() + require.NoError(t, os.WriteFile(filepath.Join(appDir, "gradlew"), []byte("#!/bin/sh\n"), 0o755)) + + wrapperDir := filepath.Join(appDir, "gradle", "wrapper") + require.NoError(t, os.MkdirAll(wrapperDir, 0o755)) + require.NoError(t, os.WriteFile( + filepath.Join(wrapperDir, "gradle-wrapper.properties"), + []byte("distributionUrl="+distributionURL+"\n"), + 0o644, + )) + + userApp, err := app.NewApp(appDir) + require.NoError(t, err) + + ctx, err := generate.NewGenerateContext( + userApp, + app.NewEnvironment(nil), + config.EmptyConfig(), + logger.NewLogger(), + ) + require.NoError(t, err) + + return ctx +}