1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-06-06 23:46:13 +02:00

refactor: 💡 Use new approach introduced via #1637

fix: 🐛 The root URI for Azure DevOps repositories contains _git

refactor so that we don't have conditional logic based on service definition

no need for this commend anymore

add comment

Fixed RegEx for HTTP remote git URL

Added Tests

pretty sure we can do this safely
This commit is contained in:
tiwood 2021-12-30 16:04:49 +01:00 committed by Jesse Duffield
parent 08ee3309cb
commit f0d0d45ba7
3 changed files with 79 additions and 88 deletions

View File

@ -6,6 +6,7 @@ var defaultUrlRegexStrings = []string{
`^(?:https?|ssh)://.*/(?P<owner>.*)/(?P<repo>.*?)(?:\.git)?$`, `^(?:https?|ssh)://.*/(?P<owner>.*)/(?P<repo>.*?)(?:\.git)?$`,
`^git@.*:(?P<owner>.*)/(?P<repo>.*?)(?:\.git)?$`, `^git@.*:(?P<owner>.*)/(?P<repo>.*?)(?:\.git)?$`,
} }
var defaultRepoURLTemplate = "https://{{.webDomain}}/{{.owner}}/{{.repo}}"
// we've got less type safety using go templates but this lends itself better to // we've got less type safety using go templates but this lends itself better to
// users adding custom service definitions in their config // users adding custom service definitions in their config
@ -15,6 +16,7 @@ var githubServiceDef = ServiceDefinition{
pullRequestURLIntoTargetBranch: "/compare/{{.To}}...{{.From}}?expand=1", pullRequestURLIntoTargetBranch: "/compare/{{.To}}...{{.From}}?expand=1",
commitURL: "/commit/{{.CommitSha}}", commitURL: "/commit/{{.CommitSha}}",
regexStrings: defaultUrlRegexStrings, regexStrings: defaultUrlRegexStrings,
repoURLTemplate: defaultRepoURLTemplate,
} }
var bitbucketServiceDef = ServiceDefinition{ var bitbucketServiceDef = ServiceDefinition{
@ -23,6 +25,7 @@ var bitbucketServiceDef = ServiceDefinition{
pullRequestURLIntoTargetBranch: "/pull-requests/new?source={{.From}}&dest={{.To}}&t=1", pullRequestURLIntoTargetBranch: "/pull-requests/new?source={{.From}}&dest={{.To}}&t=1",
commitURL: "/commits/{{.CommitSha}}", commitURL: "/commits/{{.CommitSha}}",
regexStrings: defaultUrlRegexStrings, regexStrings: defaultUrlRegexStrings,
repoURLTemplate: defaultRepoURLTemplate,
} }
var gitLabServiceDef = ServiceDefinition{ var gitLabServiceDef = ServiceDefinition{
@ -31,9 +34,27 @@ var gitLabServiceDef = ServiceDefinition{
pullRequestURLIntoTargetBranch: "/merge_requests/new?merge_request[source_branch]={{.From}}&merge_request[target_branch]={{.To}}", pullRequestURLIntoTargetBranch: "/merge_requests/new?merge_request[source_branch]={{.From}}&merge_request[target_branch]={{.To}}",
commitURL: "/commit/{{.CommitSha}}", commitURL: "/commit/{{.CommitSha}}",
regexStrings: defaultUrlRegexStrings, regexStrings: defaultUrlRegexStrings,
repoURLTemplate: defaultRepoURLTemplate,
} }
var serviceDefinitions = []ServiceDefinition{githubServiceDef, bitbucketServiceDef, gitLabServiceDef} var azdoServiceDef = ServiceDefinition{
provider: "azuredevops",
pullRequestURLIntoDefaultBranch: "/pullrequestcreate?sourceRef={{.From}}",
pullRequestURLIntoTargetBranch: "/pullrequestcreate?sourceRef={{.From}}&targetRef={{.To}}",
commitURL: "/commit/{{.CommitSha}}",
regexStrings: []string{
`^git@ssh.dev.azure.com.*/(?P<org>.*)/(?P<project>.*)/(?P<repo>.*?)(?:\.git)?$`,
`^https://.*@dev.azure.com/(?P<org>.*?)/(?P<project>.*?)/_git/(?P<repo>.*?)(?:\.git)?$`,
},
repoURLTemplate: "https://{{.webDomain}}/{{.org}}/{{.project}}/_git/{{.repo}}",
}
var serviceDefinitions = []ServiceDefinition{
githubServiceDef,
bitbucketServiceDef,
gitLabServiceDef,
azdoServiceDef,
}
var defaultServiceDomains = []ServiceDomain{ var defaultServiceDomains = []ServiceDomain{
{ {
@ -51,4 +72,9 @@ var defaultServiceDomains = []ServiceDomain{
gitDomain: "gitlab.com", gitDomain: "gitlab.com",
webDomain: "gitlab.com", webDomain: "gitlab.com",
}, },
{
serviceDefinition: azdoServiceDef,
gitDomain: "dev.azure.com",
webDomain: "dev.azure.com",
},
} }

View File

@ -1,7 +1,6 @@
package hosting_service package hosting_service
import ( import (
"fmt"
"net/url" "net/url"
"regexp" "regexp"
"strings" "strings"
@ -66,13 +65,13 @@ func (self *HostingServiceMgr) getService() (*Service, error) {
return nil, err return nil, err
} }
root, err := serviceDomain.getRootFromRemoteURL(self.remoteURL) repoURL, err := serviceDomain.serviceDefinition.getRepoURLFromRemoteURL(self.remoteURL, serviceDomain.webDomain)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Service{ return &Service{
root: root, repoURL: repoURL,
ServiceDefinition: serviceDomain.serviceDefinition, ServiceDefinition: serviceDomain.serviceDefinition,
}, nil }, nil
} }
@ -139,47 +138,32 @@ type ServiceDomain struct {
serviceDefinition ServiceDefinition serviceDefinition ServiceDefinition
} }
func (self ServiceDomain) getRootFromRemoteURL(repoURL string) (string, error) {
// we may want to make this more specific to the service in future e.g. if
// some new service comes along which has a different root url structure.
repoInfo, err := self.serviceDefinition.getRepoInfoFromURL(repoURL)
if err != nil {
return "", err
}
return fmt.Sprintf("https://%s/%s/%s", self.webDomain, repoInfo.Owner, repoInfo.Repository), nil
}
// RepoInformation holds some basic information about the repo
type RepoInformation struct {
Owner string
Repository string
}
type ServiceDefinition struct { type ServiceDefinition struct {
provider string provider string
pullRequestURLIntoDefaultBranch string pullRequestURLIntoDefaultBranch string
pullRequestURLIntoTargetBranch string pullRequestURLIntoTargetBranch string
commitURL string commitURL string
regexStrings []string regexStrings []string
// can expect 'webdomain' to be passed in. Otherwise, you get to pick what we match in the regex
repoURLTemplate string
} }
func (self ServiceDefinition) getRepoInfoFromURL(url string) (*RepoInformation, error) { func (self ServiceDefinition) getRepoURLFromRemoteURL(url string, webDomain string) (string, error) {
for _, regexStr := range self.regexStrings { for _, regexStr := range self.regexStrings {
re := regexp.MustCompile(regexStr) re := regexp.MustCompile(regexStr)
matches := utils.FindNamedMatches(re, url) input := utils.FindNamedMatches(re, url)
if matches != nil { if input != nil {
return &RepoInformation{ input["webDomain"] = webDomain
Owner: matches["owner"], return utils.ResolvePlaceholderString(self.repoURLTemplate, input), nil
Repository: matches["repo"],
}, nil
} }
} }
return nil, errors.New("Failed to parse repo information from url") return "", errors.New("Failed to parse repo information from url")
} }
type Service struct { type Service struct {
root string repoURL string
ServiceDefinition ServiceDefinition
} }
@ -196,5 +180,5 @@ func (self *Service) getCommitURL(commitSha string) string {
} }
func (self *Service) resolveUrl(templateString string, args map[string]string) string { func (self *Service) resolveUrl(templateString string, args map[string]string) string {
return self.root + utils.ResolvePlaceholderString(templateString, args) return self.repoURL + utils.ResolvePlaceholderString(templateString, args)
} }

View File

@ -8,63 +8,6 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestGetRepoInfoFromURL(t *testing.T) {
type scenario struct {
serviceDefinition ServiceDefinition
testName string
repoURL string
test func(*RepoInformation)
}
scenarios := []scenario{
{
githubServiceDef,
"Returns repository information for git remote url",
"git@github.com:petersmith/super_calculator",
func(repoInfo *RepoInformation) {
assert.EqualValues(t, repoInfo.Owner, "petersmith")
assert.EqualValues(t, repoInfo.Repository, "super_calculator")
},
},
{
githubServiceDef,
"Returns repository information for git remote url, trimming trailing '.git'",
"git@github.com:petersmith/super_calculator.git",
func(repoInfo *RepoInformation) {
assert.EqualValues(t, repoInfo.Owner, "petersmith")
assert.EqualValues(t, repoInfo.Repository, "super_calculator")
},
},
{
githubServiceDef,
"Returns repository information for ssh remote url",
"ssh://git@github.com/petersmith/super_calculator",
func(repoInfo *RepoInformation) {
assert.EqualValues(t, repoInfo.Owner, "petersmith")
assert.EqualValues(t, repoInfo.Repository, "super_calculator")
},
},
{
githubServiceDef,
"Returns repository information for http remote url",
"https://my_username@bitbucket.org/johndoe/social_network.git",
func(repoInfo *RepoInformation) {
assert.EqualValues(t, repoInfo.Owner, "johndoe")
assert.EqualValues(t, repoInfo.Repository, "social_network")
},
},
}
for _, s := range scenarios {
s := s
t.Run(s.testName, func(t *testing.T) {
result, err := s.serviceDefinition.getRepoInfoFromURL(s.repoURL)
assert.NoError(t, err)
s.test(result)
})
}
}
func TestGetPullRequestURL(t *testing.T) { func TestGetPullRequestURL(t *testing.T) {
type scenario struct { type scenario struct {
testName string testName string
@ -172,6 +115,44 @@ func TestGetPullRequestURL(t *testing.T) {
assert.Equal(t, "https://gitlab.com/peter/public/calculator/merge_requests/new?merge_request[source_branch]=feature%2Fcommit-ui&merge_request[target_branch]=epic%2Fui", url) assert.Equal(t, "https://gitlab.com/peter/public/calculator/merge_requests/new?merge_request[source_branch]=feature%2Fcommit-ui&merge_request[target_branch]=epic%2Fui", url)
}, },
}, },
{
testName: "Opens a link to new pull request on Azure DevOps (SSH)",
from: "feature/new",
remoteUrl: "git@ssh.dev.azure.com:v3/myorg/myproject/myrepo",
test: func(url string, err error) {
assert.NoError(t, err)
assert.Equal(t, "https://dev.azure.com/myorg/myproject/_git/myrepo/pullrequestcreate?sourceRef=feature/new", url)
},
},
{
testName: "Opens a link to new pull request on Azure DevOps (SSH) with specifc target",
from: "feature/new",
to: "dev",
remoteUrl: "git@ssh.dev.azure.com:v3/myorg/myproject/myrepo",
test: func(url string, err error) {
assert.NoError(t, err)
assert.Equal(t, "https://dev.azure.com/myorg/myproject/_git/myrepo/pullrequestcreate?sourceRef=feature/new&targetRef=dev", url)
},
},
{
testName: "Opens a link to new pull request on Azure DevOps (HTTP)",
from: "feature/new",
remoteUrl: "https://myorg@dev.azure.com/myorg/myproject/_git/myrepo",
test: func(url string, err error) {
assert.NoError(t, err)
assert.Equal(t, "https://dev.azure.com/myorg/myproject/_git/myrepo/pullrequestcreate?sourceRef=feature/new", url)
},
},
{
testName: "Opens a link to new pull request on Azure DevOps (HTTP) with specifc target",
from: "feature/new",
to: "dev",
remoteUrl: "https://myorg@dev.azure.com/myorg/myproject/_git/myrepo",
test: func(url string, err error) {
assert.NoError(t, err)
assert.Equal(t, "https://dev.azure.com/myorg/myproject/_git/myrepo/pullrequestcreate?sourceRef=feature/new&targetRef=dev", url)
},
},
{ {
testName: "Throws an error if git service is unsupported", testName: "Throws an error if git service is unsupported",
from: "feature/divide-operation", from: "feature/divide-operation",
@ -218,7 +199,7 @@ func TestGetPullRequestURL(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "https://bitbucket.org/johndoe/social_network/pull-requests/new?source=feature%2Fprofile-page&t=1", url) assert.Equal(t, "https://bitbucket.org/johndoe/social_network/pull-requests/new?source=feature%2Fprofile-page&t=1", url)
}, },
expectedLoggedErrors: []string{"Unknown git service type: 'noservice'. Expected one of github, bitbucket, gitlab"}, expectedLoggedErrors: []string{"Unknown git service type: 'noservice'. Expected one of github, bitbucket, gitlab, azuredevops"},
}, },
{ {
testName: "Escapes reserved URL characters in from branch name", testName: "Escapes reserved URL characters in from branch name",