From 5bc64413de7bfa0087abf18150c35cb3cd1eee5a Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Sat, 3 Feb 2018 00:06:48 -0200 Subject: [PATCH] feat: load github token from file --- Gopkg.lock | 20 ++++++--- Gopkg.toml | 4 ++ config/config.go | 5 +++ docs/020-environment.md | 4 ++ pipeline/defaults/defaults.go | 2 + pipeline/env/env.go | 44 ++++++++++++++++--- pipeline/env/env_test.go | 81 +++++++++++++++++++++++++++++++++-- 7 files changed, 145 insertions(+), 15 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index b34fc02c0..22de2ab31 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -35,16 +35,16 @@ version = "v1.5.0" [[projects]] - branch = "master" name = "github.com/golang/protobuf" packages = ["proto"] revision = "925541529c1fa6821df4e44ce2723319eb2be768" + version = "v1.0.0" [[projects]] branch = "master" name = "github.com/google/go-github" packages = ["github"] - revision = "e48060a28fac52d0f1cb758bc8b87c07bac4a87d" + revision = "897969cdc831052c4694e205e24a033fe7248c2f" [[projects]] branch = "master" @@ -89,6 +89,12 @@ ] revision = "4959821b481786922ac53e7ef25c61ae19fb7c36" +[[projects]] + branch = "master" + name = "github.com/mitchellh/go-homedir" + packages = ["."] + revision = "b8bc1bf767474819792c23f32d8286a45736f1c6" + [[projects]] name = "github.com/pkg/errors" packages = ["."] @@ -104,8 +110,8 @@ [[projects]] name = "github.com/stretchr/testify" packages = ["assert"] - revision = "b91bfb9ebec76498946beb6af7c0230c7cc7ba6c" - version = "v1.2.0" + revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" + version = "v1.2.1" [[projects]] name = "github.com/urfave/cli" @@ -120,7 +126,7 @@ "context", "context/ctxhttp" ] - revision = "0ed95abb35c445290478a5348a7b38bb154135fd" + revision = "2fb46b16b8dda405028c50f7c7f0f9dd1fa6bfb1" [[projects]] branch = "master" @@ -141,7 +147,7 @@ branch = "master" name = "golang.org/x/sys" packages = ["unix"] - revision = "ff2a66f350cefa5c93a634eadb5d25bb60c85a9c" + revision = "37707fdb30a5b38865cfb95e5aab41707daec7fd" [[projects]] name = "google.golang.org/appengine" @@ -166,6 +172,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "dd48a71cfd5292f26507d405996eb4ee06aea79c2308e6ee16e54fa6cd493832" + inputs-digest = "503ca2b65156ad338879d612768c325176abafb95f1d425bb9f6e01f5dcb1327" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index d699ce8ab..5c918e2d6 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -47,3 +47,7 @@ [[constraint]] branch = "master" name = "github.com/campoy/unique" + +[[constraint]] + branch = "master" + name = "github.com/mitchellh/go-homedir" diff --git a/config/config.go b/config/config.go index 697d96a27..e6382e6b6 100644 --- a/config/config.go +++ b/config/config.go @@ -194,6 +194,10 @@ type Changelog struct { Sort string `yaml:",omitempty"` } +type EnvFiles struct { + GitHubToken string `yaml:"github_token,omitempty"` +} + // Project includes all project configuration type Project struct { ProjectName string `yaml:"project_name,omitempty"` @@ -210,6 +214,7 @@ type Project struct { Changelog Changelog `yaml:",omitempty"` Dist string `yaml:",omitempty"` Sign Sign `yaml:",omitempty"` + EnvFiles EnvFiles `yaml:"env_files,omitempty"` // this is a hack ¯\_(ツ)_/¯ SingleBuild Build `yaml:"build,omitempty"` diff --git a/docs/020-environment.md b/docs/020-environment.md index 33f545710..fe5bea0f1 100644 --- a/docs/020-environment.md +++ b/docs/020-environment.md @@ -12,6 +12,10 @@ 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). +If you don't want to have a globaly available github token on your env, you +might as well consider having it in the `~/.config/goreleaser/github_token` +file. + ## The dist folder By default, GoReleaser will create its artifacts in the `./dist` folder. diff --git a/pipeline/defaults/defaults.go b/pipeline/defaults/defaults.go index 6e50d4ac9..7b3e1e7d2 100644 --- a/pipeline/defaults/defaults.go +++ b/pipeline/defaults/defaults.go @@ -12,6 +12,7 @@ import ( "github.com/goreleaser/goreleaser/pipeline/build" "github.com/goreleaser/goreleaser/pipeline/checksums" "github.com/goreleaser/goreleaser/pipeline/docker" + "github.com/goreleaser/goreleaser/pipeline/env" "github.com/goreleaser/goreleaser/pipeline/fpm" "github.com/goreleaser/goreleaser/pipeline/release" "github.com/goreleaser/goreleaser/pipeline/sign" @@ -27,6 +28,7 @@ func (Pipe) String() string { } var defaulters = []pipeline.Defaulter{ + env.Pipe{}, snapshot.Pipe{}, release.Pipe{}, archive.Pipe{}, diff --git a/pipeline/env/env.go b/pipeline/env/env.go index 1e199eb37..c85b8dc92 100644 --- a/pipeline/env/env.go +++ b/pipeline/env/env.go @@ -3,11 +3,13 @@ package env import ( - "errors" + "io/ioutil" "os" "github.com/goreleaser/goreleaser/context" "github.com/goreleaser/goreleaser/pipeline" + homedir "github.com/mitchellh/go-homedir" + "github.com/pkg/errors" ) // ErrMissingToken indicates an error when GITHUB_TOKEN is missing in the environment @@ -20,17 +22,49 @@ func (Pipe) String() string { return "loading environment variables" } +// Default sets the pipe defaults +func (Pipe) Default(ctx *context.Context) error { + var env = &ctx.Config.EnvFiles + if env.GitHubToken == "" { + env.GitHubToken = "~/.config/goreleaser/github_token" + } + return nil +} + // Run the pipe -func (Pipe) Run(ctx *context.Context) (err error) { - ctx.Token = os.Getenv("GITHUB_TOKEN") +func (Pipe) Run(ctx *context.Context) error { + token, err := loadEnv("GITHUB_TOKEN", ctx.Config.EnvFiles.GitHubToken) + ctx.Token = token if !ctx.Publish { return pipeline.Skip("publishing is disabled") } if !ctx.Validate { return pipeline.Skip("--skip-validate is set") } - if ctx.Token == "" { + if ctx.Token == "" && err == nil { return ErrMissingToken } - return + if err != nil { + return errors.Wrap(err, "failed to load github token") + } + return nil +} + +func loadEnv(env, path string) (string, error) { + val := os.Getenv(env) + if val == "" { + path, err := homedir.Expand(path) + if err != nil { + return "", err + } + if _, err := os.Stat(path); os.IsNotExist(err) { + return "", nil + } + bts, err := ioutil.ReadFile(path) + if err != nil { + return "", err + } + val = string(bts) + } + return val, nil } diff --git a/pipeline/env/env_test.go b/pipeline/env/env_test.go index d1981b24d..15e2dfc82 100644 --- a/pipeline/env/env_test.go +++ b/pipeline/env/env_test.go @@ -2,6 +2,7 @@ package env import ( "fmt" + "io/ioutil" "os" "testing" @@ -15,6 +16,24 @@ func TestDescription(t *testing.T) { assert.NotEmpty(t, Pipe{}.String()) } +func TestDefault(t *testing.T) { + t.Run("empty config", func(tt *testing.T) { + ctx := context.New(config.Project{}) + assert.NoError(t, Pipe{}.Default(ctx)) + assert.Equal(t, "~/.config/goreleaser/github_token", ctx.Config.EnvFiles.GitHubToken) + }) + t.Run("custom config config", func(tt *testing.T) { + cfg := "what" + ctx := context.New(config.Project{ + EnvFiles: config.EnvFiles{ + GitHubToken: cfg, + }, + }) + assert.NoError(t, Pipe{}.Default(ctx)) + assert.Equal(t, cfg, ctx.Config.EnvFiles.GitHubToken) + }) +} + func TestValidEnv(t *testing.T) { assert.NoError(t, os.Setenv("GITHUB_TOKEN", "asdf")) var ctx = &context.Context{ @@ -35,12 +54,37 @@ func TestInvalidEnv(t *testing.T) { assert.Error(t, Pipe{}.Run(ctx)) } -type flags struct { - Validate, Publish, Snapshot bool +func TestEmptyFileEnv(t *testing.T) { + assert.NoError(t, os.Unsetenv("GITHUB_TOKEN")) + var ctx = &context.Context{ + Config: config.Project{}, + Validate: true, + Publish: true, + } + assert.Error(t, Pipe{}.Run(ctx)) +} + +func TestEmptyEnvFile(t *testing.T) { + assert.NoError(t, os.Unsetenv("GITHUB_TOKEN")) + f, err := ioutil.TempFile("", "token") + assert.NoError(t, err) + assert.NoError(t, os.Chmod(f.Name(), 0377)) + var ctx = &context.Context{ + Config: config.Project{ + EnvFiles: config.EnvFiles{ + GitHubToken: f.Name(), + }, + }, + Validate: true, + Publish: true, + } + assert.EqualError(t, Pipe{}.Run(ctx), fmt.Sprintf("failed to load github token: open %s: permission denied", f.Name())) } func TestInvalidEnvChecksSkipped(t *testing.T) { - for _, flag := range []flags{ + for _, flag := range []struct { + Validate, Publish, Snapshot bool + }{ { Validate: false, Publish: true, @@ -63,3 +107,34 @@ func TestInvalidEnvChecksSkipped(t *testing.T) { }) } } + +func TestLoadEnv(t *testing.T) { + t.Run("env exists", func(tt *testing.T) { + var env = "SUPER_SECRET_ENV" + assert.NoError(tt, os.Setenv(env, "1")) + v, err := loadEnv(env, "nope") + assert.NoError(tt, err) + assert.Equal(tt, "1", v) + }) + t.Run("env file exists", func(tt *testing.T) { + var env = "SUPER_SECRET_ENV_NOPE" + assert.NoError(tt, os.Unsetenv(env)) + f, err := ioutil.TempFile("", "token") + assert.NoError(t, err) + fmt.Fprintf(f, "123") + v, err := loadEnv(env, f.Name()) + assert.NoError(tt, err) + assert.Equal(tt, "123", v) + }) + t.Run("env file is not readable", func(tt *testing.T) { + var env = "SUPER_SECRET_ENV_NOPE" + assert.NoError(tt, os.Unsetenv(env)) + f, err := ioutil.TempFile("", "token") + assert.NoError(t, err) + fmt.Fprintf(f, "123") + os.Chmod(f.Name(), 0377) + v, err := loadEnv(env, f.Name()) + assert.EqualError(tt, err, fmt.Sprintf("open %s: permission denied", f.Name())) + assert.Equal(tt, "", v) + }) +}