Skip to content

Commit

Permalink
[bug] fix incorrect azure project name generation (#78)
Browse files Browse the repository at this point in the history
Signed-off-by: Benji Visser <benji@093b.org>
  • Loading branch information
noqcks authored Jul 19, 2023
1 parent 81eeaed commit 1191b1b
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 87 deletions.
202 changes: 117 additions & 85 deletions internal/config/project_name.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package config

import (
"fmt"
"log"
"net/url"
"regexp"
"strings"

Expand All @@ -13,129 +15,159 @@ type Project struct {
Repo *git.Repository
}

type RepoType int

const (
Unknown RepoType = iota
HTTPS
SSH
)
type URLFormatter struct {
URL string
}

func NewProject(repo *git.Repository) *Project {
p := &Project{Repo: repo}
p.Name = p.GetDefaultProjectName()
type GitURL interface {
Parse(url string) error
String() string
}

return p
type Azure struct {
Owner string
Path string
}

func (p *Project) GetRemoteURL() string {
// try to get the origin remote
origin, err := p.Repo.Remote("origin")
if err == nil {
return origin.Config().URLs[0]
func (r *Azure) Parse(rawurl string) error {
if strings.HasPrefix(rawurl, "git@") {
rawurl = strings.Replace(rawurl, ":", "/", 1)
rawurl = strings.Replace(rawurl, "git@", "https://", 1)
}

// if origin is not found, get the list of remotes
remotes, err := p.Repo.Remotes()
p, err := url.Parse(rawurl)
if err != nil {
return ""
return err
}

if len(remotes) == 0 {
return ""
}

// return the URL of the first remote found
return remotes[0].Config().URLs[0]
p.Path = strings.TrimSuffix(p.Path, ".git")
p.Path = strings.TrimPrefix(p.Path, "/")
p.Path = regexp.MustCompile(`^v\d+\/`).ReplaceAllString(p.Path, "")
p.Path = regexp.MustCompile(`/_git/`).ReplaceAllString(p.Path, "/")

pathParts := strings.Split(p.Path, "/")
r.Owner = pathParts[0]
r.Path = strings.Join(pathParts[1:], "/")
r.Path = strings.ReplaceAll(r.Path, " ", "%20")
return nil
}

func (p *Project) GetDefaultProjectName() string {
url := p.GetRemoteURL()
formatter := URLFormatter{URL: url}

return formatter.Format()
func (r *Azure) String() string {
return fmt.Sprintf("azure//%s/%s", r.Owner, r.Path)
}

type URLFormatter struct {
URL string
type GitHub struct {
Owner string
Path string
}

func (f *URLFormatter) getRepoType() RepoType {
httpsPattern := `^https://`
sshPattern := `^git@`

httpsRe, err := regexp.Compile(httpsPattern)
if err != nil {
return Unknown
func (r *GitHub) Parse(rawurl string) error {
if strings.HasPrefix(rawurl, "git@") {
rawurl = strings.Replace(rawurl, ":", "/", 1)
rawurl = strings.Replace(rawurl, "git@", "https://", 1)
}

sshRe, err := regexp.Compile(sshPattern)
p, err := url.Parse(rawurl)
if err != nil {
return Unknown
return err
}
p.Path = strings.TrimSuffix(p.Path, ".git")
p.Path = strings.TrimPrefix(p.Path, "/")

if httpsRe.MatchString(f.URL) {
return HTTPS
} else if sshRe.MatchString(f.URL) {
return SSH
}
pathParts := strings.SplitN(p.Path, "/", 2)
r.Owner = pathParts[0]
r.Path = pathParts[1]
return nil
}

return Unknown
func (r *GitHub) String() string {
return fmt.Sprintf("github//%s/%s", r.Owner, r.Path)
}

func (f *URLFormatter) formatStandardHTTPS() string {
trimmedURL := strings.TrimPrefix(f.URL, "https://")
trimmedURL = strings.TrimSuffix(trimmedURL, ".git")
type GitLab struct {
Owner string
Path string
}

parts := strings.Split(trimmedURL, "/")
if len(parts) > 2 {
return fmt.Sprintf("%s/%s", parts[1], parts[2])
func (r *GitLab) Parse(rawurl string) error {
if strings.HasPrefix(rawurl, "git@") {
rawurl = strings.Replace(rawurl, ":", "/", 1)
rawurl = strings.Replace(rawurl, "git@", "https://", 1)
}
return ""

p, err := url.Parse(rawurl)
if err != nil {
return err
}
p.Path = strings.TrimSuffix(p.Path, ".git")
p.Path = strings.TrimPrefix(p.Path, "/")

pathParts := strings.SplitN(p.Path, "/", 2)
r.Owner = pathParts[0]
r.Path = pathParts[1]
return nil
}

func (f *URLFormatter) formatStandardSSH() string {
trimmedURL := strings.TrimPrefix(f.URL, "git@")
trimmedURL = strings.TrimSuffix(trimmedURL, ".git")
func (r *GitLab) String() string {
return fmt.Sprintf("gitlab//%s/%s", r.Owner, r.Path)
}

parts := strings.Split(trimmedURL, ":")
if len(parts) < 2 {
return ""
func parseRawGitURL(rawurl string) (GitURL, error) {
var g GitURL
switch {
case strings.Contains(rawurl, "github.com"):
g = &GitHub{}
case strings.Contains(rawurl, "gitlab.com"):
g = &GitLab{}
case strings.Contains(rawurl, "dev.azure.com"):
g = &Azure{}
default:
return nil, fmt.Errorf("unsupported git url: %s", rawurl)
}

parts = strings.Split(parts[1], "/")
if len(parts) > 2 {
// azure
return fmt.Sprintf("%s/%s", parts[1], parts[2])
}
if len(parts) == 2 {
// github+gitlab
return fmt.Sprintf("%s/%s", parts[0], parts[1])
err := g.Parse(rawurl)
return g, err
}

func (f *URLFormatter) Format() string {
gURL, err := parseRawGitURL(f.URL)
if err != nil {
log.Fatal(err)
}

return ""
return gURL.String()
}

func (f *URLFormatter) Format() string {
repoType := f.getRepoType()
func NewProject(repo *git.Repository) *Project {
p := &Project{Repo: repo}
p.Name = p.GetDefaultProjectName()

var projectName string
if repoType == HTTPS {
projectName = f.formatStandardHTTPS()
return p
}

func (p *Project) GetRemoteURL() string {
// try to get the origin remote
origin, err := p.Repo.Remote("origin")
if err == nil {
return origin.Config().URLs[0]
}

if repoType == SSH {
projectName = f.formatStandardSSH()
// if origin is not found, get the list of remotes
remotes, err := p.Repo.Remotes()
if err != nil {
return ""
}

switch {
case strings.Contains(f.URL, "github"):
return fmt.Sprintf("github//%s", projectName)
case strings.Contains(f.URL, "gitlab"):
return fmt.Sprintf("gitlab//%s", projectName)
case strings.Contains(f.URL, "azure"):
return fmt.Sprintf("azure//%s", projectName)
if len(remotes) == 0 {
return ""
}

return ""
// return the URL of the first remote found
return remotes[0].Config().URLs[0]
}

func (p *Project) GetDefaultProjectName() string {
url := p.GetRemoteURL()
formatter := URLFormatter{URL: url}

return formatter.Format()
}
16 changes: 14 additions & 2 deletions internal/config/project_name_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,25 @@ func TestFormat(t *testing.T) {
url: "https://github.com/xeol-io/xeol.git",
expected: "github//xeol-io/xeol",
},
{
url: "git@ssh.dev.azure.com:v3/xeol-io/example-dotnet/v3",
expected: "azure//xeol-io/example-dotnet/v3",
},
{
url: "git@ssh.dev.azure.com:v3/xeol-io/example-dotnet/example-dotnet",
expected: "azure//xeol-io/example-dotnet",
expected: "azure//xeol-io/example-dotnet/example-dotnet",
},
{
url: "https://xeol-io@dev.azure.com/xeol-io/example-dotnet/_git/example-dotnet",
expected: "azure//xeol-io/example-dotnet",
expected: "azure//xeol-io/example-dotnet/example-dotnet",
},
{
url: "git@ssh.dev.azure.com:v3/xeol-io/Software%20Development/my-dotnet-api",
expected: "azure//xeol-io/Software%20Development/my-dotnet-api",
},
{
url: "https://dev.azure.com/xeol-io/Software%20Development/_git/my-dotnet-api",
expected: "azure//xeol-io/Software%20Development/my-dotnet-api",
},
{
url: "git@github.com:noqcks/xeol.git",
Expand Down

0 comments on commit 1191b1b

Please sign in to comment.