Skip to content

Commit f414e28

Browse files
Merge pull request #1279 from akash-manna-sky/JENKINS-75009
[JENKINS-75009] Support remapping source file paths in reports
1 parent 4f06425 commit f414e28

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed

src/main/java/edu/hm/hafner/analysis/FileNameResolver.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,28 @@ public class FileNameResolver {
2929
*/
3030
public void run(final Report report, final String sourceDirectoryPrefix,
3131
final Predicate<String> skipFileNamePredicate) {
32+
run(report, sourceDirectoryPrefix, skipFileNamePredicate, "", "");
33+
}
34+
35+
/**
36+
* Resolves the file names of the affected files of the specified set of issues with optional path remapping.
37+
* This is useful when the paths in the report are generated in a different environment (e.g., inside a Docker
38+
* container) and need to be remapped to the actual workspace paths.
39+
*
40+
* @param report
41+
* the issues to resolve the paths
42+
* @param sourceDirectoryPrefix
43+
* absolute source path that should be used as parent folder to search for files
44+
* @param skipFileNamePredicate
45+
* skip specific files based on the file name
46+
* @param sourcePathPrefix
47+
* the path prefix to be replaced (e.g., path inside docker container). Empty string means no remapping.
48+
* @param targetPathPrefix
49+
* the path prefix to replace with (e.g., path in Jenkins workspace). Empty string means no remapping.
50+
*/
51+
public void run(final Report report, final String sourceDirectoryPrefix,
52+
final Predicate<String> skipFileNamePredicate, final String sourcePathPrefix,
53+
final String targetPathPrefix) {
3254
Set<String> filesToProcess = report.getFiles()
3355
.stream()
3456
.filter(fileName -> isInterestingFileName(fileName, skipFileNamePredicate))
@@ -40,6 +62,8 @@ public void run(final Report report, final String sourceDirectoryPrefix,
4062
return;
4163
}
4264

65+
filesToProcess = applyPathMapping(report, sourcePathPrefix, targetPathPrefix, skipFileNamePredicate);
66+
4367
Map<String, String> pathMapping = filesToProcess.parallelStream()
4468
.collect(Collectors.toMap(fileName -> fileName,
4569
fileName -> makeRelative(sourceDirectoryPrefix, fileName)))
@@ -57,6 +81,43 @@ public void run(final Report report, final String sourceDirectoryPrefix,
5781
pathMapping.size(), filesToProcess.size() - pathMapping.size());
5882
}
5983

84+
/**
85+
* Applies path mapping to the issues in the report. This remaps file paths from one prefix to another, which is
86+
* useful when the report was generated in a different environment (e.g., inside a Docker container).
87+
*
88+
* @param report
89+
* the issues to remap paths for
90+
* @param sourcePathPrefix
91+
* the path prefix to be replaced (e.g., path inside docker container). Empty string means no remapping.
92+
* @param targetPathPrefix
93+
* the path prefix to replace with (e.g., path in Jenkins workspace). Empty string means no remapping.
94+
* @param skipFileNamePredicate
95+
* skip specific files based on the file name
96+
*
97+
* @return the updated set of files to process after remapping
98+
*/
99+
private Set<String> applyPathMapping(final Report report, final String sourcePathPrefix,
100+
final String targetPathPrefix, final Predicate<String> skipFileNamePredicate) {
101+
boolean shouldRemap = !sourcePathPrefix.isEmpty() && !targetPathPrefix.isEmpty();
102+
if (shouldRemap) {
103+
report.logInfo("-> remapping paths from '%s' to '%s'", sourcePathPrefix, targetPathPrefix);
104+
try (var builder = new IssueBuilder()) {
105+
report.stream()
106+
.filter(issue -> issue.getFileName().startsWith(sourcePathPrefix))
107+
.forEach(issue -> {
108+
String originalPath = issue.getFileName();
109+
String remappedPath = targetPathPrefix + originalPath.substring(sourcePathPrefix.length());
110+
issue.setFileName(issue.getPath(), builder.internFileName(remappedPath));
111+
});
112+
}
113+
}
114+
115+
return report.getFiles()
116+
.stream()
117+
.filter(fileName -> isInterestingFileName(fileName, skipFileNamePredicate))
118+
.collect(Collectors.toSet());
119+
}
120+
60121
private String makeRelative(final String sourceDirectoryPrefix, final String fileName) {
61122
return PATH_UTIL.getRelativePath(sourceDirectoryPrefix, fileName);
62123
}

src/test/java/edu/hm/hafner/analysis/FileNameResolverTest.java

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,80 @@ private void assertThatOneFileIsUnresolved(final Report report) {
229229
assertThat(report.getInfoMessages().get(0)).contains("1 not found");
230230
}
231231

232+
@Test
233+
@DisplayName("Should remap paths from source to target prefix")
234+
void shouldRemapPathPrefixes() {
235+
try (var builder = new IssueBuilder()) {
236+
var dockerPath = "/app/src";
237+
var dockerFilePath = dockerPath + "/relative.txt";
238+
var report = new Report();
239+
report.add(builder.setFileName(dockerFilePath).build());
240+
241+
new FileNameResolver().run(report, RESOURCE_FOLDER_STRING, f -> false, dockerPath, RESOURCE_FOLDER_STRING);
242+
243+
assertThat(report).hasSize(1);
244+
assertThat(report.get(0)).hasFileName(RELATIVE_FILE).hasPath(RESOURCE_FOLDER_STRING);
245+
assertThat(report.getInfoMessages()).hasSize(2);
246+
assertThat(report.getInfoMessages().get(0)).contains("remapping paths from");
247+
assertThat(report.getInfoMessages().get(1)).contains("1 found");
248+
}
249+
}
250+
251+
@Test
252+
@DisplayName("Should not remap paths when source prefix is empty")
253+
void shouldNotRemapWhenSourcePrefixEmpty() {
254+
try (var builder = new IssueBuilder()) {
255+
var report = new Report();
256+
report.add(builder.setFileName(RELATIVE_FILE).build());
257+
258+
new FileNameResolver().run(report, RESOURCE_FOLDER_STRING, f -> false, "", "/some/path");
259+
260+
assertThat(report).hasSize(1);
261+
assertThat(report.get(0)).hasFileName(RELATIVE_FILE).hasPath(RESOURCE_FOLDER_STRING);
262+
assertThat(report.getInfoMessages()).hasSize(1);
263+
assertThat(report.getInfoMessages().get(0)).contains("1 found");
264+
}
265+
}
266+
267+
@Test
268+
@DisplayName("Should not remap paths when target prefix is empty")
269+
void shouldNotRemapWhenTargetPrefixEmpty() {
270+
try (var builder = new IssueBuilder()) {
271+
var report = new Report();
272+
report.add(builder.setFileName(RELATIVE_FILE).build());
273+
274+
new FileNameResolver().run(report, RESOURCE_FOLDER_STRING, f -> false, "/some/path", "");
275+
276+
assertThat(report).hasSize(1);
277+
assertThat(report.get(0)).hasFileName(RELATIVE_FILE).hasPath(RESOURCE_FOLDER_STRING);
278+
assertThat(report.getInfoMessages()).hasSize(1); // only resolving, no remapping
279+
assertThat(report.getInfoMessages().get(0)).contains("1 found");
280+
}
281+
}
282+
283+
@Test
284+
@DisplayName("Should only remap paths that start with source prefix")
285+
void shouldOnlyRemapMatchingPaths() {
286+
try (var builder = new IssueBuilder()) {
287+
var dockerPath = "/docker/src";
288+
var otherPath = "/other/path";
289+
var report = new Report();
290+
report.add(builder.setFileName(dockerPath + "/relative.txt").build());
291+
report.add(builder.setFileName(otherPath + "/other.txt").build());
292+
report.add(builder.setFileName(RELATIVE_FILE).build());
293+
294+
new FileNameResolver().run(report, RESOURCE_FOLDER_STRING, f -> false, dockerPath, RESOURCE_FOLDER_STRING);
295+
296+
assertThat(report).hasSize(3);
297+
assertThat(report.get(0)).hasFileName(RELATIVE_FILE).hasPath(RESOURCE_FOLDER_STRING);
298+
assertThat(report.get(1)).hasFileName(otherPath + "/other.txt");
299+
assertThat(report.get(2)).hasFileName(RELATIVE_FILE).hasPath(RESOURCE_FOLDER_STRING);
300+
301+
assertThat(report.getInfoMessages()).hasSize(2);
302+
assertThat(report.getInfoMessages().get(0)).contains("remapping paths");
303+
}
304+
}
305+
232306
private static String getResourcePath() {
233307
var workspace = RESOURCE_FOLDER.getPath();
234308
if (isWindows() && workspace.charAt(0) == SLASH) {

0 commit comments

Comments
 (0)