Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow git remote urls from non-scm sources #3088

Merged
merged 1 commit into from May 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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