Skip to content

Commit

Permalink
#314 Analyzing golang modules in sub-directory fails (#318)
Browse files Browse the repository at this point in the history
* #314: Fix issues with relative source paths

* Add tests with git tag

* Fix issues with absolute/relative source paths
  • Loading branch information
kaklakariada authored Jun 1, 2022
1 parent 1dd9c53 commit 8de3aae
Show file tree
Hide file tree
Showing 10 changed files with 212 additions and 109 deletions.
7 changes: 2 additions & 5 deletions doc/changes/changes_2.4.5.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
# Project Keeper 2.4.5, released 2022-??-??
# Project Keeper 2.4.5, released 2022-05-01

Code name: Bugfixes for Golang projects

## Summary

## Features

## Bugfixes

* #279: Fixed getting license for Golang test dependencies
* #316: Added new builtin replacements to BrokenLinkReplacer
* #313: Fixed labelling of of dependency change type
* #314: Fixed issues with Golang modules in sub-directories

## Dependency Updates

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ static DependencyChangeReport calculateDepencencyChanges(final GolangServices go
}

private DependencyChangeReport calculate() {
this.changes = this.golangServices.getDependencyChanges(this.projectDir, this.source.getPath());
this.changes = this.golangServices.getDependencyChanges(this.projectDir, getRelativeModPath());
final DependencyChangeReport dependencyChanges = new DependencyChangeReport();
dependencyChanges.setCompileDependencyChanges(getDependencyChanges(Type.COMPILE));
dependencyChanges.setPluginDependencyChanges(getDependencyChanges(Type.PLUGIN));
Expand All @@ -44,6 +44,14 @@ private DependencyChangeReport calculate() {
return dependencyChanges;
}

private Path getRelativeModPath() {
if (this.source.getPath().isAbsolute()) {
return this.projectDir.relativize(this.source.getPath());
} else {
return this.source.getPath();
}
}

private List<DependencyChange> getDependencyChanges(final Type type) {
return this.changes.stream().filter(change -> getType(change) == type).collect(toList());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class GolangServices {
private static final Logger LOGGER = Logger.getLogger(GolangServices.class.getName());
private static final List<String> COMMAND_LIST_DIRECT_DEPDENDENCIES = List.of("go", "list", "-f",
"{{if not .Indirect}}{{.}}{{end}}", "-m", "all");
private static final Duration EXECUTION_TIMEOUT = Duration.ofSeconds(5);
private static final Duration EXECUTION_TIMEOUT = Duration.ofSeconds(30);

private final Supplier<String> projectVersion;

Expand Down Expand Up @@ -64,10 +64,10 @@ private static String extractVersion(final ProjectKeeperConfig config) {
}
}

Map<String, GolangDependencyLicense> getLicenses(final Path projectPath, final String module) {
Map<String, GolangDependencyLicense> getLicenses(final Path absoluteSourcePath, final String module) {
final SimpleProcess process;
try {
process = GoProcess.start(projectPath, List.of("go-licenses", "csv", module));
process = GoProcess.start(absoluteSourcePath, List.of("go-licenses", "csv", module));
} catch (final IllegalStateException exception) {
throw new IllegalStateException(ExaError.messageBuilder("E-PK-CORE-142")
.message("Error starting the 'go-licenses' binary.")
Expand Down Expand Up @@ -100,11 +100,11 @@ private GolangDependencyLicense convertDependencyLicense(final String line) {
/**
* Get information about the Golang module with it's dependencies.
*
* @param projectPath the project path containing the {@code go.mod} file
* @param absoluteSourcePath the project path containing the {@code go.mod} file
* @return module information
*/
ModuleInfo getModuleInfo(final Path projectPath) {
final SimpleProcess process = SimpleProcess.start(projectPath, COMMAND_LIST_DIRECT_DEPDENDENCIES);
ModuleInfo getModuleInfo(final Path absoluteSourcePath) {
final SimpleProcess process = SimpleProcess.start(absoluteSourcePath, COMMAND_LIST_DIRECT_DEPDENDENCIES);
process.waitUntilFinished(EXECUTION_TIMEOUT);
final String[] output = process.getOutputStreamContent().split("\n");
final List<Dependency> dependencies = Arrays.stream(output) //
Expand All @@ -131,15 +131,15 @@ private Dependency convertDependency(final String line) {
/**
* Get a list of {@link DependencyChange}s in the given {@code go.mod} file since the latest release.
*
* @param projectDir the project root dir containing the {@code .git} directory
* @param modFile the absolute path to the {@code go.mod} file
* @param projectDir the project root dir containing the {@code .git} directory
* @param relativeModFile the absolute path to the {@code go.mod} file
* @return the list of {@link DependencyChange}s
*/
// [impl -> dsn~golang-changed-dependency~1]
List<DependencyChange> getDependencyChanges(final Path projectDir, final Path modFile) {
final Optional<GoModFile> lastReleaseModFile = getLastReleaseModFileContent(projectDir, modFile)
List<DependencyChange> getDependencyChanges(final Path projectDir, final Path relativeModFile) {
final Optional<GoModFile> lastReleaseModFile = getLastReleaseModFileContent(projectDir, relativeModFile)
.map(GoModFile::parse);
final GoModFile currentModFile = GoModFile.parse(readFile(modFile));
final GoModFile currentModFile = GoModFile.parse(readFile(projectDir.resolve(relativeModFile)));
return calculateChanges(lastReleaseModFile.orElse(null), currentModFile);
}

Expand All @@ -166,9 +166,7 @@ List<DependencyChange> calculateChanges(final GoModFile oldModFile, final GoModF

private Optional<String> getLastReleaseModFileContent(final Path projectDir, final Path modFile) {
try (GitRepository repo = GitRepository.open(projectDir)) {
final Path relativeModFilePath = projectDir.relativize(modFile);
return repo.findLatestReleaseCommit(getProjectVersion())
.map(tag -> getContent(repo, relativeModFilePath, tag));
return repo.findLatestReleaseCommit(getProjectVersion()).map(tag -> getContent(repo, modFile, tag));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,45 @@ public List<AnalyzedSource> analyze(final Path projectDir, final List<Source> so

private AnalyzedSource analyzeSource(final Path projectDir, final Source source) {
validateGolangSource(source);
final Path sourceDir = projectDir.resolve(source.getPath().getParent());
final boolean isRoot = projectDir.relativize(source.getPath()).equals(Path.of("go.mod"));
final Path moduleDir = source.getPath().getParent();
final ModuleInfo moduleInfo = this.golangServices.getModuleInfo(moduleDir);
final String projectName = source.getPath().normalize().getParent().getFileName().toString();
final Path absoluteSourceDir = getAbsoluteSourceDir(projectDir, source);
final ModuleInfo moduleInfo = this.golangServices.getModuleInfo(absoluteSourceDir);
final ProjectDependencies dependencies = GolangDependencyCalculator.calculateDependencies(this.golangServices,
sourceDir, moduleInfo);
absoluteSourceDir, moduleInfo);
return AnalyzedGolangSource.builder() //
.version(this.golangServices.getProjectVersion()) //
.isRootProject(isRoot) //
.isRootProject(isRootSource(source)) //
.advertise(source.isAdvertise()) //
.modules(source.getModules()) //
.path(source.getPath()) //
.projectName(projectName).moduleName(moduleInfo.getModuleName()) //
.projectName(getProjectName(projectDir, source)) //
.moduleName(moduleInfo.getModuleName()) //
.dependencies(dependencies) //
.dependencyChanges(GolangDependencyChangeCalculator.calculateDepencencyChanges(this.golangServices,
projectDir, source, dependencies)) //
.build();
}

private String getProjectName(final Path projectDir, final Source source) {
if (isRootSource(source)) {
return projectDir.getFileName().toString();
} else {
return source.getPath().getParent().getFileName().toString();
}
}

private boolean isRootSource(final Source source) {
return source.getPath().getParent() == null;
}

private Path getAbsoluteSourceDir(final Path projectDir, final Source source) {
final Path sourceDir = source.getPath().getParent();
if (sourceDir != null) {
return projectDir.resolve(sourceDir);
} else {
return projectDir;
}
}

private void validateGolangSource(final Source source) {
if (source.getType() != SourceType.GOLANG) {
throw new IllegalStateException(ExaError.messageBuilder("F-PK-CORE-130")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public void waitUntilFinished(final Duration executionTimeout) {
*/
public String getOutputStreamContent() {
try {
return this.outputStreamConsumer.getContent(Duration.ofMillis(100));
return this.outputStreamConsumer.getContent(Duration.ofMillis(500));
} catch (final InterruptedException exception) {
throw handleInterruptedException(exception);
}
Expand All @@ -116,7 +116,7 @@ public String getOutputStreamContent() {
*/
public String getErrorStreamContent() {
try {
return this.errorStreamConsumer.getContent(Duration.ofMillis(100));
return this.errorStreamConsumer.getContent(Duration.ofMillis(500));
} catch (final InterruptedException exception) {
throw handleInterruptedException(exception);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,34 @@ public class CollectingConsumer implements StreamConsumer {
private final StringBuilder stringBuilder = new StringBuilder();

@Override
public void accept(String line) {
stringBuilder.append(line).append("\n");
public void accept(final String line) {
this.stringBuilder.append(line).append("\n");
}

@Override
public void readFinished() {
countDownLatch.countDown();
this.countDownLatch.countDown();
}

@Override
public void readFailed(IOException exception) {
countDownLatch.countDown();
public void readFailed(final IOException exception) {
this.countDownLatch.countDown();
}

/**
* Waits until the stream was read completely and returns the read content from the stream.
*
*
* @param timeout the maximum time to wait
* @return the content collected from the stream
* @throws InterruptedException if the current thread is interrupted while waiting
*/
public String getContent(Duration timeout) throws InterruptedException {
boolean result = countDownLatch.await(timeout.toMillis(), TimeUnit.MILLISECONDS);
public String getContent(final Duration timeout) throws InterruptedException {
final boolean result = this.countDownLatch.await(timeout.toMillis(), TimeUnit.MILLISECONDS);
if (!result) {
throw new IllegalStateException(ExaError.messageBuilder("E-PK-CORE-99")
.message("Stream reading did not finish after timeout of {{timeout}}", timeout).ticketMitigation()
.toString());
throw new IllegalStateException(ExaError.messageBuilder("E-PK-CORE-99").message(
"Stream reading did not finish after timeout of {{timeout}}. Content collected until now: {{content}}.",
timeout, this.stringBuilder.toString()).ticketMitigation().toString());
}
return stringBuilder.toString();
return this.stringBuilder.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

import static java.util.Arrays.asList;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.assertAll;

import java.util.List;
Expand Down Expand Up @@ -81,6 +80,12 @@ void calculateChangesIgnoresIndirectDependencies() {
modFile(indirectDep("updated", "v2"), indirectDep("added", "v4")));
}

@Test
void getVersion() {
final GolangServices golangServices = new GolangServices(() -> PROJECT_VERSION);
assertThat(golangServices.getProjectVersion(), equalTo(PROJECT_VERSION));
}

private void assertChanges(final GoModFile oldMod, final GoModFile newMod,
final DependencyChange... expectedChanges) {
final GolangServices golangServices = new GolangServices(() -> PROJECT_VERSION);
Expand Down
Loading

0 comments on commit 8de3aae

Please sign in to comment.