1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-01-16 03:52:12 +02:00

refactor: improve handling of extra fields in artifacts (#3191)

* refactor: improve handling of extra fields in artifacts

Backporting from pro: with this we can parse the artifacts list from
json into the context and use them again.

Signed-off-by: Carlos A Becker <caarlos0@users.noreply.github.com>

* fix: tests

Signed-off-by: Carlos A Becker <caarlos0@users.noreply.github.com>

* test: fix

Signed-off-by: Carlos A Becker <caarlos0@users.noreply.github.com>
This commit is contained in:
Carlos Alexandro Becker 2022-06-23 23:36:19 -03:00 committed by GitHub
parent 954a0e7459
commit 72329ab722
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 159 additions and 74 deletions

View File

@ -3,6 +3,7 @@ package artifact
// nolint: gosec
import (
"bytes"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
@ -125,10 +126,10 @@ const (
)
// Extras represents the extra fields in an artifact.
type Extras map[string]interface{}
type Extras map[string]any
func (e Extras) MarshalJSON() ([]byte, error) {
m := map[string]interface{}{}
m := map[string]any{}
for k, v := range e {
if k == ExtraRefresh {
// refresh is a func, so we can't serialize it.
@ -157,13 +158,42 @@ func (a Artifact) String() string {
return a.Name
}
// Extra tries to get the extra field with the given name, returning either
// its value, the default value for its type, or an error.
//
// If the extra value cannot be cast into the given type, it'll try to convert
// it to JSON and unmarshal it into the correct type after.
//
// If that fails as well, it'll error.
func Extra[T any](a Artifact, key string) (T, error) {
ex := a.Extra[key]
if ex == nil {
return *(new(T)), nil
}
t, ok := ex.(T)
if ok {
return t, nil
}
bts, err := json.Marshal(ex)
if err != nil {
return t, err
}
decoder := json.NewDecoder(bytes.NewReader(bts))
decoder.DisallowUnknownFields()
err = decoder.Decode(&t)
return t, err
}
// ExtraOr returns the Extra field with the given key or the or value specified
// if it is nil.
func (a Artifact) ExtraOr(key string, or interface{}) interface{} {
func ExtraOr[T any](a Artifact, key string, or T) T {
if a.Extra[key] == nil {
return or
}
return a.Extra[key]
return a.Extra[key].(T)
}
// Checksum calculates the checksum of the artifact.
@ -210,11 +240,7 @@ func (a Artifact) Refresh() error {
if a.Type != Checksum {
return nil
}
fn, ok := a.ExtraOr(ExtraRefresh, noRefresh).(func() error)
if !ok {
return nil
}
if err := fn(); err != nil {
if err := ExtraOr(a, ExtraRefresh, noRefresh)(); err != nil {
return fmt.Errorf("failed to refresh %q: %w", a.Name, err)
}
return nil
@ -222,12 +248,12 @@ func (a Artifact) Refresh() error {
// ID returns the artifact ID if it exists, empty otherwise.
func (a Artifact) ID() string {
return a.ExtraOr(ExtraID, "").(string)
return ExtraOr(a, ExtraID, "")
}
// Format returns the artifact Format if it exists, empty otherwise.
func (a Artifact) Format() string {
return a.ExtraOr(ExtraFormat, "").(string)
return ExtraOr(a, ExtraFormat, "")
}
// Artifacts is a list of artifacts.
@ -318,7 +344,7 @@ type Filter func(a *Artifact) bool
//
// This is useful specially on homebrew et al, where you'll want to use only either the single-arch or the universal binaries.
func OnlyReplacingUnibins(a *Artifact) bool {
return a.ExtraOr(ExtraReplaces, true).(bool)
return ExtraOr(*a, ExtraReplaces, true)
}
// ByGoos is a predefined filter that filters by the given goos.
@ -389,7 +415,7 @@ func ByExt(exts ...string) Filter {
for _, ext := range exts {
ext := ext
filters = append(filters, func(a *Artifact) bool {
return a.ExtraOr(ExtraExt, "") == ext
return ExtraOr(*a, ExtraExt, "") == ext
})
}
return Or(filters...)

View File

@ -8,6 +8,7 @@ import (
"testing"
"github.com/goreleaser/goreleaser/internal/golden"
"github.com/goreleaser/goreleaser/pkg/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/sync/errgroup"
@ -359,14 +360,58 @@ func TestInvalidAlgorithm(t *testing.T) {
require.Empty(t, sum)
}
func TestExtraOr(t *testing.T) {
a := &Artifact{
func TestExtra(t *testing.T) {
a := Artifact{
Extra: map[string]interface{}{
"Foo": "foo",
"docker": config.Docker{
ID: "id",
Use: "docker",
},
"fail-plz": config.Homebrew{
Plist: "aaaa",
},
"unsupported": func() {},
"binaries": []string{"foo", "bar"},
},
}
require.Equal(t, "foo", a.ExtraOr("Foo", "bar"))
require.Equal(t, "bar", a.ExtraOr("Foobar", "bar"))
t.Run("string", func(t *testing.T) {
foo, err := Extra[string](a, "Foo")
require.NoError(t, err)
require.Equal(t, "foo", foo)
require.Equal(t, "foo", ExtraOr(a, "Foo", "bar"))
})
t.Run("missing field", func(t *testing.T) {
bar, err := Extra[string](a, "Foobar")
require.NoError(t, err)
require.Equal(t, "", bar)
require.Equal(t, "bar", ExtraOr(a, "Foobar", "bar"))
})
t.Run("complex", func(t *testing.T) {
docker, err := Extra[config.Docker](a, "docker")
require.NoError(t, err)
require.Equal(t, "id", docker.ID)
})
t.Run("array", func(t *testing.T) {
binaries, err := Extra[[]string](a, "binaries")
require.NoError(t, err)
require.Equal(t, []string{"foo", "bar"}, binaries)
require.Equal(t, []string{"foo", "bar"}, ExtraOr(a, "binaries", []string{}))
})
t.Run("unmarshal error", func(t *testing.T) {
_, err := Extra[config.Docker](a, "fail-plz")
require.EqualError(t, err, "json: unknown field \"Name\"")
})
t.Run("marshal error", func(t *testing.T) {
_, err := Extra[config.Docker](a, "unsupported")
require.EqualError(t, err, "json: unsupported type: func()")
})
}
func TestByIDs(t *testing.T) {
@ -508,15 +553,6 @@ func TestRefresher(t *testing.T) {
},
},
})
artifacts.Add(&Artifact{
Name: "invalid",
Type: Checksum,
Extra: map[string]interface{}{
"Refresh": func() {
t.Fatalf("should not have been called")
},
},
})
artifacts.Add(&Artifact{
Name: "no refresh",
Type: Checksum,

View File

@ -236,7 +236,7 @@ func skip(ctx *context.Context, archive config.Archive, binaries []*artifact.Art
if err != nil {
return err
}
finalName := name + binary.ExtraOr(artifact.ExtraExt, "").(string)
finalName := name + artifact.ExtraOr(*binary, artifact.ExtraExt, "")
log.WithField("binary", binary.Name).
WithField("name", finalName).
Info("skip archiving")

View File

@ -175,8 +175,8 @@ func TestRunPipe(t *testing.T) {
expectBin += ".exe"
}
require.Equal(t, "myid", arch.ID(), "all archives must have the archive ID set")
require.Equal(t, []string{expectBin}, arch.ExtraOr(artifact.ExtraBinaries, []string{}).([]string))
require.Equal(t, "", arch.ExtraOr(artifact.ExtraBinary, "").(string))
require.Equal(t, []string{expectBin}, artifact.ExtraOr(*arch, artifact.ExtraBinaries, []string{}))
require.Equal(t, "", artifact.ExtraOr(*arch, artifact.ExtraBinary, ""))
}
require.Len(t, archives, 7)
// TODO: should verify the artifact fields here too
@ -426,14 +426,14 @@ func TestRunPipeBinary(t *testing.T) {
artifact.ByGoos("darwin"),
artifact.ByGoarch("all"),
)).List()[0]
require.True(t, darwinUniversal.ExtraOr(artifact.ExtraReplaces, false).(bool))
require.True(t, artifact.ExtraOr(*darwinUniversal, artifact.ExtraReplaces, false))
windows := binaries.Filter(artifact.ByGoos("windows")).List()[0]
require.Equal(t, "mybin_0.0.1_darwin_amd64", darwinThin.Name)
require.Equal(t, "mybin", darwinThin.ExtraOr(artifact.ExtraBinary, ""))
require.Equal(t, "mybin", artifact.ExtraOr(*darwinThin, artifact.ExtraBinary, ""))
require.Equal(t, "myunibin_0.0.1_darwin_all", darwinUniversal.Name)
require.Equal(t, "myunibin", darwinUniversal.ExtraOr(artifact.ExtraBinary, ""))
require.Equal(t, "myunibin", artifact.ExtraOr(*darwinUniversal, artifact.ExtraBinary, ""))
require.Equal(t, "mybin_0.0.1_windows_amd64.exe", windows.Name)
require.Equal(t, "mybin.exe", windows.ExtraOr(artifact.ExtraBinary, ""))
require.Equal(t, "mybin.exe", artifact.ExtraOr(*windows, artifact.ExtraBinary, ""))
}
func TestRunPipeDistRemoved(t *testing.T) {
@ -659,7 +659,7 @@ func TestRunPipeWrap(t *testing.T) {
archives := ctx.Artifacts.Filter(artifact.ByType(artifact.UploadableArchive)).List()
require.Len(t, archives, 1)
require.Equal(t, "foo_macOS", archives[0].ExtraOr(artifact.ExtraWrappedIn, ""))
require.Equal(t, "foo_macOS", artifact.ExtraOr(*archives[0], artifact.ExtraWrappedIn, ""))
// Check archive contents
f, err = os.Open(filepath.Join(dist, "foo.tar.gz"))
@ -817,13 +817,13 @@ func TestBinaryOverride(t *testing.T) {
darwin := archives.Filter(artifact.ByGoos("darwin")).List()[0]
require.Equal(t, "foobar_0.0.1_darwin_amd64."+format, darwin.Name)
require.Equal(t, format, darwin.Format())
require.Empty(t, darwin.ExtraOr(artifact.ExtraWrappedIn, ""))
require.Empty(t, artifact.ExtraOr(*darwin, artifact.ExtraWrappedIn, ""))
archives = ctx.Artifacts.Filter(artifact.ByType(artifact.UploadableBinary))
windows := archives.Filter(artifact.ByGoos("windows")).List()[0]
require.Equal(t, "foobar_0.0.1_windows_amd64.exe", windows.Name)
require.Empty(t, windows.ExtraOr(artifact.ExtraWrappedIn, ""))
require.Equal(t, "mybin.exe", windows.ExtraOr(artifact.ExtraBinary, ""))
require.Empty(t, artifact.ExtraOr(*windows, artifact.ExtraWrappedIn, ""))
require.Equal(t, "mybin.exe", artifact.ExtraOr(*windows, artifact.ExtraBinary, ""))
})
}
}

View File

@ -137,10 +137,10 @@ func doRun(ctx *context.Context, aur config.AUR, cl client.Client) error {
switch art.Type {
case artifact.UploadableBinary:
name := art.Name
bin := art.ExtraOr(artifact.ExtraBinary, art.Name).(string)
bin := artifact.ExtraOr(*art, artifact.ExtraBinary, art.Name)
pkg = fmt.Sprintf(`install -Dm755 "./%s "${pkgdir}/usr/bin/%s"`, name, bin)
case artifact.UploadableArchive:
for _, bin := range art.ExtraOr(artifact.ExtraBinaries, []string{}).([]string) {
for _, bin := range artifact.ExtraOr(*art, artifact.ExtraBinaries, []string{}) {
pkg = fmt.Sprintf(`install -Dm755 "./%s" "${pkgdir}/usr/bin/%[1]s"`, bin)
break
}
@ -318,7 +318,7 @@ func dataFor(ctx *context.Context, cfg config.AUR, cl client.Client, artifacts [
DownloadURL: url,
SHA256: sum,
Arch: toPkgBuildArch(art.Goarch + art.Goarm),
Format: art.ExtraOr(artifact.ExtraFormat, "").(string),
Format: artifact.ExtraOr(*art, artifact.ExtraFormat, ""),
}
result.ReleasePackages = append(result.ReleasePackages, releasePackage)
result.Arches = append(result.Arches, releasePackage.Arch)
@ -353,7 +353,10 @@ func (Pipe) Publish(ctx *context.Context) error {
}
func doPublish(ctx *context.Context, pkgs []*artifact.Artifact) error {
cfg := pkgs[0].Extra[aurExtra].(config.AUR)
cfg, err := artifact.Extra[config.AUR](*pkgs[0], aurExtra)
if err != nil {
return err
}
if strings.TrimSpace(cfg.SkipUpload) == "true" {
return pipe.Skip("aur.skip_upload is set")

View File

@ -108,8 +108,10 @@ func publishAll(ctx *context.Context, cli client.Client) error {
}
func doPublish(ctx *context.Context, formula *artifact.Artifact, cl client.Client) error {
brew := formula.Extra[brewConfigExtra].(config.Homebrew)
var err error
brew, err := artifact.Extra[config.Homebrew](*formula, brewConfigExtra)
if err != nil {
return err
}
cl, err = client.NewIfToken(ctx, cl, brew.Tap.Token)
if err != nil {
return err
@ -295,10 +297,10 @@ func installs(cfg config.Homebrew, art *artifact.Artifact) []string {
switch art.Type {
case artifact.UploadableBinary:
name := art.Name
bin := art.ExtraOr(artifact.ExtraBinary, art.Name).(string)
bin := artifact.ExtraOr(*art, artifact.ExtraBinary, art.Name)
install[fmt.Sprintf("bin.install %q => %q", name, bin)] = true
case artifact.UploadableArchive:
for _, bin := range art.ExtraOr(artifact.ExtraBinaries, []string{}).([]string) {
for _, bin := range artifact.ExtraOr(*art, artifact.ExtraBinaries, []string{}) {
install[fmt.Sprintf("bin.install %q", bin)] = true
}
}

View File

@ -242,7 +242,11 @@ func processBuildFlagTemplates(ctx *context.Context, docker config.Docker) ([]st
func dockerPush(ctx *context.Context, image *artifact.Artifact) error {
log.WithField("image", image.Name).Info("pushing")
docker := image.Extra[dockerConfigExtra].(config.Docker)
docker, err := artifact.Extra[config.Docker](*image, dockerConfigExtra)
if err != nil {
return err
}
if strings.TrimSpace(docker.SkipPush) == "true" {
return pipe.Skip("docker.skip_push is set: " + image.Name)
}

View File

@ -229,7 +229,7 @@ func dataFor(ctx *context.Context, cfg config.GoFish, cl client.Client, artifact
}
switch art.Type {
case artifact.UploadableArchive:
for _, bin := range art.ExtraOr(artifact.ExtraBinaries, []string{}).([]string) {
for _, bin := range artifact.ExtraOr(*art, artifact.ExtraBinaries, []string{}) {
releasePackage.Binaries = append(releasePackage.Binaries, binary{
Name: bin,
Target: bin,
@ -238,7 +238,7 @@ func dataFor(ctx *context.Context, cfg config.GoFish, cl client.Client, artifact
case artifact.UploadableBinary:
releasePackage.Binaries = append(releasePackage.Binaries, binary{
Name: art.Name,
Target: art.ExtraOr(artifact.ExtraBinary, art.Name).(string),
Target: artifact.ExtraOr(*art, artifact.ExtraBinary, art.Name),
})
}
result.ReleasePackages = append(result.ReleasePackages, releasePackage)
@ -273,8 +273,11 @@ func publishAll(ctx *context.Context, cli client.Client) error {
}
func doPublish(ctx *context.Context, food *artifact.Artifact, cl client.Client) error {
rig := food.Extra[goFishConfigExtra].(config.GoFish)
var err error
rig, err := artifact.Extra[config.GoFish](*food, goFishConfigExtra)
if err != nil {
return err
}
cl, err = client.NewIfToken(ctx, cl, rig.Rig.Token)
if err != nil {
return err

View File

@ -233,7 +233,7 @@ func manifestFor(ctx *context.Context, cfg config.Krew, cl client.Client, artifa
}
for _, arch := range goarch {
bins := art.ExtraOr(artifact.ExtraBinaries, []string{}).([]string)
bins := artifact.ExtraOr(*art, artifact.ExtraBinaries, []string{})
if len(bins) != 1 {
return result, fmt.Errorf("krew: only one binary per archive allowed, got %d on %q", len(bins), art.Name)
}
@ -283,8 +283,11 @@ func publishAll(ctx *context.Context, cli client.Client) error {
}
func doPublish(ctx *context.Context, manifest *artifact.Artifact, cl client.Client) error {
cfg := manifest.Extra[krewConfigExtra].(config.Krew)
var err error
cfg, err := artifact.Extra[config.Krew](*manifest, krewConfigExtra)
if err != nil {
return err
}
cl, err = client.NewIfToken(ctx, cl, cfg.Index.Token)
if err != nil {
return err

View File

@ -242,7 +242,7 @@ func TestRunPipe(t *testing.T) {
"./testdata/folder",
"./testdata/testfile-" + pkg.Goarch + pkg.Goamd64 + pkg.Goarm + pkg.Gomips + ".txt",
binPath,
}, sources(pkg.ExtraOr(extraFiles, files.Contents{}).(files.Contents)))
}, sources(artifact.ExtraOr(*pkg, extraFiles, files.Contents{})))
require.ElementsMatch(t, []string{
"/var/log/foobar",
"/usr/share/testfile.txt",
@ -253,7 +253,7 @@ func TestRunPipe(t *testing.T) {
"/etc/nope3_mybin.conf",
"/etc/folder",
"/usr/bin/subdir/mybin",
}, destinations(pkg.ExtraOr(extraFiles, files.Contents{}).(files.Contents)))
}, destinations(artifact.ExtraOr(*pkg, extraFiles, files.Contents{})))
}
require.Len(t, ctx.Config.NFPMs[0].Contents, 8, "should not modify the config file list")
}
@ -389,8 +389,8 @@ func TestRunPipeConventionalNameTemplate(t *testing.T) {
"foo_1.0.0_x86_64v4.apk",
}, pkg.Name, "package name is not expected")
require.Equal(t, "someid", pkg.ID())
require.ElementsMatch(t, []string{binPath}, sources(pkg.ExtraOr(extraFiles, files.Contents{}).(files.Contents)))
require.ElementsMatch(t, []string{"/usr/bin/subdir/mybin"}, destinations(pkg.ExtraOr(extraFiles, files.Contents{}).(files.Contents)))
require.ElementsMatch(t, []string{binPath}, sources(artifact.ExtraOr(*pkg, extraFiles, files.Contents{})))
require.ElementsMatch(t, []string{"/usr/bin/subdir/mybin"}, destinations(artifact.ExtraOr(*pkg, extraFiles, files.Contents{})))
}
}
@ -1207,7 +1207,7 @@ func TestMeta(t *testing.T) {
"/usr/share/testfile.txt",
"/etc/nope.conf",
"/etc/nope-rpm.conf",
}, destinations(pkg.ExtraOr(extraFiles, files.Contents{}).(files.Contents)))
}, destinations(artifact.ExtraOr(*pkg, extraFiles, files.Contents{})))
}
require.Len(t, ctx.Config.NFPMs[0].Contents, 4, "should not modify the config file list")
@ -1355,7 +1355,7 @@ func TestBinDirTemplating(t *testing.T) {
// the final binary should contain the evaluated bindir (after template eval)
require.ElementsMatch(t, []string{
"/usr/lib/pro/nagios/plugins/subdir/mybin",
}, destinations(pkg.ExtraOr(extraFiles, files.Contents{}).(files.Contents)))
}, destinations(artifact.ExtraOr(*pkg, extraFiles, files.Contents{})))
}
}

View File

@ -120,9 +120,12 @@ func doPublish(ctx *context.Context, cl client.Client) error {
}
manifest := manifests[0]
scoop := manifest.Extra[scoopConfigExtra].(config.Scoop)
var err error
scoop, err := artifact.Extra[config.Scoop](*manifest, scoopConfigExtra)
if err != nil {
return err
}
cl, err = client.NewIfToken(ctx, cl, scoop.Bucket.Token)
if err != nil {
return err
@ -251,9 +254,14 @@ func dataFor(ctx *context.Context, cl client.Client, artifacts []*artifact.Artif
"sum": sum,
}).Debug("scoop url templating")
binaries, err := binaries(*artifact)
if err != nil {
return manifest, err
}
manifest.Architecture[arch] = Resource{
URL: url,
Bin: binaries(artifact),
Bin: binaries,
Hash: sum,
}
}
@ -261,12 +269,16 @@ func dataFor(ctx *context.Context, cl client.Client, artifacts []*artifact.Artif
return manifest, nil
}
func binaries(a *artifact.Artifact) []string {
func binaries(a artifact.Artifact) ([]string, error) {
// nolint: prealloc
var bins []string
wrap := a.ExtraOr(artifact.ExtraWrappedIn, "").(string)
for _, b := range a.ExtraOr(artifact.ExtraBuilds, []*artifact.Artifact{}).([]*artifact.Artifact) {
wrap := artifact.ExtraOr(a, artifact.ExtraWrappedIn, "")
builds, err := artifact.Extra[[]artifact.Artifact](a, artifact.ExtraBuilds)
if err != nil {
return nil, err
}
for _, b := range builds {
bins = append(bins, filepath.Join(wrap, b.Name))
}
return bins
return bins, nil
}

View File

@ -418,7 +418,7 @@ const (
func push(ctx *context.Context, snap *artifact.Artifact) error {
log := log.WithField("snap", snap.Name)
releases := snap.Extra[releasesExtra].([]string)
releases := artifact.ExtraOr(*snap, releasesExtra, []string{})
/* #nosec */
cmd := exec.CommandContext(ctx, "snapcraft", "upload", "--release="+strings.Join(releases, ","), snap.Path)
log.WithField("args", cmd.Args).Info("pushing snap")

View File

@ -255,7 +255,7 @@ func TestRun(t *testing.T) {
unis := ctx1.Artifacts.Filter(artifact.ByType(artifact.UniversalBinary)).List()
require.Len(t, unis, 1)
checkUniversalBinary(t, unis[0])
require.True(t, unis[0].Extra[artifact.ExtraReplaces].(bool))
require.True(t, artifact.ExtraOr(*unis[0], artifact.ExtraReplaces, false))
})
t.Run("keeping", func(t *testing.T) {
@ -264,7 +264,7 @@ func TestRun(t *testing.T) {
unis := ctx2.Artifacts.Filter(artifact.ByType(artifact.UniversalBinary)).List()
require.Len(t, unis, 1)
checkUniversalBinary(t, unis[0])
require.False(t, unis[0].Extra[artifact.ExtraReplaces].(bool))
require.False(t, artifact.ExtraOr(*unis[0], artifact.ExtraReplaces, true))
})
t.Run("bad template", func(t *testing.T) {

View File

@ -139,16 +139,12 @@ func (t *Template) WithExtraFields(f Fields) *Template {
// WithArtifact populates Fields from the artifact and replacements.
func (t *Template) WithArtifact(a *artifact.Artifact, replacements map[string]string) *Template {
bin := a.Extra[binary]
if bin == nil {
bin = t.fields[projectName]
}
t.fields[osKey] = replace(replacements, a.Goos)
t.fields[arch] = replace(replacements, a.Goarch)
t.fields[arm] = replace(replacements, a.Goarm)
t.fields[mips] = replace(replacements, a.Gomips)
t.fields[amd64] = replace(replacements, a.Goamd64)
t.fields[binary] = bin.(string)
t.fields[binary] = artifact.ExtraOr(*a, binary, t.fields[projectName].(string))
t.fields[artifactName] = a.Name
t.fields[artifactPath] = a.Path
return t