Skip to content

Commit

Permalink
Add global env (#9)
Browse files Browse the repository at this point in the history
* fix bug when slicing os.Args

* add test when slicing os.Args in cmd parsing

* fix bug when validating circular depends - check if same command

* add tests for circular deps validation

* add global env

* add mixins and update doc
  • Loading branch information
kindermax authored Feb 12, 2020
1 parent 71c6095 commit 0c7fa7c
Show file tree
Hide file tree
Showing 13 changed files with 446 additions and 89 deletions.
73 changes: 70 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ lets run --debug --level=info
Config schema

* [shell](#shell)
* [mixins](#mixins)
* [env](#global-env)
* [commands](#commands)
* [description](#description)
* [cmd](#cmd)
Expand All @@ -64,6 +66,8 @@ Config schema
### Top-level directives:

#### `shell`
`key: shell`

`type: string`

Specify shell to use when running commands
Expand All @@ -74,7 +78,55 @@ Example:
shell: bash
```

#### `global env`
`key: env`

`type: string`

Specify global env for all commands.

Example:

```sh
shell: bash
env:
MY_GLOBAL_ENV: "123"
```

#### `mixins`
`key: mixins`

`type: list of string`

Allows to split `lets.yaml` into mixins (mixin config files).

To make `lets.yaml` small and readable its convenient to split main config into many smaller ones and include them

Example:

```sh
# in lets.yaml
...
shell: bash
mixins:
- test.yaml

commands:
echo:
cmd: echo Hi

# in test.yaml
...
commands:
test:
cmd: echo Testing...
```

And `lets test` works fine.

#### `commands`
`key: commands`

`type: mapping`

Mapping of all available commands
Expand All @@ -90,6 +142,8 @@ commands:
### Command directives:

##### `description`
`key: description`

`type: string`

Short description of command - shown in help message
Expand All @@ -103,6 +157,8 @@ commands:
```

##### `cmd`
`key: cmd`

`type: string or array of strings`

Actual command to run in shell.
Expand Down Expand Up @@ -156,6 +212,8 @@ lets test -v
the `-v` will be appended, so the resulting command to run will be `go test ./... -v`

##### `depends`
`key: depends`

`type: array of string`

Specify what commands to run before the actual command. May be useful, when have one shared command.
Expand Down Expand Up @@ -184,6 +242,8 @@ commands:


##### `options`
`key: options`

`type: string (multiline string)`

One of the most cool things about `lets` than it has built in docopt parsing.
Expand Down Expand Up @@ -247,6 +307,8 @@ echo LETSCLI_DEBUG=${LETSCLI_DEBUG} # LETSCLI_DEBUG=--debug


##### `env`
`key: env`

`type: mapping string => string`

Env is as simple as it sounds. Define additional env for a commmand:
Expand All @@ -265,6 +327,8 @@ commands:


##### `eval_env`
`key: eval_env`

`type: mapping string => string`

Same as env but allows you to dynamically compute env:
Expand All @@ -285,6 +349,8 @@ Value will be executed in shell and result will be saved in env.


##### `checksum`
`key: checksum`

`type: array of string`

Checksum used for computing file hashed. It is useful when you depend on some files content changes.
Expand Down Expand Up @@ -360,19 +426,20 @@ Yet there is no binaries
- [ ] global checksums (check if some commands use checksum so we can skip its calculation)
- [ ] multiple checksums in one command (kv)
- [x] depends on other commands
- [ ] inherit configs
- [x] inherit configs
- [x] LETS_DEBUG env for debugging logs
- [ ] command to only calculate checksum
- [x] capture env from shell
- [ ] env as a list of strings `- key=val`
- [ ] env computing
- [ ] global env
- [ ] global eval_env
- [x] global env
- [x] command env
- [ ] dogfood on ci
- [x] add version flag to lets
- [ ] add verbose flag to lets
- [x] add LETSCLI_OPTION - options as is
- [ ] add all env vars event if no options were passed
- [x] add all env vars event if no options were passed
- [ ] BUG - when run git commit, lets complains that no config is found for git
- [x] Print usage if wrong opt passed for options
- [ ] Bash/zsh completion
Expand Down
6 changes: 6 additions & 0 deletions build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
env:
DOCKER_BUILDKIT: "1"

commands:
build-docker:
cmd: docker build -t lets -f docker/Dockerfile .
7 changes: 6 additions & 1 deletion commands/command/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ func parseAndValidateCmd(cmd interface{}, newCmd *Command) error {
cmdList = append(cmdList, fmt.Sprintf("%s", v))
}
// cut binary path and command name
cmdList = append(cmdList, os.Args[2:]...)
if len(os.Args) > 1 {
cmdList = append(cmdList, os.Args[2:]...)
} else if len(os.Args) == 1 {
cmdList = append(cmdList, os.Args[1:]...)
}

var escapedCmdList []string
for _, val := range cmdList {
escapedCmdList = append(escapedCmdList, escapeFlagValue(val))
Expand Down
16 changes: 16 additions & 0 deletions commands/command/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ import (
)

func TestCommandFieldCmd(t *testing.T) {
t.Run("so subcommand in os.Args", func(t *testing.T) {
testCmd := NewCommand("test-cmd")
cmdArgs := "echo Hello"
// mock args
os.Args = []string{"bin_to_run"}
err := parseAndValidateCmd(cmdArgs, &testCmd)

if err != nil {
t.Fatalf("unexpected error: %s", err)
}

if testCmd.Cmd != cmdArgs {
t.Errorf("wrong output. \nexpect %s \ngot: %s", cmdArgs, testCmd.Cmd)
}
})

t.Run("as string", func(t *testing.T) {
testCmd := NewCommand("test-cmd")
cmdArgs := "echo Hello"
Expand Down
25 changes: 10 additions & 15 deletions commands/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,14 @@ func RunCommand(cmdToRun command.Command, cfg *config.Config, out io.Writer) err
return runCmd(cmdToRun, cfg, out, "")
}

func convertEnvForCmd(envMap map[string]string) []string {
envList := make([]string, len(envMap))
func convertEnvMapToList(envMap map[string]string) []string {
var envList []string
for name, value := range envMap {
envList = append(envList, fmt.Sprintf("%s=%s", name, value))
}
return envList
}

func convertOptsToEnvForCmd(opts map[string]string) []string {
envList := make([]string, len(opts))
for name, value := range opts {
envList = append(envList, fmt.Sprintf("%s=%s", name, value))
}
return envList
}

func convertChecksumToEnvForCmd(checksum string) []string {
return []string{fmt.Sprintf("LETS_CHECKSUM=%s", checksum)}
}
Expand Down Expand Up @@ -79,11 +71,14 @@ func runCmd(cmdToRun command.Command, cfg *config.Config, out io.Writer, parentN
cmdToRun.CliOptions = command.OptsToLetsCli(opts)

// setup env for command
env := convertEnvForCmd(cmdToRun.Env)
optsEnv := convertOptsToEnvForCmd(cmdToRun.Options)
cliOptsEnv := convertOptsToEnvForCmd(cmdToRun.CliOptions)
checksumEnv := convertChecksumToEnvForCmd(cmdToRun.Checksum)
cmd.Env = composeEnvs(os.Environ(), env, optsEnv, cliOptsEnv, checksumEnv)
cmd.Env = composeEnvs(
os.Environ(),
convertEnvMapToList(cfg.Env),
convertEnvMapToList(cmdToRun.Env),
convertEnvMapToList(cmdToRun.Options),
convertEnvMapToList(cmdToRun.CliOptions),
convertChecksumToEnvForCmd(cmdToRun.Checksum),
)
if parentName == "" {
logging.Log.Debugf(
"Executing command\nname: %s\ncmd: %s\nenv:\n%s",
Expand Down
19 changes: 19 additions & 0 deletions commands/run_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package commands

import (
"testing"
)


func TestConvertEnvMapToList(t *testing.T) {
t.Run("should convert map to list of key=val", func(t *testing.T) {
env := make(map[string]string)
env["ONE"] = "1"
envList := convertEnvMapToList(env)

exp := "ONE=1"
if envList[0] != exp {
t.Errorf("failed to convert env map to list. \nexp: %s\ngot: %s", exp, envList[0])
}
})
}
Loading

0 comments on commit 0c7fa7c

Please sign in to comment.