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

Add support for using ssh cloning with GitLab #1057

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -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<String> 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);
Copy link
Member

@magicus magicus Mar 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this result in us writing to a users .ssh/config when running Skara CLI tools?

Copy link
Member Author

@rwestberg rwestberg Mar 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, CLI tools never pass a configuration object when creating a Forge.

Copy link
Member

@magicus magicus Mar 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good.

} 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);
}
}
}
@@ -26,30 +26,30 @@
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");
private final Set<String> groups;

private HostUser cachedCurrentUser = null;

GitLabHost(String name, URI uri, Credential pat, Set<String> groups) {
GitLabHost(String name, URI uri, boolean useSsh, Credential pat, Set<String> 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<String> groups) {
GitLabHost(String name, URI uri, boolean useSsh, Set<String> 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<Credential> getPat() {
return Optional.ofNullable(pat);
}
@@ -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
@@ -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<String> 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<String>();
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);