mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-02-13 13:48:40 +02:00
Merge pull request #223 from zerok/feature/snapshot
Add snapshot builds
This commit is contained in:
commit
c0db0455f7
18
README.md
18
README.md
@ -130,6 +130,8 @@ are not enforcing it though. We do remove the `v` prefix and then enforce
|
||||
that the next character is a number. So, `v0.1.0` and `0.1.0` are virtually the
|
||||
same and are both accepted, while `version0.1.0` is not.
|
||||
|
||||
If you don't want to create a tag yet but instead simply create a package based on the latest commit, then you can also use the `--snapshot` flag.
|
||||
|
||||
Now you can run GoReleaser at the root of your repository:
|
||||
|
||||
```console
|
||||
@ -164,7 +166,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
`version` will always be the name of the current Git tag.
|
||||
`version` will be the current Git tag or the name of the snapshot if you're using the `--snapshot` flag.
|
||||
|
||||
## Release customization
|
||||
|
||||
@ -288,6 +290,20 @@ release:
|
||||
You can also specify a release notes file in markdown format using the
|
||||
`--release-notes` flag.
|
||||
|
||||
### Snapshot customization
|
||||
|
||||
```yml
|
||||
# goreleaser.yml
|
||||
snapshot:
|
||||
# Allows you to change the name of the generated snapshot
|
||||
# releases. The following variables are available:
|
||||
# - Commit
|
||||
# - Tag
|
||||
# - Timestamp
|
||||
# Default: SNAPSHOT-{{.Commit}}
|
||||
name_template: SNAPSHOT-{{.Commit}}
|
||||
```
|
||||
|
||||
### Homebrew tap customization
|
||||
|
||||
The brew section specifies how the formula should be created.
|
||||
|
@ -83,13 +83,19 @@ type FPM struct {
|
||||
License string `yaml:",omitempty"`
|
||||
}
|
||||
|
||||
// Snapshot config
|
||||
type Snapshot struct {
|
||||
NameTemplate string `yaml:"name_template,omitempty"`
|
||||
}
|
||||
|
||||
// Project includes all project configuration
|
||||
type Project struct {
|
||||
Release Release `yaml:",omitempty"`
|
||||
Brew Homebrew `yaml:",omitempty"`
|
||||
Build Build `yaml:",omitempty"`
|
||||
Archive Archive `yaml:",omitempty"`
|
||||
FPM FPM `yaml:",omitempty"`
|
||||
Release Release `yaml:",omitempty"`
|
||||
Brew Homebrew `yaml:",omitempty"`
|
||||
Build Build `yaml:",omitempty"`
|
||||
Archive Archive `yaml:",omitempty"`
|
||||
FPM FPM `yaml:",omitempty"`
|
||||
Snapshot Snapshot `yaml:",omitempty"`
|
||||
|
||||
// test only property indicating the path to the dist folder
|
||||
Dist string `yaml:"-"`
|
||||
|
@ -33,6 +33,7 @@ type Context struct {
|
||||
Version string
|
||||
Validate bool
|
||||
Publish bool
|
||||
Snapshot bool
|
||||
}
|
||||
|
||||
var lock sync.Mutex
|
||||
|
@ -70,6 +70,11 @@ func Release(flags Flags) error {
|
||||
log.Println("Loaded custom release notes from", notes)
|
||||
ctx.ReleaseNotes = string(bts)
|
||||
}
|
||||
ctx.Snapshot = flags.Bool("snapshot")
|
||||
if ctx.Snapshot {
|
||||
log.Println("Publishing disabled in snapshot mode")
|
||||
ctx.Publish = false
|
||||
}
|
||||
for _, pipe := range pipes {
|
||||
log.Println(pipe.Description())
|
||||
log.SetPrefix(" -> ")
|
||||
|
4
main.go
4
main.go
@ -38,6 +38,10 @@ func main() {
|
||||
Name: "skip-publish",
|
||||
Usage: "Skip all publishing pipes of the release",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "snapshot",
|
||||
Usage: "Generate an unversioned snapshot release",
|
||||
},
|
||||
}
|
||||
app.Action = func(c *cli.Context) error {
|
||||
log.Printf("Running goreleaser %v\n", version)
|
||||
|
@ -15,6 +15,9 @@ var defaultFiles = []string{"licence", "license", "readme", "changelog"}
|
||||
// NameTemplate default name_template for the archive.
|
||||
const NameTemplate = "{{ .Binary }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
|
||||
|
||||
// SnapshotNameTemplate represents the default format for snapshot release names.
|
||||
const SnapshotNameTemplate = "SNAPSHOT-{{.Commit}}"
|
||||
|
||||
// Pipe for brew deployment
|
||||
type Pipe struct{}
|
||||
|
||||
@ -25,6 +28,9 @@ func (Pipe) Description() string {
|
||||
|
||||
// Run the pipe
|
||||
func (Pipe) Run(ctx *context.Context) error {
|
||||
if ctx.Config.Snapshot.NameTemplate == "" {
|
||||
ctx.Config.Snapshot.NameTemplate = SnapshotNameTemplate
|
||||
}
|
||||
if ctx.Config.Release.GitHub.Name == "" {
|
||||
repo, err := remoteRepo()
|
||||
ctx.Config.Release.GitHub = repo
|
||||
|
4
pipeline/env/env.go
vendored
4
pipeline/env/env.go
vendored
@ -24,6 +24,10 @@ func (Pipe) Description() string {
|
||||
// Run the pipe
|
||||
func (Pipe) Run(ctx *context.Context) (err error) {
|
||||
ctx.Token = os.Getenv("GITHUB_TOKEN")
|
||||
if !ctx.Publish {
|
||||
log.Println("GITHUB_TOKEN not validated because publishing has been disabled")
|
||||
return nil
|
||||
}
|
||||
if !ctx.Validate {
|
||||
log.Println("Skipped validations because --skip-validate is set")
|
||||
return nil
|
||||
|
24
pipeline/env/env_test.go
vendored
24
pipeline/env/env_test.go
vendored
@ -19,6 +19,7 @@ func TestValidEnv(t *testing.T) {
|
||||
var ctx = &context.Context{
|
||||
Config: config.Project{},
|
||||
Validate: true,
|
||||
Publish: true,
|
||||
}
|
||||
assert.NoError(Pipe{}.Run(ctx))
|
||||
}
|
||||
@ -29,10 +30,33 @@ func TestInvalidEnv(t *testing.T) {
|
||||
var ctx = &context.Context{
|
||||
Config: config.Project{},
|
||||
Validate: true,
|
||||
Publish: true,
|
||||
}
|
||||
assert.Error(Pipe{}.Run(ctx))
|
||||
}
|
||||
|
||||
func TestInvalidEnvDisabled(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
assert.NoError(os.Unsetenv("GITHUB_TOKEN"))
|
||||
var ctx = &context.Context{
|
||||
Config: config.Project{},
|
||||
Validate: false,
|
||||
Publish: true,
|
||||
}
|
||||
assert.NoError(Pipe{}.Run(ctx))
|
||||
}
|
||||
|
||||
func TestEnvWithoutPublish(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
assert.NoError(os.Unsetenv("GITHUB_TOKEN"))
|
||||
var ctx = &context.Context{
|
||||
Config: config.Project{},
|
||||
Validate: true,
|
||||
Publish: false,
|
||||
}
|
||||
assert.NoError(Pipe{}.Run(ctx))
|
||||
}
|
||||
|
||||
func TestSkipValidate(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
var ctx = &context.Context{
|
||||
|
@ -3,10 +3,14 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"text/template"
|
||||
|
||||
"github.com/goreleaser/goreleaser/context"
|
||||
)
|
||||
@ -38,6 +42,10 @@ func (e ErrWrongRef) Error() string {
|
||||
return fmt.Sprintf("git tag %v was not made against commit %v", e.tag, e.commit)
|
||||
}
|
||||
|
||||
// ErrNoTag happens if the underlying git repository doesn't contain any tags
|
||||
// but no snapshot-release was requested.
|
||||
var ErrNoTag = fmt.Errorf("git doesn't contain any tags. Either add a tag or use --snapshot")
|
||||
|
||||
// Pipe for brew deployment
|
||||
type Pipe struct{}
|
||||
|
||||
@ -52,38 +60,77 @@ func (Pipe) Run(ctx *context.Context) (err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if tag == "" && !ctx.Snapshot {
|
||||
return ErrNoTag
|
||||
}
|
||||
ctx.Git = context.GitInfo{
|
||||
CurrentTag: tag,
|
||||
Commit: commit,
|
||||
}
|
||||
if ctx.ReleaseNotes == "" {
|
||||
log, err := getChangelog(tag)
|
||||
var log string
|
||||
if tag != "" {
|
||||
log, err = getChangelog(tag)
|
||||
} else {
|
||||
log, err = getChangelog(commit)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.ReleaseNotes = fmt.Sprintf("## Changelog\n\n%v", log)
|
||||
}
|
||||
// removes usual `v` prefix
|
||||
ctx.Version = strings.TrimPrefix(tag, "v")
|
||||
if tag == "" || ctx.Snapshot {
|
||||
snapshotName, err := getSnapshotName(ctx, tag, commit)
|
||||
if err != nil {
|
||||
log.Printf("Failed to generate snapshot name: %s", err.Error())
|
||||
return err
|
||||
}
|
||||
ctx.Version = snapshotName
|
||||
} else {
|
||||
// removes usual `v` prefix
|
||||
ctx.Version = strings.TrimPrefix(tag, "v")
|
||||
}
|
||||
if !ctx.Validate {
|
||||
log.Println("Skipped validations because --skip-validate is set")
|
||||
return nil
|
||||
}
|
||||
return validate(commit, tag, ctx.Version)
|
||||
return validate(commit, tag, ctx.Version, ctx.Snapshot)
|
||||
}
|
||||
|
||||
func validate(commit, tag, version string) error {
|
||||
matches, err := regexp.MatchString("^[0-9.]+", version)
|
||||
if err != nil || !matches {
|
||||
return ErrInvalidVersionFormat{version}
|
||||
type snapshotNameData struct {
|
||||
Commit string
|
||||
Tag string
|
||||
Timestamp int64
|
||||
}
|
||||
|
||||
func getSnapshotName(ctx *context.Context, tag, commit string) (string, error) {
|
||||
tmpl, err := template.New("snapshot").Parse(ctx.Config.Snapshot.NameTemplate)
|
||||
var out bytes.Buffer
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := tmpl.Execute(&out, snapshotNameData{Commit: commit, Tag: tag, Timestamp: time.Now().Unix()}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out.String(), nil
|
||||
}
|
||||
|
||||
func validate(commit, tag, version string, isSnapshot bool) error {
|
||||
if !isSnapshot {
|
||||
matches, err := regexp.MatchString("^[0-9.]+", version)
|
||||
if err != nil || !matches {
|
||||
return ErrInvalidVersionFormat{version}
|
||||
}
|
||||
}
|
||||
out, err := git("status", "-s")
|
||||
if strings.TrimSpace(out) != "" || err != nil {
|
||||
return ErrDirty{out}
|
||||
}
|
||||
_, err = cleanGit("describe", "--exact-match", "--tags", "--match", tag)
|
||||
if err != nil {
|
||||
return ErrWrongRef{commit, tag}
|
||||
if !isSnapshot {
|
||||
_, err = cleanGit("describe", "--exact-match", "--tags", "--match", tag)
|
||||
if err != nil {
|
||||
return ErrWrongRef{commit, tag}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -108,7 +155,7 @@ func gitLog(refs ...string) (string, error) {
|
||||
func getInfo() (tag, commit string, err error) {
|
||||
tag, err = cleanGit("describe", "--tags", "--abbrev=0")
|
||||
if err != nil {
|
||||
return
|
||||
log.Printf("Failed to retrieve current tag: %s", err.Error())
|
||||
}
|
||||
commit, err = cleanGit("show", "--format='%H'", "HEAD")
|
||||
return
|
||||
|
@ -50,6 +50,58 @@ func TestNewRepository(t *testing.T) {
|
||||
assert.Error(Pipe{}.Run(ctx))
|
||||
}
|
||||
|
||||
func TestNoTagsSnapshot(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
_, back := createAndChdir(t)
|
||||
defer back()
|
||||
gitInit(t)
|
||||
gitCommit(t, "first")
|
||||
var ctx = &context.Context{
|
||||
Config: config.Project{
|
||||
Snapshot: config.Snapshot{NameTemplate: "SNAPSHOT-{{.Commit}}"},
|
||||
},
|
||||
Snapshot: true,
|
||||
Publish: false,
|
||||
}
|
||||
assert.NoError(Pipe{}.Run(ctx))
|
||||
assert.Contains(ctx.Version, "SNAPSHOT-")
|
||||
}
|
||||
|
||||
func TestNoTagsSnapshotInvalidTemplate(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
_, back := createAndChdir(t)
|
||||
defer back()
|
||||
gitInit(t)
|
||||
gitCommit(t, "first")
|
||||
var ctx = &context.Context{
|
||||
Config: config.Project{
|
||||
Snapshot: config.Snapshot{NameTemplate: "{{"},
|
||||
},
|
||||
Snapshot: true,
|
||||
Publish: false,
|
||||
}
|
||||
assert.Error(Pipe{}.Run(ctx))
|
||||
}
|
||||
|
||||
// TestNoTagsNoSnapshot covers the situation where a repository
|
||||
// only contains simple commits and no tags. In this case you have
|
||||
// to set the --snapshot flag otherwise an error is returned.
|
||||
func TestNoTagsNoSnapshot(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
_, back := createAndChdir(t)
|
||||
defer back()
|
||||
gitInit(t)
|
||||
gitCommit(t, "first")
|
||||
var ctx = &context.Context{
|
||||
Config: config.Project{
|
||||
Snapshot: config.Snapshot{NameTemplate: "SNAPSHOT-{{.Commit}}"},
|
||||
},
|
||||
Snapshot: false,
|
||||
Publish: false,
|
||||
}
|
||||
assert.Error(Pipe{}.Run(ctx))
|
||||
}
|
||||
|
||||
func TestInvalidTagFormat(t *testing.T) {
|
||||
var assert = assert.New(t)
|
||||
_, back := createAndChdir(t)
|
||||
|
Loading…
x
Reference in New Issue
Block a user