Skip to content

Commit 297eb4c

Browse files
committed
Merge branch '7.1.x' into 8.0.x
2 parents 14491fd + 9813638 commit 297eb4c

3 files changed

Lines changed: 160 additions & 31 deletions

File tree

.github/workflows/gradle.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -442,8 +442,13 @@ jobs:
442442
./gradlew :grails-wrapper:distZip
443443
- name: "✅ Verify grails-wrapper"
444444
if: success()
445+
env:
446+
GRAILS_WRAPPER_ALLOWED_TYPES: 'SNAPSHOT'
445447
run: |
446-
export GRAILS_WRAPPER_ALLOWED_TYPES='SNAPSHOT'
448+
# Pin the wrapper to this branch's snapshot so it doesn't resolve the
449+
# globally latest SNAPSHOT in the Apache repo, which may be compiled
450+
# for a newer Java runtime than this job's JDK.
451+
export PREFERRED_GRAILS_VERSION=$(grep '^projectVersion=' gradle.properties | cut -d'=' -f2)
447452
cp grails-wrapper/build/distributions/apache-grails-wrapper-*.zip build/wrapper.zip
448453
cd build
449454
unzip wrapper -d tmp
@@ -453,7 +458,7 @@ jobs:
453458
uses: actions/upload-artifact@v7.0.0
454459
with:
455460
name: apache-grails-wrapper-SNAPSHOT-bin
456-
path: tmp/wrapper
461+
path: build/tmp/wrapper
457462
publishForge:
458463
if: github.repository_owner == 'apache' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
459464
needs: [ buildForge, publishGradle, publish ]

grails-wrapper/src/main/java/grails/init/GrailsVersion.java

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@
3333
*/
3434
public class GrailsVersion implements Comparable<GrailsVersion> {
3535

36+
/** Environment variable honoured outside a Grails project to pin the wrapper to a specific version. */
37+
public static final String PREFERRED_GRAILS_VERSION_ENV = "PREFERRED_GRAILS_VERSION";
38+
39+
/** Environment variable used to restrict which release types the wrapper will resolve (e.g. {@code SNAPSHOT,RC}). */
40+
public static final String GRAILS_WRAPPER_ALLOWED_TYPES_ENV = "GRAILS_WRAPPER_ALLOWED_TYPES";
41+
42+
/** Property key read from {@code gradle.properties} to pin the wrapper to a specific version inside a project. */
43+
public static final String GRAILS_VERSION_PROPERTY = "grailsVersion";
44+
3645
private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)[.](\\d+)[.](\\d+)-?(.*)$");
3746

3847
private static final Pattern RC = Pattern.compile("^RC(\\d+)$");
@@ -86,7 +95,7 @@ public GrailsVersion(String version) {
8695
}
8796

8897
public static LinkedHashSet<GrailsReleaseType> getAllowedReleaseTypes(GrailsVersion preferredVersion, GrailsWrapper wrapper) {
89-
String raw = System.getenv("GRAILS_WRAPPER_ALLOWED_TYPES");
98+
String raw = System.getenv(GRAILS_WRAPPER_ALLOWED_TYPES_ENV);
9099
if (raw == null || raw.trim().isEmpty()) {
91100
if (preferredVersion != null) {
92101
//inside a grails project pull the equivalent version type or newer
@@ -116,15 +125,63 @@ public static LinkedHashSet<GrailsReleaseType> getAllowedReleaseTypes(GrailsVers
116125
try {
117126
return GrailsReleaseType.valueOf(input);
118127
} catch (Exception e) {
119-
throw new IllegalStateException("Invalid Value in GRAILS_WRAPPER_ALLOWED_TYPES: " + input);
128+
throw new IllegalStateException("Invalid Value in " + GRAILS_WRAPPER_ALLOWED_TYPES_ENV + ": " + input);
120129
}
121130
})
122131
.collect(Collectors.toCollection(LinkedHashSet::new));
123132
}
124133

125134
public static GrailsVersion getPreferredGrailsVersion() {
126-
// Check for a properties file in case inside a grails project
127-
File gradleProperties = new File("gradle.properties");
135+
return getPreferredGrailsVersion(new File("gradle.properties"));
136+
}
137+
138+
/**
139+
* Determine the preferred Grails version.
140+
*
141+
* <p>Precedence matches the documented behaviour in the wrapper README:
142+
* <ol>
143+
* <li>Inside a Grails project (a {@code gradle.properties} containing a
144+
* {@code grailsVersion} key with a non-blank value), use that value
145+
* and ignore environment variables. A present-but-blank value or an
146+
* unparseable version is treated as a project misconfiguration and
147+
* exits the process.</li>
148+
* <li>Outside a Grails project (no {@code gradle.properties}, or one
149+
* without {@code grailsVersion}), honour the
150+
* {@code PREFERRED_GRAILS_VERSION} environment variable. A
151+
* whitespace-only value is treated as unset.</li>
152+
* <li>Otherwise return {@code null} and let the wrapper resolve the
153+
* latest version from Maven metadata.</li>
154+
* </ol>
155+
*
156+
* @param gradleProperties the properties file to inspect; exposed as a
157+
* parameter for testability
158+
* @return the pinned {@link GrailsVersion} or {@code null} if none was
159+
* specified
160+
*/
161+
static GrailsVersion getPreferredGrailsVersion(File gradleProperties) {
162+
GrailsVersion fromProperties = readVersionFromProperties(gradleProperties);
163+
if (fromProperties != null) {
164+
// Inside a Grails project: gradle.properties wins and the env var is intentionally ignored per the wrapper README.
165+
return fromProperties;
166+
}
167+
168+
String overrideGrailsVersion = System.getenv(PREFERRED_GRAILS_VERSION_ENV);
169+
if (overrideGrailsVersion == null || (overrideGrailsVersion = overrideGrailsVersion.trim()).isEmpty()) {
170+
return null;
171+
}
172+
173+
try {
174+
return new GrailsVersion(overrideGrailsVersion);
175+
} catch (Exception e) {
176+
System.out.println("An invalid Grails Version [" + overrideGrailsVersion + "] was specified in " + PREFERRED_GRAILS_VERSION_ENV);
177+
e.printStackTrace();
178+
System.exit(1);
179+
}
180+
181+
return null;
182+
}
183+
184+
private static GrailsVersion readVersionFromProperties(File gradleProperties) {
128185
if (!gradleProperties.exists()) {
129186
return null;
130187
}
@@ -138,26 +195,15 @@ public static GrailsVersion getPreferredGrailsVersion() {
138195
System.exit(1);
139196
}
140197

141-
if (!properties.containsKey("grailsVersion")) {
142-
return null;
143-
}
144-
145-
String grailsVersion = properties.getProperty("grailsVersion");
198+
String grailsVersion = properties.getProperty(GRAILS_VERSION_PROPERTY);
146199
if (grailsVersion == null) {
147-
String overrideGrailsVersion = System.getenv("PREFERRED_GRAILS_VERSION");
148-
if (overrideGrailsVersion != null) {
149-
try {
150-
return new GrailsVersion(overrideGrailsVersion);
151-
} catch (Exception e) {
152-
System.out.println("An invalid Grails Version [" + overrideGrailsVersion + "] was specified in PREFERRED_GRAILS_VERSION");
153-
e.printStackTrace();
154-
System.exit(1);
155-
}
156-
}
157-
158-
System.out.println("gradle.properties does not contain grailsVersion; assuming latest Grails Version");
159200
return null;
160201
}
202+
grailsVersion = grailsVersion.trim();
203+
if (grailsVersion.isEmpty()) {
204+
System.out.println("A blank Grails Version was specified in gradle.properties for key [" + GRAILS_VERSION_PROPERTY + "]");
205+
System.exit(1);
206+
}
161207

162208
try {
163209
return new GrailsVersion(grailsVersion);

grails-wrapper/src/test/groovy/grails/init/GrailsVersionSpec.groovy

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,24 @@
1919
package grails.init
2020

2121
import spock.lang.Specification
22+
import spock.lang.TempDir
2223
import spock.lang.Unroll
2324
import uk.org.webcompere.systemstubs.SystemStubs
2425

26+
import java.nio.file.Path
27+
2528
class GrailsVersionSpec extends Specification {
2629

30+
@TempDir
31+
Path tempDir
32+
2733
def "allowed release types - specified valid"() {
2834
given:
2935
GrailsWrapper mockVersion = Mock(GrailsWrapper)
3036

3137
when:
3238
Set<GrailsReleaseType> allowedTypes = null
33-
SystemStubs.withEnvironmentVariable('GRAILS_WRAPPER_ALLOWED_TYPES', 'SNAPSHOT, RC').execute {
39+
SystemStubs.withEnvironmentVariable(GrailsVersion.GRAILS_WRAPPER_ALLOWED_TYPES_ENV, 'SNAPSHOT, RC').execute {
3440
allowedTypes = GrailsVersion.getAllowedReleaseTypes(null, mockVersion)
3541
}
3642

@@ -50,13 +56,13 @@ class GrailsVersionSpec extends Specification {
5056

5157
when:
5258
Set<GrailsReleaseType> allowedTypes = null
53-
SystemStubs.withEnvironmentVariable('GRAILS_WRAPPER_ALLOWED_TYPES', 'SNAPSHOT, FOO').execute {
59+
SystemStubs.withEnvironmentVariable(GrailsVersion.GRAILS_WRAPPER_ALLOWED_TYPES_ENV, 'SNAPSHOT, FOO').execute {
5460
allowedTypes = GrailsVersion.getAllowedReleaseTypes(null, mockVersion)
5561
}
5662

5763
then:
5864
def ie = thrown(IllegalStateException)
59-
ie.message == 'Invalid Value in GRAILS_WRAPPER_ALLOWED_TYPES: FOO'
65+
ie.message == "Invalid Value in ${GrailsVersion.GRAILS_WRAPPER_ALLOWED_TYPES_ENV}: FOO"
6066

6167
and:
6268
0 * mockVersion._
@@ -71,7 +77,7 @@ class GrailsVersionSpec extends Specification {
7177

7278
when:
7379
Set<GrailsReleaseType> allowedTypes = null
74-
SystemStubs.withEnvironmentVariable('GRAILS_WRAPPER_ALLOWED_TYPES', 'SNAPSHOT, RC').execute {
80+
SystemStubs.withEnvironmentVariable(GrailsVersion.GRAILS_WRAPPER_ALLOWED_TYPES_ENV, 'SNAPSHOT, RC').execute {
7581
allowedTypes = GrailsVersion.getAllowedReleaseTypes(preferredVersion, mockVersion)
7682
}
7783

@@ -94,7 +100,7 @@ class GrailsVersionSpec extends Specification {
94100

95101
when:
96102
Set<GrailsReleaseType> allowedTypes = null
97-
SystemStubs.withEnvironmentVariable('GRAILS_WRAPPER_ALLOWED_TYPES', null).execute {
103+
SystemStubs.withEnvironmentVariable(GrailsVersion.GRAILS_WRAPPER_ALLOWED_TYPES_ENV, null).execute {
98104
allowedTypes = GrailsVersion.getAllowedReleaseTypes(preferredVersion, mockVersion)
99105
}
100106

@@ -116,7 +122,7 @@ class GrailsVersionSpec extends Specification {
116122

117123
when:
118124
Set<GrailsReleaseType> allowedTypes = null
119-
SystemStubs.withEnvironmentVariable('GRAILS_WRAPPER_ALLOWED_TYPES', null).execute {
125+
SystemStubs.withEnvironmentVariable(GrailsVersion.GRAILS_WRAPPER_ALLOWED_TYPES_ENV, null).execute {
120126
allowedTypes = GrailsVersion.getAllowedReleaseTypes(null, mockVersion)
121127
}
122128

@@ -135,7 +141,7 @@ class GrailsVersionSpec extends Specification {
135141

136142
when:
137143
Set<GrailsReleaseType> allowedTypes = null
138-
SystemStubs.withEnvironmentVariable('GRAILS_WRAPPER_ALLOWED_TYPES', null).execute {
144+
SystemStubs.withEnvironmentVariable(GrailsVersion.GRAILS_WRAPPER_ALLOWED_TYPES_ENV, null).execute {
139145
allowedTypes = GrailsVersion.getAllowedReleaseTypes(null, mockVersion)
140146
}
141147

@@ -154,7 +160,7 @@ class GrailsVersionSpec extends Specification {
154160

155161
when:
156162
Set<GrailsReleaseType> allowedTypes = null
157-
SystemStubs.withEnvironmentVariable('GRAILS_WRAPPER_ALLOWED_TYPES', null).execute {
163+
SystemStubs.withEnvironmentVariable(GrailsVersion.GRAILS_WRAPPER_ALLOWED_TYPES_ENV, null).execute {
158164
allowedTypes = GrailsVersion.getAllowedReleaseTypes(null, mockVersion)
159165
}
160166

@@ -165,6 +171,78 @@ class GrailsVersionSpec extends Specification {
165171
allowedTypes == [GrailsReleaseType.RELEASE] as LinkedHashSet
166172
}
167173

174+
def "preferred version - gradle.properties with grailsVersion wins over env var"() {
175+
given:
176+
File gradleProperties = tempDir.resolve('gradle.properties').toFile()
177+
gradleProperties.text = 'grailsVersion=7.0.11-SNAPSHOT'
178+
179+
when:
180+
GrailsVersion resolved = null
181+
SystemStubs.withEnvironmentVariable(GrailsVersion.PREFERRED_GRAILS_VERSION_ENV, '8.0.0-SNAPSHOT').execute {
182+
resolved = GrailsVersion.getPreferredGrailsVersion(gradleProperties)
183+
}
184+
185+
then:
186+
resolved == new GrailsVersion('7.0.11-SNAPSHOT')
187+
}
188+
189+
def "preferred version - env var used when gradle.properties is missing"() {
190+
given:
191+
File missing = tempDir.resolve('does-not-exist.properties').toFile()
192+
193+
when:
194+
GrailsVersion resolved = null
195+
SystemStubs.withEnvironmentVariable(GrailsVersion.PREFERRED_GRAILS_VERSION_ENV, '7.0.11-SNAPSHOT').execute {
196+
resolved = GrailsVersion.getPreferredGrailsVersion(missing)
197+
}
198+
199+
then:
200+
resolved == new GrailsVersion('7.0.11-SNAPSHOT')
201+
}
202+
203+
def "preferred version - env var used when gradle.properties has no grailsVersion key"() {
204+
given:
205+
File gradleProperties = tempDir.resolve('gradle.properties').toFile()
206+
gradleProperties.text = 'projectVersion=7.0.11-SNAPSHOT'
207+
208+
when:
209+
GrailsVersion resolved = null
210+
SystemStubs.withEnvironmentVariable(GrailsVersion.PREFERRED_GRAILS_VERSION_ENV, '7.0.11-SNAPSHOT').execute {
211+
resolved = GrailsVersion.getPreferredGrailsVersion(gradleProperties)
212+
}
213+
214+
then:
215+
resolved == new GrailsVersion('7.0.11-SNAPSHOT')
216+
}
217+
218+
def "preferred version - env var trimmed and whitespace-only treated as unset"() {
219+
given:
220+
File missing = tempDir.resolve('does-not-exist.properties').toFile()
221+
222+
when:
223+
GrailsVersion resolved = null
224+
SystemStubs.withEnvironmentVariable(GrailsVersion.PREFERRED_GRAILS_VERSION_ENV, ' ').execute {
225+
resolved = GrailsVersion.getPreferredGrailsVersion(missing)
226+
}
227+
228+
then:
229+
resolved == null
230+
}
231+
232+
def "preferred version - returns null when neither gradle.properties nor env var is set"() {
233+
given:
234+
File missing = tempDir.resolve('does-not-exist.properties').toFile()
235+
236+
when:
237+
GrailsVersion resolved = null
238+
SystemStubs.withEnvironmentVariable(GrailsVersion.PREFERRED_GRAILS_VERSION_ENV, null).execute {
239+
resolved = GrailsVersion.getPreferredGrailsVersion(missing)
240+
}
241+
242+
then:
243+
resolved == null
244+
}
245+
168246
@Unroll
169247
def "grails version #version"(String version, String major, String minor, String patch, GrailsReleaseType releaseType, Integer candidate) {
170248
when:

0 commit comments

Comments
 (0)