mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-03-17 20:47:50 +02:00
Merge branch 'master' into make-fmt
This commit is contained in:
commit
ca3d3fda55
@ -4,7 +4,9 @@ install: make setup
|
||||
script:
|
||||
- make ci
|
||||
after_success:
|
||||
- git status
|
||||
- test -n "$TRAVIS_TAG" && gem install fpm
|
||||
- git status -sb
|
||||
- test -n "$TRAVIS_TAG" && go run main.go
|
||||
- git status -sb
|
||||
notifications:
|
||||
email: false
|
||||
|
29
README.md
29
README.md
@ -110,9 +110,14 @@ GoReleaser uses the latest [Git tag](https://git-scm.com/book/en/v2/Git-Basics-T
|
||||
Create a tag:
|
||||
|
||||
```console
|
||||
$ git tag -a v0.1 -m "First release"
|
||||
$ git tag -a v0.1.0 -m "First release"
|
||||
```
|
||||
|
||||
**Note**: we recommend the use of [semantic versioning](http://semver.org/). We
|
||||
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.
|
||||
|
||||
Now you can run GoReleaser at the root of your repository:
|
||||
|
||||
```console
|
||||
@ -285,6 +290,28 @@ class Program < Formula
|
||||
end
|
||||
```
|
||||
|
||||
### FPM build customization
|
||||
|
||||
GoReleaser can be wired to [fpm]() to generate `.deb`, `.rpm` and other archives. Check it's
|
||||
[wiki](https://github.com/jordansissel/fpm/wiki) for more info.
|
||||
|
||||
[fpm]: https://github.com/jordansissel/fpm
|
||||
|
||||
```yml
|
||||
# goreleaser.yml
|
||||
fpm:
|
||||
# Formats to generate as output
|
||||
formats:
|
||||
- deb
|
||||
- rpm
|
||||
|
||||
# Dependencies of your package
|
||||
dependencies:
|
||||
- git
|
||||
```
|
||||
|
||||
Note that GoReleaser will not install `fpm` nor any of it's dependencies for you.
|
||||
|
||||
## Integration with CI
|
||||
|
||||
You may want to wire this to auto-deploy your new tags on [Travis](https://travis-ci.org), for example:
|
||||
|
@ -43,12 +43,19 @@ type Release struct {
|
||||
Repo string
|
||||
}
|
||||
|
||||
// FPM config
|
||||
type FPM struct {
|
||||
Formats []string
|
||||
Dependencies []string
|
||||
}
|
||||
|
||||
// Project includes all project configuration
|
||||
type Project struct {
|
||||
Release Release
|
||||
Brew Homebrew
|
||||
Build Build
|
||||
Archive Archive
|
||||
FPM FPM `yaml:"fpm"`
|
||||
}
|
||||
|
||||
// Load config file
|
||||
|
@ -22,6 +22,7 @@ type Context struct {
|
||||
ReleaseRepo Repo
|
||||
BrewRepo Repo
|
||||
Archives map[string]string
|
||||
Version string
|
||||
}
|
||||
|
||||
// New context
|
||||
|
@ -3,3 +3,8 @@ brew:
|
||||
folder: Formula
|
||||
dependencies:
|
||||
- git
|
||||
fpm:
|
||||
formats:
|
||||
- deb
|
||||
dependencies:
|
||||
- git
|
||||
|
2
main.go
2
main.go
@ -12,6 +12,7 @@ import (
|
||||
"github.com/goreleaser/goreleaser/pipeline/build"
|
||||
"github.com/goreleaser/goreleaser/pipeline/defaults"
|
||||
"github.com/goreleaser/goreleaser/pipeline/env"
|
||||
"github.com/goreleaser/goreleaser/pipeline/fpm"
|
||||
"github.com/goreleaser/goreleaser/pipeline/git"
|
||||
"github.com/goreleaser/goreleaser/pipeline/release"
|
||||
"github.com/goreleaser/goreleaser/pipeline/repos"
|
||||
@ -33,6 +34,7 @@ var pipes = []pipeline.Pipe{
|
||||
// real work
|
||||
build.Pipe{},
|
||||
archive.Pipe{},
|
||||
fpm.Pipe{},
|
||||
release.Pipe{},
|
||||
brew.Pipe{},
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ const formula = `class {{ .Name }} < Formula
|
||||
desc "{{ .Desc }}"
|
||||
homepage "{{ .Homepage }}"
|
||||
url "https://github.com/{{ .Repo }}/releases/download/{{ .Tag }}/{{ .File }}.{{ .Format }}"
|
||||
version "{{ .Tag }}"
|
||||
version "{{ .Version }}"
|
||||
sha256 "{{ .SHA256 }}"
|
||||
|
||||
{{- if .Dependencies }}
|
||||
@ -50,6 +50,7 @@ type templateData struct {
|
||||
Homepage string
|
||||
Repo string
|
||||
Tag string
|
||||
Version string
|
||||
BinaryName string
|
||||
Caveats string
|
||||
File string
|
||||
@ -156,6 +157,7 @@ func dataFor(ctx *context.Context, client *github.Client) (result templateData,
|
||||
Homepage: homepage,
|
||||
Repo: ctx.Config.Release.Repo,
|
||||
Tag: ctx.Git.CurrentTag,
|
||||
Version: ctx.Version,
|
||||
BinaryName: ctx.Config.Build.BinaryName,
|
||||
Caveats: ctx.Config.Brew.Caveats,
|
||||
File: file,
|
||||
|
@ -25,6 +25,7 @@ var defaultTemplateData = templateData{
|
||||
Name: "Test",
|
||||
Repo: "caarlos0/test",
|
||||
Tag: "v0.1.3",
|
||||
Version: "0.1.3",
|
||||
File: "test_Darwin_x86_64",
|
||||
SHA256: "1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c68",
|
||||
Format: "tar.gz",
|
||||
@ -36,7 +37,7 @@ func assertDefaultTemplateData(t *testing.T, formulae string) {
|
||||
assert.Contains(formulae, "homepage \"https://google.com\"")
|
||||
assert.Contains(formulae, "url \"https://github.com/caarlos0/test/releases/download/v0.1.3/test_Darwin_x86_64.tar.gz\"")
|
||||
assert.Contains(formulae, "sha256 \"1633f61598ab0791e213135923624eb342196b3494909c91899bcd0560f84c68\"")
|
||||
assert.Contains(formulae, "version \"v0.1.3\"")
|
||||
assert.Contains(formulae, "version \"0.1.3\"")
|
||||
assert.Contains(formulae, "bin.install \"test\"")
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
package build
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
@ -41,7 +40,7 @@ func (Pipe) Run(ctx *context.Context) error {
|
||||
}
|
||||
|
||||
func build(name, goos, goarch string, ctx *context.Context) error {
|
||||
ldflags := ctx.Config.Build.Ldflags + " -X main.version=" + ctx.Git.CurrentTag
|
||||
ldflags := ctx.Config.Build.Ldflags + " -X main.version=" + ctx.Version
|
||||
output := "dist/" + name + "/" + ctx.Config.Build.BinaryName + extFor(goos)
|
||||
log.Println("Building", output)
|
||||
if ctx.Config.Build.Hooks.Pre != "" {
|
||||
@ -67,11 +66,8 @@ func run(goos, goarch string, command []string) error {
|
||||
cmd := exec.Command(command[0], command[1:]...)
|
||||
cmd.Env = append(cmd.Env, os.Environ()...)
|
||||
cmd.Env = append(cmd.Env, "GOOS="+goos, "GOARCH="+goarch)
|
||||
var stdout bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stdout
|
||||
if err := cmd.Run(); err != nil {
|
||||
return errors.New(stdout.String())
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
return errors.New(string(out))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
84
pipeline/fpm/fpm.go
Normal file
84
pipeline/fpm/fpm.go
Normal file
@ -0,0 +1,84 @@
|
||||
package fpm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/goreleaser/goreleaser/context"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
var goarchToUnix = map[string]string{
|
||||
"386": "i386",
|
||||
"amd64": "x86_64",
|
||||
}
|
||||
|
||||
// ErrNoFPM is shown when fpm cannot be found in $PATH
|
||||
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"
|
||||
}
|
||||
|
||||
// Run the pipe
|
||||
func (Pipe) Run(ctx *context.Context) error {
|
||||
if len(ctx.Config.FPM.Formats) == 0 {
|
||||
log.Println("No output formats configured, skipping")
|
||||
return nil
|
||||
}
|
||||
_, err := exec.LookPath("fpm")
|
||||
if err != nil {
|
||||
return ErrNoFPM
|
||||
}
|
||||
|
||||
var g errgroup.Group
|
||||
for _, format := range ctx.Config.FPM.Formats {
|
||||
for _, goarch := range ctx.Config.Build.Goarch {
|
||||
if ctx.Archives["linux"+goarch] == "" {
|
||||
continue
|
||||
}
|
||||
archive := ctx.Archives["linux"+goarch]
|
||||
arch := goarchToUnix[goarch]
|
||||
g.Go(func() error {
|
||||
return create(ctx, format, archive, arch)
|
||||
})
|
||||
}
|
||||
}
|
||||
return g.Wait()
|
||||
}
|
||||
|
||||
func create(ctx *context.Context, format, archive, arch string) error {
|
||||
var path = filepath.Join("dist", archive)
|
||||
var file = path + ".deb"
|
||||
var name = ctx.Config.Build.BinaryName
|
||||
log.Println("Creating", file)
|
||||
|
||||
var options = []string{
|
||||
"-s", "dir",
|
||||
"-t", format,
|
||||
"-n", name,
|
||||
"-v", ctx.Version,
|
||||
"-a", arch,
|
||||
"-C", path,
|
||||
"-p", file,
|
||||
"--force",
|
||||
}
|
||||
for _, dep := range ctx.Config.FPM.Dependencies {
|
||||
options = append(options, "-d", dep)
|
||||
}
|
||||
// This basically tells fpm to put the binary in the /usr/local/bin
|
||||
// binary=/usr/local/bin/binary
|
||||
options = append(options, name+"="+filepath.Join("/usr/local/bin", name))
|
||||
cmd := exec.Command("fpm", options...)
|
||||
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
return errors.New(string(out))
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,6 +1,20 @@
|
||||
package git
|
||||
|
||||
import "github.com/goreleaser/goreleaser/context"
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/goreleaser/goreleaser/context"
|
||||
)
|
||||
|
||||
// ErrInvalidVersionFormat is return when the version isnt in a valid format
|
||||
type ErrInvalidVersionFormat struct {
|
||||
version string
|
||||
}
|
||||
|
||||
func (e ErrInvalidVersionFormat) Error() string {
|
||||
return e.version + " is not in a valid version format"
|
||||
}
|
||||
|
||||
// Pipe for brew deployment
|
||||
type Pipe struct{}
|
||||
@ -30,5 +44,10 @@ func (Pipe) Run(ctx *context.Context) (err error) {
|
||||
PreviousTag: previous,
|
||||
Diff: log,
|
||||
}
|
||||
// removes usual `v` prefix
|
||||
ctx.Version = strings.TrimPrefix(tag, "v")
|
||||
if matches, err := regexp.MatchString("^[0-9.]+", ctx.Version); !matches || err != nil {
|
||||
return ErrInvalidVersionFormat{ctx.Version}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/google/go-github/github"
|
||||
"github.com/goreleaser/goreleaser/clients"
|
||||
@ -31,9 +32,14 @@ func (Pipe) Run(ctx *context.Context) error {
|
||||
for _, archive := range ctx.Archives {
|
||||
archive := archive
|
||||
g.Go(func() error {
|
||||
return upload(client, *r.ID, archive, ctx)
|
||||
return upload(ctx, client, *r.ID, archive, ctx.Config.Archive.Format)
|
||||
})
|
||||
|
||||
for _, format := range ctx.Config.FPM.Formats {
|
||||
format := format
|
||||
g.Go(func() error {
|
||||
return upload(ctx, client, *r.ID, archive, format)
|
||||
})
|
||||
}
|
||||
}
|
||||
return g.Wait()
|
||||
}
|
||||
@ -67,9 +73,20 @@ func description(diff string) string {
|
||||
return result + "\nBuilt with " + string(bts)
|
||||
}
|
||||
|
||||
func upload(client *github.Client, releaseID int, archive string, ctx *context.Context) error {
|
||||
archive = archive + "." + ctx.Config.Archive.Format
|
||||
file, err := os.Open("dist/" + archive)
|
||||
func upload(ctx *context.Context, client *github.Client, releaseID int, archive, format string) error {
|
||||
archive = archive + "." + format
|
||||
var path = filepath.Join("dist", archive)
|
||||
// In case the file doesn't exist, we just ignore it.
|
||||
// We do this because we can get invalid combinations of archive+format here,
|
||||
// like darwinamd64 + deb or something like that.
|
||||
// It's assumed that the archive pipe would fail the entire thing in case it fails to
|
||||
// generate some archive, as well fpm pipe is expected to fail if something wrong happens.
|
||||
// So, here, we just assume IsNotExist as an expected error.
|
||||
// TODO: maybe add a list of files to upload in the context so we don't have to do this.
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user