Skip to content

Commit

Permalink
feat: allow git remote urls from non-scm sources (#3088)
Browse files Browse the repository at this point in the history
basically allows to use goreleaser against a repo with a git url without both owner and repo name.

closes #3060
closes #3058

Signed-off-by: Carlos A Becker <caarlos0@gmail.com>
  • Loading branch information
caarlos0 committed May 9, 2022
1 parent a02cbca commit 53b1ee0
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 16 deletions.
25 changes: 19 additions & 6 deletions internal/git/config.go
Expand Up @@ -16,7 +16,7 @@ func ExtractRepoFromConfig(ctx context.Context) (result config.Repo, err error)
if !IsRepo(ctx) {
return result, errors.New("current folder is not a git repository")
}
out, err := Run(ctx, "ls-remote", "--get-url")
out, err := Clean(Run(ctx, "ls-remote", "--get-url"))
if err != nil {
return result, fmt.Errorf("no remote configured to list refs from")
}
Expand Down Expand Up @@ -44,19 +44,32 @@ func ExtractRepoFromURL(rawurl string) (config.Repo, error) {
// now we can parse it with net/url
u, err := url.Parse(s)
if err != nil {
return config.Repo{}, err
return config.Repo{
RawURL: rawurl,
}, err
}

// split the parsed url path by /, the last parts should be the owner and name
ss := strings.Split(strings.TrimPrefix(u.Path, "/"), "/")

// if less than 2 parts, its likely not a valid repository
// if empty, returns an error
if len(ss) == 0 || ss[0] == "" {
return config.Repo{
RawURL: rawurl,
}, fmt.Errorf("unsupported repository URL: %s", rawurl)
}

// if less than 2 parts, its likely not a valid repository, but we'll allow it.
if len(ss) < 2 {
return config.Repo{}, fmt.Errorf("unsupported repository URL: %s", rawurl)
return config.Repo{
RawURL: rawurl,
Owner: ss[0],
}, nil
}
repo := config.Repo{
Owner: strings.Join(ss[:len(ss)-1], "/"),
Name: ss[len(ss)-1],
RawURL: rawurl,
Owner: strings.Join(ss[:len(ss)-1], "/"),
Name: ss[len(ss)-1],
}
log.WithField("owner", repo.Owner).WithField("name", repo.Name).Debugf("parsed url")
return repo, nil
Expand Down
21 changes: 20 additions & 1 deletion internal/git/config_test.go
Expand Up @@ -60,6 +60,8 @@ func TestExtractRepoFromURL(t *testing.T) {
repo, err := git.ExtractRepoFromURL(url)
require.NoError(t, err)
require.Equal(t, "goreleaser/goreleaser", repo.String())
require.NoError(t, repo.CheckSCM())
require.Equal(t, url, repo.RawURL)
})
}

Expand All @@ -73,18 +75,35 @@ func TestExtractRepoFromURL(t *testing.T) {
repo, err := git.ExtractRepoFromURL(url)
require.NoError(t, err)
require.Equal(t, "group/nested/goreleaser/goreleaser", repo.String())
require.NoError(t, repo.CheckSCM())
require.Equal(t, url, repo.RawURL)
})
}

// invalid urls
for _, url := range []string{
"git@gist.github.com:someid.git",
"https://gist.github.com/someid.git",
} {
t.Run(url, func(t *testing.T) {
repo, err := git.ExtractRepoFromURL(url)
require.NoError(t, err)
require.Equal(t, "someid", repo.String())
require.Error(t, repo.CheckSCM())
require.Equal(t, url, repo.RawURL)
})
}

// invalid urls
for _, url := range []string{
"git@gist.github.com:",
"https://gist.github.com/",
} {
t.Run(url, func(t *testing.T) {
repo, err := git.ExtractRepoFromURL(url)
require.EqualError(t, err, "unsupported repository URL: "+url)
require.Equal(t, "", repo.String())
require.Error(t, repo.CheckSCM())
require.Equal(t, url, repo.RawURL)
})
}
}
6 changes: 6 additions & 0 deletions internal/pipe/changelog/changelog.go
Expand Up @@ -287,6 +287,9 @@ func newGithubChangeloger(ctx *context.Context) (changeloger, error) {
if err != nil {
return nil, err
}
if err := repo.CheckSCM(); err != nil {
return nil, err
}
return &githubNativeChangeloger{
client: cli,
repo: client.Repo{
Expand All @@ -305,6 +308,9 @@ func newSCMChangeloger(ctx *context.Context) (changeloger, error) {
if err != nil {
return nil, err
}
if err := repo.CheckSCM(); err != nil {
return nil, err
}
return &scmChangeloger{
client: cli,
repo: client.Repo{
Expand Down
30 changes: 30 additions & 0 deletions internal/pipe/changelog/changelog_test.go
Expand Up @@ -536,6 +536,21 @@ func TestGetChangeloger(t *testing.T) {
require.IsType(t, c, &githubNativeChangeloger{})
})

t.Run(useGitHubNative+"-invalid-repo", func(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "https://gist.github.com/")
ctx := context.New(config.Project{
Changelog: config.Changelog{
Use: useGitHubNative,
},
})
ctx.TokenType = context.TokenTypeGitHub
c, err := getChangeloger(ctx)
require.EqualError(t, err, "unsupported repository URL: https://gist.github.com/")
require.Nil(t, c)
})

t.Run(useGitLab, func(t *testing.T) {
ctx := context.New(config.Project{
Changelog: config.Changelog{
Expand All @@ -548,6 +563,21 @@ func TestGetChangeloger(t *testing.T) {
require.IsType(t, c, &scmChangeloger{})
})

t.Run(useGitHub+"-invalid-repo", func(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "https://gist.github.com/")
ctx := context.New(config.Project{
Changelog: config.Changelog{
Use: useGitHub,
},
})
ctx.TokenType = context.TokenTypeGitHub
c, err := getChangeloger(ctx)
require.EqualError(t, err, "unsupported repository URL: https://gist.github.com/")
require.Nil(t, c)
})

t.Run("invalid", func(t *testing.T) {
c, err := getChangeloger(context.New(config.Project{
Changelog: config.Changelog{
Expand Down
4 changes: 3 additions & 1 deletion internal/pipe/milestone/milestone.go
Expand Up @@ -29,10 +29,12 @@ func (Pipe) Default(ctx *context.Context) error {

if milestone.Repo.Name == "" {
repo, err := git.ExtractRepoFromConfig(ctx)

if err != nil && !ctx.Snapshot {
return err
}
if err := repo.CheckSCM(); err != nil && !ctx.Snapshot {
return err
}

milestone.Repo = repo
}
Expand Down
12 changes: 12 additions & 0 deletions internal/pipe/milestone/milestone_test.go
Expand Up @@ -31,6 +31,18 @@ func TestDefaultWithRepoConfig(t *testing.T) {
require.Equal(t, "configowner", ctx.Config.Milestones[0].Repo.Owner)
}

func TestDefaultWithInvalidRemote(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@github.com:githubowner.git")

ctx := context.New(config.Project{
Milestones: []config.Milestone{{}},
})
ctx.TokenType = context.TokenTypeGitHub
require.Error(t, Pipe{}.Default(ctx))
}

func TestDefaultWithRepoRemote(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
Expand Down
18 changes: 15 additions & 3 deletions internal/pipe/release/release.go
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/goreleaser/goreleaser/internal/extrafiles"
"github.com/goreleaser/goreleaser/internal/git"
"github.com/goreleaser/goreleaser/internal/semerrgroup"
"github.com/goreleaser/goreleaser/pkg/config"
"github.com/goreleaser/goreleaser/pkg/context"
)

Expand Down Expand Up @@ -48,7 +49,7 @@ func (Pipe) Default(ctx *context.Context) error {
switch ctx.TokenType {
case context.TokenTypeGitLab:
if ctx.Config.Release.GitLab.Name == "" {
repo, err := git.ExtractRepoFromConfig(ctx)
repo, err := getRepository(ctx)
if err != nil {
return err
}
Expand All @@ -63,7 +64,7 @@ func (Pipe) Default(ctx *context.Context) error {
)
case context.TokenTypeGitea:
if ctx.Config.Release.Gitea.Name == "" {
repo, err := git.ExtractRepoFromConfig(ctx)
repo, err := getRepository(ctx)
if err != nil {
return err
}
Expand All @@ -79,7 +80,7 @@ func (Pipe) Default(ctx *context.Context) error {
default:
// We keep github as default for now
if ctx.Config.Release.GitHub.Name == "" {
repo, err := git.ExtractRepoFromConfig(ctx)
repo, err := getRepository(ctx)
if err != nil && !ctx.Snapshot {
return err
}
Expand Down Expand Up @@ -109,6 +110,17 @@ func (Pipe) Default(ctx *context.Context) error {
return nil
}

func getRepository(ctx *context.Context) (config.Repo, error) {
repo, err := git.ExtractRepoFromConfig(ctx)
if err != nil {
return config.Repo{}, err
}
if err := repo.CheckSCM(); err != nil {
return config.Repo{}, err
}
return repo, nil
}

// Publish the release.
func (Pipe) Publish(ctx *context.Context) error {
c, err := client.New(ctx)
Expand Down
36 changes: 36 additions & 0 deletions internal/pipe/release/release_test.go
Expand Up @@ -354,6 +354,18 @@ func TestDefault(t *testing.T) {
require.Equal(t, "https://github.com/goreleaser/goreleaser/releases/tag/v1.0.0", ctx.ReleaseURL)
}

func TestDefaultInvalidURL(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@github.com:goreleaser.git")

ctx := context.New(config.Project{})
ctx.TokenType = context.TokenTypeGitHub
ctx.Config.GitHubURLs.Download = "https://github.com"
ctx.Git.CurrentTag = "v1.0.0"
require.Error(t, Pipe{}.Default(ctx))
}

func TestDefaultWithGitlab(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
Expand All @@ -369,6 +381,18 @@ func TestDefaultWithGitlab(t *testing.T) {
require.Equal(t, "https://gitlab.com/gitlabowner/gitlabrepo/-/releases/v1.0.0", ctx.ReleaseURL)
}

func TestDefaultWithGitlabInvalidURL(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@gitlab.com:gitlabrepo.git")

ctx := context.New(config.Project{})
ctx.TokenType = context.TokenTypeGitLab
ctx.Config.GitLabURLs.Download = "https://gitlab.com"
ctx.Git.CurrentTag = "v1.0.0"
require.Error(t, Pipe{}.Default(ctx))
}

func TestDefaultWithGitea(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
Expand All @@ -384,6 +408,18 @@ func TestDefaultWithGitea(t *testing.T) {
require.Equal(t, "https://git.honk.com/giteaowner/gitearepo/releases/tag/v1.0.0", ctx.ReleaseURL)
}

func TestDefaultWithGiteaInvalidURL(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@gitea.example.com:gitearepo.git")

ctx := context.New(config.Project{})
ctx.TokenType = context.TokenTypeGitea
ctx.Config.GiteaURLs.Download = "https://git.honk.com"
ctx.Git.CurrentTag = "v1.0.0"
require.Error(t, Pipe{}.Default(ctx))
}

func TestDefaultPreRelease(t *testing.T) {
testlib.Mktmp(t)
testlib.GitInit(t)
Expand Down
25 changes: 20 additions & 5 deletions pkg/config/config.go
Expand Up @@ -4,6 +4,7 @@ package config

import (
"encoding/json"
"fmt"
"io"
"os"
"strings"
Expand Down Expand Up @@ -42,16 +43,30 @@ type GiteaURLs struct {
// Repo represents any kind of repo (github, gitlab, etc).
// to upload releases into.
type Repo struct {
Owner string `yaml:"owner,omitempty"`
Name string `yaml:"name,omitempty"`
Owner string `yaml:"owner,omitempty"`
Name string `yaml:"name,omitempty"`
RawURL string `yaml:"-"`
}

// String of the repo, e.g. owner/name.
func (r Repo) String() string {
if r.Owner == "" && r.Name == "" {
return ""
if r.isSCM() {
return r.Owner + "/" + r.Name
}
return r.Owner + "/" + r.Name
return r.Owner
}

// CheckSCM returns an error if the given url is not a valid scm url.
func (r Repo) CheckSCM() error {
if r.isSCM() {
return nil
}
return fmt.Errorf("invalid scm url: %s", r.RawURL)
}

// isSCM returns true if the repo has both an owner and name.
func (r Repo) isSCM() bool {
return r.Owner != "" && r.Name != ""
}

// RepoRef represents any kind of repo which may differ
Expand Down

0 comments on commit 53b1ee0

Please sign in to comment.