From 09411ea60a763f79ea5484baccba77cc2aef3580 Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Mon, 5 Jun 2023 13:05:28 -0300 Subject: [PATCH] feat: Checksums as a template field (#4064) this would allow to add the checksums to the body of the release contents, for example. refs https://github.com/orgs/goreleaser/discussions/4063 --------- Signed-off-by: Carlos Alexandro Becker --- internal/pipe/release/body.go | 18 +- internal/pipe/release/body_test.go | 32 ++- ...TestDescribeBodyWithHeaderAndFooter.golden | 9 + www/docs/customization/templates.md | 197 +++++++++--------- 4 files changed, 159 insertions(+), 97 deletions(-) diff --git a/internal/pipe/release/body.go b/internal/pipe/release/body.go index 2fb41930d..dd539309a 100644 --- a/internal/pipe/release/body.go +++ b/internal/pipe/release/body.go @@ -2,8 +2,10 @@ package release import ( "bytes" + "os" "text/template" + "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/internal/tmpl" "github.com/goreleaser/goreleaser/pkg/context" ) @@ -15,7 +17,21 @@ const bodyTemplateText = `{{ with .Header }}{{ . }}{{ "\n" }}{{ end }} func describeBody(ctx *context.Context) (bytes.Buffer, error) { var out bytes.Buffer - t := tmpl.New(ctx) + var checksum string + if l := ctx.Artifacts.Filter(artifact.ByType(artifact.Checksum)).List(); len(l) > 0 { + if err := l[0].Refresh(); err != nil { + return out, err + } + bts, err := os.ReadFile(l[0].Path) + if err != nil { + return out, err + } + checksum = string(bts) + } + + t := tmpl.New(ctx).WithExtraFields(tmpl.Fields{ + "Checksums": checksum, + }) header, err := t.Apply(ctx.Config.Release.Header) if err != nil { diff --git a/internal/pipe/release/body_test.go b/internal/pipe/release/body_test.go index 079ba6fae..e1bb3766f 100644 --- a/internal/pipe/release/body_test.go +++ b/internal/pipe/release/body_test.go @@ -1,8 +1,11 @@ package release import ( + "os" + "path/filepath" "testing" + "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/internal/golden" "github.com/goreleaser/goreleaser/internal/testctx" "github.com/goreleaser/goreleaser/pkg/config" @@ -36,12 +39,39 @@ func TestDescribeBodyWithHeaderAndFooter(t *testing.T) { config.Project{ Release: config.Release{ Header: "## Yada yada yada\nsomething\n", - Footer: "\n---\n\nGet images at docker.io/foo/bar:{{.Tag}}\n\n---\n\nGet GoReleaser Pro at https://goreleaser.com/pro", + Footer: ` +--- + +Get images at docker.io/foo/bar:{{.Tag}} + +--- + +Get GoReleaser Pro at https://goreleaser.com/pro + +--- + +## Checksums + +` + "```\n{{ .Checksums }}\n```" + ` + `, }, }, testctx.WithCurrentTag("v1.0"), func(ctx *context.Context) { ctx.ReleaseNotes = changelog }, ) + + checksumPath := filepath.Join(t.TempDir(), "checksums.txt") + checksumContent := "f674623cf1edd0f753e620688cedee4e7c0e837ac1e53c0cbbce132ffe35fd52 foo.zip" + ctx.Artifacts.Add(&artifact.Artifact{ + Name: "checksums.txt", + Path: checksumPath, + Type: artifact.Checksum, + Extra: map[string]interface{}{ + artifact.ExtraRefresh: func() error { + return os.WriteFile(checksumPath, []byte(checksumContent), 0o644) + }, + }, + }) out, err := describeBody(ctx) require.NoError(t, err) diff --git a/internal/pipe/release/testdata/TestDescribeBodyWithHeaderAndFooter.golden b/internal/pipe/release/testdata/TestDescribeBodyWithHeaderAndFooter.golden index 1a6ac3e24..dd00d1fd8 100644 --- a/internal/pipe/release/testdata/TestDescribeBodyWithHeaderAndFooter.golden +++ b/internal/pipe/release/testdata/TestDescribeBodyWithHeaderAndFooter.golden @@ -11,3 +11,12 @@ Get images at docker.io/foo/bar:v1.0 --- Get GoReleaser Pro at https://goreleaser.com/pro + +--- + +## Checksums + +``` +f674623cf1edd0f753e620688cedee4e7c0e837ac1e53c0cbbce132ffe35fd52 foo.zip +``` + diff --git a/www/docs/customization/templates.md b/www/docs/customization/templates.md index 878a3e9bb..7e642b9f3 100644 --- a/www/docs/customization/templates.md +++ b/www/docs/customization/templates.md @@ -6,64 +6,69 @@ Those fields are often suffixed with `_template`, but sometimes they may not be. The documentation of each section should be explicit about which fields support templating. - - ## Common Fields -On fields that support templating, these fields are always available: +In fields that support templates, these fields are always available: -Key |Description -----------------------|-------------------------------------------------------------------------------------------------------------------- -`.ProjectName` |the project name -`.Version` |the version being released[^version-prefix] -`.Branch` |the current git branch -`.PrefixedTag` |the current git tag prefixed with the monorepo config tag prefix (if any) -`.Tag` |the current git tag -`.PrefixedPreviousTag`|the previous git tag prefixed with the monorepo config tag prefix (if any) -`.PreviousTag` |the previous git tag, or empty if no previous tags -`.ShortCommit` |the git commit short hash -`.FullCommit` |the git commit full hash -`.Commit` |the git commit hash (deprecated) -`.CommitDate` |the UTC commit date in RFC 3339 format -`.CommitTimestamp` |the UTC commit date in Unix format -`.GitURL` |the git remote url -`.IsGitDirty` |whether or not current git state is dirty. Since v1.19. -`.Major` |the major part of the version[^tag-is-semver] -`.Minor` |the minor part of the version[^tag-is-semver] -`.Patch` |the patch part of the version[^tag-is-semver] -`.Prerelease` |the prerelease part of the version, e.g. `beta`[^tag-is-semver] -`.RawVersion` |composed of `{Major}.{Minor}.{Patch}` [^tag-is-semver] -`.ReleaseNotes` |the generated release notes, available after the changelog step has been executed -`.IsDraft` |`true` if `release.draft` is set in the configuration, `false` otherwise. Since v1.17. -`.IsSnapshot` |`true` if `--snapshot` is set, `false` otherwise -`.IsNightly` |`true` if `--nightly` is set, `false` otherwise -`.Env` |a map with system's environment variables -`.Date` |current UTC date in RFC 3339 format -`.Now` |current UTC date as `time.Time` struct, allows all `time.Time` functions (e.g. `{{ .Now.Format "2006" }}`) . Since v1.17. -`.Timestamp` |current UTC time in Unix format -`.ModulePath` |the go module path, as reported by `go list -m` -`incpatch "v1.2.4"` |increments the patch of the given version[^panic-if-not-semver] -`incminor "v1.2.4"` |increments the minor of the given version[^panic-if-not-semver] -`incmajor "v1.2.4"` |increments the major of the given version[^panic-if-not-semver] -`.ReleaseURL` |the current release download url[^scm-release-url] -`.Summary` |the git summary, e.g. `v1.0.0-10-g34f56g3`[^git-summary] -`.PrefixedSummary` |the git summary prefixed with the monorepo config tag prefix (if any) -`.TagSubject` |the annotated tag message subject, or the message subject of the commit it points out[^git-tag-subject]. Since v1.2. -`.TagContents` |the annotated tag message, or the message of the commit it points out[^git-tag-body]. Since v1.2. -`.TagBody` |the annotated tag message's body, or the message's body of the commit it points out[^git-tag-body]. Since v1.2. -`.Runtime.Goos` |equivalent to `runtime.GOOS`. Since v1.5. -`.Runtime.Goarch` |equivalent to `runtime.GOARCH`. Since v1.5. -`.Artifacts` |the current artifact list. See table bellow for fields. Since v1.16-pro. +| Key | Description | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------------- | +| `.ProjectName` | the project name | +| `.Version` | the version being released[^version-prefix] | +| `.Branch` | the current git branch | +| `.PrefixedTag` | the current git tag prefixed with the monorepo config tag prefix (if any) | +| `.Tag` | the current git tag | +| `.PrefixedPreviousTag` | the previous git tag prefixed with the monorepo config tag prefix (if any) | +| `.PreviousTag` | the previous git tag, or empty if no previous tags | +| `.ShortCommit` | the git commit short hash | +| `.FullCommit` | the git commit full hash | +| `.Commit` | the git commit hash (deprecated) | +| `.CommitDate` | the UTC commit date in RFC 3339 format | +| `.CommitTimestamp` | the UTC commit date in Unix format | +| `.GitURL` | the git remote url | +| `.IsGitDirty` | whether or not current git state is dirty. Since v1.19. | +| `.Major` | the major part of the version[^tag-is-semver] | +| `.Minor` | the minor part of the version[^tag-is-semver] | +| `.Patch` | the patch part of the version[^tag-is-semver] | +| `.Prerelease` | the prerelease part of the version, e.g. `beta`[^tag-is-semver] | +| `.RawVersion` | composed of `{Major}.{Minor}.{Patch}` [^tag-is-semver] | +| `.ReleaseNotes` | the generated release notes, available after the changelog step has been executed | +| `.IsDraft` | `true` if `release.draft` is set in the configuration, `false` otherwise. Since v1.17. | +| `.IsSnapshot` | `true` if `--snapshot` is set, `false` otherwise | +| `.IsNightly` | `true` if `--nightly` is set, `false` otherwise | +| `.Env` | a map with system's environment variables | +| `.Date` | current UTC date in RFC 3339 format | +| `.Now` | current UTC date as `time.Time` struct, allows all `time.Time` functions (e.g. `{{ .Now.Format "2006" }}`) . Since v1.17. | +| `.Timestamp` | current UTC time in Unix format | +| `.ModulePath` | the go module path, as reported by `go list -m` | +| `incpatch "v1.2.4"` | increments the patch of the given version[^panic-if-not-semver] | +| `incminor "v1.2.4"` | increments the minor of the given version[^panic-if-not-semver] | +| `incmajor "v1.2.4"` | increments the major of the given version[^panic-if-not-semver] | +| `.ReleaseURL` | the current release download url[^scm-release-url] | +| `.Summary` | the git summary, e.g. `v1.0.0-10-g34f56g3`[^git-summary] | +| `.PrefixedSummary` | the git summary prefixed with the monorepo config tag prefix (if any) | +| `.TagSubject` | the annotated tag message subject, or the message subject of the commit it points out[^git-tag-subject]. Since v1.2. | +| `.TagContents` | the annotated tag message, or the message of the commit it points out[^git-tag-body]. Since v1.2. | +| `.TagBody` | the annotated tag message's body, or the message's body of the commit it points out[^git-tag-body]. Since v1.2. | +| `.Runtime.Goos` | equivalent to `runtime.GOOS`. Since v1.5. | +| `.Runtime.Goarch` | equivalent to `runtime.GOARCH`. Since v1.5. | +| `.Artifacts` | the current artifact list. See table bellow for fields. Since v1.16-pro. | +| `.Checksums` | the current checksum file contents. Only available in the release body. Since v1.19. | + +[^version-prefix]: + The `v` prefix is stripped, and it might be changed in + `snapshot` and `nightly` builds. -[^version-prefix]: The `v` prefix is stripped, and it might be changed in - `snapshot` and `nightly` builds. [^tag-is-semver]: Assuming `Tag` is a valid a SemVer, otherwise empty/zeroed. [^panic-if-not-semver]: Will panic if not a semantic version. -[^scm-release-url]: Composed of the current SCM's download URL and current tag. - For instance, on GitHub, it'll be - `https://github.com/{owner}/{repo}/releases/tag/{tag}`. -[^git-summary]: It is generated by `git describe --dirty --always --tags`, the - format will be `{Tag}-$N-{CommitSHA}` +[^scm-release-url]: + Composed of the current SCM's download URL and current tag. + For instance, on GitHub, it'll be + `https://github.com/{owner}/{repo}/releases/tag/{tag}`. + +[^git-summary]: + It is generated by `git describe --dirty --always --tags`, the + format will be `{Tag}-$N-{CommitSHA}` + [^git-tag-subject]: As reported by `git tag -l --format='%(contents:subject)'` [^git-tag-body]: As reported by `git tag -l --format='%(contents)'` @@ -83,8 +88,8 @@ You should be able to use all its fields on each item: - `.Type` - `.Extra` - !!! success "GoReleaser Pro" + The `.Artifacts` template variable is [GoReleaser Pro feature](/pro/). ## Single-artifact extra fields @@ -92,17 +97,17 @@ You should be able to use all its fields on each item: On fields that are related to a single artifact (e.g., the binary name), you may have some extra fields: -Key |Description ----------------|-------------------------------------------- -`.Os` |`GOOS`[^archive-replacementes] -`.Arch` |`GOARCH`[^archive-replacementes] -`.Arm` |`GOARM`[^archive-replacementes] -`.Mips` |`GOMIPS`[^archive-replacementes] -`.Amd64` |`GOAMD64`[^archive-replacementes] -`.Binary` |binary name -`.ArtifactName`|archive name -`.ArtifactPath`|absolute path to artifact -`.ArtifactExt` |binary extension (e.g. `.exe`). Since v1.11. +| Key | Description | +| --------------- | -------------------------------------------- | +| `.Os` | `GOOS`[^archive-replacementes] | +| `.Arch` | `GOARCH`[^archive-replacementes] | +| `.Arm` | `GOARM`[^archive-replacementes] | +| `.Mips` | `GOMIPS`[^archive-replacementes] | +| `.Amd64` | `GOAMD64`[^archive-replacementes] | +| `.Binary` | binary name | +| `.ArtifactName` | archive name | +| `.ArtifactPath` | absolute path to artifact | +| `.ArtifactExt` | binary extension (e.g. `.exe`). Since v1.11. | [^archive-replacementes]: Might have been replaced by `archives.replacements`. @@ -110,40 +115,41 @@ Key |Description On the nFPM name template field, you can use those extra fields as well: -Key |Description ------------------------|-------------------------------------------------------------- -`.Release` |release from the nfpm config -`.Epoch` |epoch from the nfpm config -`.PackageName` |package the name. Same as `ProjectName` if not overridden. -`.ConventionalFileName` |conventional package file name as provided by nFPM.[^arm-names] -`.ConventionalExtension`|conventional package extension as provided by nFPM. Since v1.16. +| Key | Description | +| ------------------------ | ---------------------------------------------------------------- | +| `.Release` | release from the nfpm config | +| `.Epoch` | epoch from the nfpm config | +| `.PackageName` | package the name. Same as `ProjectName` if not overridden. | +| `.ConventionalFileName` | conventional package file name as provided by nFPM.[^arm-names] | +| `.ConventionalExtension` | conventional package extension as provided by nFPM. Since v1.16. | -[^arm-names]: Please beware: some OSs might have the same names for different - ARM versions, for example, for Debian both ARMv6 and ARMv7 are called `armhf`. - Make sure that's not your case otherwise you might end up with colliding - names. It also does not handle multiple GOAMD64 versions. +[^arm-names]: + Please beware: some OSs might have the same names for different + ARM versions, for example, for Debian both ARMv6 and ARMv7 are called `armhf`. + Make sure that's not your case otherwise you might end up with colliding + names. It also does not handle multiple GOAMD64 versions. ## Functions On all fields, you have these available functions: -Usage |Description -------------------------------|------------------------------------------------------------------------------------------------------------------------------ -`replace "v1.2" "v" ""` |replaces all matches. See [ReplaceAll](https://golang.org/pkg/strings/#ReplaceAll). -`split "1.2" "."` |split string at separator. See [Split](https://golang.org/pkg/strings/#Split). Since v1.11. -`time "01/02/2006"` |current UTC time in the specified format (this is not deterministic, a new time for every call). -`tolower "V1.2"` |makes input string lowercase. See [ToLower](https://golang.org/pkg/strings/#ToLower). -`toupper "v1.2"` |makes input string uppercase. See [ToUpper](https://golang.org/pkg/strings/#ToUpper). -`trim " v1.2 "` |removes all leading and trailing white space. See [TrimSpace](https://golang.org/pkg/strings/#TrimSpace). -`trimprefix "v1.2" "v"` |removes provided leading prefix string, if present. See [TrimPrefix](https://golang.org/pkg/strings/#TrimPrefix). -`trimsuffix "1.2v" "v"` |removes provided trailing suffix string, if present. See [TrimSuffix](https://pkg.go.dev/strings#TrimSuffix). -`dir .Path` |returns all but the last element of path, typically the path's directory. See [Dir](https://golang.org/pkg/path/filepath/#Dir). -`base .Path` |returns the last element of path. See [Base](https://golang.org/pkg/path/filepath/#Base). Since v1.16. -`abs .ArtifactPath` |returns an absolute representation of path. See [Abs](https://golang.org/pkg/path/filepath/#Abs). -`filter "text" "regex"` |keeps only the lines matching the given regex, analogous to `grep -E`. Since v1.6. -`reverseFilter "text" "regex"`|keeps only the lines **not** matching the given regex, analogous to `grep -vE`. Since v1.6. -`title "foo"` |"titlenize" the string using english as language. See [Title](https://pkg.go.dev/golang.org/x/text/cases#Title). Since v1.14. -`mdv2escape "foo"` |escape characteres according to MarkdownV2, especially useful in the Telegram integration. Since v1.19. +| Usage | Description | +| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | +| `replace "v1.2" "v" ""` | replaces all matches. See [ReplaceAll](https://golang.org/pkg/strings/#ReplaceAll). | +| `split "1.2" "."` | split string at separator. See [Split](https://golang.org/pkg/strings/#Split). Since v1.11. | +| `time "01/02/2006"` | current UTC time in the specified format (this is not deterministic, a new time for every call). | +| `tolower "V1.2"` | makes input string lowercase. See [ToLower](https://golang.org/pkg/strings/#ToLower). | +| `toupper "v1.2"` | makes input string uppercase. See [ToUpper](https://golang.org/pkg/strings/#ToUpper). | +| `trim " v1.2 "` | removes all leading and trailing white space. See [TrimSpace](https://golang.org/pkg/strings/#TrimSpace). | +| `trimprefix "v1.2" "v"` | removes provided leading prefix string, if present. See [TrimPrefix](https://golang.org/pkg/strings/#TrimPrefix). | +| `trimsuffix "1.2v" "v"` | removes provided trailing suffix string, if present. See [TrimSuffix](https://pkg.go.dev/strings#TrimSuffix). | +| `dir .Path` | returns all but the last element of path, typically the path's directory. See [Dir](https://golang.org/pkg/path/filepath/#Dir). | +| `base .Path` | returns the last element of path. See [Base](https://golang.org/pkg/path/filepath/#Base). Since v1.16. | +| `abs .ArtifactPath` | returns an absolute representation of path. See [Abs](https://golang.org/pkg/path/filepath/#Abs). | +| `filter "text" "regex"` | keeps only the lines matching the given regex, analogous to `grep -E`. Since v1.6. | +| `reverseFilter "text" "regex"` | keeps only the lines **not** matching the given regex, analogous to `grep -vE`. Since v1.6. | +| `title "foo"` | "titlenize" the string using english as language. See [Title](https://pkg.go.dev/golang.org/x/text/cases#Title). Since v1.14. | +| `mdv2escape "foo"` | escape characteres according to MarkdownV2, especially useful in the Telegram integration. Since v1.19. | With all those fields, you may be able to compose the name of your artifacts pretty much the way you want: @@ -155,7 +161,7 @@ example_template: '{{ tolower .ProjectName }}_{{ .Env.USER }}_{{ time "2006" }}' For example, if you want to add the go version to some artifact: ```yaml -foo_template: 'foo_{{ .Env.GOVERSION }}' +foo_template: "foo_{{ .Env.GOVERSION }}" ``` And then you can run: @@ -165,13 +171,15 @@ GOVERSION_NR=$(go version | awk '{print $3;}') goreleaser ``` !!! warning + Note that those are hypothetical examples and the fields `foo_template` and `example_template` are not valid GoReleaser configurations. ## Custom variables !!! success "GoReleaser Pro" - Custom template variables support is a [GoReleaser Pro feature](/pro/). + + Custom template variables support is a [GoReleaser Pro feature](/pro/). You can also declare custom variables. This feature is specially useful with [includes](/customization/includes/), so you can have more generic configuration @@ -188,4 +196,3 @@ variables: ``` And then you can use those fields as `{{ .Var.description }}`, for example. -