mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-05-21 22:53:07 +02:00
feat: support custom tokens in Homebrew & Scoop (#1650)
This commit is contained in:
parent
0d4f605388
commit
ab8bb7f2f3
@ -18,11 +18,23 @@ type Info struct {
|
||||
URL string
|
||||
}
|
||||
|
||||
type Repo struct {
|
||||
Owner string
|
||||
Name string
|
||||
}
|
||||
|
||||
func (r Repo) String() string {
|
||||
if r.Owner == "" && r.Name == "" {
|
||||
return ""
|
||||
}
|
||||
return r.Owner + "/" + r.Name
|
||||
}
|
||||
|
||||
// Client interface.
|
||||
type Client interface {
|
||||
CreateRelease(ctx *context.Context, body string) (releaseID string, err error)
|
||||
ReleaseURLTemplate(ctx *context.Context) (string, error)
|
||||
CreateFile(ctx *context.Context, commitAuthor config.CommitAuthor, repo config.Repo, content []byte, path, message string) (err error)
|
||||
CreateFile(ctx *context.Context, commitAuthor config.CommitAuthor, repo Repo, content []byte, path, message string) (err error)
|
||||
Upload(ctx *context.Context, releaseID string, artifact *artifact.Artifact, file *os.File) (err error)
|
||||
}
|
||||
|
||||
@ -30,13 +42,26 @@ type Client interface {
|
||||
func New(ctx *context.Context) (Client, error) {
|
||||
log.WithField("type", ctx.TokenType).Info("token type")
|
||||
if ctx.TokenType == context.TokenTypeGitHub {
|
||||
return NewGitHub(ctx)
|
||||
return NewGitHub(ctx, ctx.Token)
|
||||
}
|
||||
if ctx.TokenType == context.TokenTypeGitLab {
|
||||
return NewGitLab(ctx)
|
||||
return NewGitLab(ctx, ctx.Token)
|
||||
}
|
||||
if ctx.TokenType == context.TokenTypeGitea {
|
||||
return NewGitea(ctx)
|
||||
return NewGitea(ctx, ctx.Token)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func NewWithToken(ctx *context.Context, token string) (Client, error) {
|
||||
if ctx.TokenType == context.TokenTypeGitHub {
|
||||
return NewGitHub(ctx, token)
|
||||
}
|
||||
if ctx.TokenType == context.TokenTypeGitLab {
|
||||
return NewGitLab(ctx, token)
|
||||
}
|
||||
if ctx.TokenType == context.TokenTypeGitea {
|
||||
return NewGitea(ctx, token)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
12
internal/client/config.go
Normal file
12
internal/client/config.go
Normal file
@ -0,0 +1,12 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/goreleaser/goreleaser/pkg/config"
|
||||
)
|
||||
|
||||
func RepoFromRef(ref config.RepoRef) Repo {
|
||||
return Repo{
|
||||
Owner: ref.Owner,
|
||||
Name: ref.Name,
|
||||
}
|
||||
}
|
@ -34,12 +34,12 @@ func getInstanceURL(apiURL string) (string, error) {
|
||||
}
|
||||
|
||||
// NewGitea returns a gitea client implementation.
|
||||
func NewGitea(ctx *context.Context) (Client, error) {
|
||||
func NewGitea(ctx *context.Context, token string) (Client, error) {
|
||||
instanceURL, err := getInstanceURL(ctx.Config.GiteaURLs.API)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := gitea.NewClient(instanceURL, ctx.Token)
|
||||
client := gitea.NewClient(instanceURL, token)
|
||||
transport := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
// nolint: gosec
|
||||
@ -56,7 +56,7 @@ func NewGitea(ctx *context.Context) (Client, error) {
|
||||
func (c *giteaClient) CreateFile(
|
||||
ctx *context.Context,
|
||||
commitAuthor config.CommitAuthor,
|
||||
repo config.Repo,
|
||||
repo Repo,
|
||||
content []byte,
|
||||
path,
|
||||
message string,
|
||||
|
@ -245,7 +245,7 @@ func TestGiteaCreateFile(t *testing.T) {
|
||||
client := giteaClient{}
|
||||
ctx := context.Context{}
|
||||
author := config.CommitAuthor{}
|
||||
repo := config.Repo{}
|
||||
repo := Repo{}
|
||||
content := []byte{}
|
||||
path := ""
|
||||
message := ""
|
||||
|
@ -25,9 +25,9 @@ type githubClient struct {
|
||||
}
|
||||
|
||||
// NewGitHub returns a github client implementation.
|
||||
func NewGitHub(ctx *context.Context) (Client, error) {
|
||||
func NewGitHub(ctx *context.Context, token string) (Client, error) {
|
||||
ts := oauth2.StaticTokenSource(
|
||||
&oauth2.Token{AccessToken: ctx.Token},
|
||||
&oauth2.Token{AccessToken: token},
|
||||
)
|
||||
httpClient := oauth2.NewClient(ctx, ts)
|
||||
base := httpClient.Transport.(*oauth2.Transport).Base
|
||||
@ -59,7 +59,7 @@ func NewGitHub(ctx *context.Context) (Client, error) {
|
||||
func (c *githubClient) CreateFile(
|
||||
ctx *context.Context,
|
||||
commitAuthor config.CommitAuthor,
|
||||
repo config.Repo,
|
||||
repo Repo,
|
||||
content []byte,
|
||||
path,
|
||||
message string,
|
||||
|
@ -11,34 +11,37 @@ import (
|
||||
|
||||
func TestNewGitHubClient(t *testing.T) {
|
||||
t.Run("good urls", func(t *testing.T) {
|
||||
_, err := NewGitHub(context.New(config.Project{
|
||||
ctx := context.New(config.Project{
|
||||
GitHubURLs: config.GitHubURLs{
|
||||
API: "https://github.mycompany.com/api",
|
||||
Upload: "https://github.mycompany.com/upload",
|
||||
},
|
||||
}))
|
||||
})
|
||||
_, err := NewGitHub(ctx, ctx.Token)
|
||||
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("bad api url", func(t *testing.T) {
|
||||
_, err := NewGitHub(context.New(config.Project{
|
||||
ctx := context.New(config.Project{
|
||||
GitHubURLs: config.GitHubURLs{
|
||||
API: "://github.mycompany.com/api",
|
||||
Upload: "https://github.mycompany.com/upload",
|
||||
},
|
||||
}))
|
||||
})
|
||||
_, err := NewGitHub(ctx, ctx.Token)
|
||||
|
||||
require.EqualError(t, err, `parse "://github.mycompany.com/api": missing protocol scheme`)
|
||||
})
|
||||
|
||||
t.Run("bad upload url", func(t *testing.T) {
|
||||
_, err := NewGitHub(context.New(config.Project{
|
||||
ctx := context.New(config.Project{
|
||||
GitHubURLs: config.GitHubURLs{
|
||||
API: "https://github.mycompany.com/api",
|
||||
Upload: "not a url:4994",
|
||||
},
|
||||
}))
|
||||
})
|
||||
_, err := NewGitHub(ctx, ctx.Token)
|
||||
|
||||
require.EqualError(t, err, `parse "not a url:4994": first path segment in URL cannot contain colon`)
|
||||
})
|
||||
@ -46,7 +49,7 @@ func TestNewGitHubClient(t *testing.T) {
|
||||
|
||||
func TestGitHubUploadReleaseIDNotInt(t *testing.T) {
|
||||
var ctx = context.New(config.Project{})
|
||||
client, err := NewGitHub(ctx)
|
||||
client, err := NewGitHub(ctx, ctx.Token)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.EqualError(
|
||||
@ -69,7 +72,7 @@ func TestGitHubReleaseURLTemplate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
client, err := NewGitHub(ctx)
|
||||
client, err := NewGitHub(ctx, ctx.Token)
|
||||
require.NoError(t, err)
|
||||
|
||||
urlTpl, err := client.ReleaseURLTemplate(ctx)
|
||||
@ -85,7 +88,7 @@ func TestGitHubCreateReleaseWrongNameTemplate(t *testing.T) {
|
||||
NameTemplate: "{{.dddddddddd",
|
||||
},
|
||||
})
|
||||
client, err := NewGitHub(ctx)
|
||||
client, err := NewGitHub(ctx, ctx.Token)
|
||||
require.NoError(t, err)
|
||||
|
||||
str, err := client.CreateRelease(ctx, "")
|
||||
|
@ -26,8 +26,7 @@ type gitlabClient struct {
|
||||
}
|
||||
|
||||
// NewGitLab returns a gitlab client implementation.
|
||||
func NewGitLab(ctx *context.Context) (Client, error) {
|
||||
token := ctx.Token
|
||||
func NewGitLab(ctx *context.Context, token string) (Client, error) {
|
||||
transport := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
// nolint: gosec
|
||||
@ -54,7 +53,7 @@ func NewGitLab(ctx *context.Context) (Client, error) {
|
||||
func (c *gitlabClient) CreateFile(
|
||||
ctx *context.Context,
|
||||
commitAuthor config.CommitAuthor,
|
||||
repo config.Repo,
|
||||
repo Repo,
|
||||
content []byte, // the content of the formula.rb
|
||||
path, // the path to the formula.rb
|
||||
message string, // the commit msg
|
||||
|
@ -47,7 +47,7 @@ func TestGitLabReleaseURLTemplate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
})
|
||||
client, err := NewGitLab(ctx)
|
||||
client, err := NewGitLab(ctx, ctx.Token)
|
||||
assert.NoError(t, err)
|
||||
|
||||
urlTpl, err := client.ReleaseURLTemplate(ctx)
|
||||
|
@ -88,11 +88,13 @@ func (Pipe) Default(ctx *context.Context) error {
|
||||
}
|
||||
if brew.GitHub.String() != "" {
|
||||
deprecate.Notice(ctx, "brews.github")
|
||||
brew.Tap = brew.GitHub
|
||||
brew.Tap.Owner = brew.GitHub.Owner
|
||||
brew.Tap.Name = brew.GitHub.Name
|
||||
}
|
||||
if brew.GitLab.String() != "" {
|
||||
deprecate.Notice(ctx, "brews.gitlab")
|
||||
brew.Tap = brew.GitLab
|
||||
brew.Tap.Owner = brew.GitLab.Owner
|
||||
brew.Tap.Name = brew.GitLab.Name
|
||||
}
|
||||
if brew.CommitAuthor.Name == "" {
|
||||
brew.CommitAuthor.Name = "goreleaserbot"
|
||||
@ -129,11 +131,24 @@ func contains(ss []string, s string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func doRun(ctx *context.Context, brew config.Homebrew, client client.Client) error {
|
||||
func doRun(ctx *context.Context, brew config.Homebrew, cl client.Client) error {
|
||||
if brew.Tap.Name == "" {
|
||||
return pipe.Skip("brew section is not configured")
|
||||
}
|
||||
|
||||
if brew.Tap.Token != "" {
|
||||
token, err := tmpl.New(ctx).ApplySingleEnvOnly(brew.Tap.Token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("using custom token to publish homebrew formula")
|
||||
c, err := client.NewWithToken(ctx, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cl = c
|
||||
}
|
||||
|
||||
// TODO: properly cover this with tests
|
||||
var filters = []artifact.Filter{
|
||||
artifact.Or(
|
||||
@ -160,7 +175,7 @@ func doRun(ctx *context.Context, brew config.Homebrew, client client.Client) err
|
||||
return ErrNoArchivesFound
|
||||
}
|
||||
|
||||
content, err := buildFormula(ctx, brew, client, archives)
|
||||
content, err := buildFormula(ctx, brew, cl, archives)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -169,7 +184,7 @@ func doRun(ctx *context.Context, brew config.Homebrew, client client.Client) err
|
||||
var path = filepath.Join(ctx.Config.Dist, filename)
|
||||
log.WithField("formula", path).Info("writing")
|
||||
if err := ioutil.WriteFile(path, []byte(content), 0644); err != nil { //nolint: gosec
|
||||
return errors.Wrap(err, "failed to write brew tap")
|
||||
return errors.Wrap(err, "failed to write brew formula")
|
||||
}
|
||||
|
||||
if strings.TrimSpace(brew.SkipUpload) == "true" {
|
||||
@ -182,7 +197,7 @@ func doRun(ctx *context.Context, brew config.Homebrew, client client.Client) err
|
||||
return pipe.Skip("prerelease detected with 'auto' upload, skipping homebrew publish")
|
||||
}
|
||||
|
||||
repo := brew.Tap
|
||||
repo := client.RepoFromRef(brew.Tap)
|
||||
|
||||
var gpath = buildFormulaPath(brew.Folder, filename)
|
||||
log.WithField("formula", gpath).
|
||||
@ -190,7 +205,7 @@ func doRun(ctx *context.Context, brew config.Homebrew, client client.Client) err
|
||||
Info("pushing")
|
||||
|
||||
var msg = fmt.Sprintf("Brew formula update for %s version %s", ctx.Config.ProjectName, ctx.Git.CurrentTag)
|
||||
return client.CreateFile(ctx, brew.CommitAuthor, repo, []byte(content), gpath, msg)
|
||||
return cl.CreateFile(ctx, brew.CommitAuthor, repo, []byte(content), gpath, msg)
|
||||
}
|
||||
|
||||
func buildFormulaPath(folder, filename string) string {
|
||||
|
@ -269,7 +269,7 @@ func TestRunPipeForMultipleArmVersions(t *testing.T) {
|
||||
Dependencies: []config.HomebrewDependency{{Name: "zsh"}, {Name: "bash", Type: "recommended"}},
|
||||
Conflicts: []string{"gtk+", "qt"},
|
||||
Install: `bin.install "{{ .ProjectName }}"`,
|
||||
Tap: config.Repo{
|
||||
Tap: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -365,7 +365,7 @@ func TestRunPipeNoDarwin64Build(t *testing.T) {
|
||||
Config: config.Project{
|
||||
Brews: []config.Homebrew{
|
||||
{
|
||||
Tap: config.Repo{
|
||||
Tap: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -383,7 +383,7 @@ func TestRunPipeMultipleArchivesSameOsBuild(t *testing.T) {
|
||||
config.Project{
|
||||
Brews: []config.Homebrew{
|
||||
{
|
||||
Tap: config.Repo{
|
||||
Tap: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -537,7 +537,7 @@ func TestRunPipeBinaryRelease(t *testing.T) {
|
||||
config.Project{
|
||||
Brews: []config.Homebrew{
|
||||
{
|
||||
Tap: config.Repo{
|
||||
Tap: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -566,7 +566,7 @@ func TestRunPipeNoUpload(t *testing.T) {
|
||||
Release: config.Release{},
|
||||
Brews: []config.Homebrew{
|
||||
{
|
||||
Tap: config.Repo{
|
||||
Tap: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -618,7 +618,7 @@ func TestRunEmptyTokenType(t *testing.T) {
|
||||
Release: config.Release{},
|
||||
Brews: []config.Homebrew{
|
||||
{
|
||||
Tap: config.Repo{
|
||||
Tap: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -653,7 +653,7 @@ func TestRunTokenTypeNotImplementedForBrew(t *testing.T) {
|
||||
Release: config.Release{},
|
||||
Brews: []config.Homebrew{
|
||||
{
|
||||
Tap: config.Repo{
|
||||
Tap: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -743,7 +743,7 @@ func (dc *DummyClient) ReleaseURLTemplate(ctx *context.Context) (string, error)
|
||||
return "https://dummyhost/download/{{ .Tag }}/{{ .ArtifactName }}", nil
|
||||
}
|
||||
|
||||
func (dc *DummyClient) CreateFile(ctx *context.Context, commitAuthor config.CommitAuthor, repo config.Repo, content []byte, path, msg string) (err error) {
|
||||
func (dc *DummyClient) CreateFile(ctx *context.Context, commitAuthor config.CommitAuthor, repo client.Repo, content []byte, path, msg string) (err error) {
|
||||
dc.CreatedFile = true
|
||||
dc.Content = string(content)
|
||||
return
|
||||
|
@ -550,7 +550,7 @@ func (c *DummyClient) ReleaseURLTemplate(ctx *context.Context) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (c *DummyClient) CreateFile(ctx *context.Context, commitAuthor config.CommitAuthor, repo config.Repo, content []byte, path, msg string) (err error) {
|
||||
func (c *DummyClient) CreateFile(ctx *context.Context, commitAuthor config.CommitAuthor, repo client.Repo, content []byte, path, msg string) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -570,7 +570,7 @@ func (c *DummyClient) Upload(ctx *context.Context, releaseID string, artifact *a
|
||||
}
|
||||
if c.FailFirstUpload {
|
||||
c.FailFirstUpload = false
|
||||
return client.RetriableError{errors.New("upload failed, should retry")}
|
||||
return client.RetriableError{Err: errors.New("upload failed, should retry")}
|
||||
}
|
||||
c.UploadedFile = true
|
||||
c.UploadedFileNames = append(c.UploadedFileNames, artifact.Name)
|
||||
|
@ -34,6 +34,7 @@ func (Pipe) Publish(ctx *context.Context) error {
|
||||
if ctx.SkipPublish {
|
||||
return pipe.ErrSkipPublishEnabled
|
||||
}
|
||||
|
||||
client, err := client.New(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -60,15 +61,24 @@ func (Pipe) Default(ctx *context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func doRun(ctx *context.Context, client client.Client) error {
|
||||
if ctx.Config.Scoop.Bucket.Name == "" {
|
||||
func doRun(ctx *context.Context, cl client.Client) error {
|
||||
scoop := ctx.Config.Scoop
|
||||
if scoop.Bucket.Name == "" {
|
||||
return pipe.Skip("scoop section is not configured")
|
||||
}
|
||||
|
||||
// TODO mavogel: in another PR
|
||||
// check if release pipe is not configured!
|
||||
// if ctx.Config.Release.Disable {
|
||||
// }
|
||||
if scoop.Bucket.Token != "" {
|
||||
token, err := tmpl.New(ctx).ApplySingleEnvOnly(scoop.Bucket.Token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug("using custom token to publish scoop manifest")
|
||||
c, err := client.NewWithToken(ctx, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cl = c
|
||||
}
|
||||
|
||||
// TODO: multiple archives
|
||||
if ctx.Config.Archives[0].Format == "binary" {
|
||||
@ -85,9 +95,9 @@ func doRun(ctx *context.Context, client client.Client) error {
|
||||
return ErrNoWindows
|
||||
}
|
||||
|
||||
var path = ctx.Config.Scoop.Name + ".json"
|
||||
var path = scoop.Name + ".json"
|
||||
|
||||
data, err := dataFor(ctx, client, archives)
|
||||
data, err := dataFor(ctx, cl, archives)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -99,10 +109,10 @@ func doRun(ctx *context.Context, client client.Client) error {
|
||||
if ctx.SkipPublish {
|
||||
return pipe.ErrSkipPublishEnabled
|
||||
}
|
||||
if strings.TrimSpace(ctx.Config.Scoop.SkipUpload) == "true" {
|
||||
if strings.TrimSpace(scoop.SkipUpload) == "true" {
|
||||
return pipe.Skip("scoop.skip_upload is true")
|
||||
}
|
||||
if strings.TrimSpace(ctx.Config.Scoop.SkipUpload) == "auto" && ctx.Semver.Prerelease != "" {
|
||||
if strings.TrimSpace(scoop.SkipUpload) == "auto" && ctx.Semver.Prerelease != "" {
|
||||
return pipe.Skip("release is prerelease")
|
||||
}
|
||||
if ctx.Config.Release.Draft {
|
||||
@ -113,15 +123,16 @@ func doRun(ctx *context.Context, client client.Client) error {
|
||||
}
|
||||
|
||||
commitMessage, err := tmpl.New(ctx).
|
||||
Apply(ctx.Config.Scoop.CommitMessageTemplate)
|
||||
Apply(scoop.CommitMessageTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return client.CreateFile(
|
||||
repo := client.RepoFromRef(scoop.Bucket)
|
||||
return cl.CreateFile(
|
||||
ctx,
|
||||
ctx.Config.Scoop.CommitAuthor,
|
||||
ctx.Config.Scoop.Bucket,
|
||||
scoop.CommitAuthor,
|
||||
repo,
|
||||
content.Bytes(),
|
||||
path,
|
||||
commitMessage,
|
||||
|
@ -113,7 +113,7 @@ func Test_doRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -156,7 +156,7 @@ func Test_doRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -216,7 +216,7 @@ func Test_doRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -259,7 +259,7 @@ func Test_doRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -319,7 +319,7 @@ func Test_doRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -377,7 +377,7 @@ func Test_doRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -420,7 +420,7 @@ func Test_doRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -498,7 +498,7 @@ func Test_doRun(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -539,7 +539,7 @@ func Test_doRun(t *testing.T) {
|
||||
Draft: true,
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -589,7 +589,7 @@ func Test_doRun(t *testing.T) {
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
SkipUpload: "auto",
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -633,7 +633,7 @@ func Test_doRun(t *testing.T) {
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
SkipUpload: "true",
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -673,7 +673,7 @@ func Test_doRun(t *testing.T) {
|
||||
Disable: true,
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -713,7 +713,7 @@ func Test_doRun(t *testing.T) {
|
||||
Draft: true,
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -780,7 +780,7 @@ func Test_buildManifest(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -820,7 +820,7 @@ func Test_buildManifest(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -862,7 +862,7 @@ func Test_buildManifest(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -968,7 +968,7 @@ func TestWrapInDirectory(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Scoop: config.Scoop{
|
||||
Bucket: config.Repo{
|
||||
Bucket: config.RepoRef{
|
||||
Owner: "test",
|
||||
Name: "test",
|
||||
},
|
||||
@ -1034,7 +1034,7 @@ func (dc *DummyClient) ReleaseURLTemplate(ctx *context.Context) (string, error)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (dc *DummyClient) CreateFile(ctx *context.Context, commitAuthor config.CommitAuthor, repo config.Repo, content []byte, path, msg string) (err error) {
|
||||
func (dc *DummyClient) CreateFile(ctx *context.Context, commitAuthor config.CommitAuthor, repo client.Repo, content []byte, path, msg string) (err error) {
|
||||
dc.CreatedFile = true
|
||||
dc.Content = string(content)
|
||||
return
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
@ -176,6 +177,41 @@ func (t *Template) Apply(s string) (string, error) {
|
||||
return out.String(), err
|
||||
}
|
||||
|
||||
type ExpectedSingleEnvErr struct{}
|
||||
|
||||
func (e ExpectedSingleEnvErr) Error() string {
|
||||
return "expected {{ .Env.VAR_NAME }} only (no plain-text or other interpolation)"
|
||||
}
|
||||
|
||||
// ApplySingleEnvOnly enforces template to only contain a single environment variable
|
||||
// and nothing else.
|
||||
func (t *Template) ApplySingleEnvOnly(s string) (string, error) {
|
||||
s = strings.TrimSpace(s)
|
||||
if len(s) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// text/template/parse (lexer) could be used here too,
|
||||
// but regexp reduces the complexity and should be sufficient,
|
||||
// given the context is mostly discouraging users from bad practice
|
||||
// of hard-coded credentials, rather than catch all possible cases
|
||||
envOnlyRe := regexp.MustCompile(`^{{\s*\.Env\.[^.\s}]+\s*}}$`)
|
||||
if !envOnlyRe.Match([]byte(s)) {
|
||||
return "", ExpectedSingleEnvErr{}
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
tmpl, err := template.New("tmpl").
|
||||
Option("missingkey=error").
|
||||
Parse(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = tmpl.Execute(&out, t.fields)
|
||||
return out.String(), err
|
||||
}
|
||||
|
||||
func replace(replacements map[string]string, original string) string {
|
||||
result := replacements[original]
|
||||
if result == "" {
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"text/template"
|
||||
|
||||
"github.com/goreleaser/goreleaser/internal/artifact"
|
||||
"github.com/goreleaser/goreleaser/pkg/config"
|
||||
@ -212,6 +213,72 @@ func TestFuncMap(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplySingleEnvOnly(t *testing.T) {
|
||||
ctx := context.New(config.Project{
|
||||
Env: []string{
|
||||
"FOO=value",
|
||||
"BAR=another",
|
||||
},
|
||||
})
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
tpl string
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
"empty tpl",
|
||||
"",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"whitespaces",
|
||||
" ",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"plain-text only",
|
||||
"raw-token",
|
||||
ExpectedSingleEnvErr{},
|
||||
},
|
||||
{
|
||||
"variable with spaces",
|
||||
"{{ .Env.FOO }}",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"variable without spaces",
|
||||
"{{.Env.FOO}}",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"variable with outer spaces",
|
||||
" {{ .Env.FOO }} ",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"unknown variable",
|
||||
"{{ .Env.UNKNOWN }}",
|
||||
template.ExecError{},
|
||||
},
|
||||
{
|
||||
"other interpolation",
|
||||
"{{ .ProjectName }}",
|
||||
ExpectedSingleEnvErr{},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
_, err := New(ctx).ApplySingleEnvOnly(tc.tpl)
|
||||
if tc.expectedErr != nil {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidTemplate(t *testing.T) {
|
||||
ctx := context.New(config.Project{})
|
||||
ctx.Git.CurrentTag = "v1.1.1"
|
||||
|
@ -34,11 +34,22 @@ type GiteaURLs struct {
|
||||
}
|
||||
|
||||
// Repo represents any kind of repo (github, gitlab, etc).
|
||||
// to upload releases into.
|
||||
type Repo struct {
|
||||
Owner string `yaml:",omitempty"`
|
||||
Name string `yaml:",omitempty"`
|
||||
}
|
||||
|
||||
// RepoRef represents any kind of repo which may differ
|
||||
// from the one we are building from and may therefore
|
||||
// also require separate authentication
|
||||
// e.g. Homebrew Tap, Scoop bucket.
|
||||
type RepoRef struct {
|
||||
Owner string `yaml:",omitempty"`
|
||||
Name string `yaml:",omitempty"`
|
||||
Token string `yaml:",omitempty"`
|
||||
}
|
||||
|
||||
// HomebrewDependency represents Homebrew dependency.
|
||||
type HomebrewDependency struct {
|
||||
Name string `yaml:",omitempty"`
|
||||
@ -78,7 +89,7 @@ func (r Repo) String() string {
|
||||
// Homebrew contains the brew section.
|
||||
type Homebrew struct {
|
||||
Name string `yaml:",omitempty"`
|
||||
Tap Repo `yaml:",omitempty"`
|
||||
Tap RepoRef `yaml:",omitempty"`
|
||||
CommitAuthor CommitAuthor `yaml:"commit_author,omitempty"`
|
||||
Folder string `yaml:",omitempty"`
|
||||
Caveats string `yaml:",omitempty"`
|
||||
@ -105,7 +116,7 @@ type Homebrew struct {
|
||||
// Scoop contains the scoop.sh section.
|
||||
type Scoop struct {
|
||||
Name string `yaml:",omitempty"`
|
||||
Bucket Repo `yaml:",omitempty"`
|
||||
Bucket RepoRef `yaml:",omitempty"`
|
||||
CommitAuthor CommitAuthor `yaml:"commit_author,omitempty"`
|
||||
CommitMessageTemplate string `yaml:"commit_msg_template,omitempty"`
|
||||
Homepage string `yaml:",omitempty"`
|
||||
|
@ -44,6 +44,8 @@ brews:
|
||||
tap:
|
||||
owner: repo-owner
|
||||
name: homebrew-tap
|
||||
# Optionally a token can be provided, if it differs from the token provided to GoReleaser
|
||||
token: {{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}
|
||||
|
||||
# Template for the url which is determined by the given Token (github or gitlab)
|
||||
# Default for github is "https://github.com/<repo_owner>/<repo_name>/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
|
||||
|
@ -21,6 +21,8 @@ scoop:
|
||||
bucket:
|
||||
owner: user
|
||||
name: scoop-bucket
|
||||
# Optionally a token can be provided, if it differs from the token provided to GoReleaser
|
||||
token: {{ .Env.SCOOP_BUCKET_GITHUB_TOKEN }}
|
||||
|
||||
# Git author used to commit to the repository.
|
||||
# Defaults are shown.
|
||||
|
Loading…
x
Reference in New Issue
Block a user