Files
kandev/apps/backend/internal/repoclone/protocol.go
T

82 lines
2.7 KiB
Go

package repoclone
import (
"context"
"fmt"
"os/exec"
"strings"
"time"
"github.com/kandev/kandev/internal/common/subproc"
)
// ProtocolSSH is the SSH git protocol.
const ProtocolSSH = "ssh"
// ProtocolHTTPS is the HTTPS git protocol.
const ProtocolHTTPS = "https"
// DetectGitProtocol returns the user's preferred git clone protocol.
// It checks the gh CLI config (`gh config get git_protocol`). If gh reports
// "https", it returns ProtocolHTTPS. Otherwise it defaults to ProtocolSSH.
func DetectGitProtocol() string {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
out, err := subproc.RunGHOutput(ctx, exec.CommandContext(ctx, "gh", "config", "get", "git_protocol"))
if err == nil {
if strings.TrimSpace(string(out)) == ProtocolHTTPS {
return ProtocolHTTPS
}
}
return ProtocolSSH
}
// CloneURL builds a clone URL for the given provider, owner, name, and protocol.
// For SSH: git@github.com:{owner}/{name}.git
// For HTTPS: https://github.com/{owner}/{name}.git
// Returns an error if the provider is not supported.
func CloneURL(provider, owner, name, protocol string) (string, error) {
return CloneURLWithHost(provider, "", owner, name, protocol)
}
// CloneURLWithHost is like CloneURL but accepts an explicit host. When host
// is non-empty (and stripped of scheme/trailing-slash), it overrides the
// provider's default — used for self-managed GitLab. When host is empty,
// behaves exactly like CloneURL.
func CloneURLWithHost(provider, host, owner, name, protocol string) (string, error) {
resolvedHost := strings.TrimRight(host, "/")
resolvedHost = strings.TrimPrefix(strings.TrimPrefix(resolvedHost, "https://"), "http://")
if resolvedHost == "" {
var err error
resolvedHost, err = providerHost(provider)
if err != nil {
return "", err
}
}
if protocol == ProtocolSSH {
// scp-style "git@host:path" can't carry a port — when the host has
// one (gitlab.acme.corp:2222) fall back to the ssh:// URL form,
// which git understands and accepts a port natively.
if strings.Contains(resolvedHost, ":") {
return fmt.Sprintf("ssh://git@%s/%s/%s.git", resolvedHost, owner, name), nil
}
return fmt.Sprintf("git@%s:%s/%s.git", resolvedHost, owner, name), nil
}
return fmt.Sprintf("https://%s/%s/%s.git", resolvedHost, owner, name), nil
}
// providerHost maps a provider name to its git host.
func providerHost(provider string) (string, error) {
switch strings.ToLower(provider) {
case "github", "":
return "github.com", nil
case "gitlab":
return "gitlab.com", nil
case "bitbucket":
// TODO: Bitbucket support is not yet implemented
return "bitbucket.org", nil
default:
return "", fmt.Errorf("unsupported git provider: %q", provider)
}
}