mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-03-17 20:47:50 +02:00
feat: checksums.split (#4707)
closes #4618 closes #4641 --------- Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>
This commit is contained in:
parent
280e68431a
commit
54ee014b50
@ -143,16 +143,18 @@ func (t Type) String() string {
|
||||
}
|
||||
|
||||
const (
|
||||
ExtraID = "ID"
|
||||
ExtraBinary = "Binary"
|
||||
ExtraExt = "Ext"
|
||||
ExtraFormat = "Format"
|
||||
ExtraWrappedIn = "WrappedIn"
|
||||
ExtraBinaries = "Binaries"
|
||||
ExtraRefresh = "Refresh"
|
||||
ExtraReplaces = "Replaces"
|
||||
ExtraDigest = "Digest"
|
||||
ExtraSize = "Size"
|
||||
ExtraID = "ID"
|
||||
ExtraBinary = "Binary"
|
||||
ExtraExt = "Ext"
|
||||
ExtraFormat = "Format"
|
||||
ExtraWrappedIn = "WrappedIn"
|
||||
ExtraBinaries = "Binaries"
|
||||
ExtraRefresh = "Refresh"
|
||||
ExtraReplaces = "Replaces"
|
||||
ExtraDigest = "Digest"
|
||||
ExtraSize = "Size"
|
||||
ExtraChecksum = "Checksum"
|
||||
ExtraChecksumOf = "ChecksumOf"
|
||||
)
|
||||
|
||||
// Extras represents the extra fields in an artifact.
|
||||
@ -258,7 +260,12 @@ func (a Artifact) Checksum(algorithm string) (string, error) {
|
||||
if _, err := io.Copy(h, file); err != nil {
|
||||
return "", fmt.Errorf("failed to checksum: %w", err)
|
||||
}
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
check := hex.EncodeToString(h.Sum(nil))
|
||||
if a.Extra == nil {
|
||||
a.Extra = make(Extras)
|
||||
}
|
||||
a.Extra[ExtraChecksum] = fmt.Sprintf("%s:%s", algorithm, check)
|
||||
return check, nil
|
||||
}
|
||||
|
||||
var noRefresh = func() error { return nil }
|
||||
|
@ -36,23 +36,72 @@ func (Pipe) Skip(ctx *context.Context) bool { return ctx.Config.Checksum.Disable
|
||||
|
||||
// Default sets the pipe defaults.
|
||||
func (Pipe) Default(ctx *context.Context) error {
|
||||
if ctx.Config.Checksum.NameTemplate == "" {
|
||||
ctx.Config.Checksum.NameTemplate = "{{ .ProjectName }}_{{ .Version }}_checksums.txt"
|
||||
cs := &ctx.Config.Checksum
|
||||
if cs.Algorithm == "" {
|
||||
cs.Algorithm = "sha256"
|
||||
}
|
||||
if ctx.Config.Checksum.Algorithm == "" {
|
||||
ctx.Config.Checksum.Algorithm = "sha256"
|
||||
if cs.NameTemplate == "" {
|
||||
if cs.Split {
|
||||
cs.NameTemplate = "{{ .ArtifactName }}.{{ .Algorithm }}"
|
||||
} else {
|
||||
cs.NameTemplate = "{{ .ProjectName }}_{{ .Version }}_checksums.txt"
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run the pipe.
|
||||
func (Pipe) Run(ctx *context.Context) error {
|
||||
if ctx.Config.Checksum.Split {
|
||||
return splitChecksum(ctx)
|
||||
}
|
||||
|
||||
return singleChecksum(ctx)
|
||||
}
|
||||
|
||||
func splitChecksum(ctx *context.Context) error {
|
||||
artifactList, err := buildArtifactList(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, art := range artifactList {
|
||||
filename, err := tmpl.New(ctx).
|
||||
WithArtifact(art).
|
||||
WithExtraFields(tmpl.Fields{
|
||||
"Algorithm": ctx.Config.Checksum.Algorithm,
|
||||
}).
|
||||
Apply(ctx.Config.Checksum.NameTemplate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("checksum: name template: %w", err)
|
||||
}
|
||||
filepath := filepath.Join(ctx.Config.Dist, filename)
|
||||
if err := refreshOne(ctx, *art, filepath); err != nil {
|
||||
return fmt.Errorf("checksum: %s: %w", art.Path, err)
|
||||
}
|
||||
ctx.Artifacts.Add(&artifact.Artifact{
|
||||
Type: artifact.Checksum,
|
||||
Path: filepath,
|
||||
Name: filename,
|
||||
Extra: map[string]interface{}{
|
||||
artifact.ExtraChecksumOf: art.Path,
|
||||
artifact.ExtraRefresh: func() error {
|
||||
log.WithField("file", filename).Info("refreshing checksums")
|
||||
return refreshOne(ctx, *art, filepath)
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func singleChecksum(ctx *context.Context) error {
|
||||
filename, err := tmpl.New(ctx).Apply(ctx.Config.Checksum.NameTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
filepath := filepath.Join(ctx.Config.Dist, filename)
|
||||
if err := refresh(ctx, filepath); err != nil {
|
||||
if err := refreshAll(ctx, filepath); err != nil {
|
||||
if errors.Is(err, errNoArtifacts) {
|
||||
return nil
|
||||
}
|
||||
@ -65,44 +114,28 @@ func (Pipe) Run(ctx *context.Context) error {
|
||||
Extra: map[string]interface{}{
|
||||
artifact.ExtraRefresh: func() error {
|
||||
log.WithField("file", filename).Info("refreshing checksums")
|
||||
return refresh(ctx, filepath)
|
||||
return refreshAll(ctx, filepath)
|
||||
},
|
||||
},
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func refresh(ctx *context.Context, filepath string) error {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
filter := artifact.Or(
|
||||
artifact.ByType(artifact.UploadableArchive),
|
||||
artifact.ByType(artifact.UploadableBinary),
|
||||
artifact.ByType(artifact.UploadableSourceArchive),
|
||||
artifact.ByType(artifact.LinuxPackage),
|
||||
artifact.ByType(artifact.SBOM),
|
||||
)
|
||||
if len(ctx.Config.Checksum.IDs) > 0 {
|
||||
filter = artifact.And(filter, artifact.ByIDs(ctx.Config.Checksum.IDs...))
|
||||
}
|
||||
|
||||
artifactList := ctx.Artifacts.Filter(filter).List()
|
||||
|
||||
extraFiles, err := extrafiles.Find(ctx, ctx.Config.Checksum.ExtraFiles)
|
||||
func refreshOne(ctx *context.Context, art artifact.Artifact, path string) error {
|
||||
check, err := art.Checksum(ctx.Config.Checksum.Algorithm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, []byte(check), 0o644)
|
||||
}
|
||||
|
||||
for name, path := range extraFiles {
|
||||
artifactList = append(artifactList, &artifact.Artifact{
|
||||
Name: name,
|
||||
Path: path,
|
||||
Type: artifact.UploadableFile,
|
||||
})
|
||||
}
|
||||
func refreshAll(ctx *context.Context, filepath string) error {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
if len(artifactList) == 0 {
|
||||
return errNoArtifacts
|
||||
artifactList, err := buildArtifactList(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g := semerrgroup.New(ctx.Parallelism)
|
||||
@ -141,6 +174,39 @@ func refresh(ctx *context.Context, filepath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func buildArtifactList(ctx *context.Context) ([]*artifact.Artifact, error) {
|
||||
filter := artifact.Or(
|
||||
artifact.ByType(artifact.UploadableArchive),
|
||||
artifact.ByType(artifact.UploadableBinary),
|
||||
artifact.ByType(artifact.UploadableSourceArchive),
|
||||
artifact.ByType(artifact.LinuxPackage),
|
||||
artifact.ByType(artifact.SBOM),
|
||||
)
|
||||
if len(ctx.Config.Checksum.IDs) > 0 {
|
||||
filter = artifact.And(filter, artifact.ByIDs(ctx.Config.Checksum.IDs...))
|
||||
}
|
||||
|
||||
artifactList := ctx.Artifacts.Filter(filter).List()
|
||||
|
||||
extraFiles, err := extrafiles.Find(ctx, ctx.Config.Checksum.ExtraFiles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for name, path := range extraFiles {
|
||||
artifactList = append(artifactList, &artifact.Artifact{
|
||||
Name: name,
|
||||
Path: path,
|
||||
Type: artifact.UploadableFile,
|
||||
})
|
||||
}
|
||||
|
||||
if len(artifactList) == 0 {
|
||||
return nil, errNoArtifacts
|
||||
}
|
||||
return artifactList, nil
|
||||
}
|
||||
|
||||
func checksums(algorithm string, a *artifact.Artifact) (string, error) {
|
||||
log.WithField("file", a.Name).Debug("checksumming")
|
||||
sha, err := a.Checksum(algorithm)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package checksums
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -103,6 +104,66 @@ func TestPipe(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPipeSplit(t *testing.T) {
|
||||
const binary = "binary"
|
||||
const archive = binary + ".tar.gz"
|
||||
const linuxPackage = binary + ".rpm"
|
||||
const sum = "61d034473102d7dac305902770471fd50f4c5b26f6831a56dd90b5184b3c30fc"
|
||||
|
||||
folder := t.TempDir()
|
||||
file := filepath.Join(folder, binary)
|
||||
require.NoError(t, os.WriteFile(file, []byte("some string"), 0o644))
|
||||
ctx := testctx.NewWithCfg(
|
||||
config.Project{
|
||||
Dist: folder,
|
||||
ProjectName: "foo",
|
||||
Checksum: config.Checksum{
|
||||
Split: true,
|
||||
},
|
||||
},
|
||||
)
|
||||
ctx.Artifacts.Add(&artifact.Artifact{
|
||||
Name: binary,
|
||||
Path: file,
|
||||
Type: artifact.UploadableBinary,
|
||||
Extra: map[string]interface{}{
|
||||
artifact.ExtraID: "id-1",
|
||||
},
|
||||
})
|
||||
ctx.Artifacts.Add(&artifact.Artifact{
|
||||
Name: archive,
|
||||
Path: file,
|
||||
Type: artifact.UploadableArchive,
|
||||
Extra: map[string]interface{}{
|
||||
artifact.ExtraID: "id-2",
|
||||
},
|
||||
})
|
||||
ctx.Artifacts.Add(&artifact.Artifact{
|
||||
Name: linuxPackage,
|
||||
Path: file,
|
||||
Type: artifact.LinuxPackage,
|
||||
Extra: map[string]interface{}{
|
||||
artifact.ExtraID: "id-3",
|
||||
},
|
||||
})
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
require.NoError(t, Pipe{}.Run(ctx))
|
||||
|
||||
require.NoError(t, ctx.Artifacts.Visit(func(a *artifact.Artifact) error {
|
||||
return a.Refresh()
|
||||
}))
|
||||
|
||||
checks := ctx.Artifacts.Filter(artifact.ByType(artifact.Checksum)).List()
|
||||
require.Len(t, checks, 3)
|
||||
|
||||
for _, check := range checks {
|
||||
require.NotEmpty(t, check.Extra[artifact.ExtraChecksumOf])
|
||||
bts, err := os.ReadFile(check.Path)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, sum, string(bts))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRefreshModifying(t *testing.T) {
|
||||
const binary = "binary"
|
||||
folder := t.TempDir()
|
||||
@ -134,6 +195,37 @@ func TestRefreshModifying(t *testing.T) {
|
||||
require.NotEqual(t, string(previous), string(current))
|
||||
}
|
||||
|
||||
func TestRefreshModifyingSplit(t *testing.T) {
|
||||
const binary = "binary"
|
||||
folder := t.TempDir()
|
||||
file := filepath.Join(folder, binary)
|
||||
require.NoError(t, os.WriteFile(file, []byte("some string"), 0o644))
|
||||
ctx := testctx.NewWithCfg(config.Project{
|
||||
Dist: folder,
|
||||
ProjectName: binary,
|
||||
Checksum: config.Checksum{
|
||||
Split: true,
|
||||
},
|
||||
Env: []string{"FOO=bar"},
|
||||
}, testctx.WithCurrentTag("1.2.3"))
|
||||
ctx.Artifacts.Add(&artifact.Artifact{
|
||||
Name: binary,
|
||||
Path: file,
|
||||
Type: artifact.UploadableBinary,
|
||||
})
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
require.NoError(t, Pipe{}.Run(ctx))
|
||||
checks := ctx.Artifacts.Filter(artifact.ByType(artifact.Checksum)).List()
|
||||
require.Len(t, checks, 1)
|
||||
previous, err := os.ReadFile(checks[0].Path)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, os.WriteFile(file, []byte("some other string"), 0o644))
|
||||
require.NoError(t, checks[0].Refresh())
|
||||
current, err := os.ReadFile(checks[0].Path)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, string(previous), string(current))
|
||||
}
|
||||
|
||||
func TestPipeFileNotExist(t *testing.T) {
|
||||
folder := t.TempDir()
|
||||
ctx := testctx.NewWithCfg(
|
||||
@ -164,26 +256,29 @@ func TestPipeInvalidNameTemplate(t *testing.T) {
|
||||
"{{ .Pro }_checksums.txt",
|
||||
"{{.Env.NOPE}}",
|
||||
} {
|
||||
t.Run(template, func(t *testing.T) {
|
||||
folder := t.TempDir()
|
||||
ctx := testctx.NewWithCfg(
|
||||
config.Project{
|
||||
Dist: folder,
|
||||
ProjectName: "name",
|
||||
Checksum: config.Checksum{
|
||||
NameTemplate: template,
|
||||
Algorithm: "sha256",
|
||||
for _, split := range []bool{true, false} {
|
||||
t.Run(fmt.Sprintf("split_%v_%s", split, template), func(t *testing.T) {
|
||||
folder := t.TempDir()
|
||||
ctx := testctx.NewWithCfg(
|
||||
config.Project{
|
||||
Dist: folder,
|
||||
ProjectName: "name",
|
||||
Checksum: config.Checksum{
|
||||
NameTemplate: template,
|
||||
Algorithm: "sha256",
|
||||
Split: split,
|
||||
},
|
||||
},
|
||||
},
|
||||
testctx.WithCurrentTag("1.2.3"),
|
||||
)
|
||||
ctx.Artifacts.Add(&artifact.Artifact{
|
||||
Name: "whatever",
|
||||
Type: artifact.UploadableBinary,
|
||||
Path: binFile.Name(),
|
||||
testctx.WithCurrentTag("1.2.3"),
|
||||
)
|
||||
ctx.Artifacts.Add(&artifact.Artifact{
|
||||
Name: "whatever",
|
||||
Type: artifact.UploadableBinary,
|
||||
Path: binFile.Name(),
|
||||
})
|
||||
testlib.RequireTemplateError(t, Pipe{}.Run(ctx))
|
||||
})
|
||||
testlib.RequireTemplateError(t, Pipe{}.Run(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -236,6 +331,21 @@ func TestDefault(t *testing.T) {
|
||||
require.Equal(t, "sha256", ctx.Config.Checksum.Algorithm)
|
||||
}
|
||||
|
||||
func TestDefaultSPlit(t *testing.T) {
|
||||
ctx := testctx.NewWithCfg(config.Project{
|
||||
Checksum: config.Checksum{
|
||||
Split: true,
|
||||
},
|
||||
})
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
require.Equal(
|
||||
t,
|
||||
"{{ .ArtifactName }}.{{ .Algorithm }}",
|
||||
ctx.Config.Checksum.NameTemplate,
|
||||
)
|
||||
require.Equal(t, "sha256", ctx.Config.Checksum.Algorithm)
|
||||
}
|
||||
|
||||
func TestDefaultSet(t *testing.T) {
|
||||
ctx := testctx.NewWithCfg(config.Project{
|
||||
Checksum: config.Checksum{
|
||||
|
@ -17,21 +17,39 @@ const bodyTemplateText = `{{ with .Header }}{{ . }}{{ "\n" }}{{ end }}
|
||||
|
||||
func describeBody(ctx *context.Context) (bytes.Buffer, error) {
|
||||
var out bytes.Buffer
|
||||
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)
|
||||
fields := tmpl.Fields{}
|
||||
|
||||
checksums := ctx.Artifacts.Filter(artifact.ByType(artifact.Checksum))
|
||||
|
||||
if err := checksums.Visit(func(a *artifact.Artifact) error {
|
||||
return a.Refresh()
|
||||
}); err != nil {
|
||||
return out, err
|
||||
}
|
||||
|
||||
checksumsList := checksums.List()
|
||||
switch len(checksumsList) {
|
||||
case 0:
|
||||
// do nothing
|
||||
case 1:
|
||||
bts, err := os.ReadFile(checksumsList[0].Path)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
checksum = string(bts)
|
||||
fields["Checksums"] = string(bts)
|
||||
default:
|
||||
checkMap := map[string]string{}
|
||||
for _, check := range checksumsList {
|
||||
bts, err := os.ReadFile(check.Path)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
checkMap[artifact.ExtraOr(*check, artifact.ExtraChecksumOf, "")] = string(bts)
|
||||
}
|
||||
fields["Checksums"] = checkMap
|
||||
}
|
||||
|
||||
t := tmpl.New(ctx).WithExtraFields(tmpl.Fields{
|
||||
"Checksums": checksum,
|
||||
})
|
||||
t := tmpl.New(ctx).WithExtraFields(fields)
|
||||
|
||||
header, err := t.Apply(ctx.Config.Release.Header)
|
||||
if err != nil {
|
||||
|
@ -34,6 +34,49 @@ func TestDontEscapeHTML(t *testing.T) {
|
||||
require.Contains(t, out.String(), changelog)
|
||||
}
|
||||
|
||||
func TestDescribeBodyMultipleChecksums(t *testing.T) {
|
||||
ctx := testctx.NewWithCfg(
|
||||
config.Project{
|
||||
Release: config.Release{
|
||||
Header: "## Yada yada yada\nsomething\n",
|
||||
Footer: `
|
||||
---
|
||||
|
||||
## Checksums
|
||||
|
||||
` + "```\n{{ range $key, $value := .Checksums }}{{ $value }} {{ $key }}\n{{ end }}```\n",
|
||||
},
|
||||
},
|
||||
testctx.WithCurrentTag("v1.0"),
|
||||
func(ctx *context.Context) { ctx.ReleaseNotes = "nothing" },
|
||||
)
|
||||
|
||||
checksums := map[string]string{
|
||||
"bar.zip": "f674623cf1edd0f753e620688cedee4e7c0e837ac1e53c0cbbce132ffe35fd52",
|
||||
"foo.zip": "271a74b75a12f6c3affc88df101f9ef29af79717b1b2f4bdd5964aacf65bcf40",
|
||||
}
|
||||
for name, check := range checksums {
|
||||
name := name
|
||||
check := check
|
||||
checksumPath := filepath.Join(t.TempDir(), name+".sha256")
|
||||
ctx.Artifacts.Add(&artifact.Artifact{
|
||||
Name: name + ".sha256",
|
||||
Path: checksumPath,
|
||||
Type: artifact.Checksum,
|
||||
Extra: map[string]interface{}{
|
||||
artifact.ExtraChecksumOf: name,
|
||||
artifact.ExtraRefresh: func() error {
|
||||
return os.WriteFile(checksumPath, []byte(check), 0o644)
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
out, err := describeBody(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
golden.RequireEqual(t, out.Bytes())
|
||||
}
|
||||
|
||||
func TestDescribeBodyWithHeaderAndFooter(t *testing.T) {
|
||||
changelog := "feature1: description\nfeature2: other description"
|
||||
ctx := testctx.NewWithCfg(
|
||||
|
14
internal/pipe/release/testdata/TestDescribeBodyMultipleChecksums.golden
vendored
Normal file
14
internal/pipe/release/testdata/TestDescribeBodyMultipleChecksums.golden
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
## Yada yada yada
|
||||
something
|
||||
|
||||
nothing
|
||||
|
||||
---
|
||||
|
||||
## Checksums
|
||||
|
||||
```
|
||||
f674623cf1edd0f753e620688cedee4e7c0e837ac1e53c0cbbce132ffe35fd52 bar.zip
|
||||
271a74b75a12f6c3affc88df101f9ef29af79717b1b2f4bdd5964aacf65bcf40 foo.zip
|
||||
```
|
||||
|
@ -1030,6 +1030,7 @@ type Snapshot struct {
|
||||
type Checksum struct {
|
||||
NameTemplate string `yaml:"name_template,omitempty" json:"name_template,omitempty"`
|
||||
Algorithm string `yaml:"algorithm,omitempty" json:"algorithm,omitempty"`
|
||||
Split bool `yaml:"split,omitempty" json:"split,omitempty"`
|
||||
IDs []string `yaml:"ids,omitempty" json:"ids,omitempty"`
|
||||
Disable bool `yaml:"disable,omitempty" json:"disable,omitempty"`
|
||||
ExtraFiles []ExtraFile `yaml:"extra_files,omitempty" json:"extra_files,omitempty"`
|
||||
|
@ -10,7 +10,8 @@ The `checksum` section allows customizations of the filename:
|
||||
checksum:
|
||||
# You can change the name of the checksums file.
|
||||
#
|
||||
# Default: {{ .ProjectName }}_{{ .Version }}_checksums.txt
|
||||
# Default: '{{ .ProjectName }}_{{ .Version }}_checksums.txt'
|
||||
# or, when split is set: '{{ .ArtifactName }}.{{ .Algorithm }}'
|
||||
# Templates: allowed
|
||||
name_template: "{{ .ProjectName }}_checksums.txt"
|
||||
|
||||
@ -20,6 +21,10 @@ checksum:
|
||||
# Default: sha256.
|
||||
algorithm: sha256
|
||||
|
||||
# If true, will create one checksum file for each artifact.
|
||||
# Since: v1.25
|
||||
split: true
|
||||
|
||||
# IDs of artifacts to include in the checksums file.
|
||||
#
|
||||
# If left empty, all published binaries, archives, linux packages and source archives
|
||||
|
@ -133,9 +133,9 @@ In the nFPM name template field, you can use those extra fields:
|
||||
|
||||
In the `release.body` field, you can use these extra fields:
|
||||
|
||||
| Key | Description |
|
||||
| ------------ | ----------------------------------------------------------------------------------- |
|
||||
| `.Checksums` | the current checksum file contents. Only available in the release body. Since v1.19 |
|
||||
| Key | Description |
|
||||
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `.Checksums` | the current checksum file contents, or a map of filename/checksum contents if `checksum.split` is set. Only available in the release body. Since v1.19 |
|
||||
|
||||
## Functions
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user