• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

moosetechnology / MooseNexus / 12844033223

18 Jan 2025 12:34PM UTC coverage: 36.172% (+0.03%) from 36.147%
12844033223

push

github

Gabriel-Darbord
Gradle: Simplify search of first line + Ensure group is not empty string
Even in the absolute minimal build file (which is empty), a project always exist

0 of 5 new or added lines in 1 file covered. (0.0%)

531 of 1468 relevant lines covered (36.17%)

1.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/MooseNexus/NexusGradleProjectImporter.class.st
1
"
2
I am responsible for importing a project that uses Gradle to manage dependencies.
3

4
Gradle has the `dependencies` option to print the full dependency tree, which contains redundancies and is difficult to parse. However, it also allows the definition of custom tasks, which are used to efficiently obtain the project description.
5

6
This freedom also means that the project definition does not follow a strict standard. In particular, the group, name, and version can be omitted or defined in different ways. To overcome this, users can write custom code to retrieve this data.
7
"
8
Class {
9
        #name : 'NexusGradleProjectImporter',
10
        #superclass : 'NexusManagedProjectImporter',
11
        #instVars : [
12
                'groupCode',
13
                'nameCode',
14
                'versionCode'
15
        ],
16
        #category : 'MooseNexus-Importer',
17
        #package : 'MooseNexus',
18
        #tag : 'Importer'
19
}
20

21
{ #category : 'testing' }
22
NexusGradleProjectImporter class >> canHandle: directory [
×
23

×
24
        | directoryReference |
×
25
        directoryReference := directory asFileReference.
×
26
        directoryReference / 'build.gradle' ifExists: [ ^ true ].
×
27
        directoryReference / 'build.gradle.kts' ifExists: [ ^ true ].
×
28
        ^ false
×
29
]
×
30

31
{ #category : 'constants' }
32
NexusGradleProjectImporter class >> taskName [
×
33

×
34
        ^ 'mooseNexusListDependencies'
×
35
]
×
36

37
{ #category : 'enumerating' }
38
NexusGradleProjectImporter >> findBuildFile [
×
39

×
40
        directory / 'build.gradle' ifExists: [ :file | ^ file ].
×
41
        directory / 'build.gradle.kts' ifExists: [ :file | ^ file ].
×
42
        Error signal: 'Cannot find gradle build file'
×
43
]
×
44

45
{ #category : 'enumerating' }
46
NexusGradleProjectImporter >> findGradleCommand [
×
47
        "Return the name of the gradle executable, can be a wrapper or the global 'gradle' command."
×
48

×
49
        OSPlatform current isWindows
×
50
                ifTrue: [ directory / 'gradlew.bat' ifExists: [ ^ 'gradlew.bat' ] ]
×
51
                ifFalse: [ "assume *nix"
×
52
                directory / 'gradlew' ifExists: [ ^ './gradlew' ] ].
×
53
        "assume gradle is installed"
×
54
        ^ 'gradle'
×
55
]
×
56

57
{ #category : 'constants' }
58
NexusGradleProjectImporter >> groovyCodeForDependencies [
×
59
        "Code to insert into a build.gradle file to get the flat list of dependencies."
×
60

×
61
        ^ String streamContents: [ :s |
×
62
                  s << 'tasks.register("' << self class taskName << '") {
×
63
        doLast {
×
64
                def rootDirPath = rootDir.canonicalPath
×
65
                allprojects.findAll { it.projectDir.canonicalPath.startsWith(rootDirPath) }.each { project ->
×
66
                        println "-${' << self groupCode << '}:${' << self nameCode
×
67
                  << '}:${' << self versionCode << '}"
×
68
                        def projectDirPath = project.projectDir.canonicalPath
×
69
                        if (projectDirPath.equals(rootDirPath)) {
×
70
                                println "."
×
71
                        } else {
×
72
                                println(projectDirPath.substring(rootDirPath.length() + 1))
×
73
                        }
×
74
                        configurations.findAll { it.canBeResolved }.each { configuration ->
×
75
                                def scope = configuration.name
×
76
                                configuration.resolvedConfiguration.resolvedArtifacts.each { artifact ->
×
77
                                        def group = artifact.moduleVersion.id.group
×
78
                                        def name = artifact.name
×
79
                                        def type = artifact.extension ?: "jar"
×
80
                                        def version = artifact.moduleVersion.id.version
×
81
                                        def path = artifact.file.absolutePath // forces artifact download
×
82
                                        println "$group:$name:$type:$version:$scope:$path"
×
83
                                }
×
84
                        }
×
85
                }
×
86
        }
×
87
}' ]
×
88
]
×
89

90
{ #category : 'accessing' }
91
NexusGradleProjectImporter >> groupCode [
×
92

×
93
        ^ groupCode ifNil: [ groupCode := 'project.group' ]
×
94
]
×
95

96
{ #category : 'accessing' }
97
NexusGradleProjectImporter >> groupCode: aString [
×
98
        "Define the code that returns the group string of a project."
×
99

×
100
        groupCode := aString
×
101
]
×
102

103
{ #category : 'initialization' }
104
NexusGradleProjectImporter >> initialize [
×
105

×
106
        localRepositories := {
×
107
                                     NexusGradleRepository default.
×
108
                                     NexusMavenRepository default }
×
109
]
×
110

111
{ #category : 'constants' }
112
NexusGradleProjectImporter >> kotlinCodeForDependencies [
×
113
        "Code to insert into a build.gradle.kts file to get the flat list of dependencies.
×
114
        Kotlin is sensitive to line endings, so they are removed for convenience."
×
115

×
116
        ^ String streamContents: [ :s |
×
117
                  s << 'tasks.register("' << self class taskName
×
118
                  <<
×
119
                  '") { doLast { val rootDirPath = rootDir.canonicalPath; allprojects.filter { it.projectDir.canonicalPath.startsWith(rootDirPath) }.forEach { project -> println("-${'
×
120
                  << self groupCode << '}:${' << self nameCode << '}:${'
×
121
                  << self versionCode
×
122
                  <<
×
123
                  '}"); val projectDirPath = project.projectDir.canonicalPath; if (projectDirPath.equals(rootDirPath)) { println(".") } else { println(projectDirPath.substring(rootDirPath.length + 1)) }; configurations.filter { it.isCanBeResolved }.forEach { configuration -> val scope = configuration.name; configuration.resolvedConfiguration.resolvedArtifacts.forEach { artifact -> val group = artifact.moduleVersion.id.group; val name = artifact.name; val type = artifact.extension ?: "jar"; val version = artifact.moduleVersion.id.version; val path = artifact.file.absolutePath; /* forces artifact download */ println("$group:$name:$type:$version:$scope:$path") }}}}}' ]
×
124
]
×
125

126
{ #category : 'accessing' }
127
NexusGradleProjectImporter >> nameCode [
×
128

×
129
        ^ nameCode ifNil: [ nameCode := 'project.name' ]
×
130
]
×
131

132
{ #category : 'accessing' }
133
NexusGradleProjectImporter >> nameCode: aString [
×
134
        "Define the code that returns the name string of a project."
×
135

×
136
        nameCode := aString
×
137
]
×
138

139
{ #category : 'instance creation' }
140
NexusGradleProjectImporter >> newProject [
×
141

×
142
        ^ NexusGradleProject new
×
143
]
×
144

145
{ #category : 'parsing' }
146
NexusGradleProjectImporter >> parse: raw [
×
147
        "Parses the result of the command listing the dependencies.
×
148
        Resolves each dependency to obtain the path to its local location.
×
149
        The result of the command is formatted as follows for each project:
×
150
-group:name:version
×
151
directory
×
152
(group:name:type:version:scope:path)*"
×
153

×
154
        | lines line index |
×
155
        lines := raw lines.
×
156
        index := 2.
×
NEW
157

×
NEW
158
        [ "project info starts at third line at the earliest, after the line '> Task ...'"
×
NEW
159
        (line := lines at: (index := index + 1)) isNotEmpty and: [
×
NEW
160
                line first == $- ] ] whileFalse.
×
161

×
162
        [ (line := lines at: index) isNotEmpty ] whileTrue: [
×
163
                | group name version descriptor dependencies |
×
164
                "read project descriptor"
×
165
                descriptor := $: split: line allButFirst.
×
NEW
166
                group := descriptor first ifEmpty: [ 'unknown' ].
×
167
                name := descriptor second.
×
168
                version := descriptor third.
×
169

×
170
                "register the project with its version; project name is unique so this never overwrites"
×
171
                descriptor := Dictionary with: #version -> version.
×
172
                project projects at: group at: name put: descriptor.
×
173

×
174
                "read project location, relative to the directory"
×
175
                descriptor at: #path put: (lines at: (index := index + 1)).
×
176

×
177
                "read dependencies per scope"
×
178
                dependencies := Dictionary new.
×
179
                [
×
180
                (line := lines at: (index := index + 1)) isEmpty or: [
×
181
                        line first == $- ] ] whileFalse: [ "resolve the descriptor: 'group:name:type:version:scope:path'"
×
182
                        | mapping |
×
183
                        mapping := self resolve: line.
×
184
                        dependencies
×
185
                                at: mapping key
×
186
                                ifPresent: [ :descriptors | descriptors add: mapping value ]
×
187
                                ifAbsentPut: [ OrderedCollection with: mapping value ] ].
×
188

×
189
                "the current project has been fully parsed
×
190
                save the dependencies as arrays for JSON compatibility"
×
191
                descriptor at: #dependencies put: (dependencies collect: #asArray) ]
×
192
]
×
193

194
{ #category : 'reading' }
195
NexusGradleProjectImporter >> read [
×
196
        "Gradle does not provide a command to output flat dependencies.
×
197
        Instead of implementing dark magic parsing, we add a task to the build file and run it.
×
198
        Either use the global 'gradle' command or a local 'gradlew' wrapper."
×
199

×
200
        | buildFile gradleCmd originalSize |
×
201
        buildFile := self findBuildFile.
×
202
        gradleCmd := self findGradleCommand.
×
203
        originalSize := buildFile size. "to restore it later"
×
204

×
205
        ^ [ "append code to the build file for a task to extract resolved dependencies"
×
206
          buildFile writeStreamDo: [ :ws |
×
207
                  ws setToEnd crlf << (buildFile extension = 'kts'
×
208
                           ifTrue: [ self kotlinCodeForDependencies ]
×
209
                           ifFalse: [ self groovyCodeForDependencies ]) ].
×
210

×
211
          "execute the task we inserted and return the raw terminal output"
×
212
          LibC resultOfCommand:
×
213
                  'cd "' , directory pathString , '" && ' , gradleCmd , ' '
×
214
                  , self class taskName ] ensure: [ "restore the original build file"
×
215
                  (File named: buildFile fullName) writeStreamDo: [ :ws |
×
216
                          ws truncate: originalSize ] ]
×
217
]
×
218

219
{ #category : 'accessing' }
220
NexusGradleProjectImporter >> versionCode [
×
221

×
222
        ^ versionCode ifNil: [ versionCode := 'project.version' ]
×
223
]
×
224

225
{ #category : 'accessing' }
226
NexusGradleProjectImporter >> versionCode: aString [
×
227
        "Define the code that returns the version string of a project."
×
228

×
229
        versionCode := aString
×
230
]
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc