diff --git a/ahoy.go b/ahoy.go index 1213a8b..4a02200 100644 --- a/ahoy.go +++ b/ahoy.go @@ -24,6 +24,7 @@ type Config struct { AhoyAPI string Commands map[string]Command Entrypoint []string + Env string } // Command is an ahoy command detailed in ahoy.yml files. Multiple @@ -32,6 +33,7 @@ type Command struct { Description string Usage string Cmd string + Env string Hide bool Optional bool Imports []string @@ -172,8 +174,38 @@ func getSubCommands(includes []string) []cli.Command { return subCommands } +// Given a filepath, return a string array of environment variables. +func getEnvironmentVars(envFile string) []string { + var envVars []string + + env, err := os.ReadFile(envFile) + if err != nil { + logger("fatal", "Invalid env file: "+envFile) + return nil + } + + lines := strings.Split(string(env), "\n") + for _, line := range lines { + line = strings.TrimSpace(line) + + // Ignore empty lines and comments (lines starting with '#'). + if line == "" || strings.HasPrefix(line, "#") { + continue + } + envVars = append(envVars, line) + } + return envVars +} + func getCommands(config Config) []cli.Command { exportCmds := []cli.Command{} + envVars := []string{} + + // Get environment variables from the 'global' environment variable file, if it is defined. + if config.Env != "" { + globalEnvFile := filepath.Join(AhoyConf.srcDir, config.Env) + envVars = append(envVars, getEnvironmentVars(globalEnvFile)...) + } var keys []string for k := range config.Commands { @@ -239,6 +271,14 @@ func getCommands(config Config) []cli.Command { } cmdItems = append(cmdEntrypoint, cmdArgs...) + // If defined, included specified command-level environment variables. + // Note that this will intentionally override any conflicting variables + // defined in the 'global' env file. + if cmd.Env != "" { + cmdEnvFile := filepath.Join(AhoyConf.srcDir, cmd.Env) + envVars = append(envVars, getEnvironmentVars(cmdEnvFile)...) + } + if verbose { log.Println("===> AHOY", name, "from", sourcefile, ":", cmdItems) } @@ -247,6 +287,7 @@ func getCommands(config Config) []cli.Command { command.Stdout = os.Stdout command.Stdin = os.Stdin command.Stderr = os.Stderr + command.Env = append(command.Env, envVars...) if err := command.Run(); err != nil { fmt.Fprintln(os.Stderr) os.Exit(1) diff --git a/testdata/.env b/testdata/.env new file mode 100644 index 0000000..4b374d9 --- /dev/null +++ b/testdata/.env @@ -0,0 +1,6 @@ +# Variables defined in this file should be accessible to all Ahoy commands, +# since it is to be included in the 'global' environment variable. +GLOBAL=global + +# This variable is to be overridden by the command-level env file, '.env.cmd'. +TO_BE_OVERRIDDEN=before diff --git a/testdata/.env.cmd b/testdata/.env.cmd new file mode 100644 index 0000000..625957a --- /dev/null +++ b/testdata/.env.cmd @@ -0,0 +1,2 @@ +COMMAND=123456789 +TO_BE_OVERRIDDEN=after diff --git a/testdata/env.ahoy.yml b/testdata/env.ahoy.yml new file mode 100644 index 0000000..4c3bdf6 --- /dev/null +++ b/testdata/env.ahoy.yml @@ -0,0 +1,17 @@ +ahoyapi: v2 +env: ./.env +commands: + test-global: + cmd: echo $GLOBAL + + test-cmd: + cmd: echo $COMMAND + env: .env.cmd + + test-override: + cmd: echo $TO_BE_OVERRIDDEN + env: .env.cmd + + test-invalid-env: + cmd: echo "This should not print!" + env: .env.thisfiledoesntexist diff --git a/tests/environment-variables.bats b/tests/environment-variables.bats new file mode 100644 index 0000000..b3923f2 --- /dev/null +++ b/tests/environment-variables.bats @@ -0,0 +1,21 @@ +#!/usr/bin/env bats + +@test "Command-level variables can be defined and used" { + run ./ahoy -f testdata/env.ahoy.yml test-cmd + [[ "$output" == "123456789" ]] +} + +@test "Environment variables can be overriden" { + run ./ahoy -f testdata/env.ahoy.yml test-override + [[ "$output" = "after" ]] +} + +@test "Global variables can be defined and used" { + run ./ahoy -f testdata/env.ahoy.yml test-global + [[ "$output" = "global" ]] +} + +@test "Fail when attempting to load invalid env files" { + run ./ahoy -f testdata/env.ahoy.yml test-invalid-env + [ $status -eq 1 ] +}