fix(setup): broaden GitHub remote parsing

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-01 07:25:34 +00:00
parent c06fd2edfc
commit af9887217a
2 changed files with 57 additions and 11 deletions

View file

@ -8,6 +8,7 @@ package setup
import (
"fmt"
"net/url"
"os"
"os/exec"
"path/filepath"
@ -294,23 +295,46 @@ func detectGitHubRepo() string {
return ""
}
url := strings.TrimSpace(string(output))
return parseGitHubRepoURL(strings.TrimSpace(string(output)))
}
// Handle SSH format: git@github.com:owner/repo.git
if strings.HasPrefix(url, "git@github.com:") {
repo := strings.TrimPrefix(url, "git@github.com:")
repo = strings.TrimSuffix(repo, ".git")
return repo
// parseGitHubRepoURL extracts owner/repo from a GitHub remote URL.
//
// Supports the common remote formats used by git:
// - git@github.com:owner/repo.git
// - ssh://git@github.com/owner/repo.git
// - https://github.com/owner/repo.git
// - git://github.com/owner/repo.git
func parseGitHubRepoURL(remote string) string {
remote = strings.TrimSpace(remote)
if remote == "" {
return ""
}
// Handle HTTPS format: https://github.com/owner/repo.git
if strings.Contains(url, "github.com/") {
parts := strings.Split(url, "github.com/")
if len(parts) == 2 {
repo := strings.TrimSuffix(parts[1], ".git")
// Handle SSH-style scp syntax first.
if strings.HasPrefix(remote, "git@github.com:") {
repo := strings.TrimPrefix(remote, "git@github.com:")
return strings.TrimSuffix(repo, ".git")
}
if parsed, err := url.Parse(remote); err == nil && parsed.Host != "" {
host := strings.TrimPrefix(parsed.Hostname(), "www.")
if host == "github.com" {
repo := strings.TrimPrefix(parsed.Path, "/")
repo = strings.TrimSuffix(repo, ".git")
repo = strings.TrimSuffix(repo, "/")
return repo
}
}
if strings.Contains(remote, "github.com/") {
parts := strings.SplitN(remote, "github.com/", 2)
if len(parts) == 2 {
repo := strings.TrimPrefix(parts[1], "/")
repo = strings.TrimSuffix(repo, ".git")
return strings.TrimSuffix(repo, "/")
}
}
return ""
}

View file

@ -28,3 +28,25 @@ func TestDetectProjectType_PrefersPackageOverComposer(t *testing.T) {
require.Equal(t, "node", detectProjectType(dir))
}
func TestParseGitHubRepoURL_Good(t *testing.T) {
cases := map[string]string{
"git@github.com:owner/repo.git": "owner/repo",
"ssh://git@github.com/owner/repo.git": "owner/repo",
"https://github.com/owner/repo.git": "owner/repo",
"git://github.com/owner/repo.git": "owner/repo",
"https://www.github.com/owner/repo": "owner/repo",
"git@github.com:owner/nested/repo.git": "owner/nested/repo",
"ssh://git@github.com/owner/nested/repo/": "owner/nested/repo",
"ssh://git@github.com:443/owner/repo.git": "owner/repo",
"https://example.com/owner/repo.git": "",
"git@bitbucket.org:owner/repo.git": "",
" ssh://git@github.com/owner/repo.git ": "owner/repo",
}
for remote, expected := range cases {
t.Run(remote, func(t *testing.T) {
require.Equal(t, expected, parseGitHubRepoURL(remote))
})
}
}