1
0
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:
Carlos Alexandro Becker 2017-05-01 09:49:34 -03:00 committed by GitHub
commit c0db0455f7
10 changed files with 183 additions and 18 deletions

View File

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

View File

@ -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:"-"`

View File

@ -33,6 +33,7 @@ type Context struct {
Version string
Validate bool
Publish bool
Snapshot bool
}
var lock sync.Mutex

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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