Skip to content

Commit 5644ebf

Browse files
Fix issues in play, spring-boot-cli containers and add tests (#1250)
* Refactor Play Framework detection logic * Adjust and refactor spring-boot-cli container * Additional classpath * Add more tests for container security provider * Add more tests and remove obsolete for ccm
1 parent 083147d commit 5644ebf

27 files changed

Lines changed: 7674 additions & 701 deletions

src/java/containers/groovy_utils.go

Lines changed: 7 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -77,34 +77,6 @@ func (g *GroovyUtils) IsBeans(filePath string) bool {
7777
return beansPattern.Match(content)
7878
}
7979

80-
// HasMainMethod checks if a Groovy file contains a static void main() method
81-
func HasMainMethod(filePath string) (bool, error) {
82-
content, err := os.ReadFile(filePath)
83-
if err != nil {
84-
return false, err
85-
}
86-
return mainMethodPattern.Match(content), nil
87-
}
88-
89-
// IsPOGO checks if a Groovy file is a Plain Old Groovy Object (contains a class definition)
90-
// POGOs are NOT standalone runnable scripts - they need to be instantiated
91-
func IsPOGO(filePath string) (bool, error) {
92-
content, err := os.ReadFile(filePath)
93-
if err != nil {
94-
return false, err
95-
}
96-
return pogoPattern.Match(content), nil
97-
}
98-
99-
// HasShebang checks if a Groovy file has a shebang line (#!/...)
100-
func HasShebang(filePath string) (bool, error) {
101-
content, err := os.ReadFile(filePath)
102-
if err != nil {
103-
return false, err
104-
}
105-
return shebangPattern.Match(content), nil
106-
}
107-
10880
// isValidGroovyFile checks if a file is a valid, readable Groovy script
10981
// Filters out binary files, empty files, and files with invalid content
11082
func isValidGroovyFile(filePath string) bool {
@@ -149,60 +121,23 @@ func isPartOfUTF8Sequence(content []byte, i int) bool {
149121
return false
150122
}
151123

152-
// FindMainGroovyScript determines which Groovy script should be executed
153-
// Following Ruby buildpack logic:
154-
// 1. Files with static void main() method
155-
// 2. Non-POGO files (simple scripts without class definitions)
156-
// 3. Files with shebang
157-
// Returns the single candidate if exactly one matches, empty string otherwise
124+
// FindMainGroovyScript determines which Groovy script should be executed.
125+
// Following Ruby buildpack logic, a file is a candidate if it has a static
126+
// void main() method, is not a POGO, or has a shebang.
127+
// Returns the single candidate if exactly one matches, empty string otherwise.
158128
func FindMainGroovyScript(scripts []string) (string, error) {
129+
g := &GroovyUtils{}
159130
candidates := make(map[string]bool)
160131

161-
// Filter out invalid files first
162-
validScripts := make([]string, 0, len(scripts))
163132
for _, script := range scripts {
164-
if isValidGroovyFile(script) {
165-
validScripts = append(validScripts, script)
166-
}
167-
}
168-
169-
// Check for main method
170-
for _, script := range validScripts {
171-
hasMain, err := HasMainMethod(script)
172-
if err != nil {
173-
// Skip files that can't be read (like binary files)
174-
continue
175-
}
176-
if hasMain {
177-
candidates[script] = true
178-
}
179-
}
180-
181-
// Check for non-POGOs (simple scripts)
182-
for _, script := range validScripts {
183-
isPOGO, err := IsPOGO(script)
184-
if err != nil {
185-
// Skip files that can't be read
186-
continue
187-
}
188-
if !isPOGO {
189-
candidates[script] = true
190-
}
191-
}
192-
193-
// Check for shebang
194-
for _, script := range validScripts {
195-
hasShebang, err := HasShebang(script)
196-
if err != nil {
197-
// Skip files that can't be read
133+
if !isValidGroovyFile(script) {
198134
continue
199135
}
200-
if hasShebang {
136+
if g.HasMainMethod(script) || !g.IsPOGO(script) || g.HasShebang(script) {
201137
candidates[script] = true
202138
}
203139
}
204140

205-
// Return the candidate if exactly one matches
206141
if len(candidates) == 1 {
207142
for script := range candidates {
208143
return script, nil

src/java/containers/groovy_utils_test.go

Lines changed: 91 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -10,98 +10,108 @@ import (
1010
"github.com/cloudfoundry/java-buildpack/src/java/containers"
1111
)
1212

13-
var _ = Describe("HasMainMethod", func() {
14-
DescribeTable("detecting main method in Groovy files",
15-
func(content string, expected bool) {
16-
tmpFile, err := os.CreateTemp("", "test-*.groovy")
17-
Expect(err).NotTo(HaveOccurred())
18-
defer os.Remove(tmpFile.Name())
13+
// groovyFile writes content to a temp file and returns its path.
14+
// The file is removed when the current spec ends.
15+
func groovyFile(content string) string {
16+
tmpFile, err := os.CreateTemp("", "test-*.groovy")
17+
Expect(err).NotTo(HaveOccurred())
18+
DeferCleanup(os.Remove, tmpFile.Name())
19+
_, err = tmpFile.WriteString(content)
20+
Expect(err).NotTo(HaveOccurred())
21+
Expect(tmpFile.Close()).To(Succeed())
22+
return tmpFile.Name()
23+
}
24+
25+
var _ = Describe("GroovyUtils", func() {
26+
var g *containers.GroovyUtils
1927

20-
_, err = tmpFile.WriteString(content)
21-
Expect(err).NotTo(HaveOccurred())
22-
tmpFile.Close()
28+
BeforeEach(func() {
29+
g = &containers.GroovyUtils{}
30+
})
2331

24-
result, err := containers.HasMainMethod(tmpFile.Name())
25-
Expect(err).NotTo(HaveOccurred())
26-
Expect(result).To(Equal(expected))
27-
},
28-
Entry("has static void main", `class MyApp {
32+
Describe("HasMainMethod", func() {
33+
DescribeTable("detecting main method in Groovy files",
34+
func(content string, expected bool) {
35+
Expect(g.HasMainMethod(groovyFile(content))).To(Equal(expected))
36+
},
37+
Entry("has static void main", `class MyApp {
2938
static void main(String[] args) {
3039
println "Hello"
3140
}
3241
}`, true),
33-
Entry("has static void main with whitespace variations", `class MyApp {
42+
Entry("has static void main with extra whitespace", `class MyApp {
3443
static void main ( String[] args ) {
3544
println "Hello"
3645
}
3746
}`, true),
38-
Entry("no main method", `class Alpha {
47+
Entry("no main method", `class Alpha {
3948
}`, false),
40-
Entry("simple script no main", `println 'Hello World'`, false),
41-
Entry("instance method not static main", `class Test {
49+
Entry("simple script no main", `println 'Hello World'`, false),
50+
Entry("instance method not static main", `class Test {
4251
void main() {
4352
println "Not static"
4453
}
4554
}`, false),
46-
)
47-
})
48-
49-
var _ = Describe("IsPOGO", func() {
50-
DescribeTable("detecting Plain Old Groovy Objects",
51-
func(content string, expected bool) {
52-
tmpFile, err := os.CreateTemp("", "test-*.groovy")
53-
Expect(err).NotTo(HaveOccurred())
54-
defer os.Remove(tmpFile.Name())
55+
)
5556

56-
_, err = tmpFile.WriteString(content)
57-
Expect(err).NotTo(HaveOccurred())
58-
tmpFile.Close()
57+
It("returns false for an unreadable file", func() {
58+
Expect(g.HasMainMethod("/nonexistent/file.groovy")).To(BeFalse())
59+
})
60+
})
5961

60-
result, err := containers.IsPOGO(tmpFile.Name())
61-
Expect(err).NotTo(HaveOccurred())
62-
Expect(result).To(Equal(expected))
63-
},
64-
Entry("simple class definition", `class Alpha {
62+
Describe("IsPOGO", func() {
63+
DescribeTable("detecting Plain Old Groovy Objects",
64+
func(content string, expected bool) {
65+
Expect(g.IsPOGO(groovyFile(content))).To(Equal(expected))
66+
},
67+
Entry("simple class definition", `class Alpha {
6568
}`, true),
66-
Entry("class with inheritance", `class MyApp extends BaseApp {
69+
Entry("class with inheritance", `class MyApp extends BaseApp {
6770
void run() {}
6871
}`, true),
69-
Entry("simple script no class", `println 'Hello World'`, false),
70-
Entry("script with variables no class", `def name = "World"
72+
Entry("simple script no class", `println 'Hello World'`, false),
73+
Entry("script with variables no class", `def name = "World"
7174
println "Hello $name"`, false),
72-
Entry("class keyword in comment", `// This is not a class
75+
Entry("class keyword in comment", `// This is not a class
7376
println 'Hello'`, false),
74-
Entry("class keyword in string", `println "This mentions class but isn't one"`, false),
75-
)
76-
})
77-
78-
var _ = Describe("HasShebang", func() {
79-
DescribeTable("detecting shebang in Groovy files",
80-
func(content string, expected bool) {
81-
tmpFile, err := os.CreateTemp("", "test-*.groovy")
82-
Expect(err).NotTo(HaveOccurred())
83-
defer os.Remove(tmpFile.Name())
77+
Entry("class keyword in string", `println "This mentions class but isn't one"`, false),
78+
)
8479

85-
_, err = tmpFile.WriteString(content)
86-
Expect(err).NotTo(HaveOccurred())
87-
tmpFile.Close()
80+
It("returns false for an unreadable file", func() {
81+
Expect(g.IsPOGO("/nonexistent/file.groovy")).To(BeFalse())
82+
})
83+
})
8884

89-
result, err := containers.HasShebang(tmpFile.Name())
90-
Expect(err).NotTo(HaveOccurred())
91-
Expect(result).To(Equal(expected))
92-
},
93-
Entry("has shebang", `#!/usr/bin/env groovy
94-
println 'Hello World'`, true),
95-
Entry("has groovy shebang", `#!/usr/bin/groovy
96-
println 'Hello'`, true),
97-
Entry("no shebang", `class Alpha {
85+
Describe("HasShebang", func() {
86+
DescribeTable("detecting shebang in Groovy files",
87+
func(content string, expected bool) {
88+
Expect(g.HasShebang(groovyFile(content))).To(Equal(expected))
89+
},
90+
Entry("has shebang", "#!/usr/bin/env groovy\nprintln 'Hello World'", true),
91+
Entry("has groovy shebang", "#!/usr/bin/groovy\nprintln 'Hello'", true),
92+
Entry("no shebang", `class Alpha {
9893
}`, false),
99-
Entry("shebang not at start", `
100-
#!/usr/bin/env groovy
94+
Entry("shebang not at start", "\n#!/usr/bin/env groovy\nprintln 'Hello'", false),
95+
Entry("comment mentioning shebang", `// Use #!/usr/bin/env groovy at the top
10196
println 'Hello'`, false),
102-
Entry("comment mentioning shebang", `// Use #!/usr/bin/env groovy at the top
103-
println 'Hello'`, false),
104-
)
97+
)
98+
99+
It("returns false for an unreadable file", func() {
100+
Expect(g.HasShebang("/nonexistent/file.groovy")).To(BeFalse())
101+
})
102+
})
103+
104+
Describe("IsBeans", func() {
105+
DescribeTable("detecting beans-style configuration",
106+
func(content string, expected bool) {
107+
Expect(g.IsBeans(groovyFile(content))).To(Equal(expected))
108+
},
109+
Entry("has beans block", `beans {
110+
bean(MyBean)
111+
}`, true),
112+
Entry("no beans block", `class Alpha {}`, false),
113+
)
114+
})
105115
})
106116

107117
var _ = Describe("FindMainGroovyScript", func() {
@@ -117,32 +127,24 @@ var _ = Describe("FindMainGroovyScript", func() {
117127
os.RemoveAll(tmpDir)
118128
})
119129

130+
writeFile := func(name, content string) string {
131+
path := filepath.Join(tmpDir, name)
132+
Expect(os.WriteFile(path, []byte(content), 0644)).To(Succeed())
133+
return path
134+
}
135+
120136
Context("with various Groovy script types", func() {
121137
var pogoFile, nonPogoFile, mainMethodFile, shebangFile string
122138

123139
BeforeEach(func() {
124-
var err error
125-
126-
pogoFile = filepath.Join(tmpDir, "Alpha.groovy")
127-
err = os.WriteFile(pogoFile, []byte("class Alpha {}"), 0644)
128-
Expect(err).NotTo(HaveOccurred())
129-
130-
nonPogoFile = filepath.Join(tmpDir, "Application.groovy")
131-
err = os.WriteFile(nonPogoFile, []byte("println 'Hello World'"), 0644)
132-
Expect(err).NotTo(HaveOccurred())
133-
134-
mainMethodFile = filepath.Join(tmpDir, "Main.groovy")
135-
mainContent := `class Main {
140+
pogoFile = writeFile("Alpha.groovy", "class Alpha {}")
141+
nonPogoFile = writeFile("Application.groovy", "println 'Hello World'")
142+
mainMethodFile = writeFile("Main.groovy", `class Main {
136143
static void main(String[] args) {
137144
println "Main"
138145
}
139-
}`
140-
err = os.WriteFile(mainMethodFile, []byte(mainContent), 0644)
141-
Expect(err).NotTo(HaveOccurred())
142-
143-
shebangFile = filepath.Join(tmpDir, "Script.groovy")
144-
err = os.WriteFile(shebangFile, []byte("#!/usr/bin/env groovy\nprintln 'Script'"), 0644)
145-
Expect(err).NotTo(HaveOccurred())
146+
}`)
147+
shebangFile = writeFile("Script.groovy", "#!/usr/bin/env groovy\nprintln 'Script'")
146148
})
147149

148150
DescribeTable("finding the main Groovy script",
@@ -176,17 +178,11 @@ var _ = Describe("FindMainGroovyScript", func() {
176178
})
177179

178180
Context("with invalid files", func() {
179-
It("should skip invalid files and select valid ones", func() {
180-
invalidFile := filepath.Join(tmpDir, "invalid.groovy")
181-
err := os.WriteFile(invalidFile, []byte{0xff, 0xfe}, 0644)
182-
Expect(err).NotTo(HaveOccurred())
183-
184-
validFile := filepath.Join(tmpDir, "valid.groovy")
185-
err = os.WriteFile(validFile, []byte("println 'Hello'"), 0644)
186-
Expect(err).NotTo(HaveOccurred())
181+
It("skips invalid files and selects the valid one", func() {
182+
invalidFile := writeFile("invalid.groovy", string([]byte{0xff, 0xfe}))
183+
validFile := writeFile("valid.groovy", "println 'Hello'")
187184

188-
scripts := []string{invalidFile, validFile}
189-
result, err := containers.FindMainGroovyScript(scripts)
185+
result, err := containers.FindMainGroovyScript([]string{invalidFile, validFile})
190186
Expect(err).NotTo(HaveOccurred())
191187
Expect(result).To(Equal(validFile))
192188
})

0 commit comments

Comments
 (0)