mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-01-10 03:47:03 +02:00
917cae54f0
## If applied, this commit will... If applied this change will allow goreleaser to handle relative remotes when attempting to parse a repo URL from git. ## Why is this change being made? To fix the error that I recently came across while trying to test my goreleaser configuration: ``` % goreleaser check • checking path= ⨯ configuration is invalid error=invalid scm url: . ⨯ .goreleaser.yml error=configuration is invalid: invalid scm url: . ⨯ command failed error=1 out of 1 configuration file(s) have issues ``` This change happened while on a branch doing some development. As part of that development I needed to test a change to my goreleaser config. My git config at the time looked like (repo obfuscated): ``` % cat .git/config [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "origin"] url = git@gitlab.com:some/repo fetch = +refs/heads/*:refs/remotes/origin/* [branch "main"] remote = origin merge = refs/heads/main [branch "release_fixes"] remote = . merge = refs/heads/main ``` It is fairly common for git to add remotes with a `.` when branch tracking is enabled. While, in general, there aren't many use cases that require a user to need to release from a non-primary branch, there are cases where the user may want to test their configuration with `goreleaser check` and the error of `invalid scm url: .` isn't very helpful. --------- Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
98 lines
2.9 KiB
Go
98 lines
2.9 KiB
Go
package git
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/url"
|
|
"path"
|
|
"strings"
|
|
|
|
"github.com/caarlos0/log"
|
|
"github.com/goreleaser/goreleaser/pkg/config"
|
|
)
|
|
|
|
// ExtractRepoFromConfig gets the repo name from the Git config.
|
|
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 := Clean(Run(ctx, "ls-remote", "--get-url"))
|
|
if err != nil {
|
|
return result, fmt.Errorf("no remote configured to list refs from")
|
|
}
|
|
// This is a relative remote URL and requires some additional processing
|
|
if out == "." {
|
|
return extractRelativeRepoFromConfig(ctx)
|
|
}
|
|
log.WithField("rawurl", out).Debugf("got git url")
|
|
return ExtractRepoFromURL(out)
|
|
}
|
|
|
|
func extractRelativeRepoFromConfig(ctx context.Context) (result config.Repo, err error) {
|
|
out, err := Clean(Run(ctx, "rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"))
|
|
if err != nil || out == "" {
|
|
return result, fmt.Errorf("unable to get upstream while qualifying relative remote")
|
|
}
|
|
out, err = Clean(Run(ctx, "config", "--get", fmt.Sprintf("branch.%s.remote", out)))
|
|
if err != nil || out == "" {
|
|
return result, fmt.Errorf("unable to get upstream's remote while qualifying relative remote")
|
|
}
|
|
out, err = Clean(Run(ctx, "ls-remote", "--get-url", out))
|
|
if err != nil {
|
|
return result, fmt.Errorf("unable to get upstream while qualifying relative remote")
|
|
}
|
|
return ExtractRepoFromURL(out)
|
|
}
|
|
|
|
func ExtractRepoFromURL(rawurl string) (config.Repo, error) {
|
|
// removes the .git suffix and any new lines
|
|
s := strings.TrimSuffix(strings.TrimSpace(rawurl), ".git")
|
|
|
|
// if the URL contains a :, indicating a SSH config,
|
|
// remove all chars until it, including itself
|
|
// on HTTP and HTTPS URLs it will remove the http(s): prefix,
|
|
// which is ok. On SSH URLs the whole user@server will be removed,
|
|
// which is required.
|
|
|
|
// If the url contains more than 1 ':' character, assume we are doing an
|
|
// http URL with a username/password in it, and normalize the URL.
|
|
// Gitlab-CI uses this type of URL
|
|
if strings.Count(s, ":") == 1 {
|
|
s = s[strings.LastIndex(s, ":")+1:]
|
|
}
|
|
|
|
// now we can parse it with net/url
|
|
u, err := url.Parse(s)
|
|
if err != nil {
|
|
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 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{
|
|
RawURL: rawurl,
|
|
Owner: ss[0],
|
|
}, nil
|
|
}
|
|
repo := config.Repo{
|
|
RawURL: rawurl,
|
|
Owner: path.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
|
|
}
|