Repository: goreleaser/goreleaser
Pull request #3509 (2022-10-29 16:36:33)

feat: chocolatey support
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!

faabiosr: Looking into `Snapcraft` feature, and after some tests, I realized that we could follow the same approach, by calling `chocolatey` to pack the generated script, although it's required to install `chocolatey` on Linux.

Also I wrote this simple step in a workflow for testing.
```yaml
jobs:
  goreleaser:
    runs-on: ubuntu-latest
    steps:
      - name: install chocolatey
        run: |
          mkdir -p /opt/chocolatey
          wget -q -O - "https://github.com/chocolatey/choco/releases/download/${CHOCOLATEY_VERSION}/chocolatey.v${CHOCOLATEY_VERSION}.tar.gz" | tar -xz -C "/opt/chocolatey"
          echo '#!/bin/bash' >> /usr/local/bin/choco.exe
          echo 'mono /opt/chocolatey/choco.exe $@' >> /usr/local/bin/choco.exe
          chmod +x /usr/local/bin/choco.exe
        env:
          CHOCOLATEY_VERSION: 1.2.0
```

After that it's just a matter of using `goreleaser`.
faabiosr: I made a repository with an example: https://github.com/faabiosr/rand-beer
caarlos0: This is looking VERY good so far!

For the push part, I think we can check if chocolatey is installed and run it, or return a `pipe.Skip` otherwise.

amazing work, thank you!
faabiosr: @caarlos0 During this week I will finish all the implementation of this functionality, thanks for the support.
codecov[bot]: # [Codecov](https://codecov.io/gh/goreleaser/goreleaser/pull/3509?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) Report
> Merging [#3509](https://codecov.io/gh/goreleaser/goreleaser/pull/3509?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) (d378e3c) into [main](https://codecov.io/gh/goreleaser/goreleaser/commit/e269a1d09599e8a1399393fc8230ed43a75bc851?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) (e269a1d) will **decrease** coverage by `0.23%`.
> The diff coverage is `71.00%`.

```diff
@@            Coverage Diff             @@
##             main    #3509      +/-   ##
==========================================
- Coverage   84.24%   84.00%   -0.24%     
==========================================
  Files         114      116       +2     
  Lines        9297     9466     +169     
==========================================
+ Hits         7832     7952     +120     
- Misses       1189     1225      +36     
- Partials      276      289      +13     
```


| [Impacted Files](https://codecov.io/gh/goreleaser/goreleaser/pull/3509?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser) | Coverage Δ | |
|---|---|---|
| [pkg/config/config.go](https://codecov.io/gh/goreleaser/goreleaser/pull/3509/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-cGtnL2NvbmZpZy9jb25maWcuZ28=) | `95.21% <ø> (ø)` | |
| [internal/pipe/chocolatey/chocolatey.go](https://codecov.io/gh/goreleaser/goreleaser/pull/3509/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-aW50ZXJuYWwvcGlwZS9jaG9jb2xhdGV5L2Nob2NvbGF0ZXkuZ28=) | `69.53% <69.53%> (ø)` | |
| [internal/pipe/chocolatey/nuspec.go](https://codecov.io/gh/goreleaser/goreleaser/pull/3509/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser#diff-aW50ZXJuYWwvcGlwZS9jaG9jb2xhdGV5L251c3BlYy5nbw==) | `83.33% <83.33%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=goreleaser)

faabiosr: All changes made, if you want to take a look in https://github.com/faabiosr/rand-beer/actions/runs/3395654844/jobs/5645812260, you will see the full integration
faabiosr: Please let me know if anything else needs to be done. Thanks a lot for the help. 😉 

Patch:
diff --git a/internal/artifact/artifact.go b/internal/artifact/artifact.go
index c917c13c..b351339b 100644
--- a/internal/artifact/artifact.go
+++ b/internal/artifact/artifact.go
@@ -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:
diff --git a/internal/pipe/chocolatey/chocolatey.go b/internal/pipe/chocolatey/chocolatey.go
new file mode 100644
index 00000000..bf0a47cc
--- /dev/null
+++ b/internal/pipe/chocolatey/chocolatey.go
@@ -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()
+}
diff --git a/internal/pipe/chocolatey/nuspec.go b/internal/pipe/chocolatey/nuspec.go
new file mode 100644
index 00000000..85d6637a
--- /dev/null
+++ b/internal/pipe/chocolatey/nuspec.go
@@ -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
+}
diff --git a/internal/pipe/chocolatey/template.go b/internal/pipe/chocolatey/template.go
new file mode 100644
index 00000000..252950f1
--- /dev/null
+++ b/internal/pipe/chocolatey/template.go
@@ -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
+`
diff --git a/internal/pipe/publish/publish.go b/internal/pipe/publish/publish.go
index cb4c288c..cb4c0a9a 100644
--- a/internal/pipe/publish/publish.go
+++ b/internal/pipe/publish/publish.go
@@ -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{},
 }
 
diff --git a/internal/pipeline/pipeline.go b/internal/pipeline/pipeline.go
index 9d5dd684..10f61ed4 100644
--- a/internal/pipeline/pipeline.go
+++ b/internal/pipeline/pipeline.go
@@ -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
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 29a803b9..56895435 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -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"`
+}
diff --git a/pkg/defaults/defaults.go b/pkg/defaults/defaults.go
index bdb625e1..321f6420 100644
--- a/pkg/defaults/defaults.go
+++ b/pkg/defaults/defaults.go
@@ -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{},
 }
diff --git a/www/docs/customization/chocolatey.md b/www/docs/customization/chocolatey.md
new file mode 100644
index 00000000..5ea9ff95
--- /dev/null
+++ b/www/docs/customization/chocolatey.md
@@ -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.
diff --git a/www/docs/static/schema.json b/www/docs/static/schema.json
index 277295cb..fac2bfe8 100644
--- a/www/docs/static/schema.json
+++ b/www/docs/static/schema.json
@@ -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"
diff --git a/www/mkdocs.yml b/www/mkdocs.yml
index 8f67dce1..b036ef40 100644
--- a/www/mkdocs.yml
+++ b/www/mkdocs.yml
@@ -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


Test patch:
diff --git a/internal/pipe/chocolatey/chocolatey_test.go b/internal/pipe/chocolatey/chocolatey_test.go
new file mode 100644
index 00000000..c89f0049
--- /dev/null
+++ b/internal/pipe/chocolatey/chocolatey_test.go
@@ -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()
+}
diff --git a/internal/pipe/chocolatey/nuspec_test.go b/internal/pipe/chocolatey/nuspec_test.go
new file mode 100644
index 00000000..3d51e0e1
--- /dev/null
+++ b/internal/pipe/chocolatey/nuspec_test.go
@@ -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")
+}
diff --git a/internal/pipe/chocolatey/testdata/TestNuspecBytes.nuspec.golden b/internal/pipe/chocolatey/testdata/TestNuspecBytes.nuspec.golden
new file mode 100644
index 00000000..02968097
--- /dev/null
+++ b/internal/pipe/chocolatey/testdata/TestNuspecBytes.nuspec.golden
@@ -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>
\ No newline at end of file
diff --git a/internal/pipe/chocolatey/testdata/Test_buildNuspec.nuspec.golden b/internal/pipe/chocolatey/testdata/Test_buildNuspec.nuspec.golden
new file mode 100644
index 00000000..7601fef7
--- /dev/null
+++ b/internal/pipe/chocolatey/testdata/Test_buildNuspec.nuspec.golden
@@ -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>
\ No newline at end of file
diff --git a/internal/pipe/chocolatey/testdata/Test_buildTemplate.script.ps1.golden b/internal/pipe/chocolatey/testdata/Test_buildTemplate.script.ps1.golden
new file mode 100644
index 00000000..779da005
--- /dev/null
+++ b/internal/pipe/chocolatey/testdata/Test_buildTemplate.script.ps1.golden
@@ -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