mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-03-17 20:47:50 +02:00
commit
20800a3019
71
README.md
71
README.md
@ -2,8 +2,8 @@
|
||||
|
||||
<img src="https://avatars2.githubusercontent.com/u/24697112?v=3&s=200" alt="goreleaser" align="right" />
|
||||
|
||||
GoReleaser builds Go binaries for several platforms, creates a github release and then
|
||||
push a homebrew formulae to a repository. All that wrapped in your favorite CI.
|
||||
GoReleaser builds Go binaries for several platforms, creates a Github release and then
|
||||
pushes a Homebrew formulae to a repository. All that wrapped in your favorite CI.
|
||||
|
||||
This project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.md).
|
||||
By participating, you are expected to uphold this code. Please report unacceptable behavior to root@carlosbecker.com.
|
||||
@ -14,45 +14,37 @@ By participating, you are expected to uphold this code. Please report unacceptab
|
||||
|
||||
The idea started with a [simple shell script](https://github.com/goreleaser/old-go-releaser),
|
||||
but it quickly became more complex and I also wanted to publish binaries via
|
||||
homebrew.
|
||||
Homebrew.
|
||||
|
||||
So, the all-new goreleaser was born.
|
||||
|
||||
## Usage
|
||||
|
||||
Create a `goreleaser.yml` file in the root of your repository. A minimal config
|
||||
would look like this:
|
||||
- You need to export a `GITHUB_TOKEN` environment variable with
|
||||
the `repo` scope selected. You can create one [here](https://github.com/settings/tokens/new).
|
||||
|
||||
```yaml
|
||||
repo: user/repo
|
||||
binary_name: my-binary
|
||||
```
|
||||
- GoReleaser uses the latest [Git tag](https://git-scm.com/book/en/v2/Git-Basics-Tagging) of your repository,
|
||||
so you need to [create a tag](https://git-scm.com/book/en/v2/Git-Basics-Tagging#Annotated-Tags) first.
|
||||
|
||||
You may then run releaser at the root of your repository:
|
||||
- Now you can run `releaser` at the root of your repository:
|
||||
|
||||
```sh
|
||||
curl -s https://raw.githubusercontent.com/goreleaser/get/master/latest | bash
|
||||
```
|
||||
|
||||
For that to work, you need to export a `GITHUB_TOKEN` environment variable with
|
||||
the `repo` scope selected. You can create one
|
||||
[here](https://github.com/settings/tokens/new).
|
||||
|
||||
GoReleaser uses the latest [Git tag](https://git-scm.com/book/en/v2/Git-Basics-Tagging) of your repository,
|
||||
so you need to [create a tag](https://git-scm.com/book/en/v2/Git-Basics-Tagging#Annotated-Tags) first.
|
||||
|
||||
This will build `main.go` as `my-binary`, for `Darwin` and `Linux`
|
||||
This will build `main.go` as binary, for `Darwin` and `Linux`
|
||||
(`amd64` and `i386`), archive the binary and common files as `.tar.gz`,
|
||||
and finally, publish a new github release in the `user/repo` repository with
|
||||
and finally, publish a new Github release in the repository with
|
||||
archives uploaded.
|
||||
|
||||
|
||||
For further customization create a `goreleaser.yml` file in the root of your repository.
|
||||
|
||||
### Homebrew
|
||||
|
||||
Add a `brew` section to push a formulae to a Homebrew tab repository:
|
||||
|
||||
```yaml
|
||||
repo: user/repo
|
||||
binary_name: my-binary
|
||||
brew:
|
||||
repo: user/homebrew-tap
|
||||
folder: optional/subfolder/inside/the/repo
|
||||
@ -66,8 +58,6 @@ See the [Homebrew docs](https://github.com/Homebrew/brew/blob/master/docs/How-to
|
||||
Just add a `build` section
|
||||
|
||||
```yaml
|
||||
repo: user/repo
|
||||
binary_name: my-binary
|
||||
build:
|
||||
main: ./cmd/main.go
|
||||
ldflags: -s -w
|
||||
@ -87,8 +77,6 @@ You can customize the name and format of the archive adding an `archive`
|
||||
section:
|
||||
|
||||
```yaml
|
||||
repo: user/repo
|
||||
binary_name: my-binary
|
||||
archive:
|
||||
name_template: "{{.BinaryName}}_{{.Version}}_{{.Os}}_{{.Arch}}"
|
||||
format: zip
|
||||
@ -111,8 +99,6 @@ You might also want to change the files that are packaged by adding a `files`
|
||||
section:
|
||||
|
||||
```yaml
|
||||
repo: user/repo
|
||||
binary_name: my-binary
|
||||
files:
|
||||
- LICENSE.txt
|
||||
- README.md
|
||||
@ -124,8 +110,7 @@ files:
|
||||
|
||||
### ldflags (main.version)
|
||||
|
||||
GoReleaser already sets a `main.version` ldflag, so, in you `main.go` program,
|
||||
you can:
|
||||
GoReleaser always sets a `main.version` ldflag. You can use it in your `main.go` file:
|
||||
|
||||
```go
|
||||
package main
|
||||
@ -137,11 +122,29 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
And this version will always be the tag name.
|
||||
And this version will always be the name of the current tag.
|
||||
|
||||
## Wire it with travis-ci
|
||||
|
||||
You may want to wire this to auto-deploy your new tags on travis, for example:
|
||||
### Other customizations
|
||||
|
||||
- By default it's assumed that the repository to release to is the same as the Git `remote origin`.
|
||||
If this is not the case for your project, you can specify a `repo`:
|
||||
|
||||
```yaml
|
||||
repo: owner/custom-repo
|
||||
```
|
||||
|
||||
- By default the binary name is the name of the project directory.
|
||||
You can specify a different `binary_name`:
|
||||
|
||||
```yaml
|
||||
binary_name: my-binary
|
||||
```
|
||||
|
||||
|
||||
## Wire it with Travis CI
|
||||
|
||||
You may want to wire this to auto-deploy your new tags on Travis, for example:
|
||||
|
||||
```yaml
|
||||
after_success:
|
||||
@ -150,12 +153,12 @@ after_success:
|
||||
|
||||
## What the end result looks like
|
||||
|
||||
The release on github looks pretty much like this:
|
||||
The release on Github looks pretty much like this:
|
||||
|
||||
[
|
||||
](https://github.com/goreleaser/releaser/releases)
|
||||
|
||||
And the [homebrew formulae](https://github.com/goreleaser/homebrew-tap/blob/master/release.rb) would look like:
|
||||
And the [Homebrew formulae](https://github.com/goreleaser/homebrew-tap/blob/master/release.rb) would look like:
|
||||
|
||||
```rb
|
||||
class Release < Formula
|
||||
|
11
main.go
11
main.go
@ -15,7 +15,6 @@ import (
|
||||
"github.com/goreleaser/releaser/pipeline/git"
|
||||
"github.com/goreleaser/releaser/pipeline/release"
|
||||
"github.com/goreleaser/releaser/pipeline/repos"
|
||||
"github.com/goreleaser/releaser/pipeline/valid"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
@ -28,9 +27,6 @@ var pipes = []pipeline.Pipe{
|
||||
git.Pipe{},
|
||||
repos.Pipe{},
|
||||
|
||||
// validate
|
||||
valid.Pipe{},
|
||||
|
||||
// real work
|
||||
build.Pipe{},
|
||||
compress.Pipe{},
|
||||
@ -52,11 +48,12 @@ func main() {
|
||||
}
|
||||
app.Action = func(c *cli.Context) (err error) {
|
||||
var file = c.String("config")
|
||||
config, err := config.Load(file)
|
||||
if err != nil {
|
||||
cfg, err := config.Load(file)
|
||||
// Allow failing to load the config file if file is not explicitly specified
|
||||
if err != nil && c.IsSet("config") {
|
||||
return cli.NewExitError(err.Error(), 1)
|
||||
}
|
||||
context := context.New(config)
|
||||
context := context.New(cfg)
|
||||
log.SetFlags(0)
|
||||
for _, pipe := range pipes {
|
||||
log.Println(pipe.Description())
|
||||
|
@ -40,7 +40,7 @@ type templateData struct {
|
||||
// Pipe for brew deployment
|
||||
type Pipe struct{}
|
||||
|
||||
// Name of the pipe
|
||||
// Description of the pipe
|
||||
func (Pipe) Description() string {
|
||||
return "Creating homebrew formulae..."
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
// Pipe for build
|
||||
type Pipe struct{}
|
||||
|
||||
// Name of the pipe
|
||||
// Description of the pipe
|
||||
func (Pipe) Description() string {
|
||||
return "Building..."
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
// Pipe for compress
|
||||
type Pipe struct{}
|
||||
|
||||
// Name of the pipe
|
||||
// Description of the pipe
|
||||
func (Pipe) Description() string {
|
||||
return "Creating archives..."
|
||||
}
|
||||
@ -32,6 +32,7 @@ func (Pipe) Run(ctx *context.Context) error {
|
||||
return g.Wait()
|
||||
}
|
||||
|
||||
// Archive represents a compression archive files from disk can be written to.
|
||||
type Archive interface {
|
||||
Close() error
|
||||
Add(name, path string) error
|
||||
|
@ -1,6 +1,7 @@
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
@ -12,13 +13,23 @@ var defaultFiles = []string{"licence", "license", "readme", "changelog"}
|
||||
// Pipe for brew deployment
|
||||
type Pipe struct{}
|
||||
|
||||
// Name of the pipe
|
||||
// Description of the pipe
|
||||
func (Pipe) Description() string {
|
||||
return "Setting defaults..."
|
||||
}
|
||||
|
||||
// Run the pipe
|
||||
func (Pipe) Run(ctx *context.Context) (err error) {
|
||||
func (Pipe) Run(ctx *context.Context) error {
|
||||
if ctx.Config.Repo == "" {
|
||||
repo, err := remoteRepo()
|
||||
ctx.Config.Repo = repo
|
||||
if err != nil {
|
||||
return errors.New("failed reading repo from Git: " + err.Error())
|
||||
}
|
||||
}
|
||||
if ctx.Config.BinaryName == "" {
|
||||
ctx.Config.BinaryName = strings.Split(ctx.Config.Repo, "/")[1]
|
||||
}
|
||||
if ctx.Config.Build.Main == "" {
|
||||
ctx.Config.Build.Main = "main.go"
|
||||
}
|
||||
@ -50,14 +61,14 @@ func (Pipe) Run(ctx *context.Context) (err error) {
|
||||
}
|
||||
}
|
||||
if len(ctx.Config.Files) != 0 {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
files, err := findFiles()
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
ctx.Config.Files = files
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
func findFiles() (files []string, err error) {
|
||||
|
@ -18,6 +18,8 @@ func TestFillBasicData(t *testing.T) {
|
||||
|
||||
assert.NoError(Pipe{}.Run(ctx))
|
||||
|
||||
assert.Equal("goreleaser/releaser", config.Repo)
|
||||
assert.Equal("releaser", config.BinaryName)
|
||||
assert.Equal("main.go", config.Build.Main)
|
||||
assert.Equal("tar.gz", config.Archive.Format)
|
||||
assert.Contains(config.Build.Oses, "darwin")
|
||||
|
29
pipeline/defaults/remote.go
Normal file
29
pipeline/defaults/remote.go
Normal file
@ -0,0 +1,29 @@
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// remoteRepo gets the repo name from the Git config.
|
||||
func remoteRepo() (result string, err error) {
|
||||
cmd := exec.Command("git", "config", "--get", "remote.origin.url")
|
||||
bts, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", errors.New(err.Error() + ": " + string(bts))
|
||||
}
|
||||
return extractRepoFromURL(string(bts)), nil
|
||||
}
|
||||
|
||||
func extractRepoFromURL(s string) string {
|
||||
for _, r := range []string{
|
||||
"git@github.com:",
|
||||
".git",
|
||||
"https://github.com/",
|
||||
"\n",
|
||||
} {
|
||||
s = strings.Replace(s, r, "", -1)
|
||||
}
|
||||
return s
|
||||
}
|
26
pipeline/defaults/remote_test.go
Normal file
26
pipeline/defaults/remote_test.go
Normal file
@ -0,0 +1,26 @@
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRepoName(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
name, err := remoteRepo()
|
||||
assert.NoError(err)
|
||||
assert.Equal("goreleaser/releaser", name)
|
||||
}
|
||||
|
||||
func TestExtractReporFromGitURL(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
url := extractRepoFromURL("git@github.com:goreleaser/releaser.git")
|
||||
assert.Equal("goreleaser/releaser", url)
|
||||
}
|
||||
|
||||
func TestExtractReporFromHttpsURL(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
url := extractRepoFromURL("https://github.com/goreleaser/releaser.git")
|
||||
assert.Equal("goreleaser/releaser", url)
|
||||
}
|
3
pipeline/env/env.go
vendored
3
pipeline/env/env.go
vendored
@ -7,12 +7,13 @@ import (
|
||||
"github.com/goreleaser/releaser/context"
|
||||
)
|
||||
|
||||
// ErrMissingToken indicates an error when GITHUB_TOKEN is missing in the environment
|
||||
var ErrMissingToken = errors.New("Missing GITHUB_TOKEN")
|
||||
|
||||
// Pipe for env
|
||||
type Pipe struct{}
|
||||
|
||||
// Name of the pipe
|
||||
// Description of the pipe
|
||||
func (Pipe) Description() string {
|
||||
return "Loading data from environment variables..."
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import "github.com/goreleaser/releaser/context"
|
||||
// Pipe for brew deployment
|
||||
type Pipe struct{}
|
||||
|
||||
// Name of the pipe
|
||||
// Description of the pipe
|
||||
func (Pipe) Description() string {
|
||||
return "Gathering Git data..."
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
// Pipe for github release
|
||||
type Pipe struct{}
|
||||
|
||||
// Name of the pipe
|
||||
// Description of the pipe
|
||||
func (Pipe) Description() string {
|
||||
return "Releasing to GitHub..."
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
// Pipe for brew deployment
|
||||
type Pipe struct{}
|
||||
|
||||
// Name of the pipe
|
||||
// Description of the pipe
|
||||
func (Pipe) Description() string {
|
||||
return "Filling repositories data..."
|
||||
}
|
||||
|
@ -1,26 +0,0 @@
|
||||
package valid
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/goreleaser/releaser/context"
|
||||
)
|
||||
|
||||
// Pipe for brew deployment
|
||||
type Pipe struct{}
|
||||
|
||||
// Name of the pipe
|
||||
func (Pipe) Description() string {
|
||||
return "Validating configuration..."
|
||||
}
|
||||
|
||||
// Run the pipe
|
||||
func (Pipe) Run(ctx *context.Context) (err error) {
|
||||
if ctx.Config.BinaryName == "" {
|
||||
return errors.New("missing binary_name")
|
||||
}
|
||||
if ctx.Config.Repo == "" {
|
||||
return errors.New("missing repo")
|
||||
}
|
||||
return
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package valid
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/goreleaser/releaser/config"
|
||||
"github.com/goreleaser/releaser/context"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func runPipe(repo, bin string) error {
|
||||
var config = &config.ProjectConfig{
|
||||
Repo: repo,
|
||||
BinaryName: bin,
|
||||
}
|
||||
var ctx = &context.Context{
|
||||
Config: config,
|
||||
}
|
||||
return Pipe{}.Run(ctx)
|
||||
}
|
||||
|
||||
func TestValidadeMissingBinaryName(t *testing.T) {
|
||||
assert.Error(t, runPipe("a/b", ""))
|
||||
}
|
||||
|
||||
func TestValidadeMissingRepo(t *testing.T) {
|
||||
assert.Error(t, runPipe("", "a"))
|
||||
}
|
||||
|
||||
func TestValidadeMinimalConfig(t *testing.T) {
|
||||
assert.NoError(t, runPipe("a/b", "a"))
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user