feat: load every environment from --env-file to workflow (#184)

* feat: load every environment from --env-file to workflow

* fix: pass dotenv's environments through by context

* updates to support --secret-file

Co-authored-by: Casey Lee <cplee@nektos.com>
This commit is contained in:
jony montana 2020-04-18 01:04:40 +08:00 committed by GitHub
parent f6e37a8d67
commit 2f395475b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 47 additions and 30 deletions

1
.gitignore vendored
View File

@ -15,3 +15,4 @@ dist/
.todo .todo
*.nupkg *.nupkg
.vscode/

View File

@ -18,6 +18,7 @@ type Input struct {
forcePull bool forcePull bool
noOutput bool noOutput bool
envfile string envfile string
secretfile string
} }
func (i *Input) resolve(path string) string { func (i *Input) resolve(path string) string {
@ -39,6 +40,11 @@ func (i *Input) Envfile() string {
return i.resolve(i.envfile) return i.resolve(i.envfile)
} }
// Secretfile returns path to secrets
func (i *Input) Secretfile() string {
return i.resolve(i.secretfile)
}
// Workdir returns path to workdir // Workdir returns path to workdir
func (i *Input) Workdir() string { func (i *Input) Workdir() string {
return i.resolve(".") return i.resolve(".")

View File

@ -46,7 +46,8 @@ func Execute(ctx context.Context, version string) {
rootCmd.PersistentFlags().BoolP("verbose", "v", false, "verbose output") rootCmd.PersistentFlags().BoolP("verbose", "v", false, "verbose output")
rootCmd.PersistentFlags().BoolVarP(&input.noOutput, "quiet", "q", false, "disable logging of output from steps") rootCmd.PersistentFlags().BoolVarP(&input.noOutput, "quiet", "q", false, "disable logging of output from steps")
rootCmd.PersistentFlags().BoolVarP(&input.dryrun, "dryrun", "n", false, "dryrun mode") rootCmd.PersistentFlags().BoolVarP(&input.dryrun, "dryrun", "n", false, "dryrun mode")
rootCmd.PersistentFlags().StringVarP(&input.envfile, "env-file", "", ".env", "environment file to read") rootCmd.PersistentFlags().StringVarP(&input.secretfile, "secret-file", "", "", "file with list of secrets to read from")
rootCmd.PersistentFlags().StringVarP(&input.envfile, "env-file", "", ".env", "environment file to read and use as env in the containers")
rootCmd.SetArgs(args()) rootCmd.SetArgs(args())
if err := rootCmd.Execute(); err != nil { if err := rootCmd.Execute(); err != nil {
@ -92,16 +93,29 @@ func setupLogging(cmd *cobra.Command, args []string) {
} }
} }
func readEnvs(path string, envs map[string]string) bool {
if _, err := os.Stat(path); err == nil {
env, err := godotenv.Read(path)
if err != nil {
log.Fatalf("Error loading from %s: %v", path, err)
}
for k, v := range env {
envs[k] = v
}
return true
}
return false
}
func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []string) error { func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []string) error {
return func(cmd *cobra.Command, args []string) error { return func(cmd *cobra.Command, args []string) error {
envfile := input.Envfile() log.Debugf("Loading environment from %s", input.Envfile())
if _, err := os.Stat(envfile); err == nil { envs := make(map[string]string)
log.Debugf("Loading environment from %s", envfile) _ = readEnvs(input.Envfile(), envs)
err := godotenv.Load(envfile)
if err != nil { log.Debugf("Loading secrets from %s", input.Secretfile())
log.Fatalf("Error loading environment from %s: %v", envfile, err) secrets := newSecrets(input.secrets)
} _ = readEnvs(input.Secretfile(), secrets)
}
planner, err := model.NewWorkflowPlanner(input.WorkflowsPath()) planner, err := model.NewWorkflowPlanner(input.WorkflowsPath())
if err != nil { if err != nil {
@ -149,7 +163,8 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
Workdir: input.Workdir(), Workdir: input.Workdir(),
BindWorkdir: input.bindWorkdir, BindWorkdir: input.bindWorkdir,
LogOutput: !input.noOutput, LogOutput: !input.noOutput,
Secrets: newSecrets(input.secrets), Env: envs,
Secrets: secrets,
Platforms: input.newPlatforms(), Platforms: input.newPlatforms(),
} }
runner, err := runner.New(config) runner, err := runner.New(config)

View File

@ -45,7 +45,7 @@ type stepResult struct {
// GetEnv returns the env for the context // GetEnv returns the env for the context
func (rc *RunContext) GetEnv() map[string]string { func (rc *RunContext) GetEnv() map[string]string {
if rc.Env == nil { if rc.Env == nil {
rc.Env = mergeMaps(rc.Run.Workflow.Env, rc.Run.Job().Env) rc.Env = mergeMaps(rc.Config.Env, rc.Run.Workflow.Env, rc.Run.Job().Env)
} }
return rc.Env return rc.Env
} }

View File

@ -24,6 +24,7 @@ type Config struct {
ReuseContainers bool // reuse containers to maintain state ReuseContainers bool // reuse containers to maintain state
ForcePull bool // force pulling of the image, if already present ForcePull bool // force pulling of the image, if already present
LogOutput bool // log the output from docker run LogOutput bool // log the output from docker run
Env map[string]string // env for containers
Secrets map[string]string // list of secrets Secrets map[string]string // list of secrets
Platforms map[string]string // list of platforms Platforms map[string]string // list of platforms
} }

View File

@ -3,7 +3,6 @@ package runner
import ( import (
"context" "context"
"fmt" "fmt"
"os"
"path/filepath" "path/filepath"
"testing" "testing"
@ -110,18 +109,8 @@ func TestRunEventSecrets(t *testing.T) {
workdir, err := filepath.Abs("testdata") workdir, err := filepath.Abs("testdata")
assert.NilError(t, err, workflowPath) assert.NilError(t, err, workflowPath)
_ = godotenv.Load(filepath.Join(workdir, workflowPath, ".env")) env, _ := godotenv.Read(filepath.Join(workdir, workflowPath, ".env"))
secrets, _ := godotenv.Read(filepath.Join(workdir, workflowPath, ".secrets"))
secrets := make(map[string]string)
for _, secret := range []string{
"MY_SECRET",
"MULTILINE_SECRET",
"JSON_SECRET",
} {
if env, ok := os.LookupEnv(secret); ok && env != "" {
secrets[secret] = env
}
}
runnerConfig := &Config{ runnerConfig := &Config{
Workdir: workdir, Workdir: workdir,
@ -129,6 +118,7 @@ func TestRunEventSecrets(t *testing.T) {
Platforms: platforms, Platforms: platforms,
ReuseContainers: false, ReuseContainers: false,
Secrets: secrets, Secrets: secrets,
Env: env,
} }
runner, err := New(runnerConfig) runner, err := New(runnerConfig)
assert.NilError(t, err, workflowPath) assert.NilError(t, err, workflowPath)

View File

@ -1,4 +1,2 @@
-W . -W .
-s MY_SECRET --secret-file .secrets
-s MULTILINE_SECRET
-s JSON_SECRET

View File

@ -1,3 +1,2 @@
MY_SECRET=top-secret HELLO=WORLD
MULTILINE_SECRET="foo\nbar\nbaz" MULTILINE_ENV="foo\nbar\nbaz"
JSON_SECRET={"foo": "bar"}

3
pkg/runner/testdata/secrets/.secrets vendored Normal file
View File

@ -0,0 +1,3 @@
MY_SECRET=top-secret
MULTILINE_SECRET="foo\nbar\nbaz"
JSON_SECRET={"foo": "bar"}

View File

@ -11,3 +11,7 @@ jobs:
echo "${{secrets.MULTILINE_SECRET}}" | wc -l | grep 3 echo "${{secrets.MULTILINE_SECRET}}" | wc -l | grep 3
- run: | - run: |
echo '${{secrets.JSON_SECRET}}' | grep "{\"foo\": \"bar\"}" echo '${{secrets.JSON_SECRET}}' | grep "{\"foo\": \"bar\"}"
- run: |
echo '${{env.HELLO}}' | grep "WORLD"
- run: |
echo "${{env.MULTILINE_ENV}}" | wc -l | grep 3