fix interface of newURL

This commit is contained in:
Songmu 2019-12-24 02:06:43 +09:00
parent 769b55d91e
commit 2237341b82
5 changed files with 62 additions and 61 deletions

View file

@ -24,7 +24,7 @@ func doList(c *cli.Context) error {
}
if query != "" {
if hasSchemePattern.MatchString(query) || scpLikeURLPattern.MatchString(query) {
if url, err := newURL(query, false); err == nil {
if url, err := newURL(query, false, false); err == nil {
if repo, err := LocalRepositoryFromURL(url); err == nil {
query = filepath.ToSlash(repo.RelPath)
}

View file

@ -47,7 +47,7 @@ func doLook(c *cli.Context) error {
}
if len(reposFound) == 0 {
if url, err := newURL(name, false); err == nil {
if url, err := newURL(name, false, false); err == nil {
repo, err := LocalRepositoryFromURL(url)
if err != nil {
return err

View file

@ -31,46 +31,11 @@ type getter struct {
}
func (g *getter) get(argURL string) error {
// If argURL is a "./foo" or "../bar" form,
// find repository name trailing after github.com/USER/.
argURL = filepath.ToSlash(argURL)
parts := strings.Split(argURL, "/")
if parts[0] == "." || parts[0] == ".." {
if wd, err := os.Getwd(); err == nil {
path := filepath.Clean(filepath.Join(wd, filepath.Join(parts...)))
var localRepoRoot string
roots, err := localRepositoryRoots(true)
if err != nil {
return err
}
for _, r := range roots {
p := strings.TrimPrefix(path, r+string(filepath.Separator))
if p != path && (localRepoRoot == "" || len(p) < len(localRepoRoot)) {
localRepoRoot = filepath.ToSlash(p)
}
}
if localRepoRoot != "" {
// Guess it
logger.Log("resolved", fmt.Sprintf("relative %q to %q", argURL, "https://"+localRepoRoot))
argURL = "https://" + localRepoRoot
}
}
}
u, err := newURL(argURL, false)
u, err := newURL(argURL, g.ssh, false)
if err != nil {
return fmt.Errorf("Could not parse URL %q: %w", argURL, err)
}
if g.ssh {
// Assume Git repository if `-p` is given.
if u, err = convertGitURLHTTPToSSH(u); err != nil {
return fmt.Errorf("Could not convert URL %q: %w", u, err)
}
}
remote, err := NewRemoteRepository(u)
if err != nil {
return err

72
url.go
View file

@ -4,11 +4,13 @@ import (
"fmt"
"net/url"
"os"
"path/filepath"
"regexp"
"runtime"
"strings"
"github.com/Songmu/gitconfig"
"github.com/motemen/ghq/logger"
)
// Convert SCP-like URL to SSH URL(e.g. [user@]host.xz:path/to/repo.git/)
@ -20,7 +22,35 @@ var (
looksLikeAuthorityPattern = regexp.MustCompile(`[A-Za-z0-9]\.[A-Za-z]+(?::\d{1,5})?$`)
)
func newURL(ref string, forceMe bool) (*url.URL, error) {
func newURL(ref string, ssh, forceMe bool) (*url.URL, error) {
// If argURL is a "./foo" or "../bar" form,
// find repository name trailing after github.com/USER/.
ref = filepath.ToSlash(ref)
parts := strings.Split(ref, "/")
if parts[0] == "." || parts[0] == ".." {
if wd, err := os.Getwd(); err == nil {
path := filepath.Clean(filepath.Join(wd, filepath.Join(parts...)))
var localRepoRoot string
roots, err := localRepositoryRoots(true)
if err != nil {
return nil, err
}
for _, r := range roots {
p := strings.TrimPrefix(path, r+string(filepath.Separator))
if p != path && (localRepoRoot == "" || len(p) < len(localRepoRoot)) {
localRepoRoot = filepath.ToSlash(p)
}
}
if localRepoRoot != "" {
// Guess it
logger.Log("resolved", fmt.Sprintf("relative %q to %q", ref, "https://"+localRepoRoot))
ref = "https://" + localRepoRoot
}
}
}
if !hasSchemePattern.MatchString(ref) {
if scpLikeURLPattern.MatchString(ref) {
matched := scpLikeURLPattern.FindStringSubmatch(ref)
@ -42,35 +72,41 @@ func newURL(ref string, forceMe bool) (*url.URL, error) {
}
}
url, err := url.Parse(ref)
u, err := url.Parse(ref)
if err != nil {
return url, err
return nil, err
}
if !url.IsAbs() {
if !strings.Contains(url.Path, "/") {
url.Path, err = fillUsernameToPath(url.Path, forceMe)
if !u.IsAbs() {
if !strings.Contains(u.Path, "/") {
u.Path, err = fillUsernameToPath(u.Path, forceMe)
if err != nil {
return url, err
return nil, err
}
}
url.Scheme = "https"
url.Host = "github.com"
if url.Path[0] != '/' {
url.Path = "/" + url.Path
u.Scheme = "https"
u.Host = "github.com"
if u.Path[0] != '/' {
u.Path = "/" + u.Path
}
}
return url, nil
if ssh {
// Assume Git repository if `-p` is given.
if u, err = convertGitURLHTTPToSSH(u); err != nil {
return nil, fmt.Errorf("Could not convert URL %q: %w", u, err)
}
}
return u, nil
}
func convertGitURLHTTPToSSH(url *url.URL) (*url.URL, error) {
func convertGitURLHTTPToSSH(u *url.URL) (*url.URL, error) {
user := "git"
if url.User != nil {
user = url.User.Username()
if u.User != nil {
user = u.User.Username()
}
sshURL := fmt.Sprintf("ssh://%s@%s%s", user, url.Host, url.Path)
return url.Parse(sshURL)
sshURL := fmt.Sprintf("ssh://%s@%s%s", user, u.Host, u.Path)
return u.Parse(sshURL)
}
func detectUserName() (string, error) {

View file

@ -77,7 +77,7 @@ completeUser = false`)
if tc.setup != nil {
defer tc.setup()()
}
repo, err := newURL(tc.url, false)
repo, err := newURL(tc.url, false, false)
if err != nil {
t.Errorf("error should be nil but: %s", err)
}
@ -107,7 +107,7 @@ func TestConvertGitURLHTTPToSSH(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.url, func(t *testing.T) {
httpsURL, err := newURL(tc.url, false)
httpsURL, err := newURL(tc.url, false, false)
if err != nil {
t.Errorf("error should be nil but: %s", err)
}
@ -124,15 +124,15 @@ func TestConvertGitURLHTTPToSSH(t *testing.T) {
func TestNewURL_err(t *testing.T) {
invalidURL := "http://foo.com/?foo\nbar"
_, err := newURL(invalidURL, false)
_, err := newURL(invalidURL, false, false)
const wantSub = "net/url: invalid control character in URL"
if got := fmt.Sprint(err); !strings.Contains(got, wantSub) {
t.Errorf("newURL(%q, false) error = %q; want substring %q", invalidURL, got, wantSub)
t.Errorf("newURL(%q, false, false) error = %q; want substring %q", invalidURL, got, wantSub)
}
defer gitconfig.WithConfig(t, `[[[`)()
var exitError *exec.ExitError
_, err = newURL("peco", false)
_, err = newURL("peco", false, false)
if !errors.As(err, &exitError) {
t.Errorf("error should be occurred but nil")
}