1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-03-19 20:57:53 +02:00

Merge pull request #463 from goreleaser/ctx-new

Refactor: context
This commit is contained in:
Carlos Alexandro Becker 2017-12-18 20:39:12 -02:00 committed by GitHub
commit aaab7e1732
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1339 additions and 1087 deletions

View File

@ -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

View File

@ -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)
}

View File

@ -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(),
}
}

View File

@ -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)
}

View File

@ -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 (

View File

@ -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 (

1
internal/artifact/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
type_string.go

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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

View File

@ -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 ""
}

View File

@ -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,
}

View File

@ -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,26 +66,21 @@ 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)
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 func() {
if e := archiveFile.Close(); e != nil {
log.WithField("archive", archivePath).Errorf("failed to close file: %v", e)
}
}()
defer archiveFile.Close() // nolint: errcheck
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)
}
}()
defer a.Close() // nolint: errcheck
files, err := findFiles(ctx)
if err != nil {
@ -92,22 +91,33 @@ func create(ctx *context.Context, platform string, groups map[string][]context.B
return fmt.Errorf("failed to add %s to the archive: %s", f, err.Error())
}
}
for _, binary := range binaries {
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.AddArtifact(archivePath)
}
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
}

View File

@ -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{
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)
}
}

View File

@ -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.

View File

@ -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,24 +167,9 @@ 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{
var ctx = context.New(config.Project{
ProjectName: "mybin",
Dist: dist,
Builds: []config.Build{
{
Env: []string{"CGO_ENABLED=0"},
Goos: []string{"linux", "darwin"},
Goarch: []string{"amd64"},
},
},
Artifactories: []config.Artifactory{
{
Name: "production-us",
@ -199,10 +184,20 @@ func TestRunPipe_ModeBinary(t *testing.T) {
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,14 +214,7 @@ 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{
var ctx = context.New(config.Project{
ProjectName: "goreleaser",
Dist: folder,
Artifactories: []config.Artifactory{
@ -237,11 +225,22 @@ func TestRunPipe_ModeArchive(t *testing.T) {
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,29 +297,47 @@ 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{
var ctx = context.New(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",
@ -330,11 +347,20 @@ func TestRunPipe_TargetTemplateError(t *testing.T) {
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,23 +393,9 @@ 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{
var ctx = context.New(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",
@ -392,15 +404,22 @@ func TestRunPipe_BadCredentials(t *testing.T) {
Username: "deployuser",
},
},
},
}
for _, plat := range []string{"darwinamd64"} {
ctx.AddBinary(plat, "mybin", "mybin", binPath)
})
ctx.Publish = true
ctx.Env = map[string]string{
"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
}
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,23 +451,9 @@ 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{
var ctx = context.New(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",
@ -457,13 +462,20 @@ func TestRunPipe_UnparsableErrorResponse(t *testing.T) {
Username: "deployuser",
},
},
},
}
for _, plat := range []string{"darwinamd64"} {
ctx.AddBinary(plat, "mybin", "mybin", binPath)
})
ctx.Publish = true
ctx.Env = map[string]string{
"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
}
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,23 +506,9 @@ 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{
var ctx = context.New(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",
@ -519,67 +517,26 @@ func TestRunPipe_UnparsableResponse(t *testing.T) {
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",
},
},
},
}
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{
func TestRunPipe_FileNotFound(t *testing.T) {
var ctx = context.New(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",
@ -588,11 +545,20 @@ func TestRunPipe_NoFile(t *testing.T) {
Username: "deployuser",
},
},
},
})
ctx.Publish = true
ctx.Env = map[string]string{
"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
}
ctx.AddBinary("darwinamd64", "mybin", "mybin", "archivetest/dist/mybin/mybin")
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))
assert.EqualError(t, Pipe{}.Run(ctx), `open archivetest/dist/mybin/mybin: no such file or directory`)
}
func TestRunPipe_UnparsableTarget(t *testing.T) {
@ -606,23 +572,9 @@ 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{
var ctx = context.New(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",
@ -631,22 +583,24 @@ func TestRunPipe_UnparsableTarget(t *testing.T) {
Username: "deployuser",
},
},
},
}
for _, plat := range []string{"darwinamd64"} {
ctx.AddBinary(plat, "mybin", "mybin", binPath)
})
ctx.Publish = true
ctx.Env = map[string]string{
"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
}
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{
var ctx = context.New(config.Project{
Artifactories: []config.Artifactory{
{
Name: "production",
@ -655,10 +609,14 @@ func TestRunPipe_SkipWhenPublishFalse(t *testing.T) {
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,23 +627,9 @@ 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{
var ctx = context.New(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",
@ -694,13 +638,20 @@ func TestRunPipe_DirUpload(t *testing.T) {
Username: "deployuser",
},
},
},
}
for _, plat := range []string{"darwinamd64"} {
ctx.AddBinary(plat, "mybin", "mybin", binPath)
})
ctx.Env = map[string]string{
"ARTIFACTORY_PRODUCTION_SECRET": "deployuser-secret",
}
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) {

View File

@ -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,

View File

@ -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"
)
@ -94,6 +95,7 @@ func TestRunPipe(t *testing.T) {
CurrentTag: "v1.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)
t.Run("default git url", func(tt *testing.T) {
assert.NoError(tt, doRun(ctx, client))
assert.True(tt, client.CreatedFile)
bts, err := ioutil.ReadFile("testdata/run_pipe.rb")
assert.NoError(t, err)
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)
})
assert.Equal(t, string(bts), client.Content)
}
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)
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")
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)

3
pipeline/brew/doc.go Normal file
View File

@ -0,0 +1,3 @@
// Package brew implements the Pipe, providing formula generation and
// uploading it to a configured repo.
package brew

View File

@ -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
<xml>whatever</xml>
EOS
end
test do
system "true"
system "#{bin}/foo -h"
end
end

View File

@ -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 != "" {

View File

@ -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()

View File

@ -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"
)

View File

@ -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
}

View File

@ -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")

View File

@ -0,0 +1,3 @@
// Package checksums provides a Pipe that creates .checksums files for
// each artifact.
package checksums

3
pipeline/doc.go Normal file
View File

@ -0,0 +1,3 @@
// Package pipeline provides the generic piper and defaulter interfaces,
// which should be implemented add new pipes to goreleaser..
package pipeline

View File

@ -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,52 +67,31 @@ 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
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 folder, binaries := range groups {
for _, binary := range binaries {
if binary.Name != docker.Binary {
continue
}
var err = process(ctx, folder, docker, binary)
var err = process(ctx, docker, binary)
if err != nil && !pipeline.IsSkip(err) {
return err
}
}
}
}
}
return nil
}
@ -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
}

View File

@ -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,
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)
}
})
}

View File

@ -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 {
arch := linux.Arch(platform) // TODO: could probably pass artifact.Goarch here
artifacts := artifacts
g.Go(func() error {
defer func() {
<-sem
}()
return create(ctx, format, folder, arch, binaries)
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
}

View File

@ -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{

3
pipeline/git/doc.go Normal file
View File

@ -0,0 +1,3 @@
// Package git implements the Pipe interface getting and validating the
// current git repository state
package git

View File

@ -1,5 +1,3 @@
// Package git implements the Pipe interface getting and validating the
// current git repository state
package git
import (

View File

@ -1,4 +1,3 @@
// Package pipeline provides a generic pipe interface.
package pipeline
import (

View File

@ -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
}

View File

@ -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{
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)

3
pipeline/release/doc.go Normal file
View File

@ -0,0 +1,3 @@
// Package release implements Pipe and manages github releases and its
// artifacts.
package release

View File

@ -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)
}

View File

@ -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,
}

View File

@ -4,7 +4,6 @@ import (
"testing"
"github.com/goreleaser/goreleaser/internal/testlib"
"github.com/stretchr/testify/assert"
)

View File

@ -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 {

View File

@ -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

View File

@ -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 {
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, folder, arch, binaries)
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
}

View File

@ -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"
@ -47,9 +48,14 @@ func TestRunPipe(t *testing.T) {
assert.NoError(t, err)
var ctx = &context.Context{
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",
@ -68,9 +74,14 @@ func TestRunPipeWithName(t *testing.T) {
assert.NoError(t, err)
var ctx = &context.Context{
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)
@ -96,9 +107,14 @@ func TestRunPipeWithPlugsAndDaemon(t *testing.T) {
assert.NoError(t, err)
var ctx = &context.Context{
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
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.AddBinary(plat, folder, name, binPath)
ctx.Artifacts.Add(artifact.Artifact{
Name: "mybin",
Path: binPath,
Goarch: goarch,
Goos: goos,
Type: artifact.Binary,
})
}
}
}