Skip to content

Commit

Permalink
Merge pull request #9 from cqse/trigger_commit_hook
Browse files Browse the repository at this point in the history
Added Teamscale commit hook event sending
  • Loading branch information
agoeb authored Jul 9, 2021
2 parents 1fe1fa5 + 067a1d6 commit e858826
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 2 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ using this tool with self-signed certificates easier.
**-o**, **--threshold-config**=*<thresholdConfig>*
The name of the threshold config that should be used.

**--repository-url**=*<remote-repository-url>*
The URL of the remote repository where the analyzed commit originated. This is required in case a commit hook event
should be sent to Teamscale for this repository if the repository URL cannot be established from the build environment.

**-t**, **--evaluate-thresholds**
If this option is set, metrics from a given threshold profile will be evaluated.

Expand Down
63 changes: 61 additions & 2 deletions src/main/java/com/teamscale/buildbreaker/BuildBreaker.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.conqat.lib.commons.string.StringUtils;
import picocli.CommandLine;
Expand Down Expand Up @@ -230,6 +231,11 @@ public void setAccessKey(String accessKey) {
description = "The duration this tool will wait for analysis of the given commit to be finished in Teamscale, given in ISO-8601 format (e.g., PT20m for 20 minutes or PT30s for 30 seconds). This is useful when Teamscale starts analyzing at the same time this tool is called, and analysis is not yet finished. Default value is 20 minutes.")
public Duration waitForAnalysisTimeoutDuration = Duration.ofMinutes(20);

/** The URL of the remote repository used to send a commit hook event to Teamscale. */
@Option(names = {"--repository-url"}, paramLabel = "<remote-repository-url>", required = false,
description = "The URL of the remote repository where the analyzed commit originated. This is required in case a commit hook event should be sent to Teamscale for this repository if the repository URL cannot be established from the build environment.")
public String remoteRepositoryUrl;

@ArgGroup(exclusive = true)
private SslConnectionOptions sslConnectionOptions;

Expand Down Expand Up @@ -282,14 +288,21 @@ public Integer call() throws Exception {

try {
LocalDateTime timeout = LocalDateTime.now().plus(waitForAnalysisTimeoutDuration);
while (!isTeamscaleAnalysisFinished() && LocalDateTime.now().isBefore(timeout)) {
boolean teamscaleAnalysisFinished = isTeamscaleAnalysisFinished();
if (!teamscaleAnalysisFinished) {
System.out.println(
"The commit that should be evaluated has not yet been analyzed on the Teamscale instance. Triggering Teamscale commit hook on repository.");
triggerCommitHookEvent();
}
while (!teamscaleAnalysisFinished && LocalDateTime.now().isBefore(timeout)) {
System.out.println(
"The commit that should be evaluated has not yet been analyzed on the Teamscale instance. Will retry in ten seconds until the timeout is reached at " +
DateTimeFormatter.RFC_1123_DATE_TIME.format(timeout.atZone(ZoneOffset.UTC)) +
". You can change this timeout using --wait-for-analysis-timeout.");
Thread.sleep(Duration.ofSeconds(10).toMillis());
teamscaleAnalysisFinished = isTeamscaleAnalysisFinished();
}
if (!isTeamscaleAnalysisFinished()) {
if (!teamscaleAnalysisFinished) {
throw new AnalysisNotFinishedException(
"The commit that should be evaluated was not analyzed by Teamscale in time before the analysis timeout.");
}
Expand Down Expand Up @@ -338,6 +351,42 @@ public Integer call() throws Exception {

}

/**
* Notifies Teamscale that the repository has been updated. This means that analysis of the new commit will start
* promptly.
*/
private void triggerCommitHookEvent() {
String repositoryUrl = determineRemoteRepositoryUrl();
if (StringUtils.isEmpty(repositoryUrl)) {
return;
}
HttpUrl.Builder builder = teamscaleServerUrl.newBuilder().addPathSegments("api/post-commit-hook")
.addQueryParameter("repository", repositoryUrl);
HttpUrl url = builder.build();
Request request = new Request.Builder().header("Authorization", Credentials.basic(user, accessKey)).url(url)
.post(RequestBody.create(null, new byte[]{})).build();
try {
sendRequest(url, request);
System.out.println("Commit hook triggered successfully.");
} catch (IOException e) {
System.out.println("Failure when trying to send the commit hook event to Teamscale: " + e);
}
}

private String determineRemoteRepositoryUrl() {
List<Supplier<String>> repoUrlDetectionStrategies =
List.of(() -> remoteRepositoryUrl, GitChecker::findRepoUrl, SvnChecker::findRepoUrl);
Optional<String> optionalUrl =
repoUrlDetectionStrategies.stream().map(Supplier::get).filter(Objects::nonNull).findFirst();
if (!optionalUrl.isPresent()) {
System.out.println(
"Failed to automatically detect the remote repository URL. Please specify it manually via --repository-url to enable sending a commit hook event to Teamscale.");
return null;
}
remoteRepositoryUrl = optionalUrl.get();
return remoteRepositoryUrl;
}

private void initDefaultOptions() {
if (sslConnectionOptions == null) {
sslConnectionOptions = new SslConnectionOptions();
Expand Down Expand Up @@ -536,6 +585,16 @@ private String detectCommit() {
return detectedCommit;
}

private String detectRepoUrl() {
List<Supplier<String>> commitDetectionStrategies =
List.of(() -> detectedCommit, EnvironmentVariableChecker::findCommit, GitChecker::findCommit,
SvnChecker::findRevision);
Optional<String> optionalCommit =
commitDetectionStrategies.stream().map(Supplier::get).filter(Objects::nonNull).findFirst();
optionalCommit.ifPresent(commit -> detectedCommit = commit);
return detectedCommit;
}

private void handleErrors(Response response) {
if (response.isRedirect()) {
String location = response.header("Location");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,24 @@ public static String findCommit() {
return null;
}

public static String findRepoUrl() {
if (!isInsideGit()) {
System.out.println("The working directory does not appear to be within a Git repository.");
return null;
}

ProcessUtils.ProcessResult result = ProcessUtils.run("git", "config", "--get", "remote.origin.url");
if (result.wasSuccessful()) {
String repoUrl = result.stdoutAndStdErr.trim();
System.out.println("Using Repository URL " + repoUrl);
return repoUrl;
}

System.out.println("Failed to read remote repository URL. 'git config --get remote.origin.url' returned: " +
result.stdoutAndStdErr);
return null;
}

private static boolean isInsideGit() {
ProcessUtils.ProcessResult result = ProcessUtils.run("git", "rev-parse", "--is-inside-work-tree");
return result.wasSuccessful() && result.stdoutAndStdErr.trim().equalsIgnoreCase("true");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,24 @@ public static String findRevision() {
return null;
}

public static String findRepoUrl() {
if (!isInsideSvn()) {
System.out.println("The working directory does not appear to be within an SVN repository.");
return null;
}

ProcessUtils.ProcessResult result = ProcessUtils.run("svn", "info", "--show-item", "repos-root-url");
if (result.wasSuccessful()) {
String repoUrl = result.stdoutAndStdErr.trim();
System.out.println("Using SVN repository URL " + repoUrl);
return repoUrl;
}

System.out.println("Failed to read checked-out SVN revision. svn info --show-item repos-root-url returned: " +
result.stdoutAndStdErr);
return null;
}

private static boolean isInsideSvn() {
ProcessUtils.ProcessResult result = ProcessUtils.run("svn", "info");
return result.wasSuccessful() && result.stdoutAndStdErr.contains("URL:");
Expand Down

0 comments on commit e858826

Please sign in to comment.