Repository: seachicken/gh-poi
Pull request #113 (2023-10-28 11:27:43)

Support ssh URL
`gh-poi` doesn't work on the repository that is cloned by https://github.com/x-motemen/ghq:

```
% gh poi
✕ Fetching pull requests...
failed to run external command: gh, args: [repo view ssh///git@github.com/shogo82148/gh-poi --json owner,name,parent,defaultBranchRef]
 exit status 1
```

the result of `git remote -v` is:

```
% git remote -v
origin	ssh://git@github.com/shogo82148/gh-poi.git (fetch)
origin	ssh://git@github.com/shogo82148/gh-poi.git (push)
upstream	git@github.com:seachicken/gh-poi.git (fetch)
upstream	git@github.com:seachicken/gh-poi.git (push)
```

It looks that `gh-poi` doesn't support ssh-url.
This pull request fixes it.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/seachicken/gh-poi/pull/113?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Seito+Tanaka) Report
> Merging [#113](https://app.codecov.io/gh/seachicken/gh-poi/pull/113?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Seito+Tanaka) (f938003) into [main](https://app.codecov.io/gh/seachicken/gh-poi/commit/da77b46abaf7cb202f6c0c8dd780b654a14ac708?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Seito+Tanaka) (da77b46) will **decrease** coverage by `0.20%`.
> The diff coverage is `75.00%`.

```diff
@@            Coverage Diff             @@
##             main     #113      +/-   ##
==========================================
- Coverage   81.88%   81.69%   -0.20%     
==========================================
  Files           5        5              
  Lines         762      781      +19     
==========================================
+ Hits          624      638      +14     
- Misses        114      117       +3     
- Partials       24       26       +2     
```


| [Files](https://app.codecov.io/gh/seachicken/gh-poi/pull/113?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Seito+Tanaka) | Coverage Δ | |
|---|---|---|
| [shared/remote.go](https://app.codecov.io/gh/seachicken/gh-poi/pull/113?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Seito+Tanaka#diff-c2hhcmVkL3JlbW90ZS5nbw==) | `77.77% <75.00%> (-9.73%)` | :arrow_down: |


seachicken: Wonderful! I also wanted to know which formats require support, so this was helpful.
http://git-scm.com/docs/git-fetch#_git_urls

Patch:
diff --git a/shared/remote.go b/shared/remote.go
index 3579da8..dd2b76f 100644
--- a/shared/remote.go
+++ b/shared/remote.go
@@ -1,6 +1,8 @@
 package shared
 
 import (
+	"fmt"
+	"net/url"
 	"regexp"
 	"strings"
 )
@@ -13,16 +15,54 @@ type (
 	}
 )
 
-// https://datatracker.ietf.org/doc/html/rfc3986#section-2.3
-var regex = regexp.MustCompile(`^(?:(?:[a-zA-Z0-9-._~]+)(?:://|@))?([a-zA-Z0-9-._~]+)[:/](.+?/.+?)(?:\.git|)$`)
+var (
+	hasSchemePattern  = regexp.MustCompile("^[^:]+://")
+	scpLikeURLPattern = regexp.MustCompile("^([^@]+@)?([^:]+):(/?.+)$")
+)
 
+// NewRemote parses the result of `git remote -v` and returns a Remote struct.
+//
+// acceptable url formats:
+//
+//	ssh://[user@]host.xz[:port]/path/to/repo.git/
+//	git://host.xz[:port]/path/to/repo.git/
+//	http[s]://host.xz[:port]/path/to/repo.git/
+//	ftp[s]://host.xz[:port]/path/to/repo.git/
+//
+// An alternative scp-like syntax may also be used with the ssh protocol:
+//
+//	[user@]host.xz:path/to/repo.git/
+//
+// ref. http://git-scm.com/docs/git-fetch#_git_urls
+// the code is heavily inspired by https://github.com/x-motemen/ghq/blob/7163e61e2309a039241ad40b4a25bea35671ea6f/url.go
 func NewRemote(remoteConfig string) Remote {
 	splitConfig := strings.Fields(remoteConfig)
-	if len(splitConfig) == 3 {
-		found := regex.FindStringSubmatch(splitConfig[1])
-		if len(found) == 3 {
-			return Remote{splitConfig[0], found[1], found[2]}
+	if len(splitConfig) != 3 {
+		return Remote{}
+	}
+
+	ref := splitConfig[1]
+	if !hasSchemePattern.MatchString(ref) {
+		if scpLikeURLPattern.MatchString(ref) {
+			matched := scpLikeURLPattern.FindStringSubmatch(ref)
+			user := matched[1]
+			host := matched[2]
+			path := matched[3]
+			ref = fmt.Sprintf("ssh://%s%s/%s", user, host, strings.TrimPrefix(path, "/"))
 		}
 	}
-	return Remote{}
+	u, err := url.Parse(ref)
+	if err != nil {
+		return Remote{}
+	}
+
+	repo := u.Path
+	repo = strings.TrimPrefix(repo, "/")
+	repo = strings.TrimSuffix(repo, ".git")
+
+	return Remote{
+		Name:     splitConfig[0],
+		Hostname: u.Host,
+		RepoName: repo,
+	}
 }


Test patch:
diff --git a/shared/remote_test.go b/shared/remote_test.go
index 4de0c40..a8fcdf6 100644
--- a/shared/remote_test.go
+++ b/shared/remote_test.go
@@ -28,6 +28,17 @@ func Test_CreateRemoteWithScpLikeUrlAndCustomUserinfo(t *testing.T) {
 	)
 }
 
+func Test_CreateRemoteWithSshUrl(t *testing.T) {
+	assert.Equal(t,
+		Remote{
+			Name:     "origin",
+			Hostname: "github.com",
+			RepoName: "org/repo",
+		},
+		NewRemote("origin	ssh://git@github.com/org/repo.git (fetch)"),
+	)
+}
+
 func Test_CreateRemoteWithScpLikeUrlWithoutUserinfo(t *testing.T) {
 	assert.Equal(t,
 		Remote{