Skip to content

Commit

Permalink
git-jcheck: add support for multiple pre-push hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
edvbld committed Feb 25, 2020
1 parent bc37f61 commit fa99659
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 22 deletions.
13 changes: 13 additions & 0 deletions cli/src/main/java/org/openjdk/skara/cli/GitFork.java
Expand Up @@ -153,6 +153,11 @@ public static void main(String[] args) throws IOException, InterruptedException
.describe("DATE")
.helptext("Same as git clones flag 'shallow-since'")
.optional(),
Option.shortcut("")
.fullname("setup-pre-push-hooks")
.describe("CHECKS")
.helptext("Setups pre-push hooks for [branches,commits]")
.optional(),
Option.shortcut("")
.fullname("host")
.describe("HOSTNAME")
Expand Down Expand Up @@ -369,6 +374,14 @@ public static void main(String[] args) throws IOException, InterruptedException
if (shouldSync) {
GitSync.sync(repo, new String[]{"--from", "upstream", "--to", "origin", "--pull"});
}

var setupPrePushHooksOption = getOption("setup-pre-push-hooks", subsection, arguments);
if (setupPrePushHooksOption != null) {
var res = GitJCheck.run(repo, new String[]{"--setup-pre-push-hooks", setupPrePushHooksOption });
if (res != 0) {
System.exit(res);
}
}
}
}
}
Expand Down
115 changes: 94 additions & 21 deletions cli/src/main/java/org/openjdk/skara/cli/GitJCheck.java
Expand Up @@ -24,10 +24,12 @@

import org.openjdk.skara.args.*;
import org.openjdk.skara.census.Census;
import org.openjdk.skara.forge.*;
import org.openjdk.skara.jcheck.*;
import org.openjdk.skara.json.JSON;
import org.openjdk.skara.json.JSONValue;
import org.openjdk.skara.vcs.*;
import org.openjdk.skara.proxy.HttpProxy;
import org.openjdk.skara.vcs.openjdk.CommitMessageParsers;
import org.openjdk.skara.version.Version;

Expand Down Expand Up @@ -79,7 +81,7 @@ static boolean getSwitch(String name, Arguments arguments) {
return value != null && value.toLowerCase().equals("true");
}

public static int run(String[] args) throws IOException {
public static int run(Repository repo, String[] args) throws IOException {
var flags = List.of(
Option.shortcut("r")
.fullname("rev")
Expand All @@ -106,6 +108,16 @@ public static int run(String[] args) throws IOException {
.describe("CHECKS")
.helptext("Ignore errors from checks with the given name")
.optional(),
Option.shortcut("")
.fullname("push-url")
.describe("URL")
.helptext("URL that is being pushed to")
.optional(),
Option.shortcut("")
.fullname("setup-pre-push-hooks")
.describe("CHECKS")
.helptext("Set up a pre-push hook for the given checks")
.optional(),
Switch.shortcut("m")
.fullname("mercurial")
.helptext("Deprecated: force use of mercurial")
Expand All @@ -114,10 +126,6 @@ public static int run(String[] args) throws IOException {
.fullname("pre-push")
.helptext("Execute as a pre-push hook")
.optional(),
Switch.shortcut("")
.fullname("setup-pre-push-hook")
.helptext("Set up a pre-push hook that runs jcheck")
.optional(),
Switch.shortcut("v")
.fullname("verbose")
.helptext("Turn on verbose output")
Expand All @@ -137,7 +145,8 @@ public static int run(String[] args) throws IOException {
Switch.shortcut("v")
.fullname("version")
.helptext("Print the version of this tool")
.optional());
.optional()
);

var parser = new ArgumentParser("git jcheck", flags, List.of());
var arguments = parser.parse(args);
Expand All @@ -152,39 +161,49 @@ public static int run(String[] args) throws IOException {
Logging.setup(level, "jcheck");
}

var cwd = Paths.get("").toAbsolutePath();
var repository = ReadOnlyRepository.get(cwd);
if (!repository.isPresent()) {
System.err.println(String.format("error: %s is not a repository", cwd.toString()));
return 1;
}
var repo = repository.get();
HttpProxy.setup();

var setupPrePushHook = getSwitch("setup-pre-push-hook", arguments);
if (setupPrePushHook) {
var setupPrePushHooksOption = getOption("setup-pre-push-hooks", arguments);
if (setupPrePushHooksOption != null) {
var hookFile = repo.root().resolve(".git").resolve("hooks").resolve("pre-push");
Files.createDirectories(hookFile.getParent());
var lines = List.of(
"#!/usr/bin/sh",
"git jcheck --pre-push"
"git jcheck --pre-push --push-url=\"$2\""
);
Files.write(hookFile, lines);
if (hookFile.getFileSystem().supportedFileAttributeViews().contains("posix")) {
var permissions = PosixFilePermissions.fromString("rwxr-xr-x");
Files.setPosixFilePermissions(hookFile, permissions);
}

for (var check : setupPrePushHooksOption.split(",")) {
switch (check.trim()) {
case "branches":
repo.config("jcheck.pre-push", "branches", "true", false);
break;
case "commits":
repo.config("jcheck.pre-push", "commits", "true", false);
break;
default:
System.err.println("error: unexpected pre-push check: " + check.trim());
return 1;
}
}
return 0;
}

var isMercurial = getSwitch("mercurial", arguments);
var isPrePush = getSwitch("pre-push", arguments);
var ranges = new ArrayList<String>();
var targetBranches = new HashSet<String>();
if (isPrePush) {
var reader = new BufferedReader(new InputStreamReader(System.in));
var lines = reader.lines().collect(Collectors.toList());
for (var line : lines) {
var parts = line.split(" ");
var localHash = new Hash(parts[1]);
var remoteRef = parts[2];
var remoteHash = new Hash(parts[3]);

if (localHash.equals(Hash.zero())) {
Expand All @@ -194,6 +213,7 @@ public static int run(String[] args) throws IOException {
if (remoteHash.equals(Hash.zero())) {
ranges.add("origin.." + localHash.hex());
} else {
targetBranches.add(Path.of(remoteRef).getFileName().toString());
ranges.add(remoteHash.hex() + ".." + localHash.hex());
}
}
Expand Down Expand Up @@ -259,18 +279,71 @@ public static int run(String[] args) throws IOException {
}
}

var lines = repo.config("jcheck.pre-push.branches");
var shouldCheckRemoteBranches = lines.size() == 1 && lines.get(0).toLowerCase().equals("true");
if (isPrePush && shouldCheckRemoteBranches) {
var url = arguments.get("push-url").asString();
if (url == null) {
System.err.println("error: url for push not provided via --url option");
return 1;
}
var webUrl = Remote.toWebURI(url);
var forge = Forge.from(webUrl);
if (!forge.isPresent()) {
System.err.println("error: cannot find forge for " + webUrl);
return 1;
}
var remoteRepo = forge.get().repository(webUrl.getPath().substring(1));
if (!remoteRepo.isPresent()) {
System.err.println("error: could not find remote repository at " + webUrl);
return 1;
}
var parentRepo = remoteRepo.get().parent();
if (!parentRepo.isPresent()) {
System.err.println("error: could not find upstream repository for " + webUrl);
return 1;
}

var upstreamBranchNames = repo.remoteBranches(parentRepo.get().webUrl().toString())
.stream()
.map(r -> r.name())
.collect(Collectors.toSet());

var displayedError = false;
for (var branch : targetBranches) {
if (upstreamBranchNames.contains(branch)) {
System.err.println("error: should not push to branch in personal fork also present in upstream repository: " + branch);
displayedError = true;
}
}
if (displayedError) {
return 1;
}
}

var visitor = new JCheckCLIVisitor(ignore);
for (var range : ranges) {
try (var errors = JCheck.check(repo, census, CommitMessageParsers.v1, range, whitelist, blacklist)) {
for (var error : errors) {
error.accept(visitor);
lines = repo.config("jcheck.pre-push.commits");
var shouldCheckCommits = lines.size() == 1 && lines.get(0).toLowerCase().equals("true");
if (!isPrePush || shouldCheckCommits) {
for (var range : ranges) {
try (var errors = JCheck.check(repo, census, CommitMessageParsers.v1, range, whitelist, blacklist)) {
for (var error : errors) {
error.accept(visitor);
}
}
}
}
return visitor.hasDisplayedErrors() ? 1 : 0;
}

public static void main(String[] args) throws IOException {
System.exit(run(args));
var cwd = Paths.get("").toAbsolutePath();
var repository = Repository.get(cwd);
if (!repository.isPresent()) {
System.err.println(String.format("error: %s is not a repository", cwd.toString()));
System.exit(1);
}

System.exit(run(repository.get(), args));
}
}
Expand Up @@ -263,7 +263,7 @@ public static void main(String[] args) throws IOException, InterruptedException
var shouldRunJCheck = getSwitch("jcheck", "create", arguments);
if (shouldRunJCheck) {
var jcheckArgs = new String[]{ "--ignore=branches,committer,reviewers,issues", "--rev", targetBranch + ".." + headRef };
var err = GitJCheck.run(jcheckArgs);
var err = GitJCheck.run(repo, jcheckArgs);
if (err != 0) {
System.exit(err);
}
Expand Down

0 comments on commit fa99659

Please sign in to comment.