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

Create forges from service providers #223

Closed
wants to merge 5 commits into from
Closed
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -23,19 +23,19 @@
package org.openjdk.skara.bot;

import org.openjdk.skara.forge.*;
import org.openjdk.skara.host.*;
import org.openjdk.skara.host.Credential;
import org.openjdk.skara.issuetracker.*;
import org.openjdk.skara.network.URIBuilder;
import org.openjdk.skara.json.JSONObject;
import org.openjdk.skara.network.URIBuilder;
import org.openjdk.skara.vcs.VCS;

import java.io.*;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.time.Duration;
import java.util.*;
import java.util.logging.Logger;
import java.util.regex.Pattern;

public class BotRunnerConfiguration {
private final Logger log;
@@ -64,8 +64,8 @@ private BotRunnerConfiguration(JSONObject config, Path cwd) throws Configuration
if (entry.value().contains("gitlab")) {
var gitlab = entry.value().get("gitlab");
var uri = URIBuilder.base(gitlab.get("url").asString()).build();
var pat = new PersonalAccessToken(gitlab.get("username").asString(), gitlab.get("pat").asString());
ret.put(entry.name(), ForgeFactory.createGitLabHost(uri, pat));
var pat = new Credential(gitlab.get("username").asString(), gitlab.get("pat").asString());
ret.put(entry.name(), Forge.from("gitlab", uri, pat, gitlab.asObject()));
} else if (entry.value().contains("github")) {
var github = entry.value().get("github");
URI uri;
@@ -74,21 +74,21 @@ private BotRunnerConfiguration(JSONObject config, Path cwd) throws Configuration
} else {
uri = URIBuilder.base("https://github.com/").build();
}
Pattern webUriPattern = null;
String webUriReplacement = null;
if (github.contains("weburl")) {
webUriPattern = Pattern.compile(github.get("weburl").get("pattern").asString());
webUriReplacement = github.get("weburl").get("replacement").asString();
}

if (github.contains("app")) {
var keyFile = cwd.resolve(github.get("app").get("key").asString());
ret.put(entry.name(), ForgeFactory.createGitHubHost(uri, webUriPattern, webUriReplacement, keyFile.toString(),
github.get("app").get("id").asString(),
github.get("app").get("installation").asString()));
try {
var keyContents = Files.readString(keyFile, StandardCharsets.UTF_8);
var pat = new Credential(github.get("app").get("id").asString() + ";" +
github.get("app").get("installation").asString(),
keyContents);
ret.put(entry.name(), Forge.from("github", uri, pat, github.asObject()));
} catch (IOException e) {
throw new ConfigurationError("Cannot find key file: " + keyFile);
}
} else {
var pat = new PersonalAccessToken(github.get("username").asString(), github.get("pat").asString());
ret.put(entry.name(), ForgeFactory.createGitHubHost(uri, pat));
var pat = new Credential(github.get("username").asString(), github.get("pat").asString());
ret.put(entry.name(), Forge.from("github", uri, pat, github.asObject()));
}
} else {
throw new ConfigurationError("Host " + entry.name());
@@ -88,7 +88,7 @@ private void publishToLogstash(Instant time, Level level, String message, Map<St
endpoint.post("/")
.body(query)
.executeUnparsed();
} catch (RuntimeException e) {
} catch (RuntimeException | IOException e) {
log.warning("Exception during logstash publishing: " + e.getMessage());
log.throwing("BotSlackHandler", "publish", e);
}
@@ -26,6 +26,7 @@
import org.openjdk.skara.network.*;
import org.openjdk.skara.json.JSON;

import java.io.IOException;
import java.net.URI;
import java.time.*;
import java.util.*;
@@ -98,7 +99,7 @@ private void publishToSlack(String message) {
}

webhook.post("").body(query).executeUnparsed();
} catch (RuntimeException e) {
} catch (RuntimeException | IOException e) {
log.warning("Exception during slack notification posting: " + e.getMessage());
log.throwing("BotSlackHandler", "publish", e);
}
@@ -146,15 +146,18 @@ public static void main(String[] args) throws IOException {
exit("No username for host " + hostName + " found, use git-credentials or the flag --username");
}

var host = Forge.from(uri, new PersonalAccessToken(credentials.username(), credentials.password()));
var host = Forge.from(uri, new Credential(credentials.username(), credentials.password()));
if (host.isEmpty() || !host.get().isValid()) {
exit("Failed to connect to host " + hostName);
}
if (path.endsWith(".git")) {
path = path.substring(0, path.length() - 4);
}
if (path.startsWith("/")) {
path = path.substring(1);
}

var fork = host.repository(path).fork();
var fork = host.get().repository(path).fork();

if (token == null) {
GitCredentials.approve(credentials);
@@ -101,11 +101,14 @@ private static String projectName(URI uri) {
}

private static HostedRepository getHostedRepositoryFor(URI uri, GitCredentials credentials) throws IOException {
var host = Forge.from(uri, new PersonalAccessToken(credentials.username(), credentials.password()));
var host = Forge.from(uri, new Credential(credentials.username(), credentials.password()));
if (System.getenv("GIT_TOKEN") == null) {
GitCredentials.approve(credentials);
}
var remoteRepo = host.repository(projectName(uri));
if (host.isEmpty() || !host.get().isValid()) {
exit("error: failed to connect to host " + uri);
}
var remoteRepo = host.get().repository(projectName(uri));
var parentRepo = remoteRepo.parent();
var targetRepo = parentRepo.isPresent() ? parentRepo.get() : remoteRepo;
return targetRepo;
@@ -309,7 +312,10 @@ public static void main(String[] args) throws IOException, InterruptedException
var token = isMercurial ? System.getenv("HG_TOKEN") : System.getenv("GIT_TOKEN");
var uri = Remote.toWebURI(remotePullPath);
var credentials = GitCredentials.fill(uri.getHost(), uri.getPath(), username, token, uri.getScheme());
var host = Forge.from(uri, new PersonalAccessToken(credentials.username(), credentials.password()));
var host = Forge.from(uri, new Credential(credentials.username(), credentials.password()));
if (host.isEmpty() || !host.get().isValid()) {
exit("error: failed to connect to host " + uri);
}

var action = arguments.at(0).asString();
if (action.equals("create")) {
@@ -412,7 +418,7 @@ public static void main(String[] args) throws IOException, InterruptedException
System.exit(1);
}

var remoteRepo = host.repository(projectName(uri));
var remoteRepo = host.get().repository(projectName(uri));
if (token == null) {
GitCredentials.approve(credentials);
}
@@ -480,7 +486,7 @@ public static void main(String[] args) throws IOException, InterruptedException
if (arguments.contains("assignees")) {
var usernames = Arrays.asList(arguments.get("assignees").asString().split(","));
var assignees = usernames.stream()
.map(host::user)
.map(u -> host.get().user(u))
.collect(Collectors.toList());
pr.setAssignees(assignees);
}
@@ -569,7 +575,7 @@ public static void main(String[] args) throws IOException, InterruptedException
System.exit(1);
}

var remoteRepo = host.repository(projectName(uri));
var remoteRepo = host.get().repository(projectName(uri));
if (token == null) {
GitCredentials.approve(credentials);
}
@@ -637,7 +643,7 @@ public static void main(String[] args) throws IOException, InterruptedException
if (arguments.contains("assignees")) {
var usernames = Arrays.asList(arguments.get("assignees").asString().split(","));
var assignees = usernames.stream()
.map(host::user)
.map(u -> host.get().user(u))
.collect(Collectors.toList());
pr.setAssignees(assignees);
}
@@ -834,7 +840,7 @@ public static void main(String[] args) throws IOException, InterruptedException
if (arguments.contains("assignees")) {
var usernames = Arrays.asList(arguments.get("assignees").asString().split(","));
var assignees = usernames.stream()
.map(host::user)
.map(u -> host.get().user(u))
.collect(Collectors.toList());
pr.setAssignees(assignees);
}
@@ -28,6 +28,8 @@ module {
requires 'org.junit.jupiter.api'
requires 'jdk.httpserver'
opens 'org.openjdk.skara.forge' to 'org.junit.platform.commons'
opens 'org.openjdk.skara.forge.github' to 'org.junit.platform.commons'
opens 'org.openjdk.skara.forge.gitlab' to 'org.junit.platform.commons'
}
}

@@ -34,4 +34,8 @@
requires java.logging;

exports org.openjdk.skara.forge;

uses org.openjdk.skara.forge.ForgeFactory;

provides org.openjdk.skara.forge.ForgeFactory with org.openjdk.skara.forge.github.GitHubForgeFactory, org.openjdk.skara.forge.gitlab.GitLabForgeFactory;
}
@@ -23,17 +23,44 @@
package org.openjdk.skara.forge;

import org.openjdk.skara.host.*;
import org.openjdk.skara.json.JSONObject;

import java.net.URI;
import java.util.*;
import java.util.stream.Collectors;

public interface Forge extends Host {
HostedRepository repository(String name);
boolean supportsReviewBody();

static Forge from(URI uri, PersonalAccessToken pat) {
return ForgeFactory.createFromURI(uri, pat);
static Forge from(String name, URI uri, Credential credential, JSONObject configuration) {
var factory = ForgeFactory.getForgeFactories().stream()
.filter(f -> f.name().equals(name))
.findFirst();
if (factory.isEmpty()) {
throw new RuntimeException("No forge factory named '" + name + "' found - check module path");
}
return factory.get().create(uri, credential, configuration);
}
static Forge from(URI uri) {
return ForgeFactory.createFromURI(uri, null);

static Optional<Forge> from(URI uri, Credential credential, JSONObject configuration) {
var factories = ForgeFactory.getForgeFactories().stream()
.sorted(Comparator.comparing(f -> !uri.getHost().contains(f.name())))
.collect(Collectors.toList());
for (var factory : factories) {
var forge = factory.create(uri, credential, configuration);
if (forge.isValid()) {
return Optional.of(forge);
}
}
return Optional.empty();
}

static Optional<Forge> from(URI uri, Credential credential) {
return from(uri, credential, null);
}

static Optional<Forge> from(URI uri) {
return from(uri, null);
}
}
@@ -22,56 +22,30 @@
*/
package org.openjdk.skara.forge;

import org.openjdk.skara.host.*;
import org.openjdk.skara.host.Credential;
import org.openjdk.skara.json.JSONObject;

import java.net.URI;
import java.util.regex.Pattern;

public class ForgeFactory {
public static Forge createGitHubHost(URI uri, Pattern webUriPattern, String webUriReplacement, String keyFile, String issue, String id) {
var app = new GitHubApplication(keyFile, issue, id);
return new GitHubHost(uri, app, webUriPattern, webUriReplacement);
}

public static Forge createGitHubHost(URI uri, PersonalAccessToken pat) {
if (pat != null) {
return new GitHubHost(uri, pat);
} else {
return new GitHubHost(uri);
}
}

public static Forge createGitLabHost(URI uri, PersonalAccessToken pat) {
if (pat != null) {
return new GitLabHost(uri, pat);
} else {
return new GitLabHost(uri);
}
import java.util.*;
import java.util.stream.*;

public interface ForgeFactory {

/**
* A user-friendly name for the given forge, used for configuration section naming. Should be lower case.
* @return
*/
String name();

/**
* Instantiate an instance of this forge.
* @return
*/
Forge create(URI uri, Credential credential, JSONObject configuration);

static List<ForgeFactory> getForgeFactories() {
return StreamSupport.stream(ServiceLoader.load(ForgeFactory.class).spliterator(), false)
.collect(Collectors.toList());
}

public static Forge createFromURI(URI uri, PersonalAccessToken pat) throws IllegalArgumentException {
// Short-circuit
if (uri.toString().contains("github")) {
return createGitHubHost(uri, pat);
} else if (uri.toString().contains("gitlab")) {
return createGitLabHost(uri, pat);
}

try {
var gitLabHost = createGitLabHost(uri, pat);
if (gitLabHost.isValid()) {
return gitLabHost;
}
} catch (RuntimeException e) {
try {
var gitHubHost = createGitHubHost(uri, pat);
if (gitHubHost.isValid()) {
return gitHubHost;
}
} catch (RuntimeException ignored) {
}
}

throw new IllegalArgumentException("Unable to detect host type from URI: " + uri);
}
}
@@ -22,6 +22,8 @@
*/
package org.openjdk.skara.forge;

import org.openjdk.skara.forge.gitlab.GitLabMergeRequest;

import java.time.ZonedDateTime;
import java.util.*;
import java.util.logging.Logger;
@@ -20,16 +20,15 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.skara.forge;
package org.openjdk.skara.forge.github;

import org.openjdk.skara.network.URIBuilder;
import org.openjdk.skara.json.*;
import org.openjdk.skara.network.URIBuilder;

import java.io.*;
import java.net.URI;
import java.net.http.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.security.*;
import java.security.spec.*;
import java.time.*;
@@ -104,31 +103,30 @@ public GitHubConfigurationError(String message) {
}
}

public GitHubApplication(String keyFile, String issue, String id) {
public GitHubApplication(String key, String issue, String id) {

log = Logger.getLogger("org.openjdk.host.github");

apiBase = URIBuilder.base("https://api.github.com/").build();
this.issue = issue;
this.id = id;

key = loadPkcs8PemFromFile(keyFile);
this.key = loadPkcs8PemFromString(key);
jwt = new Token(this::generateJsonWebToken, Duration.ofMinutes(5));
installationToken = new Token(this::generateInstallationToken, Duration.ofMinutes(30));
}

private PrivateKey loadPkcs8PemFromFile(String keyFile) {
private PrivateKey loadPkcs8PemFromString(String pem) {
try {
var pem = new String(Files.readAllBytes(Paths.get(keyFile)));
var pemPattern = Pattern.compile("^-*BEGIN PRIVATE KEY-*$(.*)^-*END PRIVATE KEY-*",
Pattern.DOTALL | Pattern.MULTILINE);
var keyString = pemPattern.matcher(pem).replaceFirst("$1");
//keyString = keyString.replace("\n", "");
var rawKey = Base64.getMimeDecoder().decode(keyString);
var factory = KeyFactory.getInstance("RSA");
return factory.generatePrivate(new PKCS8EncodedKeySpec(rawKey));
} catch (NoSuchAlgorithmException | InvalidKeySpecException | IOException e) {
throw new GitHubConfigurationError("Unable to load private key (" + keyFile + ": " + e + ")");
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new GitHubConfigurationError("Unable to load private key (" + e + ")");
}
}

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.