Skip to content

Commit 6a3684d

Browse files
Add support for v2 compatible go projects with submodule as root (#594)
Co-authored-by: asafz <[email protected]>
1 parent b24ff9c commit 6a3684d

File tree

2 files changed

+99
-14
lines changed

2 files changed

+99
-14
lines changed

build-info-extractor-go/src/main/java/org/jfrog/build/extractor/go/extractor/GoZipBallStreamer.java

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,14 @@
4141
import org.apache.commons.compress.archivers.zip.ZipFile;
4242
import org.apache.commons.io.IOUtils;
4343
import org.apache.commons.lang3.StringUtils;
44+
import org.apache.commons.lang3.exception.ExceptionUtils;
4445
import org.jfrog.build.api.util.Log;
4546

4647
import java.io.Closeable;
4748
import java.io.File;
4849
import java.io.IOException;
50+
import java.io.InputStream;
51+
import java.nio.charset.StandardCharsets;
4952
import java.util.Enumeration;
5053
import java.util.Set;
5154
import java.util.zip.ZipEntry;
@@ -58,12 +61,13 @@ public class GoZipBallStreamer implements Closeable {
5861

5962
protected ArchiveOutputStream archiveOutputStream;
6063
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;
6771
private static final String VENDOR = "vendor/";
6872

6973
public GoZipBallStreamer(ZipFile zipFile, String projectName, String version, Log log) {
@@ -89,15 +93,22 @@ protected void packProject() throws IOException {
8993
writeEntries();
9094
}
9195

96+
void setSubModuleName(String subModuleName) {
97+
this.subModuleName = subModuleName;
98+
}
99+
92100
/**
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
94102
*/
95103
private void initiateProjectType() {
96104
boolean compatibleModuleFromV2 = GoVersionUtils.getMajorVersion(version, log) >= 2 &&
97105
GoVersionUtils.isCompatibleGoModuleNaming(projectName, version, log);
98106
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+
}
101112
} else {
102113
subModuleName = GoVersionUtils.getSubModule(projectName);
103114
if (shouldPackSubModule()) {
@@ -195,7 +206,7 @@ private void scanEntries() {
195206
while (entries.hasMoreElements()) {
196207
zipEntry = entries.nextElement();
197208
if (!zipEntry.isDirectory() && isSubModule(zipEntry.getName())) {
198-
String subModulePath = zipEntry.getName().replace(MOD_FILE, "");
209+
String subModulePath = zipEntry.getName().replace(MOD_FILE_PATH, "");
199210
excludedDirectories.add(subModulePath);
200211
} else {
201212
allDirectories.add(GoVersionUtils.getParent(zipEntry.getName()));
@@ -237,15 +248,47 @@ private boolean isSubFolderOfAnotherModule(String moduleRootDir, String director
237248
/**
238249
* @return True if the entry is a go.mod file which doesn't belong to the project we are packing
239250
*/
240-
private boolean isSubModule(String entryName) {
241-
if (entryName.endsWith(MOD_FILE)) {
251+
boolean isSubModule(String entryName) {
252+
if (entryName.endsWith(MOD_FILE_PATH)) {
242253
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));
244255
} 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+
}
246288
}
247289
}
248290
return false;
291+
249292
}
250293

251294
/**
@@ -292,4 +335,18 @@ private static String stripFirstPathElement(String path) {
292335
public void close() throws IOException {
293336
zipFile.close();
294337
}
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+
}
295352
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.jfrog.build.extractor.go.extractor;
2+
3+
import org.testng.Assert;
4+
import org.testng.annotations.DataProvider;
5+
import org.testng.annotations.Test;
6+
7+
8+
public class GoZipBallStreamerTest {
9+
@Test(dataProvider = "testIsSubModuleProvider")
10+
public void testIsSubModule(String subModuleName, String entryName, boolean expectedResult) {
11+
GoZipBallStreamer goZipBallStreamer = new GoZipBallStreamer(null, "ignore", "ignore", null);
12+
goZipBallStreamer.setSubModuleName(subModuleName);
13+
boolean res = goZipBallStreamer.isSubModule(entryName);
14+
Assert.assertEquals(res, expectedResult);
15+
}
16+
17+
@DataProvider
18+
private Object[][] testIsSubModuleProvider() {
19+
return new Object[][]{
20+
{"", "root/go.mod", false},
21+
{"", "go.mod", false},
22+
{"", "root/v2/go.mod", true},
23+
{"v2", "root/go.mod", true},
24+
{"v2", "root/v2/go.mod", false}
25+
};
26+
}
27+
28+
}

0 commit comments

Comments
 (0)