Skip to content

Commit

Permalink
implement jgit repository resolver
Browse files Browse the repository at this point in the history
  • Loading branch information
laDok8 authored and mnovak1 committed Sep 27, 2024
1 parent 35b4a35 commit 708a9a6
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 0 deletions.
5 changes: 5 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>

<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
7 changes: 7 additions & 0 deletions core/src/main/java/cz/xtf/core/git/GitResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package cz.xtf.core.git;

interface GitResolver {
String resolveRepoUrl();

String resolveRepoRef();
}
16 changes: 16 additions & 0 deletions core/src/main/java/cz/xtf/core/git/GitResolverFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package cz.xtf.core.git;

import cz.xtf.core.config.XTFConfig;

class GitResolverFactory {
static final String GIT_URL = "xtf.git.repository.url";
static final String GIT_BRANCH = "xtf.git.repository.ref";

public static GitResolver createResolver() {
if (XTFConfig.get(GIT_URL) == null || XTFConfig.get(GIT_BRANCH) == null) {
return new JGitResolver();
} else {
return new SystemPropertyGitResolver();
}
}
}
14 changes: 14 additions & 0 deletions core/src/main/java/cz/xtf/core/git/GitUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package cz.xtf.core.git;

public class GitUtils {
private static final GitResolver gitResolver = GitResolverFactory.createResolver();

// Static method to get repo URL and ref
public static String getRepoUrl() {
return gitResolver.resolveRepoUrl();
}

public static String getRepoRef() {
return gitResolver.resolveRepoRef();
}
}
140 changes: 140 additions & 0 deletions core/src/main/java/cz/xtf/core/git/JGitResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package cz.xtf.core.git;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.transport.RemoteConfig;

import lombok.extern.slf4j.Slf4j;

/**
* Try to resolve repository remote URL and branch from .git directory
* <p>
* This method tries to match HEAD commit to remote references.
* If there is a match, the remote URL and branch are set.
* For attached HEAD this method could be simplified with jgit methods, but
* "universal" approach was chosen, as for example Jenkins git plugin creates a detached state.
* In case of multiple matches, upstream or origin (in this order) is preferred.
* </p>
*/
@Slf4j
class JGitResolver implements GitResolver {
private static final String URL_TEMPLATE = "https://%s/%s/%s";
private static String reference;
private static String url;

/**
* Try to set repository ref and URL from HEAD commit
*/
public JGitResolver() {
try {
resolveRepoFromHEAD();
} catch (IOException | URISyntaxException e) {
log.error("Failed to resolve repository from HEAD", e);
throw new RuntimeException("Failed to resolve repository from HEAD with error: " + e.getMessage());
}
}

/**
* Try to set repository ref and URL from HEAD commit
*/
private static void resolveRepoFromHEAD() throws IOException, URISyntaxException {
//look for a git repository recursively till system root folder
Repository repository = new FileRepositoryBuilder().findGitDir().build();

if (repository == null) {
log.error("Failed to find a git repository");
return;
}

//get current commit hash
ObjectId commitId = repository.resolve("HEAD");

//get all remote references
List<Ref> refs = repository.getRefDatabase().getRefs().stream()
.filter(reference -> reference.getName().startsWith("refs/remotes/")).collect(Collectors.toList());

List<String> matches = new ArrayList<>();
// Walk through all the refs to see if any point to this commit
for (Ref ref : refs) {
if (ref.getObjectId().equals(commitId)) {
matches.add(ref.getName());
}
}

if (matches.isEmpty()) {
log.error("No remote references found for the current commit");
return;
}

//In case there are multiple matches, we prefer upstream or origin (in this order)
List<String> preferredMatches = matches.stream()
.filter(reference -> reference.contains("upstream") || reference.contains("origin"))
.sorted(Comparator.reverseOrder()) // 1) upstream 2) origin
.collect(Collectors.toList());

if (matches.size() > 1 && !preferredMatches.isEmpty()) {
matches = preferredMatches;
}

//branch is string behind the last /
reference = matches.stream().findFirst().map(ref -> ref.substring(ref.lastIndexOf('/') + 1)).orElse(null);

log.info("xtf.git.repository.ref got automatically resolved as {}", reference);

String remote = repository.getRemoteName(matches.get(0));
url = getRemoteUrl(repository, remote);

if (url != null) {
log.info("xtf.git.repository.url got automatically resolved as {}", url);
}

}

/**
* given a remote reference, get it's remote URL
*
* @param repository git repository
* @param remoteReference reference in format "refs/remotes/remote/branch"
* @return URL in HTTPS format
*/
private static String getRemoteUrl(Repository repository, String remoteReference) throws URISyntaxException {
RemoteConfig remoteConfig = new RemoteConfig(repository.getConfig(), remoteReference);
if (remoteConfig.getURIs() == null || remoteConfig.getURIs().isEmpty()) {
log.info("Missing URI in git remote ref '{}'", remoteReference);
return null;
}
// we expect a single URI
String[] pathTokens = remoteConfig.getURIs().get(0).getPath().split("/");
if (pathTokens.length != 2) {
log.info("Unexpected path '{}' in URI '{}' of git remote ref '{}'", remoteConfig.getURIs().get(0).getPath(),
remoteConfig.getURIs().get(0), remoteReference);
return null;
}
// the URI must be in HTTPS format
return getRepositoryUrl(remoteConfig.getURIs().get(0).getHost(), pathTokens[0], pathTokens[1]);
}

/**
* We require HTTPS format, for unauthorized access to the repository, let's convert it
*/
private static String getRepositoryUrl(String host, String remote, String repository) {
return String.format(URL_TEMPLATE, host, remote, repository);
}

public String resolveRepoUrl() {
return url;
}

public String resolveRepoRef() {
return reference;
}
}
16 changes: 16 additions & 0 deletions core/src/main/java/cz/xtf/core/git/SystemPropertyGitResolver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package cz.xtf.core.git;

import cz.xtf.core.config.XTFConfig;

class SystemPropertyGitResolver implements GitResolver {
static final String GIT_URL = "xtf.git.repository.url";
static final String GIT_BRANCH = "xtf.git.repository.ref";

public String resolveRepoUrl() {
return XTFConfig.get(GIT_URL);
}

public String resolveRepoRef() {
return XTFConfig.get(GIT_BRANCH);
}
}
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
<version.lombok>1.18.22</version.lombok>
<version.gson>2.8.9</version.gson>
<version.guava>32.0.1-jre</version.guava>
<version.org.eclipse.jgit>5.13.3.202401111512-r</version.org.eclipse.jgit>

<!-- Plugin version properties -->
<version.maven-source-plugin>3.2.1</version.maven-source-plugin>
Expand Down Expand Up @@ -226,6 +227,12 @@
<artifactId>openshift-server-mock</artifactId>
<version>${version.openshift-client}</version>
</dependency>

<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>${version.org.eclipse.jgit}</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down

0 comments on commit 708a9a6

Please sign in to comment.