Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Generates the structure used to pack and push Chocolatey packages.
- Loading branch information
Showing
8 changed files
with
495 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
package chocolatey | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"os" | ||
"path/filepath" | ||
"text/template" | ||
|
||
"github.com/goreleaser/goreleaser/internal/artifact" | ||
"github.com/goreleaser/goreleaser/internal/client" | ||
"github.com/goreleaser/goreleaser/internal/tmpl" | ||
"github.com/goreleaser/goreleaser/pkg/config" | ||
"github.com/goreleaser/goreleaser/pkg/context" | ||
) | ||
|
||
// Pipe for chocolatey packaging. | ||
type Pipe struct{} | ||
|
||
func (Pipe) String() string { return "chocolatey packages" } | ||
func (Pipe) Skip(ctx *context.Context) bool { return len(ctx.Config.Chocolateys) == 0 } | ||
|
||
// Default sets the pipe defaults. | ||
func (Pipe) Default(ctx *context.Context) error { | ||
for i := range ctx.Config.Chocolateys { | ||
choco := &ctx.Config.Chocolateys[i] | ||
|
||
if choco.Name == "" { | ||
choco.Name = ctx.Config.ProjectName | ||
} | ||
|
||
if choco.Title == "" { | ||
choco.Title = ctx.Config.ProjectName | ||
} | ||
|
||
if choco.Goamd64 == "" { | ||
choco.Goamd64 = "v1" | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Run the pipe. | ||
func (Pipe) Run(ctx *context.Context) error { | ||
client, err := client.New(ctx) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, choco := range ctx.Config.Chocolateys { | ||
if err := doRun(ctx, client, choco); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func doRun(ctx *context.Context, cl client.Client, choco config.Chocolatey) error { | ||
filters := []artifact.Filter{ | ||
artifact.ByGoos("windows"), | ||
artifact.ByType(artifact.UploadableArchive), | ||
artifact.Or( | ||
artifact.And( | ||
artifact.ByGoarch("amd64"), | ||
artifact.ByGoamd64(choco.Goamd64), | ||
), | ||
artifact.ByGoarch("386"), | ||
), | ||
} | ||
|
||
if len(choco.IDs) > 0 { | ||
filters = append(filters, artifact.ByIDs(choco.IDs...)) | ||
} | ||
|
||
artifacts := ctx.Artifacts. | ||
Filter(artifact.And(filters...)). | ||
List() | ||
|
||
if len(artifacts) == 0 { | ||
return errors.New("chocolatey requires a windows build and archive") | ||
} | ||
|
||
// folderDir is the directory that then will be compressed to make the | ||
// chocolatey package. | ||
folderPath := filepath.Join(ctx.Config.Dist, choco.Name+".choco") | ||
toolsPath := filepath.Join(folderPath, "tools") | ||
if err := os.MkdirAll(toolsPath, 0o755); err != nil { | ||
return err | ||
} | ||
|
||
nuspecFile := filepath.Join(folderPath, choco.Name+".nuspec") | ||
nuspec, err := buildNuspec(ctx, choco) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if err = os.WriteFile(nuspecFile, nuspec, 0o644); err != nil { | ||
return err | ||
} | ||
|
||
scriptFile := filepath.Join(toolsPath, "chocolateyinstall.ps1") | ||
script, err := buildScript(ctx, cl, choco, artifacts) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return os.WriteFile(scriptFile, script, 0o644) | ||
} | ||
|
||
func buildNuspec(ctx *context.Context, choco config.Chocolatey) ([]byte, error) { | ||
tpl := tmpl.New(ctx) | ||
summary, err := tpl.Apply(choco.Summary) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
description, err := tpl.Apply(choco.Description) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
releaseNotes, err := tpl.Apply(choco.ReleaseNotes) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
m := &Nuspec{ | ||
Xmlns: schema, | ||
Metadata: Metadata{ | ||
ID: choco.Name, | ||
Version: ctx.Version, | ||
PackageSourceURL: choco.PackageSourceURL, | ||
Owners: choco.Owners, | ||
Title: choco.Title, | ||
Authors: choco.Authors, | ||
ProjectURL: choco.ProjectURL, | ||
IconURL: choco.IconURL, | ||
Copyright: choco.Copyright, | ||
LicenseURL: choco.LicenseURL, | ||
RequireLicenseAcceptance: choco.RequireLicenseAcceptance, | ||
ProjectSourceURL: choco.ProjectSourceURL, | ||
DocsURL: choco.DocsURL, | ||
BugTrackerURL: choco.BugTrackerURL, | ||
Tags: choco.Tags, | ||
Summary: summary, | ||
Description: description, | ||
ReleaseNotes: releaseNotes, | ||
}, | ||
Files: Files{File: []File{ | ||
{Source: "tools\\**", Target: "tools"}, | ||
}}, | ||
} | ||
|
||
deps := make([]Dependency, len(choco.Dependencies)) | ||
for i, dep := range choco.Dependencies { | ||
deps[i] = Dependency{ID: dep.ID, Version: dep.Version} | ||
} | ||
|
||
if len(deps) > 0 { | ||
m.Metadata.Dependencies = &Dependencies{Dependency: deps} | ||
} | ||
|
||
return m.Bytes() | ||
} | ||
|
||
func buildScript(ctx *context.Context, cl client.Client, choco config.Chocolatey, artifacts []*artifact.Artifact) ([]byte, error) { | ||
tp, err := template.New("install").Parse(scriptTemplate) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if choco.URLTemplate == "" { | ||
url, err := cl.ReleaseURLTemplate(ctx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
choco.URLTemplate = url | ||
} | ||
|
||
releases := make([]releasePackage, len(artifacts)) | ||
|
||
for i, artifact := range artifacts { | ||
url, err := tmpl.New(ctx). | ||
WithArtifact(artifact, map[string]string{}). | ||
Apply(choco.URLTemplate) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
sum, err := artifact.Checksum("sha256") | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
releases[i] = releasePackage{ | ||
DownloadURL: url, | ||
Checksum: sum, | ||
Arch: artifact.Goarch, | ||
} | ||
} | ||
|
||
var out bytes.Buffer | ||
if err = tp.Execute(&out, releases); err != nil { | ||
return nil, err | ||
} | ||
|
||
return out.Bytes(), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
package chocolatey | ||
|
||
import ( | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/goreleaser/goreleaser/internal/artifact" | ||
"github.com/goreleaser/goreleaser/internal/client" | ||
"github.com/goreleaser/goreleaser/internal/golden" | ||
"github.com/goreleaser/goreleaser/internal/testlib" | ||
"github.com/goreleaser/goreleaser/pkg/config" | ||
"github.com/goreleaser/goreleaser/pkg/context" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestDescription(t *testing.T) { | ||
require.NotEmpty(t, Pipe{}.String()) | ||
} | ||
|
||
func TestSkip(t *testing.T) { | ||
ctx := context.New(config.Project{}) | ||
require.True(t, Pipe{}.Skip(ctx)) | ||
} | ||
|
||
func TestDefault(t *testing.T) { | ||
testlib.Mktmp(t) | ||
|
||
ctx := &context.Context{ | ||
TokenType: context.TokenTypeGitHub, | ||
Config: config.Project{ | ||
ProjectName: "myproject", | ||
Chocolateys: []config.Chocolatey{ | ||
{}, | ||
}, | ||
}, | ||
} | ||
|
||
require.NoError(t, Pipe{}.Default(ctx)) | ||
require.Equal(t, ctx.Config.ProjectName, ctx.Config.Chocolateys[0].Name) | ||
require.Equal(t, ctx.Config.ProjectName, ctx.Config.Chocolateys[0].Title) | ||
require.Equal(t, "v1", ctx.Config.Chocolateys[0].Goamd64) | ||
} | ||
|
||
func Test_doRun(t *testing.T) { | ||
folder := t.TempDir() | ||
file := filepath.Join(folder, "archive") | ||
require.NoError(t, os.WriteFile(file, []byte("lorem ipsum"), 0o644)) | ||
|
||
tests := []struct { | ||
name string | ||
choco config.Chocolatey | ||
artifacts []artifact.Artifact | ||
err string | ||
}{ | ||
{ | ||
name: "no artifacts", | ||
choco: config.Chocolatey{ | ||
Name: "app", | ||
IDs: []string{"no-app"}, | ||
Goamd64: "v1", | ||
}, | ||
err: "chocolatey requires a windows build and archive", | ||
}, | ||
{ | ||
name: "success", | ||
choco: config.Chocolatey{ | ||
Name: "app", | ||
Goamd64: "v1", | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
ctx := &context.Context{ | ||
Git: context.GitInfo{ | ||
CurrentTag: "v1.0.1", | ||
}, | ||
Version: "1.0.1", | ||
Artifacts: artifact.New(), | ||
Config: config.Project{ | ||
Dist: folder, | ||
ProjectName: "run-all", | ||
}, | ||
} | ||
|
||
ctx.Artifacts.Add(&artifact.Artifact{ | ||
Name: "app_1.0.1_windows_amd64.zip", | ||
Path: file, | ||
Goos: "windows", | ||
Goarch: "amd64", | ||
Goamd64: "v1", | ||
Type: artifact.UploadableArchive, | ||
Extra: map[string]interface{}{ | ||
artifact.ExtraID: "app", | ||
artifact.ExtraFormat: "zip", | ||
}, | ||
}) | ||
|
||
client := client.NewMock() | ||
got := doRun(ctx, client, tt.choco) | ||
|
||
var err string | ||
if got != nil { | ||
err = got.Error() | ||
} | ||
if tt.err != err { | ||
t.Errorf("Unexpected error: %s (expected %s)", err, tt.err) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func Test_buildNuspec(t *testing.T) { | ||
ctx := &context.Context{ | ||
Version: "1.12.3", | ||
} | ||
choco := config.Chocolatey{ | ||
Name: "goreleaser", | ||
IDs: []string{}, | ||
Title: "GoReleaser", | ||
Authors: "caarlos0", | ||
ProjectURL: "https://goreleaser.com/", | ||
Tags: "go docker homebrew golang package", | ||
Summary: "Deliver Go binaries as fast and easily as possible", | ||
Description: "GoReleaser builds Go binaries for several platforms, creates a GitHub release and then pushes a Homebrew formula to a tap repository. All that wrapped in your favorite CI.", | ||
Dependencies: []config.ChocolateyDependency{ | ||
{ID: "nfpm"}, | ||
}, | ||
} | ||
|
||
out, err := buildNuspec(ctx, choco) | ||
require.NoError(t, err) | ||
|
||
golden.RequireEqualExt(t, out, ".nuspec") | ||
} | ||
|
||
func Test_buildScript(t *testing.T) { | ||
folder := t.TempDir() | ||
file := filepath.Join(folder, "archive") | ||
require.NoError(t, os.WriteFile(file, []byte("lorem ipsum"), 0o644)) | ||
|
||
ctx := &context.Context{ | ||
Version: "1.0.0", | ||
Git: context.GitInfo{ | ||
CurrentTag: "v1.0.0", | ||
}, | ||
} | ||
choco := config.Chocolatey{} | ||
client := client.NewMock() | ||
artifacts := []*artifact.Artifact{ | ||
{ | ||
Name: "app_1.0.0_windows_386.zip", | ||
Goos: "windows", | ||
Goarch: "386", | ||
Goamd64: "v1", | ||
Path: file, | ||
}, | ||
{ | ||
Name: "app_1.0.0_windows_amd64.zip", | ||
Goos: "windows", | ||
Goarch: "amd64", | ||
Goamd64: "v1", | ||
Path: file, | ||
}, | ||
} | ||
|
||
out, err := buildScript(ctx, client, choco, artifacts) | ||
require.NoError(t, err) | ||
|
||
golden.RequireEqualExt(t, out, ".ps1") | ||
} |
Oops, something went wrong.