1
0
mirror of https://github.com/goreleaser/goreleaser.git synced 2025-03-17 20:47:50 +02:00

Merge branch 'master' into artifactory-support

* master: (47 commits)
  docs: add docs for env vars in name_template
  test: add test for name_template with env var
  feat: support env vars for name_template
  docs: Fixed broken homebrew link
  docs: fixed master build status badge on readme
  chore: misspeled word on package docs
  feat: improved release notes
  chore: create config.yml
  chore: create stale.yml
  chore: push docs to master
  chore: changing the order of the tasks
  chore: using https instead of ssh url
  chore: always run make static on build
  chore: using travis deploy feature
  chore: automating docs deployment
  fix: do not decorate git log output
  chore: make static pushes repo as well
  docs: env support for docker tag_template
  feat: allow env vars for docker tag_template
  fix: move env vars to context
  ...
This commit is contained in:
Andy Grunwald 2017-12-08 21:50:02 +01:00
commit 1f5df43e75
68 changed files with 1136 additions and 671 deletions

2
.github/config.yml vendored Normal file
View File

@ -0,0 +1,2 @@
todo:
keyword: "TODO: "

17
.github/stale.yml vendored Normal file
View File

@ -0,0 +1,17 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
# Label to use when marking an issue as stale
staleLabel: wontfix
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@ -23,6 +23,7 @@ changelog:
dockers:
- image: goreleaser/goreleaser
latest: true
tag_template: '{{ .Tag }}'
archive:
name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
replacements:

View File

@ -18,5 +18,15 @@ after_success:
- bash <(curl -s https://codecov.io/bash)
- test -n "$TRAVIS_TAG" && docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
- test -n "$TRAVIS_TAG" && go run main.go
- make static
deploy:
provider: pages
skip_cleanup: true
github_token: $GITHUB_TOKEN
repo: goreleaser/goreleaser.github.io
local_dir: ./dist/goreleaser.github.io
target_branch: master
on:
tags: true
notifications:
email: false

View File

@ -8,49 +8,53 @@ setup:
go get -u github.com/golang/dep/cmd/dep
go get -u github.com/pierrre/gotestcover
go get -u golang.org/x/tools/cmd/cover
go get -u github.com/apex/static/cmd/static-docs
dep ensure
gometalinter --install
.PHONY: setup
# Run all the tests
test:
gotestcover $(TEST_OPTIONS) -covermode=atomic -coverprofile=coverage.txt $(SOURCE_FILES) -run $(TEST_PATTERN) -timeout=2m
.PHONY: cover
# Run all the tests and opens the coverage report
cover: test
go tool cover -html=coverage.txt
.PHONY: cover
# gofmt and goimports all go files
fmt:
find . -name '*.go' -not -wholename './vendor/*' | while read -r file; do gofmt -w -s "$$file"; goimports -w "$$file"; done
.PHONY: fmt
# Run all the linters
lint:
gometalinter --vendor ./...
.PHONY: lint
# Run all the tests and code checks
ci: test lint
.PHONY: ci
# Build a beta version of goreleaser
build:
go build
HIGHLIGHT=https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0
.PHONY: build
# Generate the static documentation
static:
@rm -rf dist/goreleaser.github.io
@mkdir -p dist
@git clone https://github.com/goreleaser/goreleaser.github.io.git dist/goreleaser.github.io
@rm -rf dist/goreleaser.github.io/theme
@static-docs \
--in docs \
--out ../goreleaser.github.io \
--out dist/goreleaser.github.io \
--title GoReleaser \
--subtitle "Deliver Go binaries as fast and easily as possible" \
--google UA-106198408-1 \
--script "$(HIGHLIGHT)/highlight.min.js" \
--script "$(HIGHLIGHT)/languages/go.min.js" \
--script "$(HIGHLIGHT)/languages/yaml.min.js" \
--script "$(HIGHLIGHT)/languages/dockerfile.min.js" \
--style "$(HIGHLIGHT)/styles/atom-one-dark.min.css" \
--inline-script 'hljs.initHighlightingOnLoad();' \
--inline-style 'pre { padding: 0; }'
--google UA-106198408-1
.PHONY: static
# Show to-do items per file.
todo:

View File

@ -5,7 +5,7 @@
<p align="center">
<a href="https://github.com/goreleaser/goreleaser/releases/latest"><img alt="Release" src="https://img.shields.io/github/release/goreleaser/goreleaser.svg?style=flat-square"></a>
<a href="/LICENSE.md"><img alt="Software License" src="https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square"></a>
<a href="https://travis-ci.org/goreleaser/goreleaser"><img alt="Travis" src="https://img.shields.io/travis/goreleaser/goreleaser.svg?style=flat-square"></a>
<a href="https://travis-ci.org/goreleaser/goreleaser"><img alt="Travis" src="https://img.shields.io/travis/goreleaser/goreleaser/master.svg?style=flat-square"></a>
<a href="https://codecov.io/gh/goreleaser/goreleaser"><img alt="Codecov branch" src="https://img.shields.io/codecov/c/github/goreleaser/goreleaser/master.svg?style=flat-square"></a>
<a href="https://goreportcard.com/report/github.com/goreleaser/goreleaser"><img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/goreleaser/goreleaser?style=flat-square"></a>
<a href="http://godoc.org/github.com/goreleaser/goreleaser"><img alt="Go Doc" src="https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square"></a>

View File

@ -12,14 +12,14 @@ import (
)
// SHA256 sum of the given file
func SHA256(path string) (result string, err error) {
func SHA256(path string) (string, error) {
return calculate(sha256.New(), path)
}
func calculate(hash hash.Hash, path string) (result string, err error) {
func calculate(hash hash.Hash, path string) (string, error) {
file, err := os.Open(path)
if err != nil {
return
return "", err
}
defer func() {
if err := file.Close(); err != nil {
@ -30,12 +30,10 @@ func calculate(hash hash.Hash, path string) (result string, err error) {
return doCalculate(hash, file)
}
func doCalculate(hash hash.Hash, file io.Reader) (result string, err error) {
_, err = io.Copy(hash, file)
func doCalculate(hash hash.Hash, file io.Reader) (string, error) {
_, err := io.Copy(hash, file)
if err != nil {
return
return "", err
}
result = hex.EncodeToString(hash.Sum(nil))
return
return hex.EncodeToString(hash.Sum(nil)), nil
}

View File

@ -181,14 +181,15 @@ type Checksum struct {
// Docker image config
type Docker struct {
Binary string `yaml:",omitempty"`
Goos string `yaml:",omitempty"`
Goarch string `yaml:",omitempty"`
Goarm string `yaml:",omitempty"`
Image string `yaml:",omitempty"`
Dockerfile string `yaml:",omitempty"`
Latest bool `yaml:",omitempty"`
Files []string `yaml:"extra_files,omitempty"`
Binary string `yaml:",omitempty"`
Goos string `yaml:",omitempty"`
Goarch string `yaml:",omitempty"`
Goarm string `yaml:",omitempty"`
Image string `yaml:",omitempty"`
Dockerfile string `yaml:",omitempty"`
Latest bool `yaml:",omitempty"`
TagTemplate string `yaml:"tag_template,omitempty"`
Files []string `yaml:"extra_files,omitempty"`
// Capture all undefined fields and should be empty after loading
XXX map[string]interface{} `yaml:",inline"`

View File

@ -8,6 +8,7 @@ package context
import (
ctx "context"
"os"
"path/filepath"
"strings"
"sync"
@ -31,11 +32,13 @@ type Binary struct {
type Context struct {
ctx.Context
Config config.Project
Env map[string]string
Token string
Git GitInfo
Binaries map[string]map[string][]Binary
Artifacts []string
Dockers []string
Brews []string
ReleaseNotes string
Version string
Validate bool
@ -46,9 +49,12 @@ type Context struct {
Parallelism int
}
var artifactsLock sync.Mutex
var dockersLock sync.Mutex
var binariesLock sync.Mutex
var (
artifactsLock sync.Mutex
dockersLock sync.Mutex
binariesLock sync.Mutex
brewsLock sync.Mutex
)
// AddArtifact adds a file to upload list
func (ctx *Context) AddArtifact(file string) {
@ -59,6 +65,14 @@ func (ctx *Context) AddArtifact(file string) {
log.WithField("artifact", file).Info("new release artifact")
}
// AddBrew adds a brew tap to the brews list
func (ctx *Context) AddBrew(tap string) {
brewsLock.Lock()
defer brewsLock.Unlock()
ctx.Brews = append(ctx.Brews, tap)
log.WithField("tap", tap).Info("new brew tap")
}
// AddDocker adds a docker image to the docker images list
func (ctx *Context) AddDocker(image string) {
dockersLock.Lock()
@ -96,6 +110,16 @@ func New(config config.Project) *Context {
return &Context{
Context: ctx.Background(),
Config: config,
Env: splitEnv(os.Environ()),
Parallelism: 4,
}
}
func splitEnv(env []string) map[string]string {
r := map[string]string{}
for _, e := range env {
p := strings.SplitN(e, "=", 2)
r[p[0]] = p[1]
}
return r
}

View File

@ -20,6 +20,10 @@ func TestMultipleAdds(t *testing.T) {
"c/d:2.0.0",
"e/f:3.0.0",
}
var brews = []string{
"foo/tap/foo",
"bar/bar/bar",
}
var ctx = New(config.Project{
Dist: "dist",
})
@ -40,10 +44,20 @@ func TestMultipleAdds(t *testing.T) {
})
}
assert.NoError(t, g.Wait())
for _, b := range brews {
b := b
g.Go(func() error {
ctx.AddBrew(b)
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.Dockers, len(dockerfiles))
assert.Contains(t, ctx.Dockers, "a/b:1.0.0", "c/d:2.0.0", "e/f:3.0.0")
assert.Len(t, ctx.Brews, len(brews))
assert.Contains(t, ctx.Brews, "foo/tap/foo", "bar/bar/bar")
}
func TestMultipleBinaryAdds(t *testing.T) {

View File

@ -22,5 +22,5 @@ that and rewrote the whole thing in Go.
There are three ways to get going.
1. Install Goreleaser via go get (`goreleaser` command will be globally available) `go get github.com/goreleaser/goreleaser`
2. On a Mac use [Homebrew](https://github.com/goreleaser/homebrew-tap).
3. Install directly [from the binaries](https://github.com/goreleaser/goreleaser/releases/latest).
1. On a Mac use [Homebrew](https://github.com/goreleaser/homebrew-tap).
1. Install directly [from the binaries](https://github.com/goreleaser/goreleaser/releases/latest).

View File

@ -2,7 +2,7 @@
title: Environment
---
### GitHub Token
## GitHub Token
GoReleaser requires a GitHub API token with the `repo` scope selected to
deploy the artifacts to GitHub.
@ -12,7 +12,7 @@ This token should be added to the environment variables as `GITHUB_TOKEN`.
Here is how to do it with Travis CI:
[Defining Variables in Repository Settings](https://docs.travis-ci.com/user/environment-variables/#Defining-Variables-in-Repository-Settings).
### The dist folder
## The dist folder
By default, GoReleaser will create its artifacts in the `./dist` folder.
If you must, you can change it by setting it in the `.goreleaser.yml` file:
@ -23,7 +23,7 @@ If you must, you can change it by setting it in the `.goreleaser.yml` file:
dist: another-folder-that-is-not-dist
```
### Using the `main.version`
## Using the `main.version`
GoReleaser always sets a `main.version` *ldflag*.
You can use it in your `main.go` file:

View File

@ -5,7 +5,6 @@ title: Project Name
The project name is used in the name of the Brew formula, archives, etc.
If none is given, it will be inferred from the name of the Git project.
```yaml
# .goreleaser.yml
project_name: myproject

View File

@ -78,3 +78,19 @@ builds:
pre: rice embed-go
post: ./script.sh
```
## Passing environment variables to ldflags
You can do that by using `{{ .Env.VARIABLE_NAME }}` in the template, for
example:
```yaml
builds:
- ldflags: -s -w -X "main.goversion={{.Env.GOVERSION}}"
```
Then you can run:
```console
GOVERSION=$(go version) goreleaser
```

View File

@ -20,6 +20,7 @@ archive:
# - Os
# - Arch
# - Arm (ARM version)
# - Env (environment variables)
# Default is `{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}`.
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
@ -63,3 +64,19 @@ archive:
- docs/*
- design/*.png
```
## Passing environment variables to name_template
You can do that by using `{{ .Env.VARIABLE_NAME }}` in the template, for
example:
```yaml
archive:
name_template: '{{.ProjectName}}-{{.Version}}-{{.Env.GOVERSION_NR}}'
```
Then you can run:
```console
GOVERSION_NR=$(go version | awk '{print $3;}') goreleaser
```

View File

@ -95,5 +95,5 @@ end
**Important**": Note that GoReleaser does not yet generate a valid
homebrew-core formula. The generated formulas are meant to be published as
[homebrew taps](https://docs.brew.sh/brew-tap.html), and in their current
[homebrew taps](https://docs.brew.sh/Taps.html), and in their current
form will not be accepted in any of the official homebrew repositories.

View File

@ -50,6 +50,9 @@ dockers:
image: myuser/myimage
# Path to the Dockerfile (from the project root).
dockerfile: Dockerfile
# Template of the docker tag. Defaults to `{{ .Version }}`. Other allowed
# fields are `.Tag` and `.Env.VARIABLE_NAME`.
tag_template: "{{ .Tag }}"
# Also tag and push myuser/myimage:latest.
latest: true
# If your Dockerfile copies files other than the binary itself,
@ -61,3 +64,20 @@ dockers:
These settings should allow you to generate multiple Docker images,
for example, using multiple `FROM` statements,
as well as generate one image for each binary in your project.
## Passing environment variables to tag_template
You can do that by using `{{ .Env.VARIABLE_NAME }}` in the template, for
example:
```yaml
dockers:
-
tag_template: "{{ .Tag }}-{{ .Env.GOVERSION_NR }}"
```
Then you can run:
```console
GOVERSION_NR=$(go version | awk '{print $3}') goreleaser
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -27,7 +27,7 @@ import (
yaml "gopkg.in/yaml.v2"
)
var pipes = []pipeline.Pipe{
var pipes = []pipeline.Piper{
defaults.Pipe{}, // load default configs
git.Pipe{}, // get and validate git repo state
changelog.Pipe{}, // builds the release changelog
@ -91,7 +91,7 @@ func Release(flags Flags) error {
}
ctx.RmDist = flags.Bool("rm-dist")
for _, pipe := range pipes {
log.Infof("\033[1m%s\033[0m", strings.ToUpper(pipe.Description()))
log.Infof("\033[1m%s\033[0m", strings.ToUpper(pipe.String()))
if err := handle(pipe.Run(ctx)); err != nil {
return err
}

View File

@ -8,7 +8,6 @@ import (
"github.com/apex/log"
"github.com/google/go-github/github"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/name"
"golang.org/x/oauth2"
)
@ -84,12 +83,12 @@ func (c *githubClient) CreateFile(
func (c *githubClient) CreateRelease(ctx *context.Context, body string) (releaseID int, err error) {
var release *github.RepositoryRelease
releaseTitle, err := name.ForTitle(ctx)
title, err := releaseTitle(ctx)
if err != nil {
return 0, err
}
var data = &github.RepositoryRelease{
Name: github.String(releaseTitle),
Name: github.String(title),
TagName: github.String(ctx.Git.CurrentTag),
Body: github.String(body),
Draft: github.Bool(ctx.Config.Release.Draft),

24
internal/client/name.go Normal file
View File

@ -0,0 +1,24 @@
package client
import (
"bytes"
"text/template"
"github.com/goreleaser/goreleaser/context"
)
func releaseTitle(ctx *context.Context) (string, error) {
var out bytes.Buffer
t, err := template.New("github").Parse(ctx.Config.Release.NameTemplate)
if err != nil {
return "", err
}
err = t.Execute(&out, struct {
ProjectName, Tag, Version string
}{
ProjectName: ctx.Config.ProjectName,
Tag: ctx.Git.CurrentTag,
Version: ctx.Version,
})
return out.String(), err
}

View File

@ -1,98 +0,0 @@
// Package name provides name template logic for the final archive, formulae,
// etc.
package name
import (
"bytes"
"text/template"
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/buildtarget"
)
type nameData struct {
Os string
Arch string
Arm string
Version string
Tag string
Binary string // deprecated
ProjectName string
}
// ForBuild return the name for the given context, goos, goarch, goarm and
// build, using the build.Binary property instead of project_name.
func ForBuild(ctx *context.Context, build config.Build, target buildtarget.Target) (string, error) {
return apply(
nameData{
Os: replace(ctx.Config.Archive.Replacements, target.OS),
Arch: replace(ctx.Config.Archive.Replacements, target.Arch),
Arm: replace(ctx.Config.Archive.Replacements, target.Arm),
Version: ctx.Version,
Tag: ctx.Git.CurrentTag,
Binary: build.Binary,
ProjectName: build.Binary,
},
ctx.Config.Archive.NameTemplate,
)
}
// For returns the name for the given context, goos, goarch and goarm.
func For(ctx *context.Context, target buildtarget.Target) (string, error) {
return apply(
nameData{
Os: replace(ctx.Config.Archive.Replacements, target.OS),
Arch: replace(ctx.Config.Archive.Replacements, target.Arch),
Arm: replace(ctx.Config.Archive.Replacements, target.Arm),
Version: ctx.Version,
Tag: ctx.Git.CurrentTag,
Binary: ctx.Config.ProjectName,
ProjectName: ctx.Config.ProjectName,
},
ctx.Config.Archive.NameTemplate,
)
}
// ForChecksums returns the filename for the checksums file based on its
// template
func ForChecksums(ctx *context.Context) (string, error) {
return apply(
nameData{
ProjectName: ctx.Config.ProjectName,
Tag: ctx.Git.CurrentTag,
Version: ctx.Version,
},
ctx.Config.Checksum.NameTemplate,
)
}
// ForTitle returns the release title based upon its template
func ForTitle(ctx *context.Context) (string, error) {
return apply(
nameData{
ProjectName: ctx.Config.ProjectName,
Tag: ctx.Git.CurrentTag,
Version: ctx.Version,
},
ctx.Config.Release.NameTemplate,
)
}
func apply(data nameData, templateStr string) (string, error) {
var out bytes.Buffer
t, err := template.New(data.ProjectName).Parse(templateStr)
if err != nil {
return "", err
}
err = t.Execute(&out, data)
return out.String(), err
}
func replace(replacements map[string]string, original string) string {
result := replacements[original]
if result == "" {
return original
}
return result
}

View File

@ -1,141 +0,0 @@
package name
import (
"testing"
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/buildtarget"
"github.com/goreleaser/goreleaser/pipeline/defaults"
"github.com/stretchr/testify/assert"
)
func TestChecksums(t *testing.T) {
var config = config.Project{
Checksum: config.Checksum{
NameTemplate: "{{.ProjectName }}_{{.Tag}}_{{.Version}}_checksums.txt",
},
ProjectName: "testcheck",
}
var ctx = &context.Context{
Config: config,
Version: "1.0.0",
Git: context.GitInfo{
CurrentTag: "v1.0.0",
},
}
name, err := ForChecksums(ctx)
assert.NoError(t, err)
assert.Equal(t, "testcheck_v1.0.0_1.0.0_checksums.txt", name)
}
func TestNameFor(t *testing.T) {
var config = config.Project{
Archive: config.Archive{
NameTemplate: "{{.Binary}}_{{.Os}}_{{.Arch}}_{{.Tag}}_{{.Version}}",
Replacements: map[string]string{
"darwin": "Darwin",
"amd64": "x86_64",
},
},
ProjectName: "test",
}
var ctx = &context.Context{
Config: config,
Version: "1.2.3",
Git: context.GitInfo{
CurrentTag: "v1.2.3",
},
}
name, err := For(ctx, buildtarget.New("darwin", "amd64", ""))
assert.NoError(t, err)
assert.Equal(t, "test_Darwin_x86_64_v1.2.3_1.2.3", name)
}
func TestNameForBuild(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Archive: config.Archive{
NameTemplate: "{{.Binary}}_{{.Os}}_{{.Arch}}_{{.Tag}}_{{.Version}}",
Replacements: map[string]string{
"darwin": "Darwin",
"amd64": "x86_64",
},
},
ProjectName: "test",
},
Version: "1.2.3",
Git: context.GitInfo{
CurrentTag: "v1.2.3",
},
}
name, err := ForBuild(
ctx,
config.Build{Binary: "foo"},
buildtarget.New("darwin", "amd64", ""),
)
assert.NoError(t, err)
assert.Equal(t, "foo_Darwin_x86_64_v1.2.3_1.2.3", name)
}
func TestInvalidNameTemplate(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Archive: config.Archive{
NameTemplate: "{{.Binary}_{{.Os}}_{{.Arch}}_{{.Version}}",
},
ProjectName: "test",
},
Git: context.GitInfo{
CurrentTag: "v1.2.3",
},
}
_, err := For(ctx, buildtarget.New("darwin", "amd64", ""))
assert.Error(t, err)
}
func TestNameDefaultTemplate(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Archive: config.Archive{
NameTemplate: defaults.NameTemplate,
},
ProjectName: "test",
},
Version: "1.2.3",
}
for key, target := range map[string]buildtarget.Target{
"test_1.2.3_darwin_amd64": buildtarget.New("darwin", "amd64", ""),
"test_1.2.3_linux_arm64": buildtarget.New("linux", "arm64", ""),
"test_1.2.3_linux_armv7": buildtarget.New("linux", "arm", "7"),
} {
t.Run(key, func(t *testing.T) {
name, err := For(ctx, target)
assert.NoError(t, err)
assert.Equal(t, key, name)
})
}
}
func TestNameForTitle(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Release: config.Release{
NameTemplate: "{{.ProjectName}}-v{{.Version}}",
},
ProjectName: "test",
},
Version: "1.2.3",
Git: context.GitInfo{
CurrentTag: "v1.2.3",
},
}
name, err := ForTitle(ctx)
assert.NoError(t, err)
assert.Equal(t, "test-v1.2.3", name)
}

View File

@ -19,9 +19,8 @@ import (
// Pipe for archive
type Pipe struct{}
// Description of the pipe
func (Pipe) Description() string {
return "Creating archives"
func (Pipe) String() string {
return "creating archives"
}
// Run the pipe
@ -40,6 +39,29 @@ func (Pipe) Run(ctx *context.Context) error {
return g.Wait()
}
// Default sets the pipe defaults
func (Pipe) Default(ctx *context.Context) error {
if ctx.Config.Archive.NameTemplate == "" {
ctx.Config.Archive.NameTemplate = "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
}
if ctx.Config.Archive.Format == "" {
ctx.Config.Archive.Format = "tar.gz"
}
if len(ctx.Config.Archive.Files) == 0 {
ctx.Config.Archive.Files = []string{
"licence*",
"LICENCE*",
"license*",
"LICENSE*",
"readme*",
"README*",
"changelog*",
"CHANGELOG*",
}
}
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)

View File

@ -15,7 +15,7 @@ import (
)
func TestDescription(t *testing.T) {
assert.NotEmpty(t, Pipe{}.Description())
assert.NotEmpty(t, Pipe{}.String())
}
func TestRunPipe(t *testing.T) {
@ -195,3 +195,33 @@ func TestRunPipeWrap(t *testing.T) {
assert.Equal(t, filepath.Join("mybin_darwin_amd64", n), h.Name)
}
}
func TestDefault(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Archive: config.Archive{},
},
}
assert.NoError(t, Pipe{}.Default(ctx))
assert.NotEmpty(t, ctx.Config.Archive.NameTemplate)
assert.Equal(t, "tar.gz", ctx.Config.Archive.Format)
assert.NotEmpty(t, ctx.Config.Archive.Files)
}
func TestDefaultSet(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Archive: config.Archive{
NameTemplate: "foo",
Format: "zip",
Files: []string{
"foo",
},
},
},
}
assert.NoError(t, Pipe{}.Default(ctx))
assert.Equal(t, "foo", ctx.Config.Archive.NameTemplate)
assert.Equal(t, "zip", ctx.Config.Archive.Format)
assert.Equal(t, "foo", ctx.Config.Archive.Files[0])
}

View File

@ -50,8 +50,8 @@ type artifactoryChecksums struct {
type Pipe struct{}
// Description of the pipe
func (Pipe) Description() string {
return "Releasing to Artifactory"
func (Pipe) String() string {
return "releasing to Artifactory"
}
// Run the pipe

View File

@ -393,7 +393,7 @@ func TestRunPipe_DirUpload(t *testing.T) {
}
func TestDescription(t *testing.T) {
assert.NotEmpty(t, Pipe{}.Description())
assert.NotEmpty(t, Pipe{}.String())
}
func TestNoArtifactories(t *testing.T) {

View File

@ -5,12 +5,14 @@ package brew
import (
"bytes"
"errors"
"fmt"
"path/filepath"
"strings"
"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/client"
@ -26,9 +28,8 @@ const platform = "darwinamd64"
// Pipe for brew deployment
type Pipe struct{}
// Description of the pipe
func (Pipe) Description() string {
return "Creating homebrew formula"
func (Pipe) String() string {
return "creating homebrew formula"
}
// Run the pipe
@ -40,6 +41,49 @@ func (Pipe) Run(ctx *context.Context) error {
return doRun(ctx, client)
}
// Default sets the pipe defaults
func (Pipe) Default(ctx *context.Context) error {
if ctx.Config.Brew.Install == "" {
var installs []string
for _, build := range ctx.Config.Builds {
if !isBrewBuild(build) {
continue
}
installs = append(
installs,
fmt.Sprintf(`bin.install "%s"`, build.Binary),
)
}
ctx.Config.Brew.Install = strings.Join(installs, "\n")
}
if ctx.Config.Brew.CommitAuthor.Name == "" {
ctx.Config.Brew.CommitAuthor.Name = "goreleaserbot"
}
if ctx.Config.Brew.CommitAuthor.Email == "" {
ctx.Config.Brew.CommitAuthor.Email = "goreleaser@carlosbecker.com"
}
return nil
}
func isBrewBuild(build config.Build) bool {
for _, ignore := range build.Ignore {
if ignore.Goos == "darwin" && ignore.Goarch == "amd64" {
return false
}
}
return contains(build.Goos, "darwin") && contains(build.Goarch, "amd64")
}
func contains(ss []string, s string) bool {
for _, zs := range ss {
if zs == s {
return true
}
}
return false
}
func doRun(ctx *context.Context, client client.Client) error {
if !ctx.Publish {
return pipeline.Skip("--skip-publish is set")
@ -71,9 +115,21 @@ func doRun(ctx *context.Context, client client.Client) error {
if err != nil {
return err
}
ctx.AddBrew(brewTapPath(ctx))
return client.CreateFile(ctx, content, path)
}
func brewTapPath(ctx *context.Context) string {
return strings.Join(
[]string{
ctx.Config.Brew.GitHub.Owner,
strings.TrimPrefix(ctx.Config.Brew.GitHub.Name, "homebrew-"),
ctx.Config.ProjectName,
},
"/",
)
}
func buildFormula(ctx *context.Context, client client.Client, folder string) (bytes.Buffer, error) {
data, err := dataFor(ctx, client, folder)
if err != nil {

View File

@ -14,7 +14,7 @@ import (
)
func TestDescription(t *testing.T) {
assert.NotEmpty(t, Pipe{}.Description())
assert.NotEmpty(t, Pipe{}.String())
}
func TestNameWithDash(t *testing.T) {
@ -139,6 +139,8 @@ func TestRunPipe(t *testing.T) {
// ioutil.WriteFile("testdata/run_pipe.rb", []byte(client.Content), 0644)
assert.Equal(t, string(bts), client.Content)
assert.Equal(t, "test/test/run-pipe", ctx.Brews[0])
}
func TestRunPipeFormatOverride(t *testing.T) {
@ -268,6 +270,54 @@ func TestRunPipeFormatBinary(t *testing.T) {
assert.False(t, client.CreatedFile)
}
func TestDefault(t *testing.T) {
_, back := testlib.Mktmp(t)
defer back()
var ctx = &context.Context{
Config: config.Project{
Builds: []config.Build{
{
Binary: "foo",
Goos: []string{"linux", "darwin"},
Goarch: []string{"386", "amd64"},
},
{
Binary: "bar",
Goos: []string{"linux", "darwin"},
Goarch: []string{"386", "amd64"},
Ignore: []config.IgnoredBuild{
{Goos: "darwin", Goarch: "amd64"},
},
},
{
Binary: "foobar",
Goos: []string{"linux"},
Goarch: []string{"amd64"},
},
},
},
}
assert.NoError(t, Pipe{}.Default(ctx))
assert.NotEmpty(t, ctx.Config.Brew.CommitAuthor.Name)
assert.NotEmpty(t, ctx.Config.Brew.CommitAuthor.Email)
assert.Equal(t, `bin.install "foo"`, ctx.Config.Brew.Install)
}
func TestBrewTapPath(t *testing.T) {
assert.Equal(t, "goreleaser/tap/goreleaser", brewTapPath(&context.Context{
Config: config.Project{
ProjectName: "goreleaser",
Brew: config.Homebrew{
GitHub: config.Repo{
Owner: "goreleaser",
Name: "homebrew-tap",
},
},
},
}))
}
type DummyClient struct {
CreatedFile bool
Content string

View File

@ -1,12 +1,6 @@
// Package build implements Pipe and can build Go projects for
// several platforms, with pre and post hook support.
package build
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"os/exec"
"path/filepath"
@ -17,7 +11,6 @@ import (
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/buildtarget"
"github.com/goreleaser/goreleaser/internal/ext"
"github.com/goreleaser/goreleaser/internal/name"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
)
@ -25,9 +18,8 @@ import (
// Pipe for build
type Pipe struct{}
// Description of the pipe
func (Pipe) Description() string {
return "Building binaries"
func (Pipe) String() string {
return "building binaries"
}
// Run the pipe
@ -44,50 +36,39 @@ func (Pipe) Run(ctx *context.Context) error {
return nil
}
func checkMain(ctx *context.Context, build config.Build) error {
var main = build.Main
if main == "" {
main = "."
// Default sets the pipe defaults
func (Pipe) Default(ctx *context.Context) error {
for i, build := range ctx.Config.Builds {
ctx.Config.Builds[i] = buildWithDefaults(ctx, build)
}
stat, ferr := os.Stat(main)
if os.IsNotExist(ferr) {
return errors.Wrapf(ferr, "could not open %s", main)
}
if stat.IsDir() {
packs, err := parser.ParseDir(token.NewFileSet(), main, nil, 0)
if err != nil {
return errors.Wrapf(err, "failed to parse dir: %s", main)
if len(ctx.Config.Builds) == 0 {
ctx.Config.Builds = []config.Build{
buildWithDefaults(ctx, ctx.Config.SingleBuild),
}
for _, pack := range packs {
for _, file := range pack.Files {
if hasMain(file) {
return nil
}
}
}
return fmt.Errorf("build for %s does not contain a main function", build.Binary)
}
file, err := parser.ParseFile(token.NewFileSet(), build.Main, nil, 0)
if err != nil {
return errors.Wrapf(err, "failed to parse file: %s", build.Main)
}
if hasMain(file) {
return nil
}
return fmt.Errorf("build for %s does not contain a main function", build.Binary)
return nil
}
func hasMain(file *ast.File) bool {
for _, decl := range file.Decls {
fn, isFn := decl.(*ast.FuncDecl)
if !isFn {
continue
}
if fn.Name.Name == "main" && fn.Recv == nil {
return true
}
func buildWithDefaults(ctx *context.Context, build config.Build) config.Build {
if build.Binary == "" {
build.Binary = ctx.Config.Release.GitHub.Name
}
return false
if build.Main == "" {
build.Main = "."
}
if len(build.Goos) == 0 {
build.Goos = []string{"linux", "darwin"}
}
if len(build.Goarch) == 0 {
build.Goarch = []string{"amd64", "386"}
}
if len(build.Goarm) == 0 {
build.Goarm = []string{"6"}
}
if build.Ldflags == "" {
build.Ldflags = "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}"
}
return build
}
func runPipeOnBuild(ctx *context.Context, build config.Build) error {
@ -123,19 +104,20 @@ func runHook(env []string, hook string) error {
}
func doBuild(ctx *context.Context, build config.Build, target buildtarget.Target) error {
folder, err := name.For(ctx, target)
if err != nil {
return err
}
var binaryName = build.Binary + ext.For(target)
var prettyName = binaryName
if ctx.Config.Archive.Format == "binary" {
binaryName, err = name.ForBuild(ctx, build, target)
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)
log.WithField("binary", binary).Info("building")

View File

@ -17,7 +17,7 @@ import (
var emptyEnv []string
func TestPipeDescription(t *testing.T) {
assert.NotEmpty(t, Pipe{}.Description())
assert.NotEmpty(t, Pipe{}.String())
}
func TestRun(t *testing.T) {
@ -67,6 +67,12 @@ func TestRunFullPipe(t *testing.T) {
},
},
},
Archive: config.Archive{
Replacements: map[string]string{
"linux": "linuxx",
"darwin": "darwinn",
},
},
}
assert.NoError(t, Pipe{}.Run(context.New(config)))
assert.True(t, exists(binary), binary)
@ -78,7 +84,7 @@ func TestRunPipeFormatBinary(t *testing.T) {
folder, back := testlib.Mktmp(t)
defer back()
writeGoodMain(t, folder)
var binary = filepath.Join(folder, "binary-testing")
var binary = filepath.Join(folder, "binary-testing-bar")
var config = config.Project{
ProjectName: "testing",
Dist: folder,
@ -95,10 +101,12 @@ func TestRunPipeFormatBinary(t *testing.T) {
},
Archive: config.Archive{
Format: "binary",
NameTemplate: "binary-{{.Binary}}",
NameTemplate: "binary-{{.Binary}}-{{.Env.Foo}}",
},
}
assert.NoError(t, Pipe{}.Run(context.New(config)))
ctx := context.New(config)
ctx.Env = map[string]string{"Foo": "bar"}
assert.NoError(t, Pipe{}.Run(ctx))
assert.True(t, exists(binary))
}
@ -174,27 +182,33 @@ func TestRunInvalidNametemplate(t *testing.T) {
folder, back := testlib.Mktmp(t)
defer back()
writeGoodMain(t, folder)
for _, format := range []string{"tar.gz", "zip", "binary"} {
var config = config.Project{
ProjectName: "nameeeee",
Builds: []config.Build{
{
Binary: "namet{{.est}",
Flags: "-v",
Goos: []string{
runtime.GOOS,
},
Goarch: []string{
runtime.GOARCH,
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)), `template: nameeeee:1: unexpected "}" in operand`)
Archive: config.Archive{
Format: format,
NameTemplate: "{{.Binary}",
},
}
assert.EqualError(t, Pipe{}.Run(context.New(config)), msg)
})
}
}
@ -326,6 +340,95 @@ func TestRunPipeWithMainFuncNotInMainGoFile(t *testing.T) {
})
}
func TestDefaultNoBuilds(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{},
}
assert.NoError(t, Pipe{}.Default(ctx))
}
func TestDefaultEmptyBuild(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Release: config.Release{
GitHub: config.Repo{
Name: "foo",
},
},
Builds: []config.Build{
{},
},
},
}
assert.NoError(t, Pipe{}.Default(ctx))
var build = ctx.Config.Builds[0]
assert.Equal(t, ctx.Config.Release.GitHub.Name, build.Binary)
assert.Equal(t, ".", build.Main)
assert.Equal(t, []string{"linux", "darwin"}, build.Goos)
assert.Equal(t, []string{"amd64", "386"}, build.Goarch)
assert.Equal(t, []string{"6"}, build.Goarm)
assert.Equal(t, "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}", build.Ldflags)
}
func TestDefaultPartialBuilds(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Builds: []config.Build{
{
Binary: "bar",
Goos: []string{"linux"},
Main: "./cmd/main.go",
},
{
Binary: "foo",
Ldflags: "-s -w",
Goarch: []string{"386"},
},
},
},
}
assert.NoError(t, Pipe{}.Default(ctx))
t.Run("build0", func(t *testing.T) {
var build = ctx.Config.Builds[0]
assert.Equal(t, "bar", build.Binary)
assert.Equal(t, "./cmd/main.go", build.Main)
assert.Equal(t, []string{"linux"}, build.Goos)
assert.Equal(t, []string{"amd64", "386"}, build.Goarch)
assert.Equal(t, []string{"6"}, build.Goarm)
assert.Equal(t, "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}", build.Ldflags)
})
t.Run("build1", func(t *testing.T) {
var build = ctx.Config.Builds[1]
assert.Equal(t, "foo", build.Binary)
assert.Equal(t, ".", build.Main)
assert.Equal(t, []string{"linux", "darwin"}, build.Goos)
assert.Equal(t, []string{"386"}, build.Goarch)
assert.Equal(t, []string{"6"}, build.Goarm)
assert.Equal(t, "-s -w", build.Ldflags)
})
}
func TestDefaultFillSingleBuild(t *testing.T) {
_, back := testlib.Mktmp(t)
defer back()
var ctx = &context.Context{
Config: config.Project{
Release: config.Release{
GitHub: config.Repo{
Name: "foo",
},
},
SingleBuild: config.Build{
Main: "testreleaser",
},
},
}
assert.NoError(t, Pipe{}.Default(ctx))
assert.Len(t, ctx.Config.Builds, 1)
assert.Equal(t, ctx.Config.Builds[0].Binary, "foo")
}
func exists(file string) bool {
_, err := os.Stat(file)
return !os.IsNotExist(err)

View File

@ -0,0 +1,59 @@
package build
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context"
"github.com/pkg/errors"
)
func checkMain(ctx *context.Context, build config.Build) error {
var main = build.Main
if main == "" {
main = "."
}
stat, ferr := os.Stat(main)
if os.IsNotExist(ferr) {
return errors.Wrapf(ferr, "could not open %s", main)
}
if stat.IsDir() {
packs, err := parser.ParseDir(token.NewFileSet(), main, nil, 0)
if err != nil {
return errors.Wrapf(err, "failed to parse dir: %s", main)
}
for _, pack := range packs {
for _, file := range pack.Files {
if hasMain(file) {
return nil
}
}
}
return fmt.Errorf("build for %s does not contain a main function", build.Binary)
}
file, err := parser.ParseFile(token.NewFileSet(), build.Main, nil, 0)
if err != nil {
return errors.Wrapf(err, "failed to parse file: %s", build.Main)
}
if hasMain(file) {
return nil
}
return fmt.Errorf("build for %s does not contain a main function", build.Binary)
}
func hasMain(file *ast.File) bool {
for _, decl := range file.Decls {
fn, isFn := decl.(*ast.FuncDecl)
if !isFn {
continue
}
if fn.Name.Name == "main" && fn.Recv == nil {
return true
}
}
return false
}

5
pipeline/build/doc.go Normal file
View File

@ -0,0 +1,5 @@
// Package build implements Piper and Defaulter and can build Go projects for
// several platforms, with pre and post hook support.
// Build also checks wether the current project has a main function, parses
// ldflags and other goodies.
package build

View File

@ -14,6 +14,7 @@ type ldflagsData struct {
Tag string
Commit string
Version string
Env map[string]string
}
func ldflags(ctx *context.Context, build config.Build) (string, error) {
@ -22,6 +23,7 @@ func ldflags(ctx *context.Context, build config.Build) (string, error) {
Tag: ctx.Git.CurrentTag,
Version: ctx.Version,
Date: time.Now().UTC().Format(time.RFC3339),
Env: ctx.Env,
}
var out bytes.Buffer
t, err := template.New("ldflags").Parse(build.Ldflags)

View File

@ -12,7 +12,7 @@ func TestLdFlagsFullTemplate(t *testing.T) {
var config = config.Project{
Builds: []config.Build{
{
Ldflags: "-s -w -X main.version={{.Version}} -X main.tag={{.Tag}} -X main.date={{.Date}} -X main.commit={{.Commit}}",
Ldflags: `-s -w -X main.version={{.Version}} -X main.tag={{.Tag}} -X main.date={{.Date}} -X main.commit={{.Commit}} -X "main.foo={{.Env.FOO}}"`,
},
},
}
@ -23,6 +23,7 @@ func TestLdFlagsFullTemplate(t *testing.T) {
},
Version: "1.2.3",
Config: config,
Env: map[string]string{"FOO": "123"},
}
flags, err := ldflags(ctx, ctx.Config.Builds[0])
assert.NoError(t, err)
@ -31,6 +32,7 @@ func TestLdFlagsFullTemplate(t *testing.T) {
assert.Contains(t, flags, "-X main.tag=v1.2.3")
assert.Contains(t, flags, "-X main.commit=123")
assert.Contains(t, flags, "-X main.date=")
assert.Contains(t, flags, `-X "main.foo=123"`)
}
func TestInvalidTemplate(t *testing.T) {

40
pipeline/build/name.go Normal file
View File

@ -0,0 +1,40 @@
package build
import (
"bytes"
"text/template"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/buildtarget"
)
func nameFor(ctx *context.Context, target buildtarget.Target, name string) (string, error) {
var out bytes.Buffer
t, err := template.New(name).Parse(ctx.Config.Archive.NameTemplate)
if err != nil {
return "", err
}
data := struct {
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),
Version: ctx.Version,
Tag: ctx.Git.CurrentTag,
Binary: name, // TODO: deprecated: remove this sometime
ProjectName: name,
Env: ctx.Env,
}
err = t.Execute(&out, data)
return out.String(), err
}
func replace(replacements map[string]string, original string) string {
result := replacements[original]
if result == "" {
return original
}
return result
}

View File

@ -19,9 +19,8 @@ var ErrInvalidSortDirection = errors.New("invalid sort direction")
// Pipe for checksums
type Pipe struct{}
// Description of the pipe
func (Pipe) Description() string {
return "Generating changelog"
func (Pipe) String() string {
return "generating changelog"
}
// Run the pipe
@ -125,7 +124,7 @@ func getChangelog(tag string) (string, error) {
}
func gitLog(refs ...string) (string, error) {
var args = []string{"log", "--pretty=oneline", "--abbrev-commit"}
var args = []string{"log", "--pretty=oneline", "--abbrev-commit", "--no-decorate"}
args = append(args, refs...)
return git.Run(args...)
}

View File

@ -12,7 +12,7 @@ import (
)
func TestDescription(t *testing.T) {
assert.NotEmpty(t, Pipe{}.Description())
assert.NotEmpty(t, Pipe{}.String())
}
func TestChangelogProvidedViaFlag(t *testing.T) {

View File

@ -10,21 +10,19 @@ import (
"github.com/apex/log"
"github.com/goreleaser/goreleaser/checksum"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/name"
"golang.org/x/sync/errgroup"
)
// Pipe for checksums
type Pipe struct{}
// Description of the pipe
func (Pipe) Description() string {
return "Calculating checksums"
func (Pipe) String() string {
return "calculating checksums"
}
// Run the pipe
func (Pipe) Run(ctx *context.Context) (err error) {
filename, err := name.ForChecksums(ctx)
filename, err := filenameFor(ctx)
if err != nil {
return err
}
@ -52,6 +50,14 @@ func (Pipe) Run(ctx *context.Context) (err error) {
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)

View File

@ -11,7 +11,7 @@ import (
)
func TestDescription(t *testing.T) {
assert.NotEmpty(t, Pipe{}.Description())
assert.NotEmpty(t, Pipe{}.String())
}
func TestPipe(t *testing.T) {
@ -35,7 +35,7 @@ func TestPipe(t *testing.T) {
assert.Contains(t, ctx.Artifacts, checksums, binary)
bts, err := ioutil.ReadFile(filepath.Join(folder, checksums))
assert.NoError(t, err)
assert.Equal(t, string(bts), "61d034473102d7dac305902770471fd50f4c5b26f6831a56dd90b5184b3c30fc binary\n")
assert.Equal(t, "61d034473102d7dac305902770471fd50f4c5b26f6831a56dd90b5184b3c30fc binary\n", string(bts))
}
func TestPipeFileNotExist(t *testing.T) {
@ -70,7 +70,7 @@ func TestPipeInvalidNameTemplate(t *testing.T) {
ctx.AddArtifact("whatever")
err = Pipe{}.Run(ctx)
assert.Error(t, err)
assert.Equal(t, `template: name:1: unexpected "}" in operand`, err.Error())
assert.Equal(t, `template: checksums:1: unexpected "}" in operand`, err.Error())
}
func TestPipeCouldNotOpenChecksumsTxt(t *testing.T) {
@ -91,3 +91,29 @@ func TestPipeCouldNotOpenChecksumsTxt(t *testing.T) {
assert.Error(t, err)
assert.Contains(t, err.Error(), "/checksums.txt: permission denied")
}
func TestDefault(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Checksum: config.Checksum{},
},
}
assert.NoError(t, Pipe{}.Default(ctx))
assert.Equal(
t,
"{{ .ProjectName }}_{{ .Version }}_checksums.txt",
ctx.Config.Checksum.NameTemplate,
)
}
func TestDefaultSet(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Checksum: config.Checksum{
NameTemplate: "checksums.txt",
},
},
}
assert.NoError(t, Pipe{}.Default(ctx))
assert.Equal(t, "checksums.txt", ctx.Config.Checksum.NameTemplate)
}

View File

@ -0,0 +1,24 @@
package checksums
import (
"bytes"
"text/template"
"github.com/goreleaser/goreleaser/context"
)
func filenameFor(ctx *context.Context) (string, error) {
var out bytes.Buffer
t, err := template.New("checksums").Parse(ctx.Config.Checksum.NameTemplate)
if err != nil {
return "", err
}
err = t.Execute(&out, struct {
ProjectName, Tag, Version string
}{
ProjectName: ctx.Config.ProjectName,
Tag: ctx.Git.CurrentTag,
Version: ctx.Version,
})
return out.String(), err
}

View File

@ -14,9 +14,8 @@ import (
// Pipe for cleandis
type Pipe struct{}
// Description of the pipe
func (Pipe) Description() string {
return "Checking ./dist"
func (Pipe) String() string {
return "checking ./dist"
}
// Run the pipe

View File

@ -59,5 +59,5 @@ func TestEmptyDistExists(t *testing.T) {
}
func TestDescription(t *testing.T) {
assert.NotEmpty(t, Pipe{}.Description())
assert.NotEmpty(t, Pipe{}.String())
}

16
pipeline/default.go Normal file
View File

@ -0,0 +1,16 @@
package pipeline
import (
"fmt"
"github.com/goreleaser/goreleaser/context"
)
// Defaulter can be implemented by a Piper to set default values for its
// configuration.
type Defaulter interface {
fmt.Stringer
// Default sets the configuration defaults
Default(ctx *context.Context) error
}

View File

@ -3,190 +3,51 @@
package defaults
import (
"fmt"
"strings"
"github.com/apex/log"
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/pipeline"
"github.com/goreleaser/goreleaser/pipeline/archive"
"github.com/goreleaser/goreleaser/pipeline/brew"
"github.com/goreleaser/goreleaser/pipeline/build"
"github.com/goreleaser/goreleaser/pipeline/checksums"
"github.com/goreleaser/goreleaser/pipeline/docker"
"github.com/goreleaser/goreleaser/pipeline/fpm"
"github.com/goreleaser/goreleaser/pipeline/release"
"github.com/goreleaser/goreleaser/pipeline/snapshot"
)
// NameTemplate default name_template for the archive.
const NameTemplate = "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
// ReleaseNameTemplate is the default name for the release.
const ReleaseNameTemplate = "{{.Tag}}"
// SnapshotNameTemplate represents the default format for snapshot release names.
const SnapshotNameTemplate = "SNAPSHOT-{{ .Commit }}"
// ChecksumNameTemplate is the default name_template for the checksum file.
const ChecksumNameTemplate = "{{ .ProjectName }}_{{ .Version }}_checksums.txt"
// Pipe for brew deployment
type Pipe struct{}
// Description of the pipe
func (Pipe) Description() string {
return "Setting defaults"
func (Pipe) String() string {
return "setting defaults for:"
}
var defaulters = []pipeline.Defaulter{
snapshot.Pipe{},
release.Pipe{},
archive.Pipe{},
build.Pipe{},
fpm.Pipe{},
checksums.Pipe{},
docker.Pipe{},
brew.Pipe{},
}
// Run the pipe
func (Pipe) Run(ctx *context.Context) error { // nolint: gocyclo
func (Pipe) Run(ctx *context.Context) error {
if ctx.Config.Dist == "" {
ctx.Config.Dist = "dist"
}
if ctx.Config.Release.NameTemplate == "" {
ctx.Config.Release.NameTemplate = ReleaseNameTemplate
}
if ctx.Config.Snapshot.NameTemplate == "" {
ctx.Config.Snapshot.NameTemplate = SnapshotNameTemplate
}
if ctx.Config.Checksum.NameTemplate == "" {
ctx.Config.Checksum.NameTemplate = ChecksumNameTemplate
}
if err := setReleaseDefaults(ctx); err != nil {
return err
for _, defaulter := range defaulters {
log.Infof("\t%s", defaulter.String())
if err := defaulter.Default(ctx); err != nil {
return err
}
}
if ctx.Config.ProjectName == "" {
ctx.Config.ProjectName = ctx.Config.Release.GitHub.Name
}
setBuildDefaults(ctx)
if ctx.Config.Brew.Install == "" {
var installs []string
for _, build := range ctx.Config.Builds {
if !isBrewBuild(build) {
continue
}
installs = append(
installs,
fmt.Sprintf(`bin.install "%s"`, build.Binary),
)
}
ctx.Config.Brew.Install = strings.Join(installs, "\n")
}
if ctx.Config.Brew.CommitAuthor.Name == "" {
ctx.Config.Brew.CommitAuthor.Name = "goreleaserbot"
}
if ctx.Config.Brew.CommitAuthor.Email == "" {
ctx.Config.Brew.CommitAuthor.Email = "goreleaser@carlosbecker.com"
}
err := setArchiveDefaults(ctx)
setDockerDefaults(ctx)
setFpmDefaults(ctx)
log.WithField("config", ctx.Config).Debug("defaults set")
return err
}
func setDockerDefaults(ctx *context.Context) {
if len(ctx.Config.Dockers) != 1 {
return
}
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"
}
}
func isBrewBuild(build config.Build) bool {
for _, ignore := range build.Ignore {
if ignore.Goos == "darwin" && ignore.Goarch == "amd64" {
return false
}
}
return contains(build.Goos, "darwin") && contains(build.Goarch, "amd64")
}
func contains(ss []string, s string) bool {
for _, zs := range ss {
if zs == s {
return true
}
}
return false
}
func setReleaseDefaults(ctx *context.Context) error {
if ctx.Config.Release.GitHub.Name != "" {
return nil
}
repo, err := remoteRepo()
if err != nil {
return err
}
ctx.Config.Release.GitHub = repo
return nil
}
func setBuildDefaults(ctx *context.Context) {
for i, build := range ctx.Config.Builds {
ctx.Config.Builds[i] = buildWithDefaults(ctx, build)
}
if len(ctx.Config.Builds) == 0 {
ctx.Config.Builds = []config.Build{
buildWithDefaults(ctx, ctx.Config.SingleBuild),
}
}
}
func buildWithDefaults(ctx *context.Context, build config.Build) config.Build {
if build.Binary == "" {
build.Binary = ctx.Config.Release.GitHub.Name
}
if build.Main == "" {
build.Main = "."
}
if len(build.Goos) == 0 {
build.Goos = []string{"linux", "darwin"}
}
if len(build.Goarch) == 0 {
build.Goarch = []string{"amd64", "386"}
}
if len(build.Goarm) == 0 {
build.Goarm = []string{"6"}
}
if build.Ldflags == "" {
build.Ldflags = "-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}"
}
return build
}
func setArchiveDefaults(ctx *context.Context) error {
if ctx.Config.Archive.NameTemplate == "" {
ctx.Config.Archive.NameTemplate = NameTemplate
}
if ctx.Config.Archive.Format == "" {
ctx.Config.Archive.Format = "tar.gz"
}
if len(ctx.Config.Archive.Files) == 0 {
ctx.Config.Archive.Files = []string{
"licence*",
"LICENCE*",
"license*",
"LICENSE*",
"readme*",
"README*",
"changelog*",
"CHANGELOG*",
}
}
return nil
}
func setFpmDefaults(ctx *context.Context) {
if ctx.Config.FPM.Bindir == "" {
ctx.Config.FPM.Bindir = "/usr/local/bin"
}
}

View File

@ -10,7 +10,7 @@ import (
)
func TestDescription(t *testing.T) {
assert.NotEmpty(t, Pipe{}.Description())
assert.NotEmpty(t, Pipe{}.String())
}
func TestFillBasicData(t *testing.T) {
@ -90,42 +90,3 @@ func TestFillPartial(t *testing.T) {
assert.Empty(t, ctx.Config.Dockers[0].Goarm)
assert.Equal(t, "disttt", ctx.Config.Dist)
}
func TestFillSingleBuild(t *testing.T) {
_, back := testlib.Mktmp(t)
defer back()
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@github.com:goreleaser/goreleaser.git")
var ctx = &context.Context{
Config: config.Project{
SingleBuild: config.Build{
Main: "testreleaser",
},
},
}
assert.NoError(t, Pipe{}.Run(ctx))
assert.Len(t, ctx.Config.Builds, 1)
assert.Equal(t, ctx.Config.Builds[0].Binary, "goreleaser")
}
func TestNotAGitRepo(t *testing.T) {
_, back := testlib.Mktmp(t)
defer back()
testlib.GitInit(t)
var ctx = &context.Context{
Config: config.Project{},
}
assert.Error(t, Pipe{}.Run(ctx))
assert.Empty(t, ctx.Config.Release.GitHub.String())
}
func TestGitRepoWithoutRemote(t *testing.T) {
_, back := testlib.Mktmp(t)
defer back()
var ctx = &context.Context{
Config: config.Project{},
}
assert.Error(t, Pipe{}.Run(ctx))
assert.Empty(t, ctx.Config.Release.GitHub.String())
}

View File

@ -2,17 +2,17 @@
package docker
import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"text/template"
"github.com/apex/log"
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/pipeline"
"github.com/apex/log"
"github.com/pkg/errors"
)
@ -22,9 +22,8 @@ var ErrNoDocker = errors.New("docker not present in $PATH")
// Pipe for docker
type Pipe struct{}
// Description of the pipe
func (Pipe) Description() string {
return "Creating Docker images"
func (Pipe) String() string {
return "creating Docker images"
}
// Run the pipe
@ -39,6 +38,32 @@ 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 {
for _, docker := range ctx.Config.Dockers {
var imagePlatform = docker.Goos + docker.Goarch + docker.Goarm
@ -62,10 +87,32 @@ func doRun(ctx *context.Context) error {
return nil
}
func tagName(ctx *context.Context, docker config.Docker) (string, error) {
var out bytes.Buffer
t, err := template.New("tag").Parse(docker.TagTemplate)
if err != nil {
return "", err
}
data := struct {
Version, Tag string
Env map[string]string
}{
Version: ctx.Version,
Tag: ctx.Git.CurrentTag,
Env: ctx.Env,
}
err = t.Execute(&out, data)
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)
var dockerfile = filepath.Join(root, filepath.Base(docker.Dockerfile))
var image = fmt.Sprintf("%s:%s", docker.Image, ctx.Version)
tag, err := tagName(ctx, docker)
if err != nil {
return err
}
var image = fmt.Sprintf("%s:%s", docker.Image, tag)
var latest = fmt.Sprintf("%s:latest", docker.Image)
if err := os.Link(docker.Dockerfile, dockerfile); err != nil {

View File

@ -8,11 +8,9 @@ import (
"testing"
"github.com/apex/log"
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/pipeline"
"github.com/stretchr/testify/assert"
)
@ -42,50 +40,89 @@ func TestRunPipe(t *testing.T) {
var binPath = filepath.Join(dist, "mybin", "mybin")
_, err = os.Create(binPath)
assert.NoError(t, err)
var table = map[string]struct {
docker config.Docker
err string
}{
"valid": {
docker: config.Docker{
Image: "localhost:5000/goreleaser/test_run_pipe",
Goos: "linux",
Goarch: "amd64",
Dockerfile: "testdata/Dockerfile",
Binary: "mybin",
Latest: true,
TagTemplate: "{{.Tag}}-{{.Env.FOO}}",
},
err: "",
},
"invalid": {
docker: config.Docker{
Image: "localhost:5000/goreleaser/test_run_pipe_nope",
Goos: "linux",
Goarch: "amd64",
Dockerfile: "testdata/Dockerfile",
Binary: "otherbin",
TagTemplate: "{{.Version}}",
},
err: "",
},
"template_error": {
docker: config.Docker{
Image: "localhost:5000/goreleaser/test_run_pipe_template_error",
Goos: "linux",
Goarch: "amd64",
Dockerfile: "testdata/Dockerfile",
Binary: "mybin",
Latest: true,
TagTemplate: "{{.Tag}",
},
err: `template: tag:1: unexpected "}" in operand`,
},
}
var images = []string{
"localhost:5000/goreleaser/test_run_pipe:1.0.0",
"localhost:5000/goreleaser/test_run_pipe:v1.0.0-123",
"localhost:5000/goreleaser/test_run_pipe:latest",
}
// this might fail as the image doesnt exist yet, so lets ignore the error
for _, img := range images {
_ = exec.Command("docker", "rmi", img).Run()
}
var ctx = &context.Context{
Version: "1.0.0",
Publish: true,
Config: config.Project{
ProjectName: "mybin",
Dist: dist,
Dockers: []config.Docker{
{
Image: "localhost:5000/goreleaser/test_run_pipe",
Goos: "linux",
Goarch: "amd64",
Dockerfile: "testdata/Dockerfile",
Binary: "mybin",
Latest: true,
for name, docker := range table {
t.Run(name, func(t *testing.T) {
var ctx = &context.Context{
Version: "1.0.0",
Publish: true,
Git: context.GitInfo{
CurrentTag: "v1.0.0",
},
{
Image: "localhost:5000/goreleaser/test_run_pipe_nope",
Goos: "linux",
Goarch: "amd64",
Dockerfile: "testdata/Dockerfile",
Binary: "otherbin",
Config: config.Project{
ProjectName: "mybin",
Dist: dist,
Dockers: []config.Docker{
docker.docker,
},
},
},
},
Env: map[string]string{"FOO": "123"},
}
for _, plat := range []string{"linuxamd64", "linux386", "darwinamd64"} {
ctx.AddBinary(plat, "mybin", "mybin", binPath)
}
if docker.err == "" {
assert.NoError(t, Pipe{}.Run(ctx))
} else {
assert.EqualError(t, Pipe{}.Run(ctx), docker.err)
}
})
}
for _, plat := range []string{"linuxamd64", "linux386", "darwinamd64"} {
ctx.AddBinary(plat, "mybin", "mybin", binPath)
}
assert.NoError(t, Pipe{}.Run(ctx))
// this might should not fail as the image should have been created when
// the step ran
for _, img := range images {
assert.NoError(t, exec.Command("docker", "rmi", img).Run())
}
// the test_run_pipe_nope image should not have been created, so deleting
// it should fail
assert.Error(t,
@ -96,7 +133,7 @@ func TestRunPipe(t *testing.T) {
}
func TestDescription(t *testing.T) {
assert.NotEmpty(t, Pipe{}.Description())
assert.NotEmpty(t, Pipe{}.String())
}
func TestNoDockers(t *testing.T) {
@ -131,3 +168,61 @@ func TestDockerNotInPath(t *testing.T) {
}
assert.EqualError(t, Pipe{}.Run(ctx), ErrNoDocker.Error())
}
func TestDefault(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Builds: []config.Build{
{
Binary: "foo",
},
},
Dockers: []config.Docker{
{
Latest: true,
},
},
},
}
assert.NoError(t, Pipe{}.Default(ctx))
assert.Len(t, ctx.Config.Dockers, 1)
var docker = ctx.Config.Dockers[0]
assert.Equal(t, "linux", docker.Goos)
assert.Equal(t, "amd64", docker.Goarch)
assert.Equal(t, ctx.Config.Builds[0].Binary, docker.Binary)
assert.Equal(t, "Dockerfile", docker.Dockerfile)
assert.Equal(t, "{{ .Version }}", docker.TagTemplate)
}
func TestDefaultNoDockers(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Dockers: []config.Docker{},
},
}
assert.NoError(t, Pipe{}.Default(ctx))
assert.Empty(t, ctx.Config.Dockers)
}
func TestDefaultSet(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Dockers: []config.Docker{
{
Goos: "windows",
Goarch: "i386",
Binary: "bar",
Dockerfile: "Dockerfile.foo",
},
},
},
}
assert.NoError(t, Pipe{}.Default(ctx))
assert.Len(t, ctx.Config.Dockers, 1)
var docker = ctx.Config.Dockers[0]
assert.Equal(t, "windows", docker.Goos)
assert.Equal(t, "i386", docker.Goarch)
assert.Equal(t, "bar", docker.Binary)
assert.Equal(t, "{{ .Version }}", docker.TagTemplate)
assert.Equal(t, "Dockerfile.foo", docker.Dockerfile)
}

5
pipeline/env/env.go vendored
View File

@ -16,9 +16,8 @@ var ErrMissingToken = errors.New("missing GITHUB_TOKEN")
// Pipe for env
type Pipe struct{}
// Description of the pipe
func (Pipe) Description() string {
return "Loading environment variables"
func (Pipe) String() string {
return "loading environment variables"
}
// Run the pipe

View File

@ -12,7 +12,7 @@ import (
)
func TestDescription(t *testing.T) {
assert.NotEmpty(t, Pipe{}.Description())
assert.NotEmpty(t, Pipe{}.String())
}
func TestValidEnv(t *testing.T) {

View File

@ -22,9 +22,16 @@ var ErrNoFPM = errors.New("fpm not present in $PATH")
// Pipe for fpm packaging
type Pipe struct{}
// Description of the pipe
func (Pipe) Description() string {
return "Creating Linux packages with fpm"
func (Pipe) String() string {
return "creating Linux packages with fpm"
}
// Default sets the pipe defaults
func (Pipe) Default(ctx *context.Context) error {
if ctx.Config.FPM.Bindir == "" {
ctx.Config.FPM.Bindir = "/usr/local/bin"
}
return nil
}
// Run the pipe

View File

@ -14,7 +14,7 @@ import (
)
func TestDescription(t *testing.T) {
assert.NotEmpty(t, Pipe{}.Description())
assert.NotEmpty(t, Pipe{}.String())
}
func TestRunPipeNoFormats(t *testing.T) {
@ -113,3 +113,25 @@ func TestRunPipeWithExtraFiles(t *testing.T) {
}
assert.NoError(t, Pipe{}.Run(ctx))
}
func TestDefault(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
FPM: config.FPM{},
},
}
assert.NoError(t, Pipe{}.Default(ctx))
assert.Equal(t, "/usr/local/bin", ctx.Config.FPM.Bindir)
}
func TestDefaultSet(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
FPM: config.FPM{
Bindir: "/bin",
},
},
}
assert.NoError(t, Pipe{}.Default(ctx))
assert.Equal(t, "/bin", ctx.Config.FPM.Bindir)
}

View File

@ -19,9 +19,8 @@ import (
// Pipe for brew deployment
type Pipe struct{}
// Description of the pipe
func (Pipe) Description() string {
return "Getting and validating git state"
func (Pipe) String() string {
return "getting and validating git state"
}
// Run the pipe

View File

@ -9,12 +9,11 @@ import (
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context"
"github.com/goreleaser/goreleaser/internal/testlib"
"github.com/goreleaser/goreleaser/pipeline/defaults"
"github.com/stretchr/testify/assert"
)
func TestDescription(t *testing.T) {
assert.NotEmpty(t, Pipe{}.Description())
assert.NotEmpty(t, Pipe{}.String())
}
func TestNotAGitFolder(t *testing.T) {
@ -57,7 +56,7 @@ func TestNoTagsSnapshot(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Snapshot: config.Snapshot{
NameTemplate: defaults.SnapshotNameTemplate,
NameTemplate: "SNAPSHOT-{{.Commit}}",
},
},
Snapshot: true,
@ -95,7 +94,7 @@ func TestNoTagsNoSnapshot(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Snapshot: config.Snapshot{
NameTemplate: defaults.SnapshotNameTemplate,
NameTemplate: "SNAPSHOT-{{.Commit}}",
},
},
Snapshot: false,

View File

@ -1,12 +1,15 @@
// Package pipeline provides a generic pipe interface.
package pipeline
import "github.com/goreleaser/goreleaser/context"
import (
"fmt"
// Pipe interface
type Pipe interface {
// Name of the pipe
Description() string
"github.com/goreleaser/goreleaser/context"
)
// Piper defines a pipe, which can be part of a pipeline (a serie of pipes).
type Piper interface {
fmt.Stringer
// Run the pipe
Run(ctx *context.Context) error

View File

@ -12,10 +12,18 @@ const bodyTemplate = `{{ .ReleaseNotes }}
{{- if .DockerImages }}
Docker images:
## Docker images
{{ range $element := .DockerImages }}
- {{ . -}}
{{ end -}}
- ` + "`docker pull {{ . -}}`" + `
{{- end -}}
{{- end }}
{{- if .Brews }}
## Homebrew taps
{{ range $element := .Brews }}
- ` + "`brew install {{ . -}}`" + `
{{- end -}}
{{- end }}
---
@ -37,10 +45,12 @@ func describeBodyVersion(ctx *context.Context, version string) (bytes.Buffer, er
err := template.Execute(&out, struct {
ReleaseNotes, GoVersion string
DockerImages []string
Brews []string
}{
ReleaseNotes: ctx.ReleaseNotes,
GoVersion: version,
DockerImages: ctx.Dockers,
Brews: ctx.Brews,
})
return out, err
}

View File

@ -15,7 +15,12 @@ func TestDescribeBody(t *testing.T) {
ReleaseNotes: changelog,
Dockers: []string{
"goreleaser/goreleaser:0.40.0",
"goreleaser/godownloader:0.1.0",
"goreleaser/goreleaser:latest",
"goreleaser/godownloader:v0.1.0",
},
Brews: []string{
"caarlos0/tap/foo",
"goreleaser/tap/bar",
},
}
out, err := describeBodyVersion(ctx, "go version go1.9 darwin/amd64")
@ -28,7 +33,7 @@ func TestDescribeBody(t *testing.T) {
assert.Equal(t, string(bts), out.String())
}
func TestDescribeBodyNoDockerImages(t *testing.T) {
func TestDescribeBodyNoDockerImagesNoBrews(t *testing.T) {
var changelog = "\nfeature1: description\nfeature2: other description"
var ctx = &context.Context{
ReleaseNotes: changelog,

View File

@ -16,9 +16,8 @@ import (
// Pipe for github release
type Pipe struct{}
// Description of the pipe
func (Pipe) Description() string {
return "Releasing to GitHub"
func (Pipe) String() string {
return "releasing to GitHub"
}
// Run the pipe
@ -30,6 +29,22 @@ func (Pipe) Run(ctx *context.Context) error {
return doRun(ctx, c)
}
// Default sets the pipe defaults
func (Pipe) Default(ctx *context.Context) error {
if ctx.Config.Release.NameTemplate == "" {
ctx.Config.Release.NameTemplate = "{{.Tag}}"
}
if ctx.Config.Release.GitHub.Name != "" {
return nil
}
repo, err := remoteRepo()
if err != nil {
return err
}
ctx.Config.Release.GitHub = repo
return nil
}
func doRun(ctx *context.Context, c client.Client) error {
if !ctx.Publish {
return pipeline.Skip("--skip-publish is set")

View File

@ -15,7 +15,7 @@ import (
)
func TestPipeDescription(t *testing.T) {
assert.NotEmpty(t, Pipe{}.Description())
assert.NotEmpty(t, Pipe{}.String())
}
func TestRunPipe(t *testing.T) {
@ -122,6 +122,62 @@ func TestSkipPublish(t *testing.T) {
assert.False(t, client.UploadedFile)
}
func TestDefault(t *testing.T) {
_, back := testlib.Mktmp(t)
defer back()
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@github.com:goreleaser/goreleaser.git")
var ctx = &context.Context{
Config: config.Project{},
}
assert.NoError(t, Pipe{}.Default(ctx))
assert.Equal(t, "goreleaser", ctx.Config.Release.GitHub.Name)
assert.Equal(t, "goreleaser", ctx.Config.Release.GitHub.Owner)
}
func TestDefaultFilled(t *testing.T) {
_, back := testlib.Mktmp(t)
defer back()
testlib.GitInit(t)
testlib.GitRemoteAdd(t, "git@github.com:goreleaser/goreleaser.git")
var ctx = &context.Context{
Config: config.Project{
Release: config.Release{
GitHub: config.Repo{
Name: "foo",
Owner: "bar",
},
},
},
}
assert.NoError(t, Pipe{}.Default(ctx))
assert.Equal(t, "foo", ctx.Config.Release.GitHub.Name)
assert.Equal(t, "bar", ctx.Config.Release.GitHub.Owner)
}
func TestDefaultNotAGitRepo(t *testing.T) {
_, back := testlib.Mktmp(t)
defer back()
testlib.GitInit(t)
var ctx = &context.Context{
Config: config.Project{},
}
assert.Error(t, Pipe{}.Default(ctx))
assert.Empty(t, ctx.Config.Release.GitHub.String())
}
func TestDefaultGitRepoWithoutRemote(t *testing.T) {
_, back := testlib.Mktmp(t)
defer back()
var ctx = &context.Context{
Config: config.Project{},
}
assert.Error(t, Pipe{}.Default(ctx))
assert.Empty(t, ctx.Config.Release.GitHub.String())
}
type DummyClient struct {
FailToCreateRelease bool
FailToUpload bool

View File

@ -1,4 +1,4 @@
package defaults
package release
import (
"strings"

View File

@ -1,4 +1,4 @@
package defaults
package release
import (
"testing"

View File

@ -2,10 +2,16 @@
feature1: description
feature2: other description
Docker images:
## Docker images
- goreleaser/goreleaser:0.40.0
- goreleaser/godownloader:0.1.0
- `docker pull goreleaser/goreleaser:0.40.0`
- `docker pull goreleaser/goreleaser:latest`
- `docker pull goreleaser/godownloader:v0.1.0`
## Homebrew taps
- `brew install caarlos0/tap/foo`
- `brew install goreleaser/tap/bar`
---
Automated with [GoReleaser](https://github.com/goreleaser)

View File

@ -49,9 +49,8 @@ type AppMetadata struct {
// Pipe for snapcraft packaging
type Pipe struct{}
// Description of the pipe
func (Pipe) Description() string {
return "Creating Linux packages with snapcraft"
func (Pipe) String() string {
return "creating Linux packages with snapcraft"
}
// Run the pipe

View File

@ -15,7 +15,7 @@ import (
)
func TestDescription(t *testing.T) {
assert.NotEmpty(t, Pipe{}.Description())
assert.NotEmpty(t, Pipe{}.String())
}
func TestRunPipeMissingInfo(t *testing.T) {

View File

@ -0,0 +1,19 @@
// Package snapshot provides the snapshoting functionality to goreleaser.
package snapshot
import "github.com/goreleaser/goreleaser/context"
// Pipe for checksums
type Pipe struct{}
func (Pipe) String() string {
return "generating changelog"
}
// Default sets the pipe defaults
func (Pipe) Default(ctx *context.Context) error {
if ctx.Config.Snapshot.NameTemplate == "" {
ctx.Config.Snapshot.NameTemplate = "SNAPSHOT-{{ .Commit }}"
}
return nil
}

View File

@ -0,0 +1,34 @@
package snapshot
import (
"testing"
"github.com/goreleaser/goreleaser/config"
"github.com/goreleaser/goreleaser/context"
"github.com/stretchr/testify/assert"
)
func TestStringer(t *testing.T) {
assert.NotEmpty(t, Pipe{}.String())
}
func TestDefault(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Snapshot: config.Snapshot{},
},
}
assert.NoError(t, Pipe{}.Default(ctx))
assert.Equal(t, "SNAPSHOT-{{ .Commit }}", ctx.Config.Snapshot.NameTemplate)
}
func TestDefaultSet(t *testing.T) {
var ctx = &context.Context{
Config: config.Project{
Snapshot: config.Snapshot{
NameTemplate: "snap",
},
},
}
assert.NoError(t, Pipe{}.Default(ctx))
assert.Equal(t, "snap", ctx.Config.Snapshot.NameTemplate)
}