Skip to content

Commit

Permalink
Address review comments. Accept more Azure DevOps repo formats
Browse files Browse the repository at this point in the history
  • Loading branch information
bryceatmoderne committed Jul 31, 2024
1 parent 3349de3 commit 5a78099
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public class GitProvenance implements Marker {
private static final Pattern AZURE_DEVOPS_HTTP_REMOTE =
Pattern.compile("https://(?:[\\w-]+@)?dev\\.azure\\.com/([^/]+)/([^/]+)/_git/(.*)");
private static final Pattern AZURE_DEVOPS_SSH_REMOTE =
Pattern.compile("git@ssh\\.dev\\.azure\\.com:v3/([^/]+)/([^/]+)/(.*)");
Pattern.compile("(?:ssh://)?git@ssh\\.dev\\.azure\\.com:v3/([^/]+)/([^/]+)/(.*)");
UUID id;

@Nullable
Expand Down Expand Up @@ -112,32 +112,51 @@ public class GitProvenance implements Marker {
*/
@Deprecated
public @Nullable String getOrganizationName() {
if (origin != null) {
Matcher matcher = AZURE_DEVOPS_HTTP_REMOTE.matcher(origin);
if (matcher.matches()) {
return matcher.group(1) + '/' + matcher.group(2);
}
matcher = AZURE_DEVOPS_SSH_REMOTE.matcher(origin);
if (matcher.matches()) {
return matcher.group(1) + '/' + matcher.group(2);
}
return Optional.ofNullable(origin).map(GitProvenance::getOrganizationNameFromOrigin).orElse(null);
}

try {
String path = new URIish(origin).getPath();
// Strip off any trailing repository name
path = path.substring(0, path.lastIndexOf('/'));
// Strip off any leading sub organization names
return path.substring(path.lastIndexOf('/') + 1);
} catch (URISyntaxException e) {
}
private static @Nullable String getOrganizationNameFromOrigin(String origin) {
Matcher matcher = AZURE_DEVOPS_HTTP_REMOTE.matcher(origin);
if (matcher.matches()) {
return matcher.group(1);
}
matcher = AZURE_DEVOPS_SSH_REMOTE.matcher(origin);
if (matcher.matches()) {
return matcher.group(1);
}

try {
String path = new URIish(origin).getPath();
// Strip off any trailing repository name
path = path.substring(0, path.lastIndexOf('/'));
// Strip off any leading sub organization names
return path.substring(path.lastIndexOf('/') + 1);
} catch (URISyntaxException e) {
}
return null;
}

public @Nullable String getRepositoryName() {
if (origin == null) {
return null;
public @Nullable String getProjectName() {
return Optional.ofNullable(origin).map(GitProvenance::getProjectName).orElse(null);
}

private static @Nullable String getProjectName(String origin) {
Matcher matcher = AZURE_DEVOPS_HTTP_REMOTE.matcher(origin);
if (matcher.matches()) {
return matcher.group(2);
}
matcher = AZURE_DEVOPS_SSH_REMOTE.matcher(origin);
if (matcher.matches()) {
return matcher.group(2);
}
return null;
}

public @Nullable String getRepositoryName() {
return Optional.ofNullable(origin).map(GitProvenance::getRepositoryName).orElse(null);
}

private static @Nullable String getRepositoryName(String origin) {
try {
String path = new URIish(origin).getPath();
return path.substring(path.lastIndexOf('/') + 1)
Expand All @@ -147,6 +166,21 @@ public class GitProvenance implements Marker {
}
}

public static String getRepositoryPath(String origin) {
StringBuilder builder = new StringBuilder(64);
builder.append(getOrganizationNameFromOrigin(origin));
String projectName = getProjectName(origin);
if (projectName != null) {
builder.append('/').append(projectName);
}
String repositoryName = getRepositoryName(origin);
if (repositoryName != null) {
builder.append('/').append(repositoryName);
}
return builder.toString();
}


/**
* @param projectDir The project directory.
* @return A marker containing git provenance information.
Expand All @@ -163,7 +197,8 @@ public class GitProvenance implements Marker {
* determined from a {@link BuildEnvironment} marker if possible.
* @return A marker containing git provenance information.
*/
public static @Nullable GitProvenance fromProjectDirectory(Path projectDir, @Nullable BuildEnvironment environment) {
public static @Nullable GitProvenance fromProjectDirectory(Path projectDir,
@Nullable BuildEnvironment environment) {
if (environment != null) {
if (environment instanceof JenkinsBuildEnvironment) {
JenkinsBuildEnvironment jenkinsBuildEnvironment = (JenkinsBuildEnvironment) environment;
Expand Down Expand Up @@ -220,7 +255,9 @@ private static void printRequireGitDirOrWorkTreeException(Exception e) {
}
}

private static GitProvenance fromGitConfig(Repository repository, @Nullable String branch, @Nullable String changeset) {
private static GitProvenance fromGitConfig(Repository repository,
@Nullable String branch,
@Nullable String changeset) {
if (branch == null) {
branch = resolveBranchFromGitConfig(repository);
}
Expand Down Expand Up @@ -264,7 +301,8 @@ private static GitProvenance fromGitConfig(Repository repository, @Nullable Stri

}

private static @Nullable String localBranchName(Repository repository, @Nullable String remoteBranch) throws IOException, GitAPIException {
private static @Nullable String localBranchName(Repository repository,
@Nullable String remoteBranch) throws IOException, GitAPIException {
if (remoteBranch == null) {
return null;
} else if (remoteBranch.startsWith("remotes/")) {
Expand All @@ -277,7 +315,7 @@ private static GitProvenance fromGitConfig(Repository repository, @Nullable Stri
List<RemoteConfig> remotes = git.remoteList().call();
for (RemoteConfig remote : remotes) {
if (remoteBranch.startsWith(remote.getName()) &&
(branch == null || branch.length() > remoteBranch.length() - remote.getName().length() - 1)) {
(branch == null || branch.length() > remoteBranch.length() - remote.getName().length() - 1)) {
branch = remoteBranch.substring(remote.getName().length() + 1); // +1 for the forward slash
}
}
Expand Down Expand Up @@ -340,9 +378,11 @@ private static List<Committer> getCommitters(Repository repository) {
Map<String, Committer> committers = new TreeMap<>();
for (RevCommit commit : git.log().add(head).call()) {
PersonIdent who = commit.getAuthorIdent();
Committer committer = committers.computeIfAbsent(who.getEmailAddress(),
Committer committer = committers.computeIfAbsent(
who.getEmailAddress(),
email -> new Committer(who.getName(), email, new TreeMap<>()));
committer.getCommitsByDay().compute(who.getWhen().toInstant().atZone(who.getTimeZone().toZoneId())
committer.getCommitsByDay().compute(
who.getWhen().toInstant().atZone(who.getTimeZone().toZoneId())
.toLocalDate(),
(day, count) -> count == null ? 1 : count + 1);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;

import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.openrewrite.Tree.randomId;
Expand All @@ -54,46 +54,54 @@ class GitProvenanceTest {

private static Stream<Arguments> remotes() {
return Stream.of(
Arguments.of("ssh://[email protected]/openrewrite/rewrite.git", "openrewrite", "rewrite"),
Arguments.of("https://github.com/openrewrite/rewrite.git", "openrewrite", "rewrite"),
Arguments.of("file:///openrewrite/rewrite.git", "openrewrite", "rewrite"),
Arguments.of("http://localhost:7990/scm/openrewrite/rewrite.git", "openrewrite", "rewrite"),
Arguments.of("http://localhost:7990/scm/some/openrewrite/rewrite.git", "openrewrite", "rewrite"),
Arguments.of("[email protected]:openrewrite/rewrite.git", "openrewrite", "rewrite"),
Arguments.of("[email protected]:openrewrite/rewrite.git", "openrewrite", "rewrite"),
Arguments.of("https://dev.azure.com/openrewrite/rewrite/_git/rewrite", "openrewrite/rewrite", "rewrite"),
Arguments.of("https://[email protected]/openrewrite/rewrite/_git/rewrite", "openrewrite/rewrite", "rewrite"),
Arguments.of("[email protected]:v3/openrewrite/rewrite/rewrite", "openrewrite/rewrite", "rewrite")
Arguments.of("ssh://[email protected]/openrewrite/rewrite.git", "openrewrite", null, "rewrite"),
Arguments.of("https://github.com/openrewrite/rewrite.git", "openrewrite", null, "rewrite"),
Arguments.of("file:///openrewrite/rewrite.git", "openrewrite", null, "rewrite"),
Arguments.of("http://localhost:7990/scm/openrewrite/rewrite.git", "openrewrite", null, "rewrite"),
Arguments.of("http://localhost:7990/scm/some/openrewrite/rewrite.git", "openrewrite", null, "rewrite"),
Arguments.of("[email protected]:openrewrite/rewrite.git", "openrewrite", null, "rewrite"),
Arguments.of("[email protected]:openrewrite/rewrite.git", "openrewrite", null, "rewrite"),
Arguments.of("https://dev.azure.com/openrewrite/rewrite/_git/rewrite", "openrewrite", "rewrite", "rewrite"),
Arguments.of(
"https://[email protected]/openrewrite/rewrite/_git/rewrite",
"openrewrite",
"rewrite",
"rewrite"),
Arguments.of("[email protected]:v3/openrewrite/rewrite/rewrite", "openrewrite", "rewrite", "rewrite"),
Arguments.of(
"ssh://[email protected]:v3/openrewrite/rewrite/rewrite",
"openrewrite",
"rewrite",
"rewrite")
);
}

@SuppressWarnings("deprecation")
@ParameterizedTest
@MethodSource("remotes")
void getOrganizationName(String remote, String org, String repo) {
assertThat(new GitProvenance(randomId(), remote, "main", "123", null, null, emptyList()).getOrganizationName())
.isEqualTo(org);
void getRepositoryPath(String origin, String expectedOrg, String expectedProject, String expectedRepo) {
GitProvenance gitProvenance = new GitProvenance(randomId(), origin, "main", "123", null, null, List.of());
assertThat(gitProvenance.getOrganizationName()).isEqualTo(expectedOrg);
assertThat(gitProvenance.getProjectName()).isEqualTo(expectedProject);
assertThat(gitProvenance.getRepositoryName()).isEqualTo(expectedRepo);
String expectedPath = expectedProject != null ?
expectedOrg + '/' + expectedProject + '/' + expectedRepo :
expectedOrg + '/' + expectedRepo;
assertThat(gitProvenance.getRepositoryPath(origin)).isEqualTo(expectedPath);
}

@ParameterizedTest
@CsvSource({
"[email protected]:organization/subgroup/repository.git, https://gitlab.acme.com, organization/subgroup",
"[email protected]:organization/subgroup/repository.git, [email protected], organization/subgroup",
"https://dev.azure.com/organization/project/_git/repository, https://dev.azure.com, organization/project",
"https://[email protected]/organization/project/_git/repository, https://dev.azure.com, organization/project",
"[email protected]:v3/organization/project/repository, [email protected], organization/project"
"https://dev.azure.com/organization/project/_git/repository, https://dev.azure.com, organization",
"https://[email protected]/organization/project/_git/repository, https://dev.azure.com, organization",
"[email protected]:v3/organization/project/repository, [email protected], organization"

})
void getOrganizationNameWithBaseUrl(String gitOrigin, String baseUrl, String organizationName) {
assertThat(new GitProvenance(randomId(), gitOrigin, "main", "123", null, null, emptyList()).getOrganizationName(baseUrl))
.isEqualTo(organizationName);
}

@ParameterizedTest
@MethodSource("remotes")
void getRepositoryName(String remote, String org, String repo) {
assertThat(new GitProvenance(randomId(), remote, "main", "123", null, null, emptyList()).getRepositoryName())
.isEqualTo(repo);
GitProvenance gitProvenance = new GitProvenance(randomId(), gitOrigin, "main", "123", null, null, List.of());
assertThat(gitProvenance.getOrganizationName(baseUrl)).isEqualTo(organizationName);
}

@Test
Expand Down Expand Up @@ -207,13 +215,14 @@ private static Stream<String> baseUrls() {
@ParameterizedTest
@MethodSource("baseUrls")
void multiplePathSegments(String baseUrl) {
GitProvenance provenance = new GitProvenance(randomId(),
GitProvenance provenance = new GitProvenance(
randomId(),
"http://gitlab.com/group/subgroup1/subgroup2/repo.git",
"master",
"1234567890abcdef1234567890abcdef12345678",
null,
null,
emptyList());
List.of());

assertThat(provenance.getOrganizationName(baseUrl)).isEqualTo("group/subgroup1/subgroup2");
assertThat(provenance.getRepositoryName()).isEqualTo("repo");
Expand All @@ -228,7 +237,10 @@ void shallowCloneDetachedHead(@TempDir Path projectDir) throws IOException, GitA
var cloneDir = projectDir.resolve("clone1");
try (var remoteRepo = fileKey.open(false)) {
remoteRepo.create(true);
try (Git git = Git.cloneRepository().setURI(remoteRepo.getDirectory().getAbsolutePath()).setDirectory(cloneDir.toFile()).call()) {
try (Git git = Git.cloneRepository()
.setURI(remoteRepo.getDirectory().getAbsolutePath())
.setDirectory(cloneDir.toFile())
.call()) {
Files.writeString(projectDir.resolve("test.txt"), "hi");
git.add().addFilepattern("*").call();
git.commit().setMessage("init").setSign(false).call();
Expand Down Expand Up @@ -261,7 +273,10 @@ void noLocalBranchDeriveFromRemote(@TempDir Path projectDir) throws IOException,

// push an initial commit to the remote
var cloneDir = projectDir.resolve("clone1");
try (Git gitSetup = Git.cloneRepository().setURI(remoteRepo.getDirectory().getAbsolutePath()).setDirectory(cloneDir.toFile()).call()) {
try (Git gitSetup = Git.cloneRepository()
.setURI(remoteRepo.getDirectory().getAbsolutePath())
.setDirectory(cloneDir.toFile())
.call()) {
Files.writeString(projectDir.resolve("test.txt"), "hi");
gitSetup.add().addFilepattern("*").call();
var commit = gitSetup.commit().setMessage("init").setSign(false).call();
Expand All @@ -270,8 +285,16 @@ void noLocalBranchDeriveFromRemote(@TempDir Path projectDir) throws IOException,
//Now create new workspace directory, git init and then fetch from remote.
var workspaceDir = projectDir.resolve("workspace");
try (Git git = Git.init().setDirectory(workspaceDir.toFile()).call()) {
git.remoteAdd().setName("origin").setUri(new URIish(remoteRepo.getDirectory().getAbsolutePath())).call();
git.fetch().setRemote("origin").setForceUpdate(true).setTagOpt(TagOpt.FETCH_TAGS).setRefSpecs("+refs/heads/*:refs/remotes/origin/*").call();
git.remoteAdd()
.setName("origin")
.setUri(new URIish(remoteRepo.getDirectory().getAbsolutePath()))
.call();
git.fetch()
.setRemote("origin")
.setForceUpdate(true)
.setTagOpt(TagOpt.FETCH_TAGS)
.setRefSpecs("+refs/heads/*:refs/remotes/origin/*")
.call();
git.checkout().setName(commit.getName()).call();
}
}
Expand All @@ -291,7 +314,8 @@ void supportsGitHubActions(@TempDir Path projectDir) {
envVars.put("GITHUB_SHA", "287364287357");
envVars.put("GITHUB_HEAD_REF", "");

GitProvenance prov = GitProvenance.fromProjectDirectory(projectDir,
GitProvenance prov = GitProvenance.fromProjectDirectory(
projectDir,
GithubActionsBuildEnvironment.build(envVars::get));
assertThat(prov).isNotNull();
assertThat(prov.getOrigin()).isEqualTo("https://github.com/octocat/Hello-World.git");
Expand All @@ -308,7 +332,8 @@ void ignoresBuildEnvironmentIfThereIsGitConfig(@TempDir Path projectDir) throws
envVars.put("GITHUB_SHA", "287364287357");
envVars.put("GITHUB_HEAD_REF", "");
try (Git ignored = Git.init().setDirectory(projectDir.toFile()).setInitialBranch("main").call()) {
GitProvenance prov = GitProvenance.fromProjectDirectory(projectDir,
GitProvenance prov = GitProvenance.fromProjectDirectory(
projectDir,
GithubActionsBuildEnvironment.build(envVars::get));
assertThat(prov).isNotNull();
assertThat(prov.getOrigin()).isNotEqualTo("https://github.com/octocat/Hello-World.git");
Expand All @@ -324,7 +349,8 @@ void supportsCustomBuildEnvironment(@TempDir Path projectDir) {
envVars.put("CUSTOM_GIT_REF", "main");
envVars.put("CUSTOM_GIT_SHA", "287364287357");

GitProvenance prov = GitProvenance.fromProjectDirectory(projectDir,
GitProvenance prov = GitProvenance.fromProjectDirectory(
projectDir,
CustomBuildEnvironment.build(envVars::get));

assertThat(prov).isNotNull();
Expand All @@ -340,7 +366,8 @@ void supportsGitLab(@TempDir Path projectDir) {
envVars.put("CI_COMMIT_REF_NAME", "main");
envVars.put("CI_COMMIT_SHA", "287364287357");

GitProvenance prov = GitProvenance.fromProjectDirectory(projectDir,
GitProvenance prov = GitProvenance.fromProjectDirectory(
projectDir,
GitlabBuildEnvironment.build(envVars::get));

assertThat(prov).isNotNull();
Expand All @@ -357,7 +384,8 @@ void supportsDrone(@TempDir Path projectDir) {
envVars.put("DRONE_REMOTE_URL", "https://github.com/octocat/Hello-World.git");
envVars.put("DRONE_COMMIT_SHA", "287364287357");

GitProvenance prov = GitProvenance.fromProjectDirectory(projectDir,
GitProvenance prov = GitProvenance.fromProjectDirectory(
projectDir,
DroneBuildEnvironment.build(envVars::get));

assertThat(prov).isNotNull();
Expand Down

0 comments on commit 5a78099

Please sign in to comment.