Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement jgit repository resolver #583

Merged
merged 1 commit into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading