1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-04-13 11:50:34 +02:00

feat: chocolatey support (#3509)

This PR adds support for generating the structure used to pack and push
Chocolatey Packages. And will solve the #3154

Is not ready for merge yet, but has the main structure, and ready for
comments.

Accordingly to Chocolatey, in order to build a package, it's necessary a
`.nuspec` and `chocolateyinstall.ps1` files at least, having these ones,
we could pack and distribute without adding the binary inside the final
package and that was implemented here.

To complete, will be necessary to define the package build and
distribute, however will be required to have Chocolatey installed
(Windows Only). One of alternatives that I thought was, publish the
files like Scoop and Brew in a separate repository, and there we could
use `chocolatey` through
[crazy-max/ghaction-chocolatey](https://github.com/crazy-max/ghaction-chocolatey).

Chocolatey has a lot of good examples of repositories:

https://github.com/chocolatey-community/chocolatey-packages/tree/master/automatic/curl

A final compilation of the missing parts:
- [x] How to pack and push (chocolatey)
- [x] Documentation

Sorry for the long description😄 

All feedback very welcome!

Co-authored-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
This commit is contained in:
Fabio Ribeiro 2022-11-12 03:52:32 +01:00 committed by GitHub
parent 22a7a9a835
commit f2281e8ff2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1191 additions and 0 deletions

View File

@ -66,6 +66,8 @@ const (
ScoopManifest
// SBOM is a Software Bill of Materials file.
SBOM
// PublishableChocolatey is a chocolatey package yet to be published.
PublishableChocolatey
// Header is a C header file, generated for CGo library builds.
Header
// CArchive is a C static library, generated via a CGo build with buildmode=c-archive.
@ -110,6 +112,8 @@ func (t Type) String() string {
return "PKGBUILD"
case SrcInfo:
return "SRCINFO"
case PublishableChocolatey:
return "Chocolatey"
case Header:
return "C Header"
case CArchive:

View File

@ -0,0 +1,340 @@
package chocolatey
import (
"bytes"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"text/template"
"github.com/caarlos0/log"
"github.com/goreleaser/goreleaser/internal/artifact"
"github.com/goreleaser/goreleaser/internal/client"
"github.com/goreleaser/goreleaser/internal/pipe"
"github.com/goreleaser/goreleaser/internal/tmpl"
"github.com/goreleaser/goreleaser/pkg/config"
"github.com/goreleaser/goreleaser/pkg/context"
)
// nuget package extension.
const nupkgFormat = "nupkg"
// custom chocolatey config placed in artifact.
const chocoConfigExtra = "ChocolateyConfig"
// cmd represents a command executor.
var cmd cmder = stdCmd{}
// 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"
}
if choco.SourceRepo == "" {
choco.SourceRepo = "https://push.chocolatey.org/"
}
}
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
}
// Publish packages.
func (Pipe) Publish(ctx *context.Context) error {
if ctx.SkipPublish {
return pipe.ErrSkipPublishEnabled
}
artifacts := ctx.Artifacts.Filter(
artifact.ByType(artifact.PublishableChocolatey),
).List()
for _, artifact := range artifacts {
if err := doPush(ctx, artifact); 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
}
data, err := dataFor(ctx, cl, choco, artifacts)
if err != nil {
return err
}
script, err := buildTemplate(choco.Name, scriptTemplate, data)
if err != nil {
return err
}
scriptFile := filepath.Join(toolsPath, "chocolateyinstall.ps1")
log.WithField("file", scriptFile).Debug("creating")
if err = os.WriteFile(scriptFile, script, 0o644); err != nil {
return err
}
log.WithField("nuspec", nuspecFile).Info("packing")
out, err := cmd.Exec(ctx, "choco", "pack", nuspecFile, "--out", ctx.Config.Dist)
if err != nil {
return fmt.Errorf("failed to generate chocolatey package: %w: %s", err, string(out))
}
if choco.SkipPublish {
return nil
}
pkgFile := fmt.Sprintf("%s.%s.%s", choco.Name, ctx.Version, nupkgFormat)
ctx.Artifacts.Add(&artifact.Artifact{
Type: artifact.PublishableChocolatey,
Path: filepath.Join(ctx.Config.Dist, pkgFile),
Name: pkgFile,
Extra: map[string]interface{}{
artifact.ExtraFormat: nupkgFormat,
chocoConfigExtra: choco,
},
})
return nil
}
func doPush(ctx *context.Context, art *artifact.Artifact) error {
choco, err := artifact.Extra[config.Chocolatey](*art, chocoConfigExtra)
if err != nil {
return err
}
key, err := tmpl.New(ctx).Apply(choco.APIKey)
if err != nil {
return err
}
log := log.WithField("name", choco.Name)
if key == "" {
log.Warn("skip pushing: no api key")
return nil
}
log.Info("pushing package")
args := []string{
"push",
"--source",
choco.SourceRepo,
"--api-key",
key,
art.Path,
}
if out, err := cmd.Exec(ctx, "choco", args...); err != nil {
return fmt.Errorf("failed to push chocolatey package: %w: %s", err, string(out))
}
log.Info("package sent")
return nil
}
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 buildTemplate(name string, text string, data templateData) ([]byte, error) {
tp, err := template.New(name).Parse(text)
if err != nil {
return nil, err
}
var out bytes.Buffer
if err = tp.Execute(&out, data); err != nil {
return nil, err
}
return out.Bytes(), nil
}
func dataFor(ctx *context.Context, cl client.Client, choco config.Chocolatey, artifacts []*artifact.Artifact) (templateData, error) {
result := templateData{}
if choco.URLTemplate == "" {
url, err := cl.ReleaseURLTemplate(ctx)
if err != nil {
return result, err
}
choco.URLTemplate = url
}
for _, artifact := range artifacts {
sum, err := artifact.Checksum("sha256")
if err != nil {
return result, err
}
url, err := tmpl.New(ctx).
WithArtifact(artifact, map[string]string{}).
Apply(choco.URLTemplate)
if err != nil {
return result, err
}
pkg := releasePackage{
DownloadURL: url,
Checksum: sum,
Arch: artifact.Goarch,
}
result.Packages = append(result.Packages, pkg)
}
return result, nil
}
// cmder is a special interface to execute external commands.
//
// The intention is to be used to wrap the standard exec and provide the
// ability to create a fake one for testing.
type cmder interface {
// Exec executes an command.
Exec(*context.Context, string, ...string) ([]byte, error)
}
// stdCmd uses the standard golang exec.
type stdCmd struct{}
var _ cmder = &stdCmd{}
func (stdCmd) Exec(ctx *context.Context, name string, args ...string) ([]byte, error) {
return exec.CommandContext(ctx, name, args...).CombinedOutput()
}

View File

@ -0,0 +1,330 @@
package chocolatey
import (
"errors"
"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
exec func() ([]byte, error)
published int
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: "choco command not found",
choco: config.Chocolatey{
Name: "app",
Goamd64: "v1",
},
exec: func() ([]byte, error) {
return nil, errors.New(`exec: "choco.exe": executable file not found in $PATH`)
},
err: `failed to generate chocolatey package: exec: "choco.exe": executable file not found in $PATH: `,
},
{
name: "skip publish",
choco: config.Chocolatey{
Name: "app",
Goamd64: "v1",
SkipPublish: true,
},
exec: func() ([]byte, error) {
return []byte("success"), nil
},
},
{
name: "success",
choco: config.Chocolatey{
Name: "app",
Goamd64: "v1",
},
exec: func() ([]byte, error) {
return []byte("success"), nil
},
published: 1,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd = fakeCmd{execFn: tt.exec}
t.Cleanup(func() {
cmd = stdCmd{}
})
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)
}
list := ctx.Artifacts.Filter(artifact.ByType(artifact.PublishableChocolatey)).List()
require.Len(t, list, tt.published)
})
}
}
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_buildTemplate(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",
},
}
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,
},
}
choco := config.Chocolatey{
Name: "app",
}
client := client.NewMock()
data, err := dataFor(ctx, client, choco, artifacts)
if err != nil {
t.Error(err)
}
out, err := buildTemplate(choco.Name, scriptTemplate, data)
require.NoError(t, err)
golden.RequireEqualExt(t, out, ".script.ps1")
}
func TestPublish(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
artifacts []artifact.Artifact
exec func() ([]byte, error)
skip bool
err string
}{
{
name: "skip publish",
skip: true,
err: "publishing is disabled",
},
{
name: "no artifacts",
},
{
name: "no api key",
artifacts: []artifact.Artifact{
{
Type: artifact.PublishableChocolatey,
Name: "app.1.0.1.nupkg",
Extra: map[string]interface{}{
artifact.ExtraFormat: nupkgFormat,
chocoConfigExtra: config.Chocolatey{},
},
},
},
},
{
name: "push error",
artifacts: []artifact.Artifact{
{
Type: artifact.PublishableChocolatey,
Name: "app.1.0.1.nupkg",
Extra: map[string]interface{}{
artifact.ExtraFormat: nupkgFormat,
chocoConfigExtra: config.Chocolatey{
APIKey: "abcd",
},
},
},
},
exec: func() ([]byte, error) {
return nil, errors.New(`unable to push`)
},
err: "failed to push chocolatey package: unable to push: ",
},
{
name: "success",
artifacts: []artifact.Artifact{
{
Type: artifact.PublishableChocolatey,
Name: "app.1.0.1.nupkg",
Extra: map[string]interface{}{
artifact.ExtraFormat: nupkgFormat,
chocoConfigExtra: config.Chocolatey{
APIKey: "abcd",
},
},
},
},
exec: func() ([]byte, error) {
return []byte("success"), nil
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd = fakeCmd{execFn: tt.exec}
t.Cleanup(func() {
cmd = stdCmd{}
})
ctx := &context.Context{
SkipPublish: tt.skip,
Artifacts: artifact.New(),
}
for _, artifact := range tt.artifacts {
ctx.Artifacts.Add(&artifact)
}
got := Pipe{}.Publish(ctx)
var err string
if got != nil {
err = got.Error()
}
if tt.err != err {
t.Errorf("Unexpected error: %s (expected %s)", err, tt.err)
}
})
}
}
type fakeCmd struct {
execFn func() ([]byte, error)
}
var _ cmder = fakeCmd{}
func (f fakeCmd) Exec(ctx *context.Context, name string, args ...string) ([]byte, error) {
return f.execFn()
}

View File

@ -0,0 +1,87 @@
package chocolatey
import (
"bytes"
"encoding/xml"
"strings"
)
const schema = "http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd"
// Nuspec represents a Nuget/Chocolatey Nuspec.
// More info: https://learn.microsoft.com/en-us/nuget/reference/nuspec
// https://docs.chocolatey.org/en-us/create/create-packages
type Nuspec struct {
XMLName xml.Name `xml:"package"`
Xmlns string `xml:"xmlns,attr,omitempty"`
Metadata Metadata `xml:"metadata"`
Files Files `xml:"files,omitempty"`
}
// Metadata contains information about a single package.
type Metadata struct {
ID string `xml:"id"`
Version string `xml:"version"`
PackageSourceURL string `xml:"packageSourceUrl,omitempty"`
Owners string `xml:"owners,omitempty"`
Title string `xml:"title,omitempty"`
Authors string `xml:"authors"`
ProjectURL string `xml:"projectUrl,omitempty"`
IconURL string `xml:"iconUrl,omitempty"`
Copyright string `xml:"copyright,omitempty"`
LicenseURL string `xml:"licenseUrl,omitempty"`
RequireLicenseAcceptance bool `xml:"requireLicenseAcceptance"`
ProjectSourceURL string `xml:"projectSourceUrl,omitempty"`
DocsURL string `xml:"docsUrl,omitempty"`
BugTrackerURL string `xml:"bugTrackerUrl,omitempty"`
Tags string `xml:"tags,omitempty"`
Summary string `xml:"summary,omitempty"`
Description string `xml:"description"`
ReleaseNotes string `xml:"releaseNotes,omitempty"`
Dependencies *Dependencies `xml:"dependencies,omitempty"`
}
// Dependency represents a dependency element.
type Dependency struct {
ID string `xml:"id,attr"`
Version string `xml:"version,attr,omitempty"`
}
// Dependencies represents a collection zero or more dependency elements.
type Dependencies struct {
Dependency []Dependency `xml:"dependency"`
}
// File represents a file to be copied.
type File struct {
Source string `xml:"src,attr"`
Target string `xml:"target,attr,omitempty"`
}
// Files represents files that will be copied during packaging.
type Files struct {
File []File `xml:"file"`
}
// Bytes marshals the Nuspec into XML format and return as []byte.
func (m *Nuspec) Bytes() ([]byte, error) {
b := &bytes.Buffer{}
b.WriteString(strings.ToLower(xml.Header))
enc := xml.NewEncoder(b)
enc.Indent("", " ")
if err := enc.Encode(m); err != nil {
return nil, err
}
out := b.Bytes()
// Follows the nuget specification of self-closing xml tags.
tags := []string{"dependency", "file"}
for _, tag := range tags {
out = bytes.ReplaceAll(out, []byte("></"+tag+">"), []byte(" />"))
}
return out, nil
}

View File

@ -0,0 +1,45 @@
package chocolatey
import (
"testing"
"github.com/goreleaser/goreleaser/internal/golden"
"github.com/stretchr/testify/require"
)
func TestNuspecBytes(t *testing.T) {
m := &Nuspec{
Xmlns: schema,
Metadata: Metadata{
ID: "goreleaser",
Version: "1.12.3",
PackageSourceURL: "https://github.com/goreleaser/goreleaser",
Owners: "caarlos0",
Title: "GoReleaser",
Authors: "caarlos0",
ProjectURL: "https://goreleaser.com/",
IconURL: "https://raw.githubusercontent.com/goreleaser/goreleaser/main/www/docs/static/avatar.png",
Copyright: "2016-2022 Carlos Alexandro Becker",
LicenseURL: "https://github.com/goreleaser/goreleaser/blob/main/LICENSE.md",
RequireLicenseAcceptance: true,
ProjectSourceURL: "https://github.com/goreleaser/goreleaser",
DocsURL: "https://github.com/goreleaser/goreleaser/blob/main/README.md",
BugTrackerURL: "https://github.com/goreleaser/goreleaser/issues",
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.",
ReleaseNotes: "This tag is only to keep version parity with the pro version, which does have a couple of bugfixes.",
Dependencies: &Dependencies{Dependency: []Dependency{
{ID: "nfpm", Version: "2.20.0"},
}},
},
Files: Files{File: []File{
{Source: "tools\\**", Target: "tools"},
}},
}
out, err := m.Bytes()
require.NoError(t, err)
golden.RequireEqualExt(t, out, ".nuspec")
}

View File

@ -0,0 +1,38 @@
package chocolatey
type templateData struct {
Packages []releasePackage
}
type releasePackage struct {
DownloadURL string
Checksum string
Arch string
}
const scriptTemplate = `# This file was generated by GoReleaser. DO NOT EDIT.
$ErrorActionPreference = 'Stop';
$version = $env:chocolateyPackageVersion
$packageName = $env:chocolateyPackageName
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$packageArgs = @{
packageName = $packageName
unzipLocation = $toolsDir
fileType = 'exe'
{{- range $release := .Packages }}
{{- if eq $release.Arch "amd64" }}
url64bit = '{{ $release.DownloadURL }}'
checksum64 = '{{ $release.Checksum }}'
checksumType64 = 'sha256'
{{- else }}
url = '{{ $release.DownloadURL }}'
checksum = '{{ $release.Checksum }}'
checksumType = 'sha256'
{{- end }}
{{- end }}
}
Install-ChocolateyZipPackage @packageArgs
`

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
<metadata>
<id>goreleaser</id>
<version>1.12.3</version>
<packageSourceUrl>https://github.com/goreleaser/goreleaser</packageSourceUrl>
<owners>caarlos0</owners>
<title>GoReleaser</title>
<authors>caarlos0</authors>
<projectUrl>https://goreleaser.com/</projectUrl>
<iconUrl>https://raw.githubusercontent.com/goreleaser/goreleaser/main/www/docs/static/avatar.png</iconUrl>
<copyright>2016-2022 Carlos Alexandro Becker</copyright>
<licenseUrl>https://github.com/goreleaser/goreleaser/blob/main/LICENSE.md</licenseUrl>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<projectSourceUrl>https://github.com/goreleaser/goreleaser</projectSourceUrl>
<docsUrl>https://github.com/goreleaser/goreleaser/blob/main/README.md</docsUrl>
<bugTrackerUrl>https://github.com/goreleaser/goreleaser/issues</bugTrackerUrl>
<tags>go docker homebrew golang package</tags>
<summary>Deliver Go binaries as fast and easily as possible</summary>
<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.</description>
<releaseNotes>This tag is only to keep version parity with the pro version, which does have a couple of bugfixes.</releaseNotes>
<dependencies>
<dependency id="nfpm" version="2.20.0" />
</dependencies>
</metadata>
<files>
<file src="tools\**" target="tools" />
</files>
</package>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
<metadata>
<id>goreleaser</id>
<version>1.12.3</version>
<title>GoReleaser</title>
<authors>caarlos0</authors>
<projectUrl>https://goreleaser.com/</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<tags>go docker homebrew golang package</tags>
<summary>Deliver Go binaries as fast and easily as possible</summary>
<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.</description>
<dependencies>
<dependency id="nfpm" />
</dependencies>
</metadata>
<files>
<file src="tools\**" target="tools" />
</files>
</package>

View File

@ -0,0 +1,20 @@
# This file was generated by GoReleaser. DO NOT EDIT.
$ErrorActionPreference = 'Stop';
$version = $env:chocolateyPackageVersion
$packageName = $env:chocolateyPackageName
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
$packageArgs = @{
packageName = $packageName
unzipLocation = $toolsDir
fileType = 'exe'
url = 'https://dummyhost/download/v1.0.0/app_1.0.0_windows_386.zip'
checksum = '5e2bf57d3f40c4b6df69daf1936cb766f832374b4fc0259a7cbff06e2f70f269'
checksumType = 'sha256'
url64bit = 'https://dummyhost/download/v1.0.0/app_1.0.0_windows_amd64.zip'
checksum64 = '5e2bf57d3f40c4b6df69daf1936cb766f832374b4fc0259a7cbff06e2f70f269'
checksumType64 = 'sha256'
}
Install-ChocolateyZipPackage @packageArgs

View File

@ -11,6 +11,7 @@ import (
"github.com/goreleaser/goreleaser/internal/pipe/aur"
"github.com/goreleaser/goreleaser/internal/pipe/blob"
"github.com/goreleaser/goreleaser/internal/pipe/brew"
"github.com/goreleaser/goreleaser/internal/pipe/chocolatey"
"github.com/goreleaser/goreleaser/internal/pipe/custompublishers"
"github.com/goreleaser/goreleaser/internal/pipe/docker"
"github.com/goreleaser/goreleaser/internal/pipe/krew"
@ -48,6 +49,7 @@ var publishers = []Publisher{
aur.Pipe{},
krew.Pipe{},
scoop.Pipe{},
chocolatey.Pipe{},
milestone.Pipe{},
}

View File

@ -12,6 +12,7 @@ import (
"github.com/goreleaser/goreleaser/internal/pipe/build"
"github.com/goreleaser/goreleaser/internal/pipe/changelog"
"github.com/goreleaser/goreleaser/internal/pipe/checksums"
"github.com/goreleaser/goreleaser/internal/pipe/chocolatey"
"github.com/goreleaser/goreleaser/internal/pipe/defaults"
"github.com/goreleaser/goreleaser/internal/pipe/dist"
"github.com/goreleaser/goreleaser/internal/pipe/docker"
@ -106,6 +107,8 @@ var Pipeline = append(
krew.Pipe{},
// create scoop buckets
scoop.Pipe{},
// create chocolatey pkg and publish
chocolatey.Pipe{},
// create and push docker images
docker.Pipe{},
// creates a metadata.json and an artifacts.json files in the dist folder

View File

@ -930,6 +930,7 @@ type Project struct {
GoMod GoMod `yaml:"gomod,omitempty" json:"gomod,omitempty"`
Announce Announce `yaml:"announce,omitempty" json:"announce,omitempty"`
SBOMs []SBOM `yaml:"sboms,omitempty" json:"sboms,omitempty"`
Chocolateys []Chocolatey `yaml:"chocolateys,omitempty" json:"chocolatey,omitempty"`
UniversalBinaries []UniversalBinary `yaml:"universal_binaries,omitempty" json:"universal_binaries,omitempty"`
@ -1116,3 +1117,37 @@ func (a *SlackAttachment) UnmarshalYAML(unmarshal func(interface{}) error) error
func (a SlackAttachment) MarshalJSON() ([]byte, error) {
return json.Marshal(a.Internal)
}
// Chocolatey contains the chocolatey section.
type Chocolatey struct {
Name string `yaml:"name,omitempty" json:"name,omitempty"`
IDs []string `yaml:"ids,omitempty" json:"ids,omitempty"`
PackageSourceURL string `yaml:"package_source_url,omitempty" json:"package_source_url,omitempty"`
Owners string `yaml:"owners,omitempty" json:"authoers,omitempty"`
Title string `yaml:"title,omitempty" json:"title,omitempty"`
Authors string `yaml:"authors,omitempty" json:"authors,omitempty"`
ProjectURL string `yaml:"project_url,omitempty" json:"project_url,omitempty"`
URLTemplate string `yaml:"url_template,omitempty" json:"url_template,omitempty"`
IconURL string `yaml:"icon_url,omitempty" json:"icon_url,omitempty"`
Copyright string `yaml:"copyright,omitempty" json:"copyright,omitempty"`
LicenseURL string `yaml:"license_url,omitempty" json:"license_url,omitempty"`
RequireLicenseAcceptance bool `yaml:"require_license_acceptance,omitempty" json:"require_license_acceptance,omitempty"`
ProjectSourceURL string `yaml:"project_source_url,omitempty" json:"project_source_url,omitempty"`
DocsURL string `yaml:"docs_url,omitempty" json:"docs_url,omitempty"`
BugTrackerURL string `yaml:"bug_tracker_url,omitempty" json:"bug_tracker_url,omitempty"`
Tags string `yaml:"tags,omitempty" json:"tags,omitempty"`
Summary string `yaml:"summary,omitempty" json:"summary,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
ReleaseNotes string `yaml:"release_notes,omitempty" json:"release_notes,omitempty"`
Dependencies []ChocolateyDependency `yaml:"dependencies,omitempty" json:"dependencies,omitempty"`
SkipPublish bool `yaml:"skip_publish,omitempty" json:"skip_publish,omitempty"`
APIKey string `yaml:"api_key,omitempty" json:"api_key,omitempty"`
SourceRepo string `yaml:"source_repo,omitempty" json:"source_repo,omitempty"`
Goamd64 string `yaml:"goamd64,omitempty" json:"goamd64,omitempty"`
}
// ChcolateyDependency represents Chocolatey dependency.
type ChocolateyDependency struct {
ID string `yaml:"id,omitempty" json:"id,omitempty"`
Version string `yaml:"version,omitempty" json:"version,omitempty"`
}

View File

@ -12,6 +12,7 @@ import (
"github.com/goreleaser/goreleaser/internal/pipe/brew"
"github.com/goreleaser/goreleaser/internal/pipe/build"
"github.com/goreleaser/goreleaser/internal/pipe/checksums"
"github.com/goreleaser/goreleaser/internal/pipe/chocolatey"
"github.com/goreleaser/goreleaser/internal/pipe/discord"
"github.com/goreleaser/goreleaser/internal/pipe/docker"
"github.com/goreleaser/goreleaser/internal/pipe/gomod"
@ -84,4 +85,5 @@ var Defaulters = []Defaulter{
linkedin.Pipe{},
telegram.Pipe{},
webhook.Pipe{},
chocolatey.Pipe{},
}

View File

@ -0,0 +1,133 @@
# Chocolatey Packages
GoReleaser can also generate `nupkg` packages.
[Chocolatey](http://chocolatey.org/) are packages based on `nupkg` format, that
will let you publish your project directly to the Chocolatey Repository. From
there it will be able to install locally or in Windows distributions.
You can read more about it in the [chocolatey docs](https://docs.chocolatey.org/).
Available options:
```yaml
# .goreleaser.yaml
chocolateys:
-
# Your app's package name.
# The value may not contain spaces or character that are not valid for a URL.
# If you want a good separator for words, use '-', not '.'.
#
# Defaults to `ProjectName`.
name: foo
# IDs of the archives to use.
# Defaults to empty, which includes all artifacts.
ids:
- foo
- bar
# Your app's owner.
# It basically means your.
# Defaults empty.
owners: Drum Roll Inc
# The app's title.
# A human-friendly title of the package.
# Defaults to `ProjectName`.
title: Foo Bar
# Your app's authors (probably you).
# Defaults are shown below.
authors: Drummer
# Your app's project url.
# It is a required field.
project_url: https://example.com/
# Template for the url which is determined by the given Token (github,
# gitlab or gitea)
# Default depends on the client.
url_template: "https://github.com/foo/bar/releases/download/{{ .Tag }}/{{ .ArtifactName }}"
# App's icon.
# Default is empty.
icon_url: 'https://rawcdn.githack.com/foo/bar/efbdc760-395b-43f1-bf69-ba25c374d473/icon.png'
# Your app's copyright details.
# Default is empty.
copyright: 2022 Drummer Roll Inc
# App's license information url.
license_url: https://github.com/foo/bar/blob/main/LICENSE
# Your apps's require license acceptance:
# Specify whether the client must prompt the consumer to accept the package
# license before installing.
# Default is false.
require_license_acceptance: false
# Your app's source url.
# Default is empty.
project_source_url: https://github.com/foo/bar
# Your app's documentation url.
# Default is empty.
docs_url: https://github.com/foo/bar/blob/main/README.md
# App's bugtracker url.
# Default is empty.
bug_tracker_url: https://github.com/foo/barr/issues
# Your app's tag list.
# Default is empty.
tags: "foo bar baz"
# Your app's summary:
summary: Software to create fast and easy drum rolls.
# This the description of your chocolatey package.
# Supports markdown.
description: |
{{ .ProjectName }} installer package.
Software to create fast and easy drum rolls.
# Your app's release notes.
# A description of the changes made in this release of the package.
# Supports markdown. To prevent the need to continually update this field,
# providing a URL to an external list of Release Notes is perfectly
# acceptable.
# Default is empty.
release_notes: "https://github.com/foo/bar/releases/tag/v{{ .Version }}"
# App's dependencies
# Default is empty. Version is not required.
dependencies:
- id: nfpm
version: 2.20.0
# The api key that should be used to push to the chocolatey repository.
#
# WARNING: do not expose your api key in the configuration file!
api_key: '{{ .Env.CHOCOLATEY_API_KEY }}'
# The source repository that will push the package to.
#
# Defaults are shown below.
source_repo: "https://push.chocolatey.org/"
# Setting this will prevent goreleaser to actually try to push the package
# to chocolatey repository, leaving the responsability of publishing it to
# the user.
skip_publish: false
# GOAMD64 to specify which amd64 version to use if there are multiple
# versions from the build section.
# Default is v1.
goamd64: v1
```
!!! tip
Learn more about the [name template engine](/customization/templates/).
!!! note
GoReleaser will not install `chocolatey` nor any of its dependencies for you.

102
www/docs/static/schema.json generated vendored
View File

@ -496,6 +496,102 @@
"additionalProperties": false,
"type": "object"
},
"Chocolatey": {
"properties": {
"name": {
"type": "string"
},
"ids": {
"items": {
"type": "string"
},
"type": "array"
},
"package_source_url": {
"type": "string"
},
"authoers": {
"type": "string"
},
"title": {
"type": "string"
},
"authors": {
"type": "string"
},
"project_url": {
"type": "string"
},
"url_template": {
"type": "string"
},
"icon_url": {
"type": "string"
},
"copyright": {
"type": "string"
},
"license_url": {
"type": "string"
},
"require_license_acceptance": {
"type": "boolean"
},
"project_source_url": {
"type": "string"
},
"docs_url": {
"type": "string"
},
"bug_tracker_url": {
"type": "string"
},
"tags": {
"type": "string"
},
"summary": {
"type": "string"
},
"description": {
"type": "string"
},
"release_notes": {
"type": "string"
},
"dependencies": {
"items": {
"$ref": "#/$defs/ChocolateyDependency"
},
"type": "array"
},
"skip_publish": {
"type": "boolean"
},
"api_key": {
"type": "string"
},
"source_repo": {
"type": "string"
},
"goamd64": {
"type": "string"
}
},
"additionalProperties": false,
"type": "object"
},
"ChocolateyDependency": {
"properties": {
"id": {
"type": "string"
},
"version": {
"type": "string"
}
},
"additionalProperties": false,
"type": "object"
},
"CommitAuthor": {
"properties": {
"name": {
@ -1747,6 +1843,12 @@
},
"type": "array"
},
"chocolatey": {
"items": {
"$ref": "#/$defs/Chocolatey"
},
"type": "array"
},
"universal_binaries": {
"items": {
"$ref": "#/$defs/UniversalBinary"

View File

@ -96,6 +96,7 @@ nav:
- customization/nfpm.md
- customization/checksum.md
- customization/snapcraft.md
- customization/chocolatey.md
- customization/docker.md
- customization/docker_manifest.md
- customization/sbom.md