41
41
import org .apache .commons .compress .archivers .zip .ZipFile ;
42
42
import org .apache .commons .io .IOUtils ;
43
43
import org .apache .commons .lang3 .StringUtils ;
44
+ import org .apache .commons .lang3 .exception .ExceptionUtils ;
44
45
import org .jfrog .build .api .util .Log ;
45
46
46
47
import java .io .Closeable ;
47
48
import java .io .File ;
48
49
import java .io .IOException ;
50
+ import java .io .InputStream ;
51
+ import java .nio .charset .StandardCharsets ;
49
52
import java .util .Enumeration ;
50
53
import java .util .Set ;
51
54
import java .util .zip .ZipEntry ;
@@ -58,12 +61,13 @@ public class GoZipBallStreamer implements Closeable {
58
61
59
62
protected ArchiveOutputStream archiveOutputStream ;
60
63
private final Log log ;
61
- private ZipFile zipFile ;
62
- private String projectName ;
63
- private String version ;
64
- private Set <String > excludedDirectories ;
65
- private String subModuleName ;
66
- private static final String MOD_FILE = "/go.mod" ;
64
+ private final ZipFile zipFile ;
65
+ private final String projectName ;
66
+ private final String version ;
67
+ private final Set <String > excludedDirectories ;
68
+ private String subModuleName = "" ;
69
+ private static final String MOD_FILE = "go.mod" ;
70
+ private static final String MOD_FILE_PATH = "/" + MOD_FILE ;
67
71
private static final String VENDOR = "vendor/" ;
68
72
69
73
public GoZipBallStreamer (ZipFile zipFile , String projectName , String version , Log log ) {
@@ -89,15 +93,22 @@ protected void packProject() throws IOException {
89
93
writeEntries ();
90
94
}
91
95
96
+ void setSubModuleName (String subModuleName ) {
97
+ this .subModuleName = subModuleName ;
98
+ }
99
+
92
100
/**
93
- * Determine if the project is a compatible module from version 2+, a sub module or an incompatible module
101
+ * Determine if the project is a compatible module from version 2+, a submodule or an incompatible module
94
102
*/
95
103
private void initiateProjectType () {
96
104
boolean compatibleModuleFromV2 = GoVersionUtils .getMajorVersion (version , log ) >= 2 &&
97
105
GoVersionUtils .isCompatibleGoModuleNaming (projectName , version , log );
98
106
if (compatibleModuleFromV2 ) {
99
- subModuleName = "v" + GoVersionUtils .getMajorProjectVersion (projectName , log );
100
- log .debug (projectName + "@" + version + " is compatible Go module from major version " + subModuleName );
107
+ String majorVersion = "v" + GoVersionUtils .getMajorProjectVersion (projectName , log );
108
+ if (!hasRootModFileOfCompatibleModuleFromV2 (majorVersion )) {
109
+ subModuleName = majorVersion ;
110
+ log .debug (projectName + "@" + version + " is compatible Go module from major version " + subModuleName );
111
+ }
101
112
} else {
102
113
subModuleName = GoVersionUtils .getSubModule (projectName );
103
114
if (shouldPackSubModule ()) {
@@ -195,7 +206,7 @@ private void scanEntries() {
195
206
while (entries .hasMoreElements ()) {
196
207
zipEntry = entries .nextElement ();
197
208
if (!zipEntry .isDirectory () && isSubModule (zipEntry .getName ())) {
198
- String subModulePath = zipEntry .getName ().replace (MOD_FILE , "" );
209
+ String subModulePath = zipEntry .getName ().replace (MOD_FILE_PATH , "" );
199
210
excludedDirectories .add (subModulePath );
200
211
} else {
201
212
allDirectories .add (GoVersionUtils .getParent (zipEntry .getName ()));
@@ -237,15 +248,47 @@ private boolean isSubFolderOfAnotherModule(String moduleRootDir, String director
237
248
/**
238
249
* @return True if the entry is a go.mod file which doesn't belong to the project we are packing
239
250
*/
240
- private boolean isSubModule (String entryName ) {
241
- if (entryName .endsWith (MOD_FILE )) {
251
+ boolean isSubModule (String entryName ) {
252
+ if (entryName .endsWith (MOD_FILE_PATH )) {
242
253
if (shouldPackSubModule ()) {
243
- return (!entryName .substring (entryName .indexOf ('/' ) + 1 ).endsWith (subModuleName + MOD_FILE ));
254
+ return (!entryName .substring (entryName .indexOf ('/' ) + 1 ).endsWith (subModuleName + MOD_FILE_PATH ));
244
255
} else {
245
- return entryName .substring (entryName .indexOf ('/' ) + 1 ).endsWith (subModuleName + MOD_FILE );
256
+ return !entryName .substring (entryName .indexOf ('/' ) + 1 ).equals (MOD_FILE );
257
+ }
258
+ }
259
+ return false ;
260
+ }
261
+
262
+ /**
263
+ * @param majorVersion - major version of a compatible v2 project
264
+ * example zip structure:
265
+ * - go.mod (module github.com/owner/repo/v2)
266
+ * - hello.go
267
+ * - v3/
268
+ * majorVersion = v3 -> false
269
+ * majorVersion = v2 -> true
270
+ * @return true, if the go.mod file in the root specifies a path of submodule of the given major version
271
+ */
272
+ private boolean hasRootModFileOfCompatibleModuleFromV2 (String majorVersion ) {
273
+ Enumeration <? extends ZipArchiveEntry > entries = zipFile .getEntries ();
274
+ // no need to iterate over the entire zip, all we need is to check the content of the go.mod file in the root (if exists)
275
+ if (entries .hasMoreElements ()) {
276
+ ZipArchiveEntry zipEntry = entries .nextElement ();
277
+ ZipArchiveEntry modEntry = zipFile .getEntry (getFirstPathElement (zipEntry .getName ()) + MOD_FILE_PATH );
278
+ if (modEntry == null ) {
279
+ modEntry = zipFile .getEntry (MOD_FILE );
280
+ }
281
+ if (modEntry != null ) {
282
+ try (InputStream inputStream = zipFile .getInputStream (modEntry )) {
283
+ String modFileContent = IOUtils .toString (inputStream , StandardCharsets .UTF_8 .name ());
284
+ return StringUtils .substringBefore (modFileContent , System .lineSeparator ()).endsWith ("/" + majorVersion );
285
+ } catch (IOException e ) {
286
+ log .warn ("Failed to read go.mod file of the root project: " + ExceptionUtils .getRootCauseMessage (e ));
287
+ }
246
288
}
247
289
}
248
290
return false ;
291
+
249
292
}
250
293
251
294
/**
@@ -292,4 +335,18 @@ private static String stripFirstPathElement(String path) {
292
335
public void close () throws IOException {
293
336
zipFile .close ();
294
337
}
338
+
339
+ /**
340
+ * null -> null
341
+ * example/file -> example
342
+ *
343
+ * @return first path element without '/' as a string
344
+ */
345
+ private String getFirstPathElement (String path ) {
346
+ if (path == null ) {
347
+ return "" ;
348
+ }
349
+ path = StringUtils .removeStart (path , "/" );
350
+ return StringUtils .substringBefore (path , "/" );
351
+ }
295
352
}
0 commit comments