diff --git a/forge/src/main/java/org/openjdk/skara/forge/gitlab/GitLabForgeFactory.java b/forge/src/main/java/org/openjdk/skara/forge/gitlab/GitLabForgeFactory.java index 9bb6ce359..e05e00302 100644 --- a/forge/src/main/java/org/openjdk/skara/forge/gitlab/GitLabForgeFactory.java +++ b/forge/src/main/java/org/openjdk/skara/forge/gitlab/GitLabForgeFactory.java @@ -27,9 +27,13 @@ import org.openjdk.skara.json.JSONObject; import org.openjdk.skara.json.JSONValue; +import java.io.*; import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.*; import java.util.HashSet; import java.util.Set; +import java.util.regex.Pattern; import java.util.stream.Collectors; public class GitLabForgeFactory implements ForgeFactory { @@ -43,6 +47,33 @@ public Set knownHosts() { return Set.of("gitlab.com"); } + private void configureSshKey(String userName, String hostName, String sshKey) { + var cfgPath = Path.of(System.getProperty("user.home"), ".ssh"); + var cfgFile = cfgPath.resolve("config"); + var existing = ""; + try { + existing = Files.readString(cfgFile, StandardCharsets.UTF_8); + } catch (IOException ignored) { + } + + var userHost = userName + "." + hostName; + var existingBlock = Pattern.compile("^Match host " + Pattern.quote(userHost) + "(?:\\R[ \\t]+.*)+", Pattern.MULTILINE); + var existingMatcher = existingBlock.matcher(existing); + var filtered = existingMatcher.replaceAll(""); + var result = "Match host " + userHost + "\n" + + " Hostname " + hostName + "\n" + + " PreferredAuthentications publickey\n" + + " StrictHostKeyChecking no\n" + + " IdentityFile " + sshKey + "\n" + + "\n"; + + try { + Files.writeString(cfgFile, result + filtered.strip() + "\n", StandardCharsets.UTF_8); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + @Override public Forge create(URI uri, Credential credential, JSONObject configuration) { var name = "GitLab"; @@ -56,10 +87,15 @@ public Forge create(URI uri, Credential credential, JSONObject configuration) { .map(JSONValue::asString) .collect(Collectors.toSet()); } + var useSsh = false; + if (configuration != null && configuration.contains("sshkey") && credential != null) { + configureSshKey(credential.username(), uri.getHost(), configuration.get("sshkey").asString()); + useSsh = true; + } if (credential != null) { - return new GitLabHost(name, uri, credential, groups); + return new GitLabHost(name, uri, useSsh, credential, groups); } else { - return new GitLabHost(name, uri, groups); + return new GitLabHost(name, uri, useSsh, groups); } } } diff --git a/forge/src/main/java/org/openjdk/skara/forge/gitlab/GitLabHost.java b/forge/src/main/java/org/openjdk/skara/forge/gitlab/GitLabHost.java index 50da448c0..00d6c5239 100644 --- a/forge/src/main/java/org/openjdk/skara/forge/gitlab/GitLabHost.java +++ b/forge/src/main/java/org/openjdk/skara/forge/gitlab/GitLabHost.java @@ -26,20 +26,19 @@ import org.openjdk.skara.host.*; import org.openjdk.skara.json.*; import org.openjdk.skara.network.*; -import org.openjdk.skara.vcs.*; +import org.openjdk.skara.vcs.Hash; import java.io.IOException; import java.net.*; import java.nio.charset.StandardCharsets; -import java.nio.file.Path; import java.util.*; -import java.util.stream.Collectors; import java.util.logging.Logger; -import java.time.ZonedDateTime; +import java.util.stream.Collectors; public class GitLabHost implements Forge { private final String name; private final URI uri; + private final boolean useSsh; private final Credential pat; private final RestRequest request; private final Logger log = Logger.getLogger("org.openjdk.skara.forge.gitlab"); @@ -47,9 +46,10 @@ public class GitLabHost implements Forge { private HostUser cachedCurrentUser = null; - GitLabHost(String name, URI uri, Credential pat, Set groups) { + GitLabHost(String name, URI uri, boolean useSsh, Credential pat, Set groups) { this.name = name; this.uri = uri; + this.useSsh = useSsh; this.pat = pat; this.groups = groups; @@ -59,9 +59,10 @@ public class GitLabHost implements Forge { request = new RestRequest(baseApi, pat.username(), () -> Arrays.asList("Private-Token", pat.password())); } - GitLabHost(String name, URI uri, Set groups) { + GitLabHost(String name, URI uri, boolean useSsh, Set groups) { this.name = name; this.uri = uri; + this.useSsh = useSsh; this.pat = null; this.groups = groups; @@ -75,6 +76,10 @@ public URI getUri() { return uri; } + boolean useSsh() { + return useSsh; + } + Optional getPat() { return Optional.ofNullable(pat); } diff --git a/forge/src/main/java/org/openjdk/skara/forge/gitlab/GitLabRepository.java b/forge/src/main/java/org/openjdk/skara/forge/gitlab/GitLabRepository.java index 36d263e9b..fba8f2182 100644 --- a/forge/src/main/java/org/openjdk/skara/forge/gitlab/GitLabRepository.java +++ b/forge/src/main/java/org/openjdk/skara/forge/gitlab/GitLabRepository.java @@ -22,8 +22,8 @@ */ package org.openjdk.skara.forge.gitlab; -import org.openjdk.skara.host.HostUser; import org.openjdk.skara.forge.*; +import org.openjdk.skara.host.HostUser; import org.openjdk.skara.json.*; import org.openjdk.skara.network.*; import org.openjdk.skara.vcs.*; @@ -34,10 +34,9 @@ import java.time.*; import java.time.format.DateTimeFormatter; import java.util.*; -import java.util.function.Supplier; +import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.concurrent.ConcurrentHashMap; public class GitLabRepository implements HostedRepository { private final GitLabHost gitLabHost; @@ -173,11 +172,15 @@ public String name() { @Override public URI url() { - var builder = URIBuilder - .base(gitLabHost.getUri()) - .setPath("/" + projectName + ".git"); - gitLabHost.getPat().ifPresent(pat -> builder.setAuthentication(pat.username() + ":" + pat.password())); - return builder.build(); + if (gitLabHost.useSsh()) { + return URI.create("ssh://git@" + gitLabHost.getPat().orElseThrow().username() + "." + gitLabHost.getUri().getHost() + ":" + projectName + ".git"); + } else { + var builder = URIBuilder + .base(gitLabHost.getUri()) + .setPath("/" + projectName + ".git"); + gitLabHost.getPat().ifPresent(pat -> builder.setAuthentication(pat.username() + ":" + pat.password())); + return builder.build(); + } } @Override diff --git a/vcs/src/main/java/org/openjdk/skara/vcs/git/GitRepository.java b/vcs/src/main/java/org/openjdk/skara/vcs/git/GitRepository.java index 85e719594..40f533871 100644 --- a/vcs/src/main/java/org/openjdk/skara/vcs/git/GitRepository.java +++ b/vcs/src/main/java/org/openjdk/skara/vcs/git/GitRepository.java @@ -482,7 +482,7 @@ public Hash fetch(URI uri, String refspec, boolean includeTags) throws IOExcepti } else { cmd.add("--no-tags"); } - cmd.add(uri.toString()); + cmd.add(decodeUri(uri)); cmd.add(refspec); try (var p = capture(cmd)) { await(p); @@ -492,7 +492,7 @@ public Hash fetch(URI uri, String refspec, boolean includeTags) throws IOExcepti @Override public void fetchAll(URI uri, boolean includeTags) throws IOException { - var cmd = new ArrayList<>(List.of("git", "fetch", "--recurse-submodules=on-demand", "--prune", uri.toString())); + var cmd = new ArrayList<>(List.of("git", "fetch", "--recurse-submodules=on-demand", "--prune", decodeUri(uri))); cmd.add("+refs/heads/*:refs/heads/*"); if (includeTags) { cmd.add("+refs/tags/*:refs/tags/*"); @@ -566,7 +566,7 @@ public Repository init() throws IOException { @Override public void pushAll(URI uri) throws IOException { - try (var p = capture("git", "push", "--mirror", uri.toString())) { + try (var p = capture("git", "push", "--mirror", decodeUri(uri))) { await(p); } } @@ -579,7 +579,7 @@ public void push(Hash hash, URI uri, String ref, boolean force) throws IOExcepti } refspec += hash.hex() + ":" + ref; - try (var p = capture("git", "push", uri.toString(), refspec)) { + try (var p = capture("git", "push", decodeUri(uri), refspec)) { await(p); } } @@ -1344,6 +1344,14 @@ public Optional upstreamFor(Branch b) throws IOException { } } + private static String decodeUri(URI from) { + if (from.getScheme().equals("ssh")) { + return from.toString().substring(6); + } else { + return from.toString(); + } + } + public static Repository clone(URI from, Path to, boolean isBare, Path seed) throws IOException { var cmd = new ArrayList(); cmd.addAll(List.of("git", "clone")); @@ -1356,7 +1364,7 @@ public static Repository clone(URI from, Path to, boolean isBare, Path seed) thr cmd.add("--reference-if-able"); cmd.add(seed.toString()); } - cmd.addAll(List.of(from.toString(), to.toString())); + cmd.addAll(List.of(decodeUri(from), to.toString())); try (var p = capture(Path.of("").toAbsolutePath(), cmd)) { await(p); } @@ -1365,7 +1373,7 @@ public static Repository clone(URI from, Path to, boolean isBare, Path seed) thr public static Repository mirror(URI from, Path to) throws IOException { var cwd = Path.of("").toAbsolutePath(); - try (var p = capture(cwd, "git", "clone", "--mirror", from.toString(), to.toString())) { + try (var p = capture(cwd, "git", "clone", "--mirror", decodeUri(from), to.toString())) { await(p); } return new GitRepository(to);