diff --git a/Makefile b/Makefile index e374c31ca..f45a06933 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ TEST_OPTIONS?= # Install all the build and lint dependencies setup: + go get -u golang.org/x/tools/cmd/stringer go get -u github.com/alecthomas/gometalinter go get -u github.com/golang/dep/cmd/dep go get -u github.com/pierrre/gotestcover @@ -36,11 +37,12 @@ lint: .PHONY: lint # Run all the tests and code checks -ci: test lint +ci: build test lint .PHONY: ci # Build a beta version of goreleaser build: + go generate ./... go build .PHONY: build diff --git a/checksum/checksum.go b/checksum/checksum.go index 099a4d91d..6a4b1b850 100644 --- a/checksum/checksum.go +++ b/checksum/checksum.go @@ -7,8 +7,6 @@ import ( "hash" "io" "os" - - "github.com/apex/log" ) // SHA256 sum of the given file @@ -21,11 +19,7 @@ func calculate(hash hash.Hash, path string) (string, error) { if err != nil { return "", err } - defer func() { - if err := file.Close(); err != nil { - log.WithError(err).Errorf("failed to close %s", path) - } - }() + defer file.Close() // nolint: errcheck return doCalculate(hash, file) } diff --git a/context/context.go b/context/context.go index 594def0e4..b42177211 100644 --- a/context/context.go +++ b/context/context.go @@ -9,12 +9,10 @@ package context import ( ctx "context" "os" - "path/filepath" "strings" - "sync" - "github.com/apex/log" "github.com/goreleaser/goreleaser/config" + "github.com/goreleaser/goreleaser/internal/artifact" ) // GitInfo includes tags and diffs used in some point @@ -23,11 +21,6 @@ type GitInfo struct { Commit string } -// Binary with pretty name and path -type Binary struct { - Name, Path string -} - // Context carries along some data through the pipes type Context struct { ctx.Context @@ -35,10 +28,7 @@ type Context struct { Env map[string]string Token string Git GitInfo - Binaries map[string]map[string][]Binary - Artifacts []string - Checksums []string - Dockers []string + Artifacts artifact.Artifacts ReleaseNotes string Version string Validate bool @@ -49,63 +39,6 @@ type Context struct { Parallelism int } -var ( - artifactsLock sync.Mutex - checksumsLock sync.Mutex - dockersLock sync.Mutex - binariesLock sync.Mutex -) - -// AddArtifact adds a file to upload list -func (ctx *Context) AddArtifact(file string) { - artifactsLock.Lock() - defer artifactsLock.Unlock() - file = strings.TrimPrefix(file, ctx.Config.Dist+string(filepath.Separator)) - ctx.Artifacts = append(ctx.Artifacts, file) - log.WithField("artifact", file).Info("new release artifact") -} - -// AddChecksum adds a checksum file. -func (ctx *Context) AddChecksum(file string) { - checksumsLock.Lock() - defer checksumsLock.Unlock() - file = strings.TrimPrefix(file, ctx.Config.Dist+string(filepath.Separator)) - ctx.Checksums = append(ctx.Checksums, file) - log.WithField("checksum", file).Info("new checksum file") -} - -// AddDocker adds a docker image to the docker images list -func (ctx *Context) AddDocker(image string) { - dockersLock.Lock() - defer dockersLock.Unlock() - ctx.Dockers = append(ctx.Dockers, image) - log.WithField("image", image).Info("new docker image") -} - -// AddBinary adds a built binary to the current context -func (ctx *Context) AddBinary(platform, folder, name, path string) { - binariesLock.Lock() - defer binariesLock.Unlock() - if ctx.Binaries == nil { - ctx.Binaries = map[string]map[string][]Binary{} - } - if ctx.Binaries[platform] == nil { - ctx.Binaries[platform] = map[string][]Binary{} - } - ctx.Binaries[platform][folder] = append( - ctx.Binaries[platform][folder], - Binary{ - Name: name, - Path: path, - }, - ) - log.WithField("platform", platform). - WithField("folder", folder). - WithField("name", name). - WithField("path", path). - Debug("new binary") -} - // New context func New(config config.Project) *Context { return &Context{ @@ -113,6 +46,7 @@ func New(config config.Project) *Context { Config: config, Env: splitEnv(os.Environ()), Parallelism: 4, + Artifacts: artifact.New(), } } diff --git a/context/context_test.go b/context/context_test.go index 40e167c12..faab26e05 100644 --- a/context/context_test.go +++ b/context/context_test.go @@ -4,81 +4,11 @@ import ( "testing" "github.com/goreleaser/goreleaser/config" - "github.com/stretchr/testify/assert" - "golang.org/x/sync/errgroup" + "github.com/tj/assert" ) -func TestMultipleAdds(t *testing.T) { - var artifacts = []string{ - "dist/a", - "dist/b", - "dist/c", - "dist/d", - } - var checksums = []string{ - "dist/a.sha256", - } - var dockerfiles = []string{ - "a/b:1.0.0", - "c/d:2.0.0", - "e/f:3.0.0", - } - var ctx = New(config.Project{ - Dist: "dist", - }) - var g errgroup.Group - for _, f := range artifacts { - f := f - g.Go(func() error { - ctx.AddArtifact(f) - return nil - }) - } - assert.NoError(t, g.Wait()) - for _, c := range checksums { - c := c - g.Go(func() error { - ctx.AddChecksum(c) - return nil - }) - } - assert.NoError(t, g.Wait()) - for _, d := range dockerfiles { - d := d - g.Go(func() error { - ctx.AddDocker(d) - return nil - }) - } - assert.NoError(t, g.Wait()) - assert.Len(t, ctx.Artifacts, len(artifacts)) - assert.Contains(t, ctx.Artifacts, "a", "b", "c", "d") - assert.Len(t, ctx.Checksums, len(checksums)) - assert.Contains(t, ctx.Checksums, "a.sha256") - assert.Len(t, ctx.Dockers, len(dockerfiles)) - assert.Contains(t, ctx.Dockers, "a/b:1.0.0", "c/d:2.0.0", "e/f:3.0.0") -} - -func TestMultipleBinaryAdds(t *testing.T) { - var list = map[string]string{ - "a": "folder/a", - "b": "folder/b", - "c": "folder/c", - "d": "folder/d", - } - var ctx = New(config.Project{ - Dist: "dist", - }) - var g errgroup.Group - for k, f := range list { - f := f - k := k - g.Go(func() error { - ctx.AddBinary("linuxamd64", k, k, f) - return nil - }) - } - assert.NoError(t, g.Wait()) - assert.Len(t, ctx.Binaries["linuxamd64"], len(list)) - assert.Len(t, ctx.Binaries, 1) +func TestNew(t *testing.T) { + var ctx = New(config.Project{}) + assert.NotEmpty(t, ctx.Env) + assert.Equal(t, 4, ctx.Parallelism) } diff --git a/goreleaserlib/goreleaser.go b/goreleaserlib/goreleaser.go index 0eb52223e..b856eb39a 100644 --- a/goreleaserlib/goreleaser.go +++ b/goreleaserlib/goreleaser.go @@ -8,6 +8,8 @@ import ( "github.com/apex/log" "github.com/apex/log/handlers/cli" + yaml "gopkg.in/yaml.v2" + "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" "github.com/goreleaser/goreleaser/pipeline" @@ -27,7 +29,6 @@ import ( "github.com/goreleaser/goreleaser/pipeline/release" "github.com/goreleaser/goreleaser/pipeline/sign" "github.com/goreleaser/goreleaser/pipeline/snapcraft" - yaml "gopkg.in/yaml.v2" ) var ( diff --git a/internal/archiveformat/format.go b/internal/archiveformat/format.go index ce87f215a..564ea5094 100644 --- a/internal/archiveformat/format.go +++ b/internal/archiveformat/format.go @@ -1,5 +1,6 @@ // Package archiveformat provides functions to get the format of given package // based on the config +// TODO: this can be moved inside the archive pipe package package archiveformat import ( diff --git a/internal/artifact/.gitignore b/internal/artifact/.gitignore new file mode 100644 index 000000000..8c26bb47a --- /dev/null +++ b/internal/artifact/.gitignore @@ -0,0 +1 @@ +type_string.go diff --git a/internal/artifact/artifact.go b/internal/artifact/artifact.go new file mode 100644 index 000000000..1458f179a --- /dev/null +++ b/internal/artifact/artifact.go @@ -0,0 +1,151 @@ +// Package artifact provides the core artifact storage for goreleaser +package artifact + +import ( + "sync" + + "github.com/apex/log" +) + +// Type defines the type of an artifact +//go:generate stringer -type=Type +type Type int + +const ( + // UploadableArchive a tar.gz/zip archive to be uploaded + UploadableArchive Type = iota + // UploadableBinary is a binary file to be uploaded + UploadableBinary + // Binary is a binary (output of a gobuild) + Binary + // LinuxPackage is a linux package generated by fpm or snapcraft + LinuxPackage + // DockerImage is a docker image + DockerImage + // Checksum is a checksums file + Checksum + // Signature is a signature file + Signature +) + +// Artifact represents an artifact and its relevant info +type Artifact struct { + Name string + Path string + Goos string + Goarch string + Goarm string + Type Type + Extra map[string]string +} + +// Artifacts is a list of artifacts +type Artifacts struct { + items []Artifact + lock *sync.Mutex +} + +// New return a new list of artifacts +func New() Artifacts { + return Artifacts{ + items: []Artifact{}, + lock: &sync.Mutex{}, + } +} + +// List return the actual list of artifacts +func (artifacts Artifacts) List() []Artifact { + return artifacts.items +} + +// GroupByPlatform groups the artifacts by their platform +func (artifacts Artifacts) GroupByPlatform() map[string][]Artifact { + var result = map[string][]Artifact{} + for _, a := range artifacts.items { + plat := a.Goos + a.Goarch + a.Goarm + result[plat] = append(result[plat], a) + } + return result +} + +// Add safely adds a new artifact to an artifact list +func (artifacts *Artifacts) Add(a Artifact) { + artifacts.lock.Lock() + defer artifacts.lock.Unlock() + log.WithFields(log.Fields{ + "name": a.Name, + "path": a.Path, + "type": a.Type, + }).Info("added new artifact") + artifacts.items = append(artifacts.items, a) +} + +// Filter defines an artifact filter which can be used within the Filter +// function +type Filter func(a Artifact) bool + +// ByGoos is a predefined filter that filters by the given goos +func ByGoos(s string) Filter { + return func(a Artifact) bool { + return a.Goos == s + } +} + +// ByGoarch is a predefined filter that filters by the given goarch +func ByGoarch(s string) Filter { + return func(a Artifact) bool { + return a.Goarch == s + } +} + +// ByGoarm is a predefined filter that filters by the given goarm +func ByGoarm(s string) Filter { + return func(a Artifact) bool { + return a.Goarm == s + } +} + +// ByType is a predefined filter that filters by the given type +func ByType(t Type) Filter { + return func(a Artifact) bool { + return a.Type == t + } +} + +// Or performs an OR between all given filters +func Or(filters ...Filter) Filter { + return func(a Artifact) bool { + for _, f := range filters { + if f(a) { + return true + } + } + return false + } +} + +// And performs an AND between all given filters +func And(filters ...Filter) Filter { + return func(a Artifact) bool { + for _, f := range filters { + if !f(a) { + return false + } + } + return true + } +} + +// Filter filters the artifact list, returning a new instance. +// There are some pre-defined filters but anything of the Type Filter +// is accepted. +// You can compose filters by using the And and Or filters. +func (artifacts *Artifacts) Filter(filter Filter) Artifacts { + var result = New() + for _, a := range artifacts.items { + if filter(a) { + result.items = append(result.items, a) + } + } + return result +} diff --git a/internal/artifact/artifact_test.go b/internal/artifact/artifact_test.go new file mode 100644 index 000000000..d1e24d2a6 --- /dev/null +++ b/internal/artifact/artifact_test.go @@ -0,0 +1,137 @@ +package artifact + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "golang.org/x/sync/errgroup" +) + +// ensure Type implements the stringer interface... +var _ fmt.Stringer = Type(0) + +func TestAdd(t *testing.T) { + var g errgroup.Group + var artifacts = New() + for _, a := range []Artifact{ + { + Name: "foo", + Type: UploadableArchive, + }, + { + Name: "bar", + Type: Binary, + }, + { + Name: "foobar", + Type: DockerImage, + }, + { + Name: "check", + Type: Checksum, + }, + } { + a := a + g.Go(func() error { + artifacts.Add(a) + return nil + }) + } + assert.NoError(t, g.Wait()) + assert.Len(t, artifacts.List(), 4) +} + +func TestFilter(t *testing.T) { + var data = []Artifact{ + { + Name: "foo", + Goos: "linux", + Goarch: "arm", + }, + { + Name: "bar", + Goarch: "amd64", + }, + { + Name: "foobar", + Goarm: "6", + }, + { + Name: "check", + Type: Checksum, + }, + { + Name: "checkzumm", + Type: Checksum, + }, + } + var artifacts = New() + for _, a := range data { + artifacts.Add(a) + } + + assert.Len(t, artifacts.Filter(ByGoos("linux")).items, 1) + assert.Len(t, artifacts.Filter(ByGoos("darwin")).items, 0) + + assert.Len(t, artifacts.Filter(ByGoarch("amd64")).items, 1) + assert.Len(t, artifacts.Filter(ByGoarch("386")).items, 0) + + assert.Len(t, artifacts.Filter(ByGoarm("6")).items, 1) + assert.Len(t, artifacts.Filter(ByGoarm("7")).items, 0) + + assert.Len(t, artifacts.Filter(ByType(Checksum)).items, 2) + assert.Len(t, artifacts.Filter(ByType(Binary)).items, 0) + + assert.Len(t, artifacts.Filter( + And( + ByType(Checksum), + func(a Artifact) bool { + return a.Name == "checkzumm" + }, + ), + ).List(), 1) + + assert.Len(t, artifacts.Filter( + Or( + ByType(Checksum), + And( + ByGoos("linux"), + ByGoarm("arm"), + ), + ), + ).List(), 2) +} + +func TestGroupByPlatform(t *testing.T) { + var data = []Artifact{ + { + Name: "foo", + Goos: "linux", + Goarch: "amd64", + }, + { + Name: "bar", + Goos: "linux", + Goarch: "amd64", + }, + { + Name: "foobar", + Goos: "linux", + Goarch: "arm", + Goarm: "6", + }, + { + Name: "check", + Type: Checksum, + }, + } + var artifacts = New() + for _, a := range data { + artifacts.Add(a) + } + + var groups = artifacts.GroupByPlatform() + assert.Len(t, groups["linuxamd64"], 2) + assert.Len(t, groups["linuxarm6"], 1) +} diff --git a/internal/buildtarget/buildtarget.go b/internal/buildtarget/buildtarget.go index 83e1892b8..ad8fc2e75 100644 --- a/internal/buildtarget/buildtarget.go +++ b/internal/buildtarget/buildtarget.go @@ -28,6 +28,7 @@ func (t Target) Env() []string { } func (t Target) String() string { + // TODO: maybe replace this as suggested to OS_ArchArm? return fmt.Sprintf("%v%v%v", t.OS, t.Arch, t.Arm) } diff --git a/internal/buildtarget/doc.go b/internal/buildtarget/doc.go new file mode 100644 index 000000000..828f8ecf5 --- /dev/null +++ b/internal/buildtarget/doc.go @@ -0,0 +1,3 @@ +// Package buildtarget provides the utilities targeting build matrixes. +// TODO: probably this package should be removed and used only inside the build package +package buildtarget diff --git a/internal/ext/ext.go b/internal/ext/ext.go index 0d9ee6611..583d26743 100644 --- a/internal/ext/ext.go +++ b/internal/ext/ext.go @@ -3,9 +3,9 @@ package ext import "github.com/goreleaser/goreleaser/internal/buildtarget" // For returns the binary extension for the given platform -func For(target buildtarget.Target) (ext string) { +func For(target buildtarget.Target) string { if target.OS == "windows" { - ext = ".exe" + return ".exe" } - return + return "" } diff --git a/pipeline/build/name.go b/internal/nametemplate/name.go similarity index 51% rename from pipeline/build/name.go rename to internal/nametemplate/name.go index 5cd155103..24d8f4e3f 100644 --- a/pipeline/build/name.go +++ b/internal/nametemplate/name.go @@ -1,16 +1,18 @@ -package build +package nametemplate import ( "bytes" "text/template" "github.com/goreleaser/goreleaser/context" - "github.com/goreleaser/goreleaser/internal/buildtarget" + "github.com/goreleaser/goreleaser/internal/artifact" ) -func nameFor(ctx *context.Context, target buildtarget.Target, name string) (string, error) { +// Apply applies the name template to the given artifact and name +// TODO: this should be refactored alongside with other name template related todos +func Apply(ctx *context.Context, a artifact.Artifact, name string) (string, error) { var out bytes.Buffer - t, err := template.New(name).Parse(ctx.Config.Archive.NameTemplate) + t, err := template.New("archive_name").Parse(ctx.Config.Archive.NameTemplate) if err != nil { return "", err } @@ -18,12 +20,11 @@ func nameFor(ctx *context.Context, target buildtarget.Target, name string) (stri Os, Arch, Arm, Version, Tag, Binary, ProjectName string Env map[string]string }{ - Os: replace(ctx.Config.Archive.Replacements, target.OS), - Arch: replace(ctx.Config.Archive.Replacements, target.Arch), - Arm: replace(ctx.Config.Archive.Replacements, target.Arm), + Os: replace(ctx.Config.Archive.Replacements, a.Goos), + Arch: replace(ctx.Config.Archive.Replacements, a.Goarch), + Arm: replace(ctx.Config.Archive.Replacements, a.Goarm), Version: ctx.Version, Tag: ctx.Git.CurrentTag, - Binary: name, // TODO: deprecated: remove this sometime ProjectName: name, Env: ctx.Env, } diff --git a/pipeline/archive/archive.go b/pipeline/archive/archive.go index 25a313683..3724f1cc6 100644 --- a/pipeline/archive/archive.go +++ b/pipeline/archive/archive.go @@ -7,13 +7,17 @@ import ( "fmt" "os" "path/filepath" + "strings" "github.com/apex/log" + "github.com/mattn/go-zglob" + "golang.org/x/sync/errgroup" + "github.com/goreleaser/archive" "github.com/goreleaser/goreleaser/context" "github.com/goreleaser/goreleaser/internal/archiveformat" - "github.com/mattn/go-zglob" - "golang.org/x/sync/errgroup" + "github.com/goreleaser/goreleaser/internal/artifact" + "github.com/goreleaser/goreleaser/internal/nametemplate" ) // Pipe for archive @@ -26,14 +30,14 @@ func (Pipe) String() string { // Run the pipe func (Pipe) Run(ctx *context.Context) error { var g errgroup.Group - for platform, binaries := range ctx.Binaries { - platform := platform - binaries := binaries + var filtered = ctx.Artifacts.Filter(artifact.ByType(artifact.Binary)) + for _, artifacts := range filtered.GroupByPlatform() { + artifacts := artifacts g.Go(func() error { if ctx.Config.Archive.Format == "binary" { - return skip(ctx, platform, binaries) + return skip(ctx, artifacts) } - return create(ctx, platform, binaries) + return create(ctx, artifacts) }) } return g.Wait() @@ -62,52 +66,58 @@ func (Pipe) Default(ctx *context.Context) error { return nil } -func create(ctx *context.Context, platform string, groups map[string][]context.Binary) error { - for folder, binaries := range groups { - var format = archiveformat.For(ctx, platform) - archivePath := filepath.Join(ctx.Config.Dist, folder+"."+format) - archiveFile, err := os.Create(archivePath) - if err != nil { - return fmt.Errorf("failed to create directory %s: %s", archivePath, err.Error()) - } - defer func() { - if e := archiveFile.Close(); e != nil { - log.WithField("archive", archivePath).Errorf("failed to close file: %v", e) - } - }() - log.WithField("archive", archivePath).Info("creating") - var a = archive.New(archiveFile) - defer func() { - if e := a.Close(); e != nil { - log.WithField("archive", archivePath).Errorf("failed to close archive: %v", e) - } - }() - - files, err := findFiles(ctx) - if err != nil { - return fmt.Errorf("failed to find files to archive: %s", err.Error()) - } - for _, f := range files { - if err = a.Add(wrap(ctx, f, folder), f); err != nil { - return fmt.Errorf("failed to add %s to the archive: %s", f, err.Error()) - } - } - for _, binary := range binaries { - if err := a.Add(wrap(ctx, binary.Name, folder), binary.Path); err != nil { - return fmt.Errorf("failed to add %s -> %s to the archive: %s", binary.Path, binary.Name, err.Error()) - } - } - ctx.AddArtifact(archivePath) +func create(ctx *context.Context, artifacts []artifact.Artifact) error { + var format = archiveformat.For(ctx, artifacts[0].Goos) + folder, err := nametemplate.Apply(ctx, artifacts[0], ctx.Config.ProjectName) + if err != nil { + return err } + archivePath := filepath.Join(ctx.Config.Dist, folder+"."+format) + archiveFile, err := os.Create(archivePath) + if err != nil { + return fmt.Errorf("failed to create directory %s: %s", archivePath, err.Error()) + } + defer archiveFile.Close() // nolint: errcheck + log.WithField("archive", archivePath).Info("creating") + var a = archive.New(archiveFile) + defer a.Close() // nolint: errcheck + + files, err := findFiles(ctx) + if err != nil { + return fmt.Errorf("failed to find files to archive: %s", err.Error()) + } + for _, f := range files { + if err = a.Add(wrap(ctx, f, folder), f); err != nil { + return fmt.Errorf("failed to add %s to the archive: %s", f, err.Error()) + } + } + for _, binary := range artifacts { + if err := a.Add(wrap(ctx, binary.Name, folder), binary.Path); err != nil { + return fmt.Errorf("failed to add %s -> %s to the archive: %s", binary.Path, binary.Name, err.Error()) + } + } + ctx.Artifacts.Add(artifact.Artifact{ + Type: artifact.UploadableArchive, + Name: folder + "." + format, + Path: archivePath, + Goos: artifacts[0].Goos, + Goarch: artifacts[0].Goarch, + Goarm: artifacts[0].Goarm, + }) return nil } -func skip(ctx *context.Context, platform string, groups map[string][]context.Binary) error { - for _, binaries := range groups { - for _, binary := range binaries { - log.WithField("binary", binary.Name).Info("skip archiving") - ctx.AddArtifact(binary.Path) +func skip(ctx *context.Context, artifacts []artifact.Artifact) error { + for _, a := range artifacts { + log.WithField("binary", a.Name).Info("skip archiving") + // TODO: this should not happen here, maybe add another extra field for the extension and/or name without extension? + name, err := nametemplate.Apply(ctx, a, strings.TrimSuffix(a.Name, ".exe")) + if err != nil { + return err } + a.Type = artifact.UploadableBinary + a.Name = name + a.Extra["Ext"] + ctx.Artifacts.Add(a) } return nil } diff --git a/pipeline/archive/archive_test.go b/pipeline/archive/archive_test.go index 031b48278..9c8df4b9d 100644 --- a/pipeline/archive/archive_test.go +++ b/pipeline/archive/archive_test.go @@ -8,10 +8,12 @@ import ( "path/filepath" "testing" + "github.com/stretchr/testify/assert" + "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/internal/testlib" - "github.com/stretchr/testify/assert" ) func TestDescription(t *testing.T) { @@ -23,18 +25,19 @@ func TestRunPipe(t *testing.T) { defer back() var dist = filepath.Join(folder, "dist") assert.NoError(t, os.Mkdir(dist, 0755)) - assert.NoError(t, os.Mkdir(filepath.Join(dist, "mybin_darwin_amd64"), 0755)) - assert.NoError(t, os.Mkdir(filepath.Join(dist, "mybin_windows_amd64"), 0755)) - _, err := os.Create(filepath.Join(dist, "mybin_darwin_amd64", "mybin")) + assert.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0755)) + assert.NoError(t, os.Mkdir(filepath.Join(dist, "windowsamd64"), 0755)) + _, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin")) assert.NoError(t, err) - _, err = os.Create(filepath.Join(dist, "mybin_windows_amd64", "mybin.exe")) + _, err = os.Create(filepath.Join(dist, "windowsamd64", "mybin.exe")) assert.NoError(t, err) _, err = os.Create(filepath.Join(folder, "README.md")) assert.NoError(t, err) - var ctx = &context.Context{ - Config: config.Project{ + var ctx = context.New( + config.Project{ Dist: dist, Archive: config.Archive{ + NameTemplate: "whatever", Files: []string{ "README.*", }, @@ -46,9 +49,27 @@ func TestRunPipe(t *testing.T) { }, }, }, - } - ctx.AddBinary("darwinamd64", "mybin_darwin_amd64", "mybin", filepath.Join(dist, "mybin_darwin_amd64", "mybin")) - ctx.AddBinary("windowsamd64", "mybin_windows_amd64", "mybin.exe", filepath.Join(dist, "mybin_windows_amd64", "mybin.exe")) + ) + ctx.Artifacts.Add(artifact.Artifact{ + Goos: "darwin", + Goarch: "amd64", + Name: "mybin", + Path: filepath.Join(dist, "darwinamd64", "mybin"), + Type: artifact.Binary, + Extra: map[string]string{ + "Binary": "mybin", + }, + }) + ctx.Artifacts.Add(artifact.Artifact{ + Goos: "windows", + Goarch: "amd64", + Name: "mybin.exe", + Path: filepath.Join(dist, "windowsamd64", "mybin.exe"), + Type: artifact.Binary, + Extra: map[string]string{ + "Binary": "mybin", + }, + }) for _, format := range []string{"tar.gz", "zip"} { t.Run("Archive format "+format, func(t *testing.T) { ctx.Config.Archive.Format = format @@ -57,7 +78,7 @@ func TestRunPipe(t *testing.T) { } // Check archive contents - f, err := os.Open(filepath.Join(dist, "mybin_darwin_amd64.tar.gz")) + f, err := os.Open(filepath.Join(dist, "whatever.tar.gz")) assert.NoError(t, err) defer func() { assert.NoError(t, f.Close()) }() gr, err := gzip.NewReader(f) @@ -79,16 +100,16 @@ func TestRunPipeBinary(t *testing.T) { defer back() var dist = filepath.Join(folder, "dist") assert.NoError(t, os.Mkdir(dist, 0755)) - assert.NoError(t, os.Mkdir(filepath.Join(dist, "mybin_darwin"), 0755)) - assert.NoError(t, os.Mkdir(filepath.Join(dist, "mybin_win"), 0755)) - _, err := os.Create(filepath.Join(dist, "mybin_darwin", "mybin")) + assert.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0755)) + assert.NoError(t, os.Mkdir(filepath.Join(dist, "windowsamd64"), 0755)) + _, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin")) assert.NoError(t, err) - _, err = os.Create(filepath.Join(dist, "mybin_win", "mybin.exe")) + _, err = os.Create(filepath.Join(dist, "windowsamd64", "mybin.exe")) assert.NoError(t, err) _, err = os.Create(filepath.Join(folder, "README.md")) assert.NoError(t, err) - var ctx = &context.Context{ - Config: config.Project{ + var ctx = context.New( + config.Project{ Dist: dist, Builds: []config.Build{ {Binary: "mybin"}, @@ -97,60 +118,88 @@ func TestRunPipeBinary(t *testing.T) { Format: "binary", }, }, - } - ctx.AddBinary("darwinamd64", "mybin_darwin", "mybin", filepath.Join(dist, "mybin_darwin", "mybin")) - ctx.AddBinary("windowsamd64", "mybin_win", "mybin.exe", filepath.Join(dist, "mybin_win", "mybin.exe")) + ) + ctx.Artifacts.Add(artifact.Artifact{ + Goos: "darwin", + Goarch: "amd64", + Name: "mybin", + Path: filepath.Join(dist, "darwinamd64", "mybin"), + Type: artifact.Binary, + Extra: map[string]string{ + "Binary": "mybin", + }, + }) + ctx.Artifacts.Add(artifact.Artifact{ + Goos: "windows", + Goarch: "amd64", + Name: "mybin.exe", + Path: filepath.Join(dist, "windowsamd64", "mybin.exe"), + Type: artifact.Binary, + Extra: map[string]string{ + "Binary": "mybin", + }, + }) assert.NoError(t, Pipe{}.Run(ctx)) - assert.Contains(t, ctx.Artifacts, "mybin_darwin/mybin") - assert.Contains(t, ctx.Artifacts, "mybin_win/mybin.exe") - assert.Len(t, ctx.Artifacts, 2) + var binaries = ctx.Artifacts.Filter(artifact.ByType(artifact.UploadableBinary)) + assert.Len(t, binaries.Filter(artifact.ByGoos("darwin")).List(), 1) + assert.Len(t, binaries.Filter(artifact.ByGoos("windows")).List(), 1) + assert.Len(t, binaries.List(), 2) } func TestRunPipeDistRemoved(t *testing.T) { - var ctx = &context.Context{ - Config: config.Project{ + var ctx = context.New( + config.Project{ Dist: "/path/nope", Archive: config.Archive{ - Format: "zip", + NameTemplate: "nope", + Format: "zip", }, }, - } - ctx.AddBinary("windowsamd64", "nope", "no", "blah") - assert.Error(t, Pipe{}.Run(ctx)) + ) + ctx.Artifacts.Add(artifact.Artifact{ + Goos: "windows", + Goarch: "amd64", + Name: "mybin.exe", + Path: filepath.Join("/path/to/nope", "windowsamd64", "mybin.exe"), + Type: artifact.Binary, + Extra: map[string]string{ + "Binary": "mybin", + }, + }) + assert.EqualError(t, Pipe{}.Run(ctx), `failed to create directory /path/nope/nope.zip: open /path/nope/nope.zip: no such file or directory`) } func TestRunPipeInvalidGlob(t *testing.T) { - var ctx = &context.Context{ - Config: config.Project{ - Dist: "/tmp", + folder, back := testlib.Mktmp(t) + defer back() + var dist = filepath.Join(folder, "dist") + assert.NoError(t, os.Mkdir(dist, 0755)) + assert.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0755)) + _, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin")) + assert.NoError(t, err) + var ctx = context.New( + config.Project{ + Dist: dist, Archive: config.Archive{ + NameTemplate: "foo", + Format: "zip", Files: []string{ "[x-]", }, }, }, - } - ctx.AddBinary("windowsamd64", "whatever", "foo", "bar") - assert.Error(t, Pipe{}.Run(ctx)) -} - -func TestRunPipeGlobFailsToAdd(t *testing.T) { - folder, back := testlib.Mktmp(t) - defer back() - assert.NoError(t, os.MkdirAll(filepath.Join(folder, "folder", "another"), 0755)) - - var ctx = &context.Context{ - Config: config.Project{ - Dist: folder, - Archive: config.Archive{ - Files: []string{ - "folder", - }, - }, + ) + ctx.Artifacts.Add(artifact.Artifact{ + Goos: "darwin", + Goarch: "amd64", + Name: "mybin", + Path: filepath.Join("dist", "darwinamd64", "mybin"), + Type: artifact.Binary, + Extra: map[string]string{ + "Binary": "mybin", }, - } - ctx.AddBinary("windows386", "mybin", "mybin", "dist/mybin") - assert.Error(t, Pipe{}.Run(ctx)) + }) + assert.EqualError(t, Pipe{}.Run(ctx), `failed to find files to archive: globbing failed for pattern [x-]: file does not exist`) } func TestRunPipeWrap(t *testing.T) { @@ -158,15 +207,16 @@ func TestRunPipeWrap(t *testing.T) { defer back() var dist = filepath.Join(folder, "dist") assert.NoError(t, os.Mkdir(dist, 0755)) - assert.NoError(t, os.Mkdir(filepath.Join(dist, "mybin_darwin_amd64"), 0755)) - _, err := os.Create(filepath.Join(dist, "mybin_darwin_amd64", "mybin")) + assert.NoError(t, os.Mkdir(filepath.Join(dist, "darwinamd64"), 0755)) + _, err := os.Create(filepath.Join(dist, "darwinamd64", "mybin")) assert.NoError(t, err) _, err = os.Create(filepath.Join(folder, "README.md")) assert.NoError(t, err) - var ctx = &context.Context{ - Config: config.Project{ + var ctx = context.New( + config.Project{ Dist: dist, Archive: config.Archive{ + NameTemplate: "foo", WrapInDirectory: true, Format: "tar.gz", Files: []string{ @@ -174,12 +224,21 @@ func TestRunPipeWrap(t *testing.T) { }, }, }, - } - ctx.AddBinary("darwinamd64", "mybin_darwin_amd64", "mybin", filepath.Join(dist, "mybin_darwin_amd64", "mybin")) + ) + ctx.Artifacts.Add(artifact.Artifact{ + Goos: "darwin", + Goarch: "amd64", + Name: "mybin", + Path: filepath.Join("dist", "darwinamd64", "mybin"), + Type: artifact.Binary, + Extra: map[string]string{ + "Binary": "mybin", + }, + }) assert.NoError(t, Pipe{}.Run(ctx)) // Check archive contents - f, err := os.Open(filepath.Join(dist, "mybin_darwin_amd64.tar.gz")) + f, err := os.Open(filepath.Join(dist, "foo.tar.gz")) assert.NoError(t, err) defer func() { assert.NoError(t, f.Close()) }() gr, err := gzip.NewReader(f) @@ -192,7 +251,7 @@ func TestRunPipeWrap(t *testing.T) { break } assert.NoError(t, err) - assert.Equal(t, filepath.Join("mybin_darwin_amd64", n), h.Name) + assert.Equal(t, filepath.Join("foo", n), h.Name) } } diff --git a/pipeline/artifactory/artifactory.go b/pipeline/artifactory/artifactory.go index 73275e617..8bbb0a893 100644 --- a/pipeline/artifactory/artifactory.go +++ b/pipeline/artifactory/artifactory.go @@ -11,17 +11,16 @@ import ( "net/http" "net/url" "os" - "path/filepath" "strings" - "github.com/goreleaser/goreleaser/config" - "github.com/goreleaser/goreleaser/context" - "github.com/goreleaser/goreleaser/internal/buildtarget" - "github.com/goreleaser/goreleaser/pipeline" - "github.com/apex/log" "github.com/pkg/errors" "golang.org/x/sync/errgroup" + + "github.com/goreleaser/goreleaser/config" + "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" + "github.com/goreleaser/goreleaser/pipeline" ) // artifactoryResponse reflects the response after an upload request @@ -69,7 +68,7 @@ func (Pipe) Default(ctx *context.Context) error { // Check if a mode was set for i := range ctx.Config.Artifactories { if ctx.Config.Artifactories[i].Mode == "" { - ctx.Config.Artifactories[i].Mode = "archive" + ctx.Config.Artifactories[i].Mode = modeArchive } } @@ -121,15 +120,10 @@ func doRun(ctx *context.Context) error { var err error switch v := strings.ToLower(instance.Mode); v { case modeArchive: - err = runPipeForModeArchive(ctx, instance) + err = runPipeByFilter(ctx, instance, artifact.ByType(artifact.UploadableArchive)) case modeBinary: - // Loop over all builds, because we want to publish every build to Artifactory - for _, build := range ctx.Config.Builds { - if err = runPipeForModeBinary(ctx, instance, build); err != nil { - return err - } - } + err = runPipeByFilter(ctx, instance, artifact.ByType(artifact.UploadableBinary)) default: err = fmt.Errorf("artifactory: mode \"%s\" not supported", v) @@ -147,50 +141,29 @@ func doRun(ctx *context.Context) error { return nil } -// runPipeForModeArchive uploads all ctx.Artifacts to instance -func runPipeForModeArchive(ctx *context.Context, instance config.Artifactory) error { +func runPipeByFilter(ctx *context.Context, instance config.Artifactory, filter artifact.Filter) error { sem := make(chan bool, ctx.Parallelism) var g errgroup.Group - - // Get all artifacts and upload them - for _, artifact := range ctx.Artifacts { + for _, artifact := range ctx.Artifacts.Filter(filter).List() { sem <- true artifact := artifact g.Go(func() error { defer func() { <-sem }() - - return uploadArchive(ctx, instance, artifact) + return uploadAsset(ctx, instance, artifact) }) } - return g.Wait() } -// uploadArchive will upload artifact in mode archive -func uploadArchive(ctx *context.Context, instance config.Artifactory, artifact string) error { - var path = filepath.Join(ctx.Config.Dist, artifact) - return uploadAssetAndLog(ctx, instance, path, nil) -} - -// uploadBinary will upload the current build and the current target in mode binary -func uploadBinary(ctx *context.Context, instance config.Artifactory, build config.Build, target buildtarget.Target) error { - binary, err := getBinaryForUploadPerBuild(ctx, target) - if err != nil { - return err - } - - return uploadAssetAndLog(ctx, instance, binary.Path, &target) -} - -// uploadAssetAndLog uploads file to target and logs all actions -func uploadAssetAndLog(ctx *context.Context, instance config.Artifactory, path string, target *buildtarget.Target) error { +// uploadAsset uploads file to target and logs all actions +func uploadAsset(ctx *context.Context, instance config.Artifactory, artifact artifact.Artifact) error { envName := fmt.Sprintf("ARTIFACTORY_%s_SECRET", strings.ToUpper(instance.Name)) secret := ctx.Env[envName] // Generate the target url - targetURL, err := resolveTargetTemplate(ctx, instance, target) + targetURL, err := resolveTargetTemplate(ctx, instance, artifact) if err != nil { msg := "artifactory: error while building the target url" log.WithField("instance", instance.Name).WithError(err).Error(msg) @@ -198,20 +171,19 @@ func uploadAssetAndLog(ctx *context.Context, instance config.Artifactory, path s } // Handle the artifact - file, err := os.Open(path) + file, err := os.Open(artifact.Path) if err != nil { return err } defer file.Close() // nolint: errcheck - _, name := filepath.Split(path) // The target url needs to contain the artifact name if !strings.HasSuffix(targetURL, "/") { targetURL += "/" } - targetURL += name + targetURL += artifact.Name - artifact, _, err := uploadAssetToArtifactory(ctx, targetURL, instance.Username, secret, file) + uploaded, _, err := uploadAssetToArtifactory(ctx, targetURL, instance.Username, secret, file) if err != nil { msg := "artifactory: upload failed" log.WithError(err).WithFields(log.Fields{ @@ -224,54 +196,12 @@ func uploadAssetAndLog(ctx *context.Context, instance config.Artifactory, path s log.WithFields(log.Fields{ "instance": instance.Name, "mode": instance.Mode, - "uri": artifact.DownloadURI, + "uri": uploaded.DownloadURI, }).Info("uploaded successful") return nil } -// runPipeForModeBinary uploads all configured builds to instance -func runPipeForModeBinary(ctx *context.Context, instance config.Artifactory, build config.Build) error { - sem := make(chan bool, ctx.Parallelism) - var g errgroup.Group - - // Lets generate the build matrix, because we want - // to publish every target to Artifactory - for _, target := range buildtarget.All(build) { - sem <- true - target := target - build := build - g.Go(func() error { - defer func() { - <-sem - }() - - return uploadBinary(ctx, instance, build, target) - }) - } - - return g.Wait() -} - -// getBinaryForUploadPerBuild determines the correct binary for the upload -func getBinaryForUploadPerBuild(ctx *context.Context, target buildtarget.Target) (*context.Binary, error) { - var group = ctx.Binaries[target.String()] - if group == nil { - return nil, fmt.Errorf("binary for build target %s not found", target.String()) - } - - var binary context.Binary - for _, binaries := range group { - for _, b := range binaries { - binary = b - break - } - break - } - - return &binary, nil -} - // targetData is used as a template struct for // Artifactory.Target type targetData struct { @@ -287,18 +217,17 @@ type targetData struct { // resolveTargetTemplate returns the resolved target template with replaced variables // Those variables can be replaced by the given context, goos, goarch, goarm and more -func resolveTargetTemplate(ctx *context.Context, artifactory config.Artifactory, target *buildtarget.Target) (string, error) { +func resolveTargetTemplate(ctx *context.Context, artifactory config.Artifactory, artifact artifact.Artifact) (string, error) { data := targetData{ Version: ctx.Version, Tag: ctx.Git.CurrentTag, ProjectName: ctx.Config.ProjectName, } - // Only supported in mode binary - if target != nil { - data.Os = replace(ctx.Config.Archive.Replacements, target.OS) - data.Arch = replace(ctx.Config.Archive.Replacements, target.Arch) - data.Arm = replace(ctx.Config.Archive.Replacements, target.Arm) + if artifactory.Mode == modeBinary { + data.Os = replace(ctx.Config.Archive.Replacements, artifact.Goos) + data.Arch = replace(ctx.Config.Archive.Replacements, artifact.Goarch) + data.Arm = replace(ctx.Config.Archive.Replacements, artifact.Goarm) } var out bytes.Buffer @@ -404,10 +333,6 @@ type Error struct { Message string `json:"message"` // Message describing the error. } -func (e *Error) Error() string { - return fmt.Sprintf("%v (%v)", e.Message, e.Status) -} - // checkResponse checks the API response for errors, and returns them if // present. A response is considered an error if it has a status code outside // the 200 range. diff --git a/pipeline/artifactory/artifactory_test.go b/pipeline/artifactory/artifactory_test.go index 98ebb5723..52dfafa08 100644 --- a/pipeline/artifactory/artifactory_test.go +++ b/pipeline/artifactory/artifactory_test.go @@ -11,8 +11,8 @@ import ( "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/pipeline" - "github.com/stretchr/testify/assert" ) @@ -167,42 +167,37 @@ func TestRunPipe_ModeBinary(t *testing.T) { }`) }) - var ctx = &context.Context{ - Version: "1.0.0", - Publish: true, - Parallelism: 4, - Env: map[string]string{ - "ARTIFACTORY_PRODUCTION-US_SECRET": "deployuser-secret", - "ARTIFACTORY_PRODUCTION-EU_SECRET": "productionuser-apikey", - }, - Config: config.Project{ - ProjectName: "mybin", - Dist: dist, - Builds: []config.Build{ - { - Env: []string{"CGO_ENABLED=0"}, - Goos: []string{"linux", "darwin"}, - Goarch: []string{"amd64"}, - }, + var ctx = context.New(config.Project{ + ProjectName: "mybin", + Dist: dist, + Artifactories: []config.Artifactory{ + { + Name: "production-us", + Mode: "binary", + Target: fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL), + Username: "deployuser", }, - Artifactories: []config.Artifactory{ - { - Name: "production-us", - Mode: "binary", - Target: fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL), - Username: "deployuser", - }, - { - Name: "production-eu", - Mode: "binary", - Target: fmt.Sprintf("%s/production-repo-remote/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL), - Username: "productionuser", - }, + { + Name: "production-eu", + Mode: "binary", + Target: fmt.Sprintf("%s/production-repo-remote/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL), + Username: "productionuser", }, }, + }) + ctx.Env = map[string]string{ + "ARTIFACTORY_PRODUCTION-US_SECRET": "deployuser-secret", + "ARTIFACTORY_PRODUCTION-EU_SECRET": "productionuser-apikey", } - for _, plat := range []string{"linuxamd64", "linux386", "darwinamd64"} { - ctx.AddBinary(plat, "mybin", "mybin", binPath) + ctx.Publish = true + for _, goos := range []string{"linux", "darwin"} { + ctx.Artifacts.Add(artifact.Artifact{ + Name: "mybin", + Path: binPath, + Goarch: "amd64", + Goos: goos, + Type: artifact.UploadableBinary, + }) } assert.NoError(t, Pipe{}.Run(ctx)) @@ -219,29 +214,33 @@ func TestRunPipe_ModeArchive(t *testing.T) { debfile, err := os.Create(filepath.Join(folder, "bin.deb")) assert.NoError(t, err) - var ctx = &context.Context{ - Version: "1.0.0", - Publish: true, - Parallelism: 4, - Env: map[string]string{ - "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", - }, - Config: config.Project{ - ProjectName: "goreleaser", - Dist: folder, - Artifactories: []config.Artifactory{ - { - Name: "production", - Mode: "archive", - Target: fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Version }}/", server.URL), - Username: "deployuser", - }, + var ctx = context.New(config.Project{ + ProjectName: "goreleaser", + Dist: folder, + Artifactories: []config.Artifactory{ + { + Name: "production", + Mode: "archive", + Target: fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Version }}/", server.URL), + Username: "deployuser", }, }, + }) + ctx.Env = map[string]string{ + "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", } - - ctx.AddArtifact(tarfile.Name()) - ctx.AddArtifact(debfile.Name()) + ctx.Publish = true + ctx.Version = "1.0.0" + ctx.Artifacts.Add(artifact.Artifact{ + Type: artifact.UploadableArchive, + Name: "bin.tar.gz", + Path: tarfile.Name(), + }) + ctx.Artifacts.Add(artifact.Artifact{ + Type: artifact.LinuxPackage, + Name: "bin.deb", + Path: debfile.Name(), + }) // Dummy artifactories mux.HandleFunc("/example-repo-local/goreleaser/1.0.0/bin.tar.gz", func(w http.ResponseWriter, r *http.Request) { @@ -298,43 +297,70 @@ func TestRunPipe_ModeArchive(t *testing.T) { assert.NoError(t, Pipe{}.Run(ctx)) } +func TestRunPipe_ArtifactoryDown(t *testing.T) { + folder, err := ioutil.TempDir("", "goreleasertest") + assert.NoError(t, err) + tarfile, err := os.Create(filepath.Join(folder, "bin.tar.gz")) + assert.NoError(t, err) + + var ctx = context.New(config.Project{ + ProjectName: "goreleaser", + Dist: folder, + Artifactories: []config.Artifactory{ + { + Name: "production", + Mode: "archive", + Target: "http://localhost:1234/example-repo-local/{{ .ProjectName }}/{{ .Version }}/", + Username: "deployuser", + }, + }, + }) + ctx.Version = "2.0.0" + ctx.Env = map[string]string{ + "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", + } + ctx.Publish = true + ctx.Artifacts.Add(artifact.Artifact{ + Type: artifact.UploadableArchive, + Name: "bin.tar.gz", + Path: tarfile.Name(), + }) + + assert.EqualError(t, Pipe{}.Run(ctx), `artifactory: upload failed: Put http://localhost:1234/example-repo-local/goreleaser/2.0.0/bin.tar.gz: dial tcp 127.0.0.1:1234: getsockopt: connection refused`) +} + func TestRunPipe_TargetTemplateError(t *testing.T) { folder, err := ioutil.TempDir("", "archivetest") assert.NoError(t, err) var dist = filepath.Join(folder, "dist") var binPath = filepath.Join(dist, "mybin", "mybin") - var ctx = &context.Context{ - Version: "1.0.0", - Publish: true, - Parallelism: 4, - Env: map[string]string{ - "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", - }, - Config: config.Project{ - ProjectName: "mybin", - Dist: dist, - Builds: []config.Build{ - { - Env: []string{"CGO_ENABLED=0"}, - Goos: []string{"darwin"}, - Goarch: []string{"amd64"}, - }, - }, - Artifactories: []config.Artifactory{ - { - Name: "production", - Mode: "binary", - // This template is not correct and should fail - Target: "http://storage.company.com/example-repo-local/{{ .ProjectName /{{ .Version }}/", - Username: "deployuser", - }, + var ctx = context.New(config.Project{ + ProjectName: "mybin", + Dist: dist, + Artifactories: []config.Artifactory{ + { + Name: "production", + Mode: "binary", + // This template is not correct and should fail + Target: "http://storage.company.com/example-repo-local/{{ .ProjectName /{{ .Version }}/", + Username: "deployuser", }, }, + }) + ctx.Publish = true + ctx.Env = map[string]string{ + "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", } - ctx.AddBinary("darwinamd64", "mybin", "mybin", binPath) + ctx.Artifacts.Add(artifact.Artifact{ + Name: "mybin", + Path: binPath, + Goarch: "amd64", + Goos: "darwin", + Type: artifact.UploadableBinary, + }) - assert.Error(t, Pipe{}.Run(ctx)) + assert.EqualError(t, Pipe{}.Run(ctx), `artifactory: error while building the target url: template: mybin:1: unexpected "/" in operand`) } func TestRunPipe_BadCredentials(t *testing.T) { @@ -367,40 +393,33 @@ func TestRunPipe_BadCredentials(t *testing.T) { }`) }) - var ctx = &context.Context{ - Version: "1.0.0", - Publish: true, - Parallelism: 4, - Env: map[string]string{ - "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", - }, - Config: config.Project{ - ProjectName: "mybin", - Dist: dist, - Builds: []config.Build{ - { - Env: []string{"CGO_ENABLED=0"}, - Goos: []string{"darwin"}, - Goarch: []string{"amd64"}, - }, - }, - Artifactories: []config.Artifactory{ - { - Name: "production", - Mode: "binary", - Target: fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL), - Username: "deployuser", - }, + var ctx = context.New(config.Project{ + ProjectName: "mybin", + Dist: dist, + Artifactories: []config.Artifactory{ + { + Name: "production", + Mode: "binary", + Target: fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL), + Username: "deployuser", }, }, + }) + ctx.Publish = true + ctx.Env = map[string]string{ + "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", } - for _, plat := range []string{"darwinamd64"} { - ctx.AddBinary(plat, "mybin", "mybin", binPath) - } + ctx.Artifacts.Add(artifact.Artifact{ + Name: "mybin", + Path: binPath, + Goarch: "amd64", + Goos: "darwin", + Type: artifact.UploadableBinary, + }) err = Pipe{}.Run(ctx) assert.Error(t, err) - assert.True(t, len(err.Error()) > 0) + assert.Contains(t, err.Error(), "Bad credentials") } func TestRunPipe_UnparsableErrorResponse(t *testing.T) { @@ -432,38 +451,31 @@ func TestRunPipe_UnparsableErrorResponse(t *testing.T) { }`) }) - var ctx = &context.Context{ - Version: "1.0.0", - Publish: true, - Parallelism: 4, - Env: map[string]string{ - "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", - }, - Config: config.Project{ - ProjectName: "mybin", - Dist: dist, - Builds: []config.Build{ - { - Env: []string{"CGO_ENABLED=0"}, - Goos: []string{"darwin"}, - Goarch: []string{"amd64"}, - }, - }, - Artifactories: []config.Artifactory{ - { - Name: "production", - Mode: "binary", - Target: fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL), - Username: "deployuser", - }, + var ctx = context.New(config.Project{ + ProjectName: "mybin", + Dist: dist, + Artifactories: []config.Artifactory{ + { + Name: "production", + Mode: "binary", + Target: fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL), + Username: "deployuser", }, }, + }) + ctx.Publish = true + ctx.Env = map[string]string{ + "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", } - for _, plat := range []string{"darwinamd64"} { - ctx.AddBinary(plat, "mybin", "mybin", binPath) - } + ctx.Artifacts.Add(artifact.Artifact{ + Name: "mybin", + Path: binPath, + Goarch: "amd64", + Goos: "darwin", + Type: artifact.UploadableBinary, + }) - assert.Error(t, Pipe{}.Run(ctx)) + assert.EqualError(t, Pipe{}.Run(ctx), `artifactory: upload failed: invalid character '.' looking for beginning of value`) } func TestRunPipe_UnparsableResponse(t *testing.T) { @@ -494,105 +506,59 @@ func TestRunPipe_UnparsableResponse(t *testing.T) { }`) }) - var ctx = &context.Context{ - Version: "1.0.0", - Publish: true, - Parallelism: 4, - Env: map[string]string{ - "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", - }, - Config: config.Project{ - ProjectName: "mybin", - Dist: dist, - Builds: []config.Build{ - { - Env: []string{"CGO_ENABLED=0"}, - Goos: []string{"darwin"}, - Goarch: []string{"amd64"}, - }, - }, - Artifactories: []config.Artifactory{ - { - Name: "production", - Mode: "binary", - Target: fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL), - Username: "deployuser", - }, + var ctx = context.New(config.Project{ + ProjectName: "mybin", + Dist: dist, + Artifactories: []config.Artifactory{ + { + Name: "production", + Mode: "binary", + Target: fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL), + Username: "deployuser", }, }, + }) + ctx.Publish = true + ctx.Env = map[string]string{ + "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", } - ctx.AddBinary("darwinamd64", "mybin", "mybin", binPath) + ctx.Artifacts.Add(artifact.Artifact{ + Name: "mybin", + Path: binPath, + Goarch: "amd64", + Goos: "darwin", + Type: artifact.UploadableBinary, + }) - assert.Error(t, Pipe{}.Run(ctx)) + assert.EqualError(t, Pipe{}.Run(ctx), `artifactory: upload failed: invalid character 'i' looking for beginning of value`) } -func TestRunPipe_WithoutBinaryTarget(t *testing.T) { - folder, err := ioutil.TempDir("", "archivetest") - assert.NoError(t, err) - var dist = filepath.Join(folder, "dist") - - var ctx = &context.Context{ - Version: "1.0.0", - Publish: true, - Parallelism: 4, - Env: map[string]string{ - "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", - }, - Config: config.Project{ - ProjectName: "mybin", - Dist: dist, - Builds: []config.Build{ - { - Env: []string{"CGO_ENABLED=0"}, - Goos: []string{"darwin"}, - Goarch: []string{"amd64"}, - }, - }, - Artifactories: []config.Artifactory{ - { - Name: "production", - Mode: "binary", - Target: fmt.Sprintf("%s/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", server.URL), - Username: "deployuser", - }, +func TestRunPipe_FileNotFound(t *testing.T) { + var ctx = context.New(config.Project{ + ProjectName: "mybin", + Dist: "archivetest/dist", + Artifactories: []config.Artifactory{ + { + Name: "production", + Mode: "binary", + Target: "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", + Username: "deployuser", }, }, + }) + ctx.Publish = true + ctx.Env = map[string]string{ + "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", } + ctx.Artifacts.Add(artifact.Artifact{ + Name: "mybin", + Path: "archivetest/dist/mybin/mybin", + Goarch: "amd64", + Goos: "darwin", + Type: artifact.UploadableBinary, + }) - assert.Error(t, Pipe{}.Run(ctx)) -} - -func TestRunPipe_NoFile(t *testing.T) { - var ctx = &context.Context{ - Version: "1.0.0", - Publish: true, - Parallelism: 4, - Env: map[string]string{ - "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", - }, - Config: config.Project{ - ProjectName: "mybin", - Dist: "archivetest/dist", - Builds: []config.Build{ - { - Env: []string{"CGO_ENABLED=0"}, - Goos: []string{"darwin"}, - Goarch: []string{"amd64"}, - }, - }, - Artifactories: []config.Artifactory{ - { - Name: "production", - Mode: "binary", - Target: "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", - Username: "deployuser", - }, - }, - }, - } - ctx.AddBinary("darwinamd64", "mybin", "mybin", "archivetest/dist/mybin/mybin") - - assert.Error(t, Pipe{}.Run(ctx)) + assert.EqualError(t, Pipe{}.Run(ctx), `open archivetest/dist/mybin/mybin: no such file or directory`) } func TestRunPipe_UnparsableTarget(t *testing.T) { @@ -606,59 +572,51 @@ func TestRunPipe_UnparsableTarget(t *testing.T) { err = ioutil.WriteFile(binPath, d1, 0666) assert.NoError(t, err) - var ctx = &context.Context{ - Version: "1.0.0", - Publish: true, - Parallelism: 4, - Env: map[string]string{ - "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", - }, - Config: config.Project{ - ProjectName: "mybin", - Dist: dist, - Builds: []config.Build{ - { - Env: []string{"CGO_ENABLED=0"}, - Goos: []string{"darwin"}, - Goarch: []string{"amd64"}, - }, - }, - Artifactories: []config.Artifactory{ - { - Name: "production", - Mode: "binary", - Target: "://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", - Username: "deployuser", - }, + var ctx = context.New(config.Project{ + ProjectName: "mybin", + Dist: dist, + Artifactories: []config.Artifactory{ + { + Name: "production", + Mode: "binary", + Target: "://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", + Username: "deployuser", }, }, + }) + ctx.Publish = true + ctx.Env = map[string]string{ + "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", } - for _, plat := range []string{"darwinamd64"} { - ctx.AddBinary(plat, "mybin", "mybin", binPath) - } + ctx.Artifacts.Add(artifact.Artifact{ + Name: "mybin", + Path: binPath, + Goarch: "amd64", + Goos: "darwin", + Type: artifact.UploadableBinary, + }) - assert.Error(t, Pipe{}.Run(ctx)) + assert.EqualError(t, Pipe{}.Run(ctx), `artifactory: upload failed: parse ://artifacts.company.com/example-repo-local/mybin/darwin/amd64/mybin: missing protocol scheme`) } func TestRunPipe_SkipWhenPublishFalse(t *testing.T) { - var ctx = &context.Context{ - Publish: false, - Env: map[string]string{ - "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", - }, - Config: config.Project{ - Artifactories: []config.Artifactory{ - { - Name: "production", - Mode: "binary", - Target: "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", - Username: "deployuser", - }, + var ctx = context.New(config.Project{ + Artifactories: []config.Artifactory{ + { + Name: "production", + Mode: "binary", + Target: "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", + Username: "deployuser", }, }, + }) + ctx.Env = map[string]string{ + "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", } - assert.True(t, pipeline.IsSkip(Pipe{}.Run(ctx))) + err := Pipe{}.Run(ctx) + assert.True(t, pipeline.IsSkip(err)) + assert.Equal(t, err.Error(), "--skip-publish is set") } func TestRunPipe_DirUpload(t *testing.T) { @@ -669,38 +627,31 @@ func TestRunPipe_DirUpload(t *testing.T) { assert.NoError(t, os.Mkdir(filepath.Join(dist, "mybin"), 0755)) var binPath = filepath.Join(dist, "mybin") - var ctx = &context.Context{ - Version: "1.0.0", - Publish: true, - Parallelism: 4, - Env: map[string]string{ - "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", - }, - Config: config.Project{ - ProjectName: "mybin", - Dist: dist, - Builds: []config.Build{ - { - Env: []string{"CGO_ENABLED=0"}, - Goos: []string{"darwin"}, - Goarch: []string{"amd64"}, - }, - }, - Artifactories: []config.Artifactory{ - { - Name: "production", - Mode: "binary", - Target: "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", - Username: "deployuser", - }, + var ctx = context.New(config.Project{ + ProjectName: "mybin", + Dist: dist, + Artifactories: []config.Artifactory{ + { + Name: "production", + Mode: "binary", + Target: "http://artifacts.company.com/example-repo-local/{{ .ProjectName }}/{{ .Os }}/{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}", + Username: "deployuser", }, }, + }) + ctx.Env = map[string]string{ + "ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret", } - for _, plat := range []string{"darwinamd64"} { - ctx.AddBinary(plat, "mybin", "mybin", binPath) - } + ctx.Publish = true + ctx.Artifacts.Add(artifact.Artifact{ + Name: "mybin", + Path: filepath.Dir(binPath), + Goarch: "amd64", + Goos: "darwin", + Type: artifact.UploadableBinary, + }) - assert.Error(t, Pipe{}.Run(ctx)) + assert.EqualError(t, Pipe{}.Run(ctx), `artifactory: upload failed: the asset to upload can't be a directory`) } func TestDescription(t *testing.T) { diff --git a/pipeline/brew/brew.go b/pipeline/brew/brew.go index 8c1d82540..530acbc6b 100644 --- a/pipeline/brew/brew.go +++ b/pipeline/brew/brew.go @@ -1,5 +1,3 @@ -// Package brew implements the Pipe, providing formula generation and -// uploading it to a configured repo. package brew import ( @@ -11,19 +9,20 @@ import ( "text/template" "github.com/apex/log" + "github.com/goreleaser/goreleaser/checksum" "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" - "github.com/goreleaser/goreleaser/internal/archiveformat" + "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/internal/client" "github.com/goreleaser/goreleaser/pipeline" ) -// ErrNoDarwin64Build when there is no build for darwin_amd64 (goos doesn't -// contain darwin and/or goarch doesn't contain amd64) -var ErrNoDarwin64Build = errors.New("brew tap requires a darwin amd64 build") +// ErrNoDarwin64Build when there is no build for darwin_amd64 +var ErrNoDarwin64Build = errors.New("brew tap requires one darwin amd64 build") -const platform = "darwinamd64" +// ErrTooManyDarwin64Builds when there are too many builds for darwin_amd64 +var ErrTooManyDarwin64Builds = errors.New("brew tap requires at most one darwin amd64 build") // Pipe for brew deployment type Pipe struct{} @@ -98,28 +97,33 @@ func doRun(ctx *context.Context, client client.Client) error { return pipeline.Skip("archive format is binary") } - var group = ctx.Binaries["darwinamd64"] - if group == nil { + var archives = ctx.Artifacts.Filter( + artifact.And( + artifact.ByGoos("darwin"), + artifact.ByGoarch("amd64"), + artifact.ByGoarm(""), + artifact.ByType(artifact.UploadableArchive), + ), + ).List() + if len(archives) == 0 { return ErrNoDarwin64Build } - var folder string - for f := range group { - folder = f - break + if len(archives) > 1 { + return ErrTooManyDarwin64Builds } var path = filepath.Join(ctx.Config.Brew.Folder, ctx.Config.ProjectName+".rb") log.WithField("formula", path). WithField("repo", ctx.Config.Brew.GitHub.String()). Info("pushing") - content, err := buildFormula(ctx, client, folder) + content, err := buildFormula(ctx, client, archives[0]) if err != nil { return err } return client.CreateFile(ctx, content, path) } -func buildFormula(ctx *context.Context, client client.Client, folder string) (bytes.Buffer, error) { - data, err := dataFor(ctx, client, folder) +func buildFormula(ctx *context.Context, client client.Client, artifact artifact.Artifact) (bytes.Buffer, error) { + data, err := dataFor(ctx, client, artifact) if err != nil { return bytes.Buffer{}, err } @@ -135,9 +139,8 @@ func doBuildFormula(data templateData) (out bytes.Buffer, err error) { return } -func dataFor(ctx *context.Context, client client.Client, folder string) (result templateData, err error) { - var file = folder + "." + archiveformat.For(ctx, platform) - sum, err := checksum.SHA256(filepath.Join(ctx.Config.Dist, file)) +func dataFor(ctx *context.Context, client client.Client, artifact artifact.Artifact) (result templateData, err error) { + sum, err := checksum.SHA256(artifact.Path) if err != nil { return } @@ -154,7 +157,7 @@ func dataFor(ctx *context.Context, client client.Client, folder string) (result Tag: ctx.Git.CurrentTag, Version: ctx.Version, Caveats: ctx.Config.Brew.Caveats, - File: file, + File: artifact.Name, SHA256: sum, Dependencies: ctx.Config.Brew.Dependencies, Conflicts: ctx.Config.Brew.Conflicts, diff --git a/pipeline/brew/brew_test.go b/pipeline/brew/brew_test.go index 7e513bf71..3fda0ba36 100644 --- a/pipeline/brew/brew_test.go +++ b/pipeline/brew/brew_test.go @@ -9,6 +9,7 @@ import ( "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/internal/testlib" "github.com/stretchr/testify/assert" ) @@ -93,7 +94,8 @@ func TestRunPipe(t *testing.T) { Git: context.GitInfo{ CurrentTag: "v1.0.1", }, - Version: "1.0.1", + Version: "1.0.1", + Artifacts: artifact.New(), Config: config.Project{ Dist: folder, ProjectName: "run-pipe", @@ -124,55 +126,42 @@ func TestRunPipe(t *testing.T) { Publish: true, } var path = filepath.Join(folder, "bin.tar.gz") - ctx.AddBinary("darwinamd64", "bin", "bin", path) + ctx.Artifacts.Add(artifact.Artifact{ + Name: "bin.tar.gz", + Path: path, + Goos: "darwin", + Goarch: "amd64", + Type: artifact.UploadableArchive, + }) client := &DummyClient{} assert.Error(t, doRun(ctx, client)) assert.False(t, client.CreatedFile) _, err = os.Create(path) assert.NoError(t, err) - assert.NoError(t, doRun(ctx, client)) - assert.True(t, client.CreatedFile) - bts, err := ioutil.ReadFile("testdata/run_pipe.rb") - assert.NoError(t, err) - // ioutil.WriteFile("testdata/run_pipe.rb", []byte(client.Content), 0644) + t.Run("default git url", func(tt *testing.T) { + assert.NoError(tt, doRun(ctx, client)) + assert.True(tt, client.CreatedFile) - assert.Equal(t, string(bts), client.Content) -} + bts, err := ioutil.ReadFile("testdata/run_pipe.rb") + assert.NoError(tt, err) + // TODO: make writing this file toggleable somehow? + // ioutil.WriteFile("testdata/run_pipe.rb", []byte(client.Content), 0644) + assert.Equal(tt, string(bts), client.Content) + }) -func TestRunPipeFormatOverride(t *testing.T) { - folder, err := ioutil.TempDir("", "goreleasertest") - assert.NoError(t, err) - var path = filepath.Join(folder, "bin.zip") - _, err = os.Create(path) - assert.NoError(t, err) - var ctx = &context.Context{ - Config: config.Project{ - Dist: folder, - Archive: config.Archive{ - Format: "tar.gz", - FormatOverrides: []config.FormatOverride{ - { - Format: "zip", - Goos: "darwin", - }, - }, - }, - Brew: config.Homebrew{ - GitHub: config.Repo{ - Owner: "test", - Name: "test", - }, - }, - }, - Publish: true, - } - ctx.AddBinary("darwinamd64", "bin", "bin", path) - client := &DummyClient{} - assert.NoError(t, doRun(ctx, client)) - assert.True(t, client.CreatedFile) - assert.Contains(t, client.Content, "bin.zip") + t.Run("github enterprise url", func(tt *testing.T) { + ctx.Config.GitHubURLs.Download = "http://github.example.org" + assert.NoError(tt, doRun(ctx, client)) + assert.True(tt, client.CreatedFile) + + bts, err := ioutil.ReadFile("testdata/run_pipe_enterprise.rb") + assert.NoError(tt, err) + // TODO: make writing this file toggleable somehow? + // ioutil.WriteFile("testdata/run_pipe_enterprise.rb", []byte(client.Content), 0644) + assert.Equal(tt, string(bts), client.Content) + }) } func TestRunPipeNoDarwin64Build(t *testing.T) { @@ -195,6 +184,40 @@ func TestRunPipeNoDarwin64Build(t *testing.T) { assert.False(t, client.CreatedFile) } +func TestRunPipeMultipleDarwin64Build(t *testing.T) { + var ctx = context.New( + config.Project{ + Archive: config.Archive{ + Format: "tar.gz", + }, + Brew: config.Homebrew{ + GitHub: config.Repo{ + Owner: "test", + Name: "test", + }, + }, + }, + ) + ctx.Publish = true + ctx.Artifacts.Add(artifact.Artifact{ + Name: "bin1", + Path: "doesnt mather", + Goos: "darwin", + Goarch: "amd64", + Type: artifact.UploadableArchive, + }) + ctx.Artifacts.Add(artifact.Artifact{ + Name: "bin2", + Path: "doesnt mather", + Goos: "darwin", + Goarch: "amd64", + Type: artifact.UploadableArchive, + }) + client := &DummyClient{} + assert.Equal(t, ErrTooManyDarwin64Builds, doRun(ctx, client)) + assert.False(t, client.CreatedFile) +} + func TestRunPipeBrewNotSetup(t *testing.T) { var ctx = &context.Context{ Config: config.Project{}, @@ -206,9 +229,8 @@ func TestRunPipeBrewNotSetup(t *testing.T) { } func TestRunPipeBinaryRelease(t *testing.T) { - var ctx = &context.Context{ - Publish: true, - Config: config.Project{ + var ctx = context.New( + config.Project{ Archive: config.Archive{ Format: "binary", }, @@ -219,8 +241,15 @@ func TestRunPipeBinaryRelease(t *testing.T) { }, }, }, - } - ctx.AddBinary("darwinamd64", "foo", "bar", "baz") + ) + ctx.Publish = true + ctx.Artifacts.Add(artifact.Artifact{ + Name: "bin", + Path: "doesnt mather", + Goos: "darwin", + Goarch: "amd64", + Type: artifact.Binary, + }) client := &DummyClient{} testlib.AssertSkipped(t, doRun(ctx, client)) assert.False(t, client.CreatedFile) diff --git a/pipeline/brew/doc.go b/pipeline/brew/doc.go new file mode 100644 index 000000000..2cddc12c0 --- /dev/null +++ b/pipeline/brew/doc.go @@ -0,0 +1,3 @@ +// Package brew implements the Pipe, providing formula generation and +// uploading it to a configured repo. +package brew diff --git a/pipeline/brew/testdata/run_pipe_enterprise.rb b/pipeline/brew/testdata/run_pipe_enterprise.rb new file mode 100644 index 000000000..4b24ce03a --- /dev/null +++ b/pipeline/brew/testdata/run_pipe_enterprise.rb @@ -0,0 +1,33 @@ +class RunPipe < Formula + desc "A run pipe test formula" + homepage "https://github.com/goreleaser" + url "http://github.example.org/test/test/releases/download/v1.0.1/bin.tar.gz" + version "1.0.1" + sha256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + + depends_on "zsh" + depends_on "bash" + + conflicts_with "gtk+" + conflicts_with "qt" + + def install + bin.install "foo" + end + + def caveats + "don't do this" + end + + plist_options :startup => false + + def plist; <<-EOS.undent + whatever + EOS + end + + test do + system "true" + system "#{bin}/foo -h" + end +end diff --git a/pipeline/build/build.go b/pipeline/build/build.go index 1aef8e5a1..749311dcf 100644 --- a/pipeline/build/build.go +++ b/pipeline/build/build.go @@ -7,12 +7,14 @@ import ( "strings" "github.com/apex/log" - "github.com/goreleaser/goreleaser/config" - "github.com/goreleaser/goreleaser/context" - "github.com/goreleaser/goreleaser/internal/buildtarget" - "github.com/goreleaser/goreleaser/internal/ext" "github.com/pkg/errors" "golang.org/x/sync/errgroup" + + "github.com/goreleaser/goreleaser/config" + "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" + "github.com/goreleaser/goreleaser/internal/buildtarget" + "github.com/goreleaser/goreleaser/internal/ext" ) // Pipe for build @@ -104,22 +106,21 @@ func runHook(env []string, hook string) error { } func doBuild(ctx *context.Context, build config.Build, target buildtarget.Target) error { - var binaryName = build.Binary + ext.For(target) - var prettyName = binaryName - if ctx.Config.Archive.Format == "binary" { - var err error - binaryName, err = nameFor(ctx, target, build.Binary) - if err != nil { - return err - } - binaryName = binaryName + ext.For(target) - } - folder, err := nameFor(ctx, target, ctx.Config.ProjectName) - if err != nil { - return err - } - var binary = filepath.Join(ctx.Config.Dist, folder, binaryName) - ctx.AddBinary(target.String(), folder, prettyName, binary) + var ext = ext.For(target) + var binaryName = build.Binary + ext + var binary = filepath.Join(ctx.Config.Dist, target.String(), binaryName) + ctx.Artifacts.Add(artifact.Artifact{ + Type: artifact.Binary, + Path: binary, + Name: binaryName, + Goos: target.OS, + Goarch: target.Arch, + Goarm: target.Arm, + Extra: map[string]string{ + "Binary": build.Binary, + "Ext": ext, + }, + }) log.WithField("binary", binary).Info("building") cmd := []string{"go", "build"} if build.Flags != "" { diff --git a/pipeline/build/build_test.go b/pipeline/build/build_test.go index c4c01934a..38d6afd55 100644 --- a/pipeline/build/build_test.go +++ b/pipeline/build/build_test.go @@ -46,7 +46,7 @@ func TestRunFullPipe(t *testing.T) { folder, back := testlib.Mktmp(t) defer back() writeGoodMain(t, folder) - var binary = filepath.Join(folder, "testing") + var binary = filepath.Join(folder, buildtarget.Runtime.String(), "testing") var pre = filepath.Join(folder, "pre") var post = filepath.Join(folder, "post") var config = config.Project{ @@ -80,41 +80,11 @@ func TestRunFullPipe(t *testing.T) { assert.True(t, exists(post), post) } -func TestRunPipeFormatBinary(t *testing.T) { - folder, back := testlib.Mktmp(t) - defer back() - writeGoodMain(t, folder) - var binary = filepath.Join(folder, "binary-testing-bar") - var config = config.Project{ - ProjectName: "testing", - Dist: folder, - Builds: []config.Build{ - { - Binary: "testing", - Goos: []string{ - runtime.GOOS, - }, - Goarch: []string{ - runtime.GOARCH, - }, - }, - }, - Archive: config.Archive{ - Format: "binary", - NameTemplate: "binary-{{.Binary}}-{{.Env.Foo}}", - }, - } - ctx := context.New(config) - ctx.Env = map[string]string{"Foo": "bar"} - assert.NoError(t, Pipe{}.Run(ctx)) - assert.True(t, exists(binary)) -} - func TestRunPipeArmBuilds(t *testing.T) { folder, back := testlib.Mktmp(t) defer back() writeGoodMain(t, folder) - var binary = filepath.Join(folder, "armtesting") + var binary = filepath.Join(folder, "linuxarm6", "armtesting") var config = config.Project{ Builds: []config.Build{ { @@ -178,40 +148,6 @@ func TestRunPipeWithInvalidOS(t *testing.T) { assert.NoError(t, Pipe{}.Run(context.New(config))) } -func TestRunInvalidNametemplate(t *testing.T) { - folder, back := testlib.Mktmp(t) - defer back() - writeGoodMain(t, folder) - for format, msg := range map[string]string{ - "binary": `template: bar:1: unexpected "}" in operand`, - "tar.gz": `template: foo:1: unexpected "}" in operand`, - "zip": `template: foo:1: unexpected "}" in operand`, - } { - t.Run(format, func(t *testing.T) { - var config = config.Project{ - ProjectName: "foo", - Builds: []config.Build{ - { - Binary: "bar", - Flags: "-v", - Goos: []string{ - runtime.GOOS, - }, - Goarch: []string{ - runtime.GOARCH, - }, - }, - }, - Archive: config.Archive{ - Format: format, - NameTemplate: "{{.Binary}", - }, - } - assert.EqualError(t, Pipe{}.Run(context.New(config)), msg) - }) - } -} - func TestRunInvalidLdflags(t *testing.T) { folder, back := testlib.Mktmp(t) defer back() diff --git a/pipeline/changelog/changelog_test.go b/pipeline/changelog/changelog_test.go index 5cf58fd90..76f7bd782 100644 --- a/pipeline/changelog/changelog_test.go +++ b/pipeline/changelog/changelog_test.go @@ -7,7 +7,6 @@ import ( "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" "github.com/goreleaser/goreleaser/internal/testlib" - "github.com/stretchr/testify/assert" ) diff --git a/pipeline/checksums/checksums.go b/pipeline/checksums/checksums.go index 8a61fc87b..8399db3eb 100644 --- a/pipeline/checksums/checksums.go +++ b/pipeline/checksums/checksums.go @@ -1,5 +1,3 @@ -// Package checksums provides a Pipe that creates .checksums files for -// each artifact. package checksums import ( @@ -8,9 +6,11 @@ import ( "path/filepath" "github.com/apex/log" + "golang.org/x/sync/errgroup" + "github.com/goreleaser/goreleaser/checksum" "github.com/goreleaser/goreleaser/context" - "golang.org/x/sync/errgroup" + "github.com/goreleaser/goreleaser/internal/artifact" ) // Pipe for checksums @@ -20,6 +20,14 @@ func (Pipe) String() string { return "calculating checksums" } +// 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" + } + return nil +} + // Run the pipe func (Pipe) Run(ctx *context.Context) (err error) { filename, err := filenameFor(ctx) @@ -34,38 +42,36 @@ func (Pipe) Run(ctx *context.Context) (err error) { if err != nil { return err } - defer func() { - if err := file.Close(); err != nil { - log.WithError(err).Errorf("failed to close %s", file.Name()) - } - ctx.AddArtifact(file.Name()) - ctx.AddChecksum(file.Name()) - }() + defer file.Close() // nolint: errcheck + + // TODO: parallelism should be considered here as well. var g errgroup.Group - for _, artifact := range ctx.Artifacts { + for _, artifact := range ctx.Artifacts.Filter( + artifact.Or( + artifact.ByType(artifact.UploadableArchive), + artifact.ByType(artifact.UploadableBinary), + artifact.ByType(artifact.LinuxPackage), + ), + ).List() { artifact := artifact g.Go(func() error { return checksums(ctx, file, artifact) }) } + ctx.Artifacts.Add(artifact.Artifact{ + Type: artifact.Checksum, + Path: file.Name(), + Name: filename, + }) return g.Wait() } -// 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" - } - return nil -} - -func checksums(ctx *context.Context, file *os.File, name string) error { - log.WithField("file", name).Info("checksumming") - var artifact = filepath.Join(ctx.Config.Dist, name) - sha, err := checksum.SHA256(artifact) +func checksums(ctx *context.Context, file *os.File, artifact artifact.Artifact) error { + log.WithField("file", artifact.Name).Info("checksumming") + sha, err := checksum.SHA256(artifact.Path) if err != nil { return err } - _, err = file.WriteString(fmt.Sprintf("%v %v\n", sha, name)) + _, err = file.WriteString(fmt.Sprintf("%v %v\n", sha, artifact.Name)) return err } diff --git a/pipeline/checksums/checksums_test.go b/pipeline/checksums/checksums_test.go index 246eb299a..cbb5aa6bb 100644 --- a/pipeline/checksums/checksums_test.go +++ b/pipeline/checksums/checksums_test.go @@ -7,6 +7,7 @@ import ( "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" "github.com/stretchr/testify/assert" ) @@ -21,18 +22,26 @@ func TestPipe(t *testing.T) { assert.NoError(t, err) var file = filepath.Join(folder, binary) assert.NoError(t, ioutil.WriteFile(file, []byte("some string"), 0644)) - var ctx = &context.Context{ - Config: config.Project{ + var ctx = context.New( + config.Project{ Dist: folder, ProjectName: binary, Checksum: config.Checksum{ NameTemplate: "{{ .ProjectName }}_checksums.txt", }, }, - } - ctx.AddArtifact(file) + ) + ctx.Artifacts.Add(artifact.Artifact{ + Name: binary, + Path: file, + Type: artifact.UploadableBinary, + }) assert.NoError(t, Pipe{}.Run(ctx)) - assert.Contains(t, ctx.Artifacts, checksums, binary) + var artifacts []string + for _, a := range ctx.Artifacts.List() { + artifacts = append(artifacts, a.Name) + } + assert.Contains(t, artifacts, checksums, binary) bts, err := ioutil.ReadFile(filepath.Join(folder, checksums)) assert.NoError(t, err) assert.Equal(t, "61d034473102d7dac305902770471fd50f4c5b26f6831a56dd90b5184b3c30fc binary\n", string(bts)) @@ -41,15 +50,19 @@ func TestPipe(t *testing.T) { func TestPipeFileNotExist(t *testing.T) { folder, err := ioutil.TempDir("", "goreleasertest") assert.NoError(t, err) - var ctx = &context.Context{ - Config: config.Project{ + var ctx = context.New( + config.Project{ Dist: folder, Checksum: config.Checksum{ NameTemplate: "checksums.txt", }, }, - } - ctx.AddArtifact("nope") + ) + ctx.Artifacts.Add(artifact.Artifact{ + Name: "nope", + Path: "/nope", + Type: artifact.UploadableBinary, + }) err = Pipe{}.Run(ctx) assert.Error(t, err) assert.Contains(t, err.Error(), "/nope: no such file or directory") @@ -58,16 +71,19 @@ func TestPipeFileNotExist(t *testing.T) { func TestPipeInvalidNameTemplate(t *testing.T) { folder, err := ioutil.TempDir("", "goreleasertest") assert.NoError(t, err) - var ctx = &context.Context{ - Config: config.Project{ + var ctx = context.New( + config.Project{ Dist: folder, ProjectName: "name", Checksum: config.Checksum{ NameTemplate: "{{ .Pro }_checksums.txt", }, }, - } - ctx.AddArtifact("whatever") + ) + ctx.Artifacts.Add(artifact.Artifact{ + Name: "whatever", + Type: artifact.UploadableBinary, + }) err = Pipe{}.Run(ctx) assert.Error(t, err) assert.Equal(t, `template: checksums:1: unexpected "}" in operand`, err.Error()) @@ -78,15 +94,18 @@ func TestPipeCouldNotOpenChecksumsTxt(t *testing.T) { assert.NoError(t, err) var file = filepath.Join(folder, "checksums.txt") assert.NoError(t, ioutil.WriteFile(file, []byte("some string"), 0000)) - var ctx = &context.Context{ - Config: config.Project{ + var ctx = context.New( + config.Project{ Dist: folder, Checksum: config.Checksum{ NameTemplate: "checksums.txt", }, }, - } - ctx.AddArtifact("nope") + ) + ctx.Artifacts.Add(artifact.Artifact{ + Name: "whatever", + Type: artifact.UploadableBinary, + }) err = Pipe{}.Run(ctx) assert.Error(t, err) assert.Contains(t, err.Error(), "/checksums.txt: permission denied") diff --git a/pipeline/checksums/doc.go b/pipeline/checksums/doc.go new file mode 100644 index 000000000..e2ba0cb42 --- /dev/null +++ b/pipeline/checksums/doc.go @@ -0,0 +1,3 @@ +// Package checksums provides a Pipe that creates .checksums files for +// each artifact. +package checksums diff --git a/pipeline/default.go b/pipeline/defaulter.go similarity index 100% rename from pipeline/default.go rename to pipeline/defaulter.go diff --git a/pipeline/doc.go b/pipeline/doc.go new file mode 100644 index 000000000..9f32a880d --- /dev/null +++ b/pipeline/doc.go @@ -0,0 +1,3 @@ +// Package pipeline provides the generic piper and defaulter interfaces, +// which should be implemented add new pipes to goreleaser.. +package pipeline diff --git a/pipeline/docker/docker.go b/pipeline/docker/docker.go index 88a2fad49..b1612c401 100644 --- a/pipeline/docker/docker.go +++ b/pipeline/docker/docker.go @@ -10,10 +10,12 @@ import ( "text/template" "github.com/apex/log" + "github.com/pkg/errors" + "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/pipeline" - "github.com/pkg/errors" ) // ErrNoDocker is shown when docker cannot be found in $PATH @@ -26,6 +28,33 @@ func (Pipe) String() string { return "creating Docker images" } +// Default sets the pipe defaults +func (Pipe) Default(ctx *context.Context) error { + for i := range ctx.Config.Dockers { + var docker = &ctx.Config.Dockers[i] + if docker.TagTemplate == "" { + docker.TagTemplate = "{{ .Version }}" + } + if docker.Goos == "" { + docker.Goos = "linux" + } + if docker.Goarch == "" { + docker.Goarch = "amd64" + } + } + // only set defaults if there is exacly 1 docker setup in the config file. + if len(ctx.Config.Dockers) != 1 { + return nil + } + if ctx.Config.Dockers[0].Binary == "" { + ctx.Config.Dockers[0].Binary = ctx.Config.Builds[0].Binary + } + if ctx.Config.Dockers[0].Dockerfile == "" { + ctx.Config.Dockers[0].Dockerfile = "Dockerfile" + } + return nil +} + // Run the pipe func (Pipe) Run(ctx *context.Context) error { if len(ctx.Config.Dockers) == 0 || ctx.Config.Dockers[0].Image == "" { @@ -38,49 +67,28 @@ func (Pipe) Run(ctx *context.Context) error { return doRun(ctx) } -// Default sets the pipe defaults -func (Pipe) Default(ctx *context.Context) error { - for i := range ctx.Config.Dockers { - if ctx.Config.Dockers[i].TagTemplate == "" { - ctx.Config.Dockers[i].TagTemplate = "{{ .Version }}" - } - } - // only set defaults if there is exacly 1 docker setup in the config file. - if len(ctx.Config.Dockers) != 1 { - return nil - } - if ctx.Config.Dockers[0].Goos == "" { - ctx.Config.Dockers[0].Goos = "linux" - } - if ctx.Config.Dockers[0].Goarch == "" { - ctx.Config.Dockers[0].Goarch = "amd64" - } - if ctx.Config.Dockers[0].Binary == "" { - ctx.Config.Dockers[0].Binary = ctx.Config.Builds[0].Binary - } - if ctx.Config.Dockers[0].Dockerfile == "" { - ctx.Config.Dockers[0].Dockerfile = "Dockerfile" - } - return nil -} - func doRun(ctx *context.Context) error { + // TODO: could be done in parallel. for _, docker := range ctx.Config.Dockers { - var imagePlatform = docker.Goos + docker.Goarch + docker.Goarm - for platform, groups := range ctx.Binaries { - if platform != imagePlatform { - continue - } - for folder, binaries := range groups { - for _, binary := range binaries { - if binary.Name != docker.Binary { - continue - } - var err = process(ctx, folder, docker, binary) - if err != nil && !pipeline.IsSkip(err) { - return err - } - } + log.WithField("docker", docker).Debug("looking for binaries matching") + var binaries = ctx.Artifacts.Filter( + artifact.And( + artifact.ByGoos(docker.Goos), + artifact.ByGoarch(docker.Goarch), + artifact.ByGoarm(docker.Goarm), + artifact.ByType(artifact.Binary), + func(a artifact.Artifact) bool { + return a.Extra["Binary"] == docker.Binary + }, + ), + ).List() + if len(binaries) == 0 { + log.Warn("no binaries found") + } + for _, binary := range binaries { + var err = process(ctx, docker, binary) + if err != nil && !pipeline.IsSkip(err) { + return err } } } @@ -105,8 +113,8 @@ func tagName(ctx *context.Context, docker config.Docker) (string, error) { return out.String(), err } -func process(ctx *context.Context, folder string, docker config.Docker, binary context.Binary) error { - var root = filepath.Join(ctx.Config.Dist, folder) +func process(ctx *context.Context, docker config.Docker, artifact artifact.Artifact) error { + var root = filepath.Dir(artifact.Path) var dockerfile = filepath.Join(root, filepath.Base(docker.Dockerfile)) tag, err := tagName(ctx, docker) if err != nil { @@ -143,17 +151,16 @@ func publish(ctx *context.Context, docker config.Docker, image, latest string) e if ctx.Config.Release.Draft { return pipeline.Skip("release is marked as draft") } - if err := dockerPush(image); err != nil { + if err := dockerPush(ctx, image); err != nil { return err } - ctx.AddDocker(image) if !docker.Latest { return nil } if err := dockerTag(image, latest); err != nil { return err } - return dockerPush(latest) + return dockerPush(ctx, latest) } func dockerBuild(root, dockerfile, image string) error { @@ -182,7 +189,7 @@ func dockerTag(image, tag string) error { return nil } -func dockerPush(image string) error { +func dockerPush(ctx *context.Context, image string) error { log.WithField("image", image).Info("pushing docker image") /* #nosec */ var cmd = exec.Command("docker", "push", image) @@ -192,5 +199,10 @@ func dockerPush(image string) error { return errors.Wrapf(err, "failed to push docker image: \n%s", string(out)) } log.Debugf("docker push output: \n%s", string(out)) + ctx.Artifacts.Add(artifact.Artifact{ + Type: artifact.DockerImage, + Name: image, + // TODO: are the rest of the params relevant here? + }) return nil } diff --git a/pipeline/docker/docker_test.go b/pipeline/docker/docker_test.go index 04d9348a7..a8cd1f493 100644 --- a/pipeline/docker/docker_test.go +++ b/pipeline/docker/docker_test.go @@ -7,30 +7,19 @@ import ( "path/filepath" "testing" - "github.com/apex/log" "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/pipeline" "github.com/stretchr/testify/assert" ) -func killAndRm() { - log.Info("killing registry") +func killAndRm(t *testing.T) { + t.Log("killing registry") _ = exec.Command("docker", "kill", "registry").Run() _ = exec.Command("docker", "rm", "registry").Run() } -func TestMain(m *testing.M) { - killAndRm() - if err := exec.Command( - "docker", "run", "-d", "-p", "5000:5000", "--name", "registry", "registry:2", - ).Run(); err != nil { - log.WithError(err).Fatal("failed to start docker registry") - } - defer killAndRm() - os.Exit(m.Run()) -} - func TestRunPipe(t *testing.T) { folder, err := ioutil.TempDir("", "archivetest") assert.NoError(t, err) @@ -90,11 +79,21 @@ func TestRunPipe(t *testing.T) { _ = exec.Command("docker", "rmi", img).Run() } + killAndRm(t) + if err := exec.Command( + "docker", "run", "-d", "-p", "5000:5000", "--name", "registry", "registry:2", + ).Run(); err != nil { + t.Log("failed to start docker registry", err) + t.FailNow() + } + defer killAndRm(t) + for name, docker := range table { - t.Run(name, func(t *testing.T) { + t.Run(name, func(tt *testing.T) { var ctx = &context.Context{ - Version: "1.0.0", - Publish: true, + Version: "1.0.0", + Publish: true, + Artifacts: artifact.New(), Git: context.GitInfo{ CurrentTag: "v1.0.0", }, @@ -107,13 +106,24 @@ func TestRunPipe(t *testing.T) { }, Env: map[string]string{"FOO": "123"}, } - for _, plat := range []string{"linuxamd64", "linux386", "darwinamd64"} { - ctx.AddBinary(plat, "mybin", "mybin", binPath) + for _, os := range []string{"linux", "darwin"} { + for _, arch := range []string{"amd64", "386"} { + ctx.Artifacts.Add(artifact.Artifact{ + Name: "mybin", + Path: binPath, + Goarch: arch, + Goos: os, + Type: artifact.Binary, + Extra: map[string]string{ + "Binary": "mybin", + }, + }) + } } if docker.err == "" { - assert.NoError(t, Pipe{}.Run(ctx)) + assert.NoError(tt, Pipe{}.Run(ctx)) } else { - assert.EqualError(t, Pipe{}.Run(ctx), docker.err) + assert.EqualError(tt, Pipe{}.Run(ctx), docker.err) } }) } diff --git a/pipeline/fpm/fpm.go b/pipeline/fpm/fpm.go index 141367cec..2e7618442 100644 --- a/pipeline/fpm/fpm.go +++ b/pipeline/fpm/fpm.go @@ -6,14 +6,16 @@ import ( "io/ioutil" "os/exec" "path/filepath" - "strings" "github.com/apex/log" - "github.com/goreleaser/goreleaser/context" - "github.com/goreleaser/goreleaser/internal/linux" - "github.com/goreleaser/goreleaser/pipeline" "github.com/pkg/errors" "golang.org/x/sync/errgroup" + + "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" + "github.com/goreleaser/goreleaser/internal/linux" + "github.com/goreleaser/goreleaser/internal/nametemplate" + "github.com/goreleaser/goreleaser/pipeline" ) // ErrNoFPM is shown when fpm cannot be found in $PATH @@ -50,28 +52,33 @@ func doRun(ctx *context.Context) error { var g errgroup.Group sem := make(chan bool, ctx.Parallelism) for _, format := range ctx.Config.FPM.Formats { - for platform, groups := range ctx.Binaries { - if !strings.Contains(platform, "linux") { - log.WithField("platform", platform).Debug("skipped non-linux builds for fpm") - continue - } + for platform, artifacts := range ctx.Artifacts.Filter( + artifact.And( + artifact.ByType(artifact.Binary), + artifact.ByGoos("linux"), + ), + ).GroupByPlatform() { sem <- true format := format - arch := linux.Arch(platform) - for folder, binaries := range groups { - g.Go(func() error { - defer func() { - <-sem - }() - return create(ctx, format, folder, arch, binaries) - }) - } + arch := linux.Arch(platform) // TODO: could probably pass artifact.Goarch here + artifacts := artifacts + g.Go(func() error { + defer func() { + <-sem + }() + return create(ctx, format, arch, artifacts) + }) } } return g.Wait() } -func create(ctx *context.Context, format, folder, arch string, binaries []context.Binary) error { +func create(ctx *context.Context, format, arch string, binaries []artifact.Artifact) error { + // TODO: should add template support here probably... for now, let's use archive's template + folder, err := nametemplate.Apply(ctx, binaries[0], ctx.Config.ProjectName) + if err != nil { + return err + } var path = filepath.Join(ctx.Config.Dist, folder) var file = path + "." + format var log = log.WithField("format", format).WithField("arch", arch) @@ -111,7 +118,14 @@ func create(ctx *context.Context, format, folder, arch string, binaries []contex if out, err := exec.Command("fpm", options...).CombinedOutput(); err != nil { return errors.Wrap(err, string(out)) } - ctx.AddArtifact(file) + ctx.Artifacts.Add(artifact.Artifact{ + Type: artifact.LinuxPackage, + Name: folder + "." + format, + Path: file, + Goos: binaries[0].Goos, + Goarch: binaries[0].Goarch, + Goarm: binaries[0].Goarm, + }) return nil } diff --git a/pipeline/fpm/fpm_test.go b/pipeline/fpm/fpm_test.go index 0c9f22f4f..8d1e35654 100644 --- a/pipeline/fpm/fpm_test.go +++ b/pipeline/fpm/fpm_test.go @@ -9,6 +9,7 @@ import ( "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/internal/testlib" "github.com/stretchr/testify/assert" ) @@ -39,9 +40,14 @@ func TestRunPipe(t *testing.T) { Version: "1.0.0", Parallelism: runtime.NumCPU(), Debug: true, + Artifacts: artifact.New(), Config: config.Project{ ProjectName: "mybin", Dist: dist, + // TODO: remove this when fpm have its own name template + Archive: config.Archive{ + NameTemplate: "foo", + }, FPM: config.FPM{ Formats: []string{"deb", "rpm"}, Dependencies: []string{"make"}, @@ -54,8 +60,16 @@ func TestRunPipe(t *testing.T) { }, }, } - for _, plat := range []string{"linuxamd64", "linux386", "darwinamd64"} { - ctx.AddBinary(plat, "mybin", "mybin", binPath) + for _, goos := range []string{"linux", "darwin"} { + for _, goarch := range []string{"amd64", "386"} { + ctx.Artifacts.Add(artifact.Artifact{ + Name: "mybin", + Path: binPath, + Goarch: goarch, + Goos: goos, + Type: artifact.Binary, + }) + } } assert.NoError(t, Pipe{}.Run(ctx)) } @@ -87,6 +101,7 @@ func TestCreateFileDoesntExist(t *testing.T) { var ctx = &context.Context{ Version: "1.0.0", Parallelism: runtime.NumCPU(), + Artifacts: artifact.New(), Config: config.Project{ Dist: dist, FPM: config.FPM{ @@ -97,23 +112,16 @@ func TestCreateFileDoesntExist(t *testing.T) { }, }, } - ctx.AddBinary("linuxamd64", "mybin", "mybin", filepath.Join(dist, "mybin", "mybin")) + ctx.Artifacts.Add(artifact.Artifact{ + Name: "mybin", + Path: filepath.Join(dist, "mybin", "mybin"), + Goos: "linux", + Goarch: "amd64", + Type: artifact.Binary, + }) assert.Error(t, Pipe{}.Run(ctx)) } -func TestRunPipeWithExtraFiles(t *testing.T) { - var ctx = &context.Context{ - Version: "1.0.0", - Parallelism: runtime.NumCPU(), - Config: config.Project{ - FPM: config.FPM{ - Formats: []string{"deb", "rpm"}, - }, - }, - } - assert.NoError(t, Pipe{}.Run(ctx)) -} - func TestDefault(t *testing.T) { var ctx = &context.Context{ Config: config.Project{ diff --git a/pipeline/git/doc.go b/pipeline/git/doc.go new file mode 100644 index 000000000..138e84cfd --- /dev/null +++ b/pipeline/git/doc.go @@ -0,0 +1,3 @@ +// Package git implements the Pipe interface getting and validating the +// current git repository state +package git diff --git a/pipeline/git/git.go b/pipeline/git/git.go index ce514099c..19d9bad08 100644 --- a/pipeline/git/git.go +++ b/pipeline/git/git.go @@ -1,5 +1,3 @@ -// Package git implements the Pipe interface getting and validating the -// current git repository state package git import ( diff --git a/pipeline/piper.go b/pipeline/piper.go index 767250b50..ff4028515 100644 --- a/pipeline/piper.go +++ b/pipeline/piper.go @@ -1,4 +1,3 @@ -// Package pipeline provides a generic pipe interface. package pipeline import ( diff --git a/pipeline/release/body.go b/pipeline/release/body.go index 25e86325e..0a130d299 100644 --- a/pipeline/release/body.go +++ b/pipeline/release/body.go @@ -6,6 +6,7 @@ import ( "text/template" "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" ) const bodyTemplate = `{{ .ReleaseNotes }} @@ -34,13 +35,17 @@ func describeBody(ctx *context.Context) (bytes.Buffer, error) { func describeBodyVersion(ctx *context.Context, version string) (bytes.Buffer, error) { var out bytes.Buffer var template = template.Must(template.New("release").Parse(bodyTemplate)) + var dockers []string + for _, a := range ctx.Artifacts.Filter(artifact.ByType(artifact.DockerImage)).List() { + dockers = append(dockers, a.Name) + } err := template.Execute(&out, struct { ReleaseNotes, GoVersion string DockerImages []string }{ ReleaseNotes: ctx.ReleaseNotes, GoVersion: version, - DockerImages: ctx.Dockers, + DockerImages: dockers, }) return out, err } diff --git a/pipeline/release/body_test.go b/pipeline/release/body_test.go index f0918bb08..d5edf043e 100644 --- a/pipeline/release/body_test.go +++ b/pipeline/release/body_test.go @@ -5,19 +5,25 @@ import ( "os" "testing" + "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" "github.com/stretchr/testify/assert" ) func TestDescribeBody(t *testing.T) { var changelog = "\nfeature1: description\nfeature2: other description" - var ctx = &context.Context{ - ReleaseNotes: changelog, - Dockers: []string{ - "goreleaser/goreleaser:0.40.0", - "goreleaser/goreleaser:latest", - "goreleaser/godownloader:v0.1.0", - }, + var ctx = context.New(config.Project{}) + ctx.ReleaseNotes = changelog + for _, d := range []string{ + "goreleaser/goreleaser:0.40.0", + "goreleaser/goreleaser:latest", + "goreleaser/godownloader:v0.1.0", + } { + ctx.Artifacts.Add(artifact.Artifact{ + Name: d, + Type: artifact.DockerImage, + }) } out, err := describeBodyVersion(ctx, "go version go1.9 darwin/amd64") assert.NoError(t, err) diff --git a/pipeline/release/doc.go b/pipeline/release/doc.go new file mode 100644 index 000000000..b6e2a9d5c --- /dev/null +++ b/pipeline/release/doc.go @@ -0,0 +1,3 @@ +// Package release implements Pipe and manages github releases and its +// artifacts. +package release diff --git a/pipeline/release/release.go b/pipeline/release/release.go index 6644a3bab..5ce1fe025 100644 --- a/pipeline/release/release.go +++ b/pipeline/release/release.go @@ -1,16 +1,15 @@ -// Package release implements Pipe and manages github releases and its -// artifacts. package release import ( "os" - "path/filepath" "github.com/apex/log" + "golang.org/x/sync/errgroup" + "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/internal/client" "github.com/goreleaser/goreleaser/pipeline" - "golang.org/x/sync/errgroup" ) // Pipe for github release @@ -20,15 +19,6 @@ func (Pipe) String() string { return "releasing to GitHub" } -// Run the pipe -func (Pipe) Run(ctx *context.Context) error { - c, err := client.NewGitHub(ctx) - if err != nil { - return err - } - return doRun(ctx, c) -} - // Default sets the pipe defaults func (Pipe) Default(ctx *context.Context) error { if ctx.Config.Release.NameTemplate == "" { @@ -45,6 +35,15 @@ func (Pipe) Default(ctx *context.Context) error { return nil } +// Run the pipe +func (Pipe) Run(ctx *context.Context) error { + c, err := client.NewGitHub(ctx) + if err != nil { + return err + } + return doRun(ctx, c) +} + func doRun(ctx *context.Context, c client.Client) error { if !ctx.Publish { return pipeline.Skip("--skip-publish is set") @@ -62,7 +61,15 @@ func doRun(ctx *context.Context, c client.Client) error { } var g errgroup.Group sem := make(chan bool, ctx.Parallelism) - for _, artifact := range ctx.Artifacts { + for _, artifact := range ctx.Artifacts.Filter( + artifact.Or( + artifact.ByType(artifact.UploadableArchive), + artifact.ByType(artifact.UploadableBinary), + artifact.ByType(artifact.Checksum), + artifact.ByType(artifact.Signature), + artifact.ByType(artifact.LinuxPackage), + ), + ).List() { sem <- true artifact := artifact g.Go(func() error { @@ -75,14 +82,12 @@ func doRun(ctx *context.Context, c client.Client) error { return g.Wait() } -func upload(ctx *context.Context, c client.Client, releaseID int, artifact string) error { - var path = filepath.Join(ctx.Config.Dist, artifact) - file, err := os.Open(path) +func upload(ctx *context.Context, c client.Client, releaseID int, artifact artifact.Artifact) error { + file, err := os.Open(artifact.Path) if err != nil { return err } - defer func() { _ = file.Close() }() - _, name := filepath.Split(path) - log.WithField("file", file.Name()).WithField("name", name).Info("uploading to release") - return c.Upload(ctx, releaseID, name, file) + defer file.Close() // nolint: errcheck + log.WithField("file", file.Name()).WithField("name", artifact.Name).Info("uploading to release") + return c.Upload(ctx, releaseID, artifact.Name, file) } diff --git a/pipeline/release/release_test.go b/pipeline/release/release_test.go index 6f2bd37c9..5d99a4bbd 100644 --- a/pipeline/release/release_test.go +++ b/pipeline/release/release_test.go @@ -10,6 +10,7 @@ import ( "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/internal/testlib" "github.com/stretchr/testify/assert" ) @@ -37,8 +38,16 @@ func TestRunPipe(t *testing.T) { var ctx = context.New(config) ctx.Git = context.GitInfo{CurrentTag: "v1.0.0"} ctx.Publish = true - ctx.AddArtifact(tarfile.Name()) - ctx.AddArtifact(debfile.Name()) + ctx.Artifacts.Add(artifact.Artifact{ + Type: artifact.UploadableArchive, + Name: "bin.tar.gz", + Path: tarfile.Name(), + }) + ctx.Artifacts.Add(artifact.Artifact{ + Type: artifact.LinuxPackage, + Name: "bin.deb", + Path: debfile.Name(), + }) client := &DummyClient{} assert.NoError(t, doRun(ctx, client)) assert.True(t, client.CreatedRelease) @@ -79,7 +88,11 @@ func TestRunPipeWithFileThatDontExist(t *testing.T) { var ctx = context.New(config) ctx.Git = context.GitInfo{CurrentTag: "v1.0.0"} ctx.Publish = true - ctx.AddArtifact("this-file-wont-exist-hopefully") + ctx.Artifacts.Add(artifact.Artifact{ + Type: artifact.UploadableArchive, + Name: "bin.tar.gz", + Path: "/nope/nope/nope", + }) client := &DummyClient{} assert.Error(t, doRun(ctx, client)) assert.True(t, client.CreatedRelease) @@ -102,7 +115,11 @@ func TestRunPipeUploadFailure(t *testing.T) { var ctx = context.New(config) ctx.Git = context.GitInfo{CurrentTag: "v1.0.0"} ctx.Publish = true - ctx.AddArtifact(tarfile.Name()) + ctx.Artifacts.Add(artifact.Artifact{ + Type: artifact.UploadableArchive, + Name: "bin.tar.gz", + Path: tarfile.Name(), + }) client := &DummyClient{ FailToUpload: true, } diff --git a/pipeline/release/remote_test.go b/pipeline/release/remote_test.go index 39fdf46b7..6c04f1d2b 100644 --- a/pipeline/release/remote_test.go +++ b/pipeline/release/remote_test.go @@ -4,7 +4,6 @@ import ( "testing" "github.com/goreleaser/goreleaser/internal/testlib" - "github.com/stretchr/testify/assert" ) diff --git a/pipeline/sign/sign.go b/pipeline/sign/sign.go index e06e4aedf..18e641e80 100644 --- a/pipeline/sign/sign.go +++ b/pipeline/sign/sign.go @@ -7,6 +7,7 @@ import ( "path/filepath" "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/pipeline" ) @@ -39,9 +40,15 @@ func (Pipe) Default(ctx *context.Context) error { func (Pipe) Run(ctx *context.Context) error { switch ctx.Config.Sign.Artifacts { case "checksum": - return sign(ctx, ctx.Checksums) + return sign(ctx, ctx.Artifacts.Filter(artifact.ByType(artifact.Checksum)).List()) case "all": - return sign(ctx, ctx.Artifacts) + return sign(ctx, ctx.Artifacts.Filter( + artifact.Or( + artifact.ByType(artifact.UploadableArchive), + artifact.ByType(artifact.UploadableBinary), + artifact.ByType(artifact.Checksum), + artifact.ByType(artifact.LinuxPackage), + )).List()) case "none": return pipeline.Skip("artifact signing disabled") default: @@ -49,7 +56,7 @@ func (Pipe) Run(ctx *context.Context) error { } } -func sign(ctx *context.Context, artifacts []string) error { +func sign(ctx *context.Context, artifacts []artifact.Artifact) error { var sigs []string for _, a := range artifacts { sig, err := signone(ctx, a) @@ -59,17 +66,20 @@ func sign(ctx *context.Context, artifacts []string) error { sigs = append(sigs, sig) } for _, sig := range sigs { - ctx.AddArtifact(sig) + ctx.Artifacts.Add(artifact.Artifact{ + Type: artifact.Signature, + Name: sig, + Path: filepath.Join(ctx.Config.Dist, sig), + }) } return nil } -func signone(ctx *context.Context, artifact string) (string, error) { +func signone(ctx *context.Context, artifact artifact.Artifact) (string, error) { cfg := ctx.Config.Sign - artifact = filepath.Join(ctx.Config.Dist, artifact) env := map[string]string{ - "artifact": artifact, + "artifact": artifact.Path, } env["signature"] = expand(cfg.Signature, env) @@ -87,7 +97,7 @@ func signone(ctx *context.Context, artifact string) (string, error) { if err != nil { return "", fmt.Errorf("sign: %s failed with %q", cfg.Cmd, string(output)) } - return env["signature"], nil + return filepath.Base(env["signature"]), nil } func expand(s string, env map[string]string) string { diff --git a/pipeline/sign/sign_test.go b/pipeline/sign/sign_test.go index fe791c776..c5ae034a9 100644 --- a/pipeline/sign/sign_test.go +++ b/pipeline/sign/sign_test.go @@ -11,7 +11,7 @@ import ( "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" - + "github.com/goreleaser/goreleaser/internal/artifact" "github.com/stretchr/testify/assert" ) @@ -44,9 +44,7 @@ func TestSignInvalidArtifacts(t *testing.T) { func TestSignArtifacts(t *testing.T) { // fix permission on keyring dir to suppress warning about insecure permissions - if err := os.Chmod(keyring, 0700); err != nil { - t.Fatal("Chmod: ", err) - } + assert.NoError(t, os.Chmod(keyring, 0700)) tests := []struct { desc string @@ -55,24 +53,20 @@ func TestSignArtifacts(t *testing.T) { }{ { desc: "sign all artifacts", - ctx: &context.Context{ - Config: config.Project{ + ctx: context.New( + config.Project{ Sign: config.Sign{Artifacts: "all"}, }, - Artifacts: []string{"artifact1", "artifact2", "checksum"}, - Checksums: []string{"checksum"}, - }, + ), signatures: []string{"artifact1.sig", "artifact2.sig", "checksum.sig"}, }, { desc: "sign only checksums", - ctx: &context.Context{ - Config: config.Project{ + ctx: context.New( + config.Project{ Sign: config.Sign{Artifacts: "checksum"}, }, - Artifacts: []string{"artifact1", "artifact2", "checksum"}, - Checksums: []string{"checksum"}, - }, + ), signatures: []string{"checksum.sig"}, }, } @@ -90,49 +84,50 @@ const user = "nopass" func testSign(t *testing.T, ctx *context.Context, signatures []string) { // create temp dir for file and signature tmpdir, err := ioutil.TempDir("", "goreleaser") - if err != nil { - t.Fatal("TempDir: ", err) - } + assert.NoError(t, err) defer os.RemoveAll(tmpdir) ctx.Config.Dist = tmpdir // create some fake artifacts - artifacts := ctx.Artifacts + var artifacts = []string{"artifact1", "artifact2", "checksum"} for _, f := range artifacts { file := filepath.Join(tmpdir, f) - if err2 := ioutil.WriteFile(file, []byte("foo"), 0644); err2 != nil { - t.Fatal("WriteFile: ", err2) - } + assert.NoError(t, ioutil.WriteFile(file, []byte("foo"), 0644)) } + ctx.Artifacts.Add(artifact.Artifact{ + Name: "artifact1", + Path: filepath.Join(tmpdir, "artifact1"), + Type: artifact.UploadableArchive, + }) + ctx.Artifacts.Add(artifact.Artifact{ + Name: "artifact2", + Path: filepath.Join(tmpdir, "artifact2"), + Type: artifact.UploadableArchive, + }) + ctx.Artifacts.Add(artifact.Artifact{ + Name: "checksum", + Path: filepath.Join(tmpdir, "checksum"), + Type: artifact.Checksum, + }) // configure the pipeline // make sure we are using the test keyring - err = Pipe{}.Default(ctx) - if err != nil { - t.Fatal("Default: ", err) - } + assert.NoError(t, Pipe{}.Default(ctx)) ctx.Config.Sign.Args = append([]string{"--homedir", keyring}, ctx.Config.Sign.Args...) // run the pipeline - err = Pipe{}.Run(ctx) - if err != nil { - t.Fatal("Run: ", err) - } + assert.NoError(t, Pipe{}.Run(ctx)) // verify that only the artifacts and the signatures are in the dist dir files, err := ioutil.ReadDir(tmpdir) - if err != nil { - t.Fatal("ReadDir: ", err) - } + assert.NoError(t, err) gotFiles := []string{} for _, f := range files { gotFiles = append(gotFiles, f.Name()) } - wantFiles := append(artifacts, signatures...) sort.Strings(wantFiles) - assert.Equal(t, wantFiles, gotFiles) // verify the signatures @@ -140,8 +135,12 @@ func testSign(t *testing.T, ctx *context.Context, signatures []string) { verifySignature(t, ctx, sig) } + var signArtifacts []string + for _, sig := range ctx.Artifacts.Filter(artifact.ByType(artifact.Signature)).List() { + signArtifacts = append(signArtifacts, sig.Name) + } // check signature is an artifact - assert.Equal(t, ctx.Artifacts, append(artifacts, signatures...)) + assert.Equal(t, signArtifacts, signatures) } func verifySignature(t *testing.T, ctx *context.Context, sig string) { @@ -150,10 +149,7 @@ func verifySignature(t *testing.T, ctx *context.Context, sig string) { // verify signature was made with key for usesr 'nopass' cmd := exec.Command("gpg", "--homedir", keyring, "--verify", filepath.Join(ctx.Config.Dist, sig), filepath.Join(ctx.Config.Dist, artifact)) out, err := cmd.CombinedOutput() - if err != nil { - t.Log(string(out)) - t.Fatal("verify: ", err) - } + assert.NoError(t, err) // check if the signature matches the user we expect to do this properly we // might need to have either separate keyrings or export the key from the diff --git a/pipeline/snapcraft/snapcraft.go b/pipeline/snapcraft/snapcraft.go index 001dd7eba..6bb29c239 100644 --- a/pipeline/snapcraft/snapcraft.go +++ b/pipeline/snapcraft/snapcraft.go @@ -8,14 +8,16 @@ import ( "os" "os/exec" "path/filepath" - "strings" "github.com/apex/log" - "github.com/goreleaser/goreleaser/context" - "github.com/goreleaser/goreleaser/internal/linux" - "github.com/goreleaser/goreleaser/pipeline" "golang.org/x/sync/errgroup" yaml "gopkg.in/yaml.v2" + + "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" + "github.com/goreleaser/goreleaser/internal/linux" + "github.com/goreleaser/goreleaser/internal/nametemplate" + "github.com/goreleaser/goreleaser/pipeline" ) // ErrNoSnapcraft is shown when snapcraft cannot be found in $PATH @@ -70,29 +72,34 @@ func (Pipe) Run(ctx *context.Context) error { } var g errgroup.Group - for platform, groups := range ctx.Binaries { - if !strings.Contains(platform, "linux") { - log.WithField("platform", platform).Debug("skipped non-linux builds for snapcraft") - continue - } - arch := linux.Arch(platform) - for folder, binaries := range groups { - g.Go(func() error { - return create(ctx, folder, arch, binaries) - }) - } + for platform, binaries := range ctx.Artifacts.Filter( + artifact.And( + artifact.ByGoos("linux"), + artifact.ByType(artifact.Binary), + ), + ).GroupByPlatform() { + arch := linux.Arch(platform) // TODO: could use artifact.goarch here + binaries := binaries + g.Go(func() error { + return create(ctx, arch, binaries) + }) } return g.Wait() } -func create(ctx *context.Context, folder, arch string, binaries []context.Binary) error { +func create(ctx *context.Context, arch string, binaries []artifact.Artifact) error { var log = log.WithField("arch", arch) + // TODO: should add template support here probably... for now, let's use archive's template + folder, err := nametemplate.Apply(ctx, binaries[0], ctx.Config.ProjectName) + if err != nil { + return err + } // prime is the directory that then will be compressed to make the .snap package. var folderDir = filepath.Join(ctx.Config.Dist, folder) var primeDir = filepath.Join(folderDir, "prime") var metaDir = filepath.Join(primeDir, "meta") // #nosec - if err := os.MkdirAll(metaDir, 0755); err != nil { + if err = os.MkdirAll(metaDir, 0755); err != nil { return err } @@ -128,7 +135,7 @@ func create(ctx *context.Context, folder, arch string, binaries []context.Binary metadata.Apps[binary.Name] = appMetadata destBinaryPath := filepath.Join(primeDir, filepath.Base(binary.Path)) - if err := os.Link(binary.Path, destBinaryPath); err != nil { + if err = os.Link(binary.Path, destBinaryPath); err != nil { return err } } @@ -147,6 +154,13 @@ func create(ctx *context.Context, folder, arch string, binaries []context.Binary if out, err = cmd.CombinedOutput(); err != nil { return fmt.Errorf("failed to generate snap package: %s", string(out)) } - ctx.AddArtifact(snap) + ctx.Artifacts.Add(artifact.Artifact{ + Type: artifact.LinuxPackage, + Name: folder + ".snap", + Path: snap, + Goos: binaries[0].Goos, + Goarch: binaries[0].Goarch, + Goarm: binaries[0].Goarm, + }) return nil } diff --git a/pipeline/snapcraft/snapcraft_test.go b/pipeline/snapcraft/snapcraft_test.go index d6dfd7b38..ff596d03f 100644 --- a/pipeline/snapcraft/snapcraft_test.go +++ b/pipeline/snapcraft/snapcraft_test.go @@ -9,6 +9,7 @@ import ( "github.com/goreleaser/goreleaser/config" "github.com/goreleaser/goreleaser/context" + "github.com/goreleaser/goreleaser/internal/artifact" "github.com/goreleaser/goreleaser/pipeline" "github.com/stretchr/testify/assert" yaml "gopkg.in/yaml.v2" @@ -46,10 +47,15 @@ func TestRunPipe(t *testing.T) { assert.NoError(t, os.Mkdir(dist, 0755)) assert.NoError(t, err) var ctx = &context.Context{ - Version: "testversion", + Version: "testversion", + Artifacts: artifact.New(), Config: config.Project{ ProjectName: "mybin", Dist: dist, + // TODO: remove this when we start using our own name template + Archive: config.Archive{ + NameTemplate: "foo_{{.Arch}}", + }, Snapcraft: config.Snapcraft{ Summary: "test summary", Description: "test description", @@ -67,10 +73,15 @@ func TestRunPipeWithName(t *testing.T) { assert.NoError(t, os.Mkdir(dist, 0755)) assert.NoError(t, err) var ctx = &context.Context{ - Version: "testversion", + Version: "testversion", + Artifacts: artifact.New(), Config: config.Project{ ProjectName: "testprojectname", Dist: dist, + // TODO: remove this when we start using our own name template + Archive: config.Archive{ + NameTemplate: "foo_{{.Arch}}", + }, Snapcraft: config.Snapcraft{ Name: "testsnapname", Summary: "test summary", @@ -80,7 +91,7 @@ func TestRunPipeWithName(t *testing.T) { } addBinaries(t, ctx, "testprojectname", dist) assert.NoError(t, Pipe{}.Run(ctx)) - yamlFile, err := ioutil.ReadFile(filepath.Join(dist, "testprojectname_linuxamd64", "prime", "meta", "snap.yaml")) + yamlFile, err := ioutil.ReadFile(filepath.Join(dist, "foo_amd64", "prime", "meta", "snap.yaml")) assert.NoError(t, err) var metadata Metadata err = yaml.Unmarshal(yamlFile, &metadata) @@ -95,10 +106,15 @@ func TestRunPipeWithPlugsAndDaemon(t *testing.T) { assert.NoError(t, os.Mkdir(dist, 0755)) assert.NoError(t, err) var ctx = &context.Context{ - Version: "testversion", + Version: "testversion", + Artifacts: artifact.New(), Config: config.Project{ ProjectName: "mybin", Dist: dist, + // TODO: remove this when we start using our own name template + Archive: config.Archive{ + NameTemplate: "foo_{{.Arch}}", + }, Snapcraft: config.Snapcraft{ Summary: "test summary", Description: "test description", @@ -113,7 +129,7 @@ func TestRunPipeWithPlugsAndDaemon(t *testing.T) { } addBinaries(t, ctx, "mybin", dist) assert.NoError(t, Pipe{}.Run(ctx)) - yamlFile, err := ioutil.ReadFile(filepath.Join(dist, "mybin_linuxamd64", "prime", "meta", "snap.yaml")) + yamlFile, err := ioutil.ReadFile(filepath.Join(dist, "foo_amd64", "prime", "meta", "snap.yaml")) assert.NoError(t, err) var metadata Metadata err = yaml.Unmarshal(yamlFile, &metadata) @@ -140,19 +156,20 @@ func TestNoSnapcraftInPath(t *testing.T) { } func addBinaries(t *testing.T, ctx *context.Context, name, dist string) { - for _, plat := range []string{ - "linuxamd64", - "linux386", - "darwinamd64", - "linuxarm64", - "linuxarm6", - "linuxwtf", - } { - var folder = name + "_" + plat - assert.NoError(t, os.Mkdir(filepath.Join(dist, folder), 0755)) - var binPath = filepath.Join(dist, folder, name) - _, err := os.Create(binPath) - assert.NoError(t, err) - ctx.AddBinary(plat, folder, name, binPath) + for _, goos := range []string{"linux", "darwin"} { + for _, goarch := range []string{"amd64", "386"} { + var folder = goos + goarch + assert.NoError(t, os.Mkdir(filepath.Join(dist, folder), 0755)) + var binPath = filepath.Join(dist, folder, name) + _, err := os.Create(binPath) + assert.NoError(t, err) + ctx.Artifacts.Add(artifact.Artifact{ + Name: "mybin", + Path: binPath, + Goarch: goarch, + Goos: goos, + Type: artifact.Binary, + }) + } } }