feat: support for cascading workflow/job/step env
This commit is contained in:
parent
3b70f47676
commit
f6a93c0b55
9 changed files with 199 additions and 26 deletions
|
@ -9,7 +9,7 @@ syntax](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for
|
||||||
- [ ] run-name
|
- [ ] run-name
|
||||||
- [ ] on
|
- [ ] on
|
||||||
- [ ] permissions
|
- [ ] permissions
|
||||||
- [ ] env
|
- [x] env
|
||||||
- [ ] defaults
|
- [ ] defaults
|
||||||
- [x] jobs
|
- [x] jobs
|
||||||
- [x] jobs.<job_id>.name
|
- [x] jobs.<job_id>.name
|
||||||
|
@ -20,7 +20,7 @@ syntax](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for
|
||||||
- [ ] jobs.<job_id>.environment
|
- [ ] jobs.<job_id>.environment
|
||||||
- [ ] jobs.<job_id>.concurrency
|
- [ ] jobs.<job_id>.concurrency
|
||||||
- [ ] jobs.<job_id>.outputs
|
- [ ] jobs.<job_id>.outputs
|
||||||
- [ ] jobs.<job_id>.env
|
- [x] jobs.<job_id>.env
|
||||||
- [x] jobs.<job_id>.defaults
|
- [x] jobs.<job_id>.defaults
|
||||||
- [ ] jobs.<job_id>.run.shell
|
- [ ] jobs.<job_id>.run.shell
|
||||||
- [x] jobs.<job_id>.run.working-directory
|
- [x] jobs.<job_id>.run.working-directory
|
||||||
|
@ -42,7 +42,7 @@ syntax](https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for
|
||||||
- [x] jobs.<job_id>.steps[*].working-directory
|
- [x] jobs.<job_id>.steps[*].working-directory
|
||||||
- [ ] jobs.<job_id>.steps[*].shell
|
- [ ] jobs.<job_id>.steps[*].shell
|
||||||
- [ ] jobs.<job_id>.steps[*].with
|
- [ ] jobs.<job_id>.steps[*].with
|
||||||
- [ ] jobs.<job_id>.steps[*].env
|
- [x] jobs.<job_id>.steps[*].env
|
||||||
- [X] jobs.<job_id>.steps[*].continue-on-error
|
- [X] jobs.<job_id>.steps[*].continue-on-error
|
||||||
- [ ] jobs.<job_id>.steps[*].timeout-minutes
|
- [ ] jobs.<job_id>.steps[*].timeout-minutes
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ type ContainerDriver interface {
|
||||||
Pull(string) error
|
Pull(string) error
|
||||||
Start(string, string) error
|
Start(string, string) error
|
||||||
Stop(string) error
|
Stop(string) error
|
||||||
Exec(containerId string, command string, cwd string) CommandResult
|
Exec(containerId string, command string, cwd string, env map[string]string) CommandResult
|
||||||
}
|
}
|
||||||
|
|
||||||
// Represents the outcome of a command call made by the driver.
|
// Represents the outcome of a command call made by the driver.
|
||||||
|
|
|
@ -49,12 +49,12 @@ func (d *MockDriver) Stop(uri string) error {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *MockDriver) Exec(containerName string, command string, cwd string) CommandResult {
|
func (d *MockDriver) Exec(containerName string, command string, cwd string, env map[string]string) CommandResult {
|
||||||
if _, init := d.calls["Exec"]; !init {
|
if _, init := d.calls["Exec"]; !init {
|
||||||
d.calls["Exec"] = []MockCall{}
|
d.calls["Exec"] = []MockCall{}
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []string{containerName, command, cwd}
|
args := []string{containerName, command, cwd, fmt.Sprintf("%#v", env)}
|
||||||
d.calls["Exec"] = append(d.calls["Exec"], MockCall{fname: "Exec", args: args})
|
d.calls["Exec"] = append(d.calls["Exec"], MockCall{fname: "Exec", args: args})
|
||||||
|
|
||||||
mockKeys := []string{
|
mockKeys := []string{
|
||||||
|
|
|
@ -79,13 +79,34 @@ func (d PodmanDriver) Stop(containerName string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d PodmanDriver) Exec(containerId string, command string, cwd string) CommandResult {
|
func (d PodmanDriver) Exec(containerId string, command string, cwd string, env map[string]string) CommandResult {
|
||||||
cmd := exec.Command("podman", "exec", "--workdir", cwd, containerId, "bash", "-c", command)
|
envArgs := []string{}
|
||||||
|
|
||||||
|
for key, value := range env {
|
||||||
|
envArgs = append(envArgs, fmt.Sprintf("-e=%s=%s", key, value))
|
||||||
|
}
|
||||||
|
|
||||||
|
commandArgs := []string{
|
||||||
|
"exec",
|
||||||
|
"--workdir",
|
||||||
|
cwd,
|
||||||
|
}
|
||||||
|
|
||||||
|
commandArgs = append(commandArgs, envArgs...)
|
||||||
|
|
||||||
|
commandArgs = append(commandArgs, containerId,
|
||||||
|
"bash",
|
||||||
|
"-c",
|
||||||
|
command,
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd := exec.Command("podman", commandArgs...)
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
err := cmd.Run()
|
err := cmd.Run()
|
||||||
|
|
||||||
|
fmt.Printf("%#v", cmd)
|
||||||
|
|
||||||
return CommandResult{
|
return CommandResult{
|
||||||
Error: err,
|
Error: err,
|
||||||
ExitCode: cmd.ProcessState.ExitCode(),
|
ExitCode: cmd.ProcessState.ExitCode(),
|
||||||
|
|
|
@ -103,8 +103,8 @@ func (r Runner) runJob(jobContext context.Context, jobTracker *TaskTracker, jobW
|
||||||
//
|
//
|
||||||
// If the command raises an error while in the container or fails to run
|
// If the command raises an error while in the container or fails to run
|
||||||
// the command at all, an error is returned, otherwise nil.
|
// the command at all, an error is returned, otherwise nil.
|
||||||
func (r *Runner) RunCommandInContainer(containerId string, command string, stepCwd string) error {
|
func (r *Runner) RunCommandInContainer(containerId string, command string, stepCwd string, stepEnv map[string]string) error {
|
||||||
result := r.Driver.Exec(containerId, command, stepCwd)
|
result := r.Driver.Exec(containerId, command, stepCwd, stepEnv)
|
||||||
|
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
return result.Error
|
return result.Error
|
||||||
|
@ -133,12 +133,14 @@ func (r *Runner) RunJobInContainer(imageUri string, containerId string, jobConte
|
||||||
logger.Info("Started %s", containerId)
|
logger.Info("Started %s", containerId)
|
||||||
for stepIndex, step := range job.Steps {
|
for stepIndex, step := range job.Steps {
|
||||||
stepCwd := jobContext.Value("workflow").(workflow.Workflow).GetWorkingDirectory(job.Name, stepIndex)
|
stepCwd := jobContext.Value("workflow").(workflow.Workflow).GetWorkingDirectory(job.Name, stepIndex)
|
||||||
|
stepEnv := jobContext.Value("workflow").(workflow.Workflow).GetEnv(job.Name, stepIndex)
|
||||||
|
|
||||||
logger.Info("Run: %s", step.Run)
|
logger.Info("Run: %s", step.Run)
|
||||||
logger.Info("Using working directory %s", stepCwd)
|
logger.Info("Using working directory %s", stepCwd)
|
||||||
var stepError error
|
var stepError error
|
||||||
|
|
||||||
if step.Run != "" {
|
if step.Run != "" {
|
||||||
stepError = r.RunCommandInContainer(containerId, step.Run, stepCwd)
|
stepError = r.RunCommandInContainer(containerId, step.Run, stepCwd, stepEnv)
|
||||||
}
|
}
|
||||||
|
|
||||||
if stepError != nil && !step.ContinueOnError {
|
if stepError != nil && !step.ContinueOnError {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
logger "courgette/internal/logging"
|
logger "courgette/internal/logging"
|
||||||
workflow "courgette/internal/workflow"
|
workflow "courgette/internal/workflow"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,13 +15,13 @@ func init() {
|
||||||
|
|
||||||
func TestRunnerRunCommandInContainerReturnsErrorFromDriver(t *testing.T) {
|
func TestRunnerRunCommandInContainerReturnsErrorFromDriver(t *testing.T) {
|
||||||
mockDriver := NewMockDriver()
|
mockDriver := NewMockDriver()
|
||||||
mockDriver.WithMockedCall("Exec", CommandResult{ExitCode: 0, Error: errors.New("test")}, "test-container", "test-command", ".")
|
mockDriver.WithMockedCall("Exec", CommandResult{ExitCode: 0, Error: errors.New("test")}, "test-container", "test-command", ".", fmt.Sprintf("%#v", map[string]string{}))
|
||||||
|
|
||||||
runner := Runner{
|
runner := Runner{
|
||||||
Driver: &mockDriver,
|
Driver: &mockDriver,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := runner.RunCommandInContainer("test-container", "test-command", ".")
|
err := runner.RunCommandInContainer("test-container", "test-command", ".", map[string]string{})
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Expected error, got nil.")
|
t.Errorf("Expected error, got nil.")
|
||||||
|
@ -29,13 +30,13 @@ func TestRunnerRunCommandInContainerReturnsErrorFromDriver(t *testing.T) {
|
||||||
|
|
||||||
func TestRunnerRunCommandInContainerReturnsErrorIfCommandExitCodeNonzero(t *testing.T) {
|
func TestRunnerRunCommandInContainerReturnsErrorIfCommandExitCodeNonzero(t *testing.T) {
|
||||||
mockDriver := NewMockDriver()
|
mockDriver := NewMockDriver()
|
||||||
mockDriver.WithMockedCall("Exec", CommandResult{ExitCode: 1, Error: nil}, "test-container", "test-command", ".")
|
mockDriver.WithMockedCall("Exec", CommandResult{ExitCode: 1, Error: nil}, "test-container", "test-command", ".", fmt.Sprintf("%#v", map[string]string{}))
|
||||||
|
|
||||||
runner := Runner{
|
runner := Runner{
|
||||||
Driver: &mockDriver,
|
Driver: &mockDriver,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := runner.RunCommandInContainer("test-container", "test-command", ".")
|
err := runner.RunCommandInContainer("test-container", "test-command", ".", map[string]string{})
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Expected error, got nil.")
|
t.Errorf("Expected error, got nil.")
|
||||||
|
@ -44,7 +45,7 @@ func TestRunnerRunCommandInContainerReturnsErrorIfCommandExitCodeNonzero(t *test
|
||||||
|
|
||||||
func TestRunJobInContainerSchedulesStoppingContainers(t *testing.T) {
|
func TestRunJobInContainerSchedulesStoppingContainers(t *testing.T) {
|
||||||
mockDriver := NewMockDriver()
|
mockDriver := NewMockDriver()
|
||||||
mockDriver.WithMockedCall("Exec", CommandResult{ExitCode: 1, Error: nil}, "test-container", "test-command", ".")
|
mockDriver.WithMockedCall("Exec", CommandResult{ExitCode: 1, Error: nil}, "test-container", "test-command", ".", fmt.Sprintf("%#v", map[string]string{}))
|
||||||
|
|
||||||
runner := NewRunner(&mockDriver, map[string]string{})
|
runner := NewRunner(&mockDriver, map[string]string{})
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
workflow "courgette/internal/workflow"
|
workflow "courgette/internal/workflow"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -70,7 +71,7 @@ jobs:
|
||||||
workflow, _ := workflow.FromYamlBytes([]byte(workflowSample))
|
workflow, _ := workflow.FromYamlBytes([]byte(workflowSample))
|
||||||
mockDriver := NewMockDriver()
|
mockDriver := NewMockDriver()
|
||||||
|
|
||||||
mockDriver.WithMockedCall("Exec", CommandResult{Error: errors.New("exit 1!"), ExitCode: 1}, "testContainer", "exit 1")
|
mockDriver.WithMockedCall("Exec", CommandResult{Error: errors.New("exit 1!"), ExitCode: 1}, "testContainer", "exit 1", fmt.Sprintf("%#v", map[string]string{}))
|
||||||
runner := NewRunner(&mockDriver, map[string]string{"test": "test"})
|
runner := NewRunner(&mockDriver, map[string]string{"test": "test"})
|
||||||
|
|
||||||
jobContext := context.WithValue(context.Background(), "workflow", *workflow)
|
jobContext := context.WithValue(context.Background(), "workflow", *workflow)
|
||||||
|
@ -98,7 +99,7 @@ jobs:
|
||||||
workflow, _ := workflow.FromYamlBytes([]byte(workflowSample))
|
workflow, _ := workflow.FromYamlBytes([]byte(workflowSample))
|
||||||
mockDriver := NewMockDriver()
|
mockDriver := NewMockDriver()
|
||||||
|
|
||||||
mockDriver.WithMockedCall("Exec", CommandResult{Error: errors.New("exit 1!"), ExitCode: 1}, "testContainer", "exit 1")
|
mockDriver.WithMockedCall("Exec", CommandResult{Error: errors.New("exit 1!"), ExitCode: 1}, "testContainer", "exit 1", fmt.Sprintf("%#v", map[string]string{}))
|
||||||
runner := NewRunner(&mockDriver, map[string]string{"test": "test"})
|
runner := NewRunner(&mockDriver, map[string]string{"test": "test"})
|
||||||
|
|
||||||
jobContext := context.WithValue(context.Background(), "workflow", *workflow)
|
jobContext := context.WithValue(context.Background(), "workflow", *workflow)
|
||||||
|
@ -130,7 +131,7 @@ jobs:
|
||||||
workflow, _ := workflow.FromYamlBytes([]byte(workflowSample))
|
workflow, _ := workflow.FromYamlBytes([]byte(workflowSample))
|
||||||
mockDriver := NewMockDriver()
|
mockDriver := NewMockDriver()
|
||||||
|
|
||||||
mockDriver.WithMockedCall("Exec", CommandResult{Error: errors.New("exit 1!"), ExitCode: 1}, "testContainer", "exit 1", ".")
|
mockDriver.WithMockedCall("Exec", CommandResult{Error: errors.New("exit 1!"), ExitCode: 1}, "testContainer", "exit 1", ".", fmt.Sprintf("%#v", map[string]string{}))
|
||||||
runner := NewRunner(&mockDriver, map[string]string{"test": "test"})
|
runner := NewRunner(&mockDriver, map[string]string{"test": "test"})
|
||||||
|
|
||||||
jobContext := context.WithValue(context.Background(), "workflow", *workflow)
|
jobContext := context.WithValue(context.Background(), "workflow", *workflow)
|
||||||
|
@ -161,7 +162,7 @@ jobs:
|
||||||
workflow, _ := workflow.FromYamlBytes([]byte(workflowSample))
|
workflow, _ := workflow.FromYamlBytes([]byte(workflowSample))
|
||||||
mockDriver := NewMockDriver()
|
mockDriver := NewMockDriver()
|
||||||
|
|
||||||
mockDriver.WithMockedCall("Exec", CommandResult{Error: errors.New("exit 1!"), ExitCode: 1}, "testContainer", "exit 1", ".")
|
mockDriver.WithMockedCall("Exec", CommandResult{Error: errors.New("exit 1!"), ExitCode: 1}, "testContainer", "exit 1", ".", fmt.Sprintf("%#v", map[string]string{}))
|
||||||
runner := NewRunner(&mockDriver, map[string]string{"test": "test"})
|
runner := NewRunner(&mockDriver, map[string]string{"test": "test"})
|
||||||
|
|
||||||
jobContext := context.WithValue(context.Background(), "workflow", *workflow)
|
jobContext := context.WithValue(context.Background(), "workflow", *workflow)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package workflow
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"maps"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Job struct {
|
type Job struct {
|
||||||
|
@ -11,7 +12,8 @@ type Job struct {
|
||||||
// Job name; this isn't guaranteed to be unique.
|
// Job name; this isn't guaranteed to be unique.
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
// If truthy, job-level failure does not bubble up to the workflow.
|
// If truthy, job-level failure does not bubble up to the workflow.
|
||||||
ContinueOnError bool `yaml:"continue-on-error"`
|
ContinueOnError bool `yaml:"continue-on-error"`
|
||||||
|
Env map[string]string `yaml:"env"`
|
||||||
Defaults struct {
|
Defaults struct {
|
||||||
Run struct {
|
Run struct {
|
||||||
WorkingDirectory string `yaml:"working-directory"`
|
WorkingDirectory string `yaml:"working-directory"`
|
||||||
|
@ -38,9 +40,10 @@ func (j Job) Validate() []error {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Step struct {
|
type Step struct {
|
||||||
Run string `yaml:"run"`
|
Run string `yaml:"run"`
|
||||||
WorkingDirectory string `yaml:"working-directory"`
|
WorkingDirectory string `yaml:"working-directory"`
|
||||||
ContinueOnError bool `yaml:"continue-on-error"`
|
ContinueOnError bool `yaml:"continue-on-error"`
|
||||||
|
Env map[string]string `yaml:"env"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Step) Validate() []error {
|
func (s Step) Validate() []error {
|
||||||
|
@ -55,7 +58,8 @@ func (s Step) Validate() []error {
|
||||||
|
|
||||||
type Workflow struct {
|
type Workflow struct {
|
||||||
SourcePath string
|
SourcePath string
|
||||||
Jobs map[string]Job `yaml:"jobs"`
|
Jobs map[string]Job `yaml:"jobs"`
|
||||||
|
Env map[string]string `yaml:"env"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the given workflow's job+step working directory inside the container
|
// Returns the given workflow's job+step working directory inside the container
|
||||||
|
@ -79,6 +83,22 @@ func (w Workflow) GetWorkingDirectory(jobName string, stepIndex int) string {
|
||||||
return "."
|
return "."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the merged map of environment variants defined by:
|
||||||
|
// - Workflow-level "env"
|
||||||
|
// - Job-level "env"
|
||||||
|
// - Step-level "env"
|
||||||
|
// The environment is merged in order, and name collisions overwrite
|
||||||
|
// previous values such that jobs can overwrite workflows, and steps, jobs.
|
||||||
|
func (w Workflow) GetEnv(jobName string, stepIndex int) map[string]string {
|
||||||
|
finalEnv := map[string]string{}
|
||||||
|
|
||||||
|
maps.Copy(finalEnv, w.Env)
|
||||||
|
maps.Copy(finalEnv, w.Jobs[jobName].Env)
|
||||||
|
maps.Copy(finalEnv, w.Jobs[jobName].Steps[stepIndex].Env)
|
||||||
|
|
||||||
|
return finalEnv
|
||||||
|
}
|
||||||
|
|
||||||
func (w Workflow) Validate() []error {
|
func (w Workflow) Validate() []error {
|
||||||
validationErrors := []error{}
|
validationErrors := []error{}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package workflow
|
package workflow
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -78,3 +79,130 @@ jobs:
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWorkflowEnv(t *testing.T) {
|
||||||
|
sample := `
|
||||||
|
env:
|
||||||
|
TEST: 1
|
||||||
|
jobs:
|
||||||
|
jobA:
|
||||||
|
runs-on: default
|
||||||
|
steps:
|
||||||
|
- run: echo "test"
|
||||||
|
`
|
||||||
|
|
||||||
|
workflow, _ := FromYamlBytes([]byte(sample))
|
||||||
|
|
||||||
|
env := workflow.GetEnv("jobA", 0)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(env, map[string]string{"TEST": "1"}) {
|
||||||
|
t.Errorf("Unexpected env: %#v", env)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJobEnv(t *testing.T) {
|
||||||
|
sample := `
|
||||||
|
jobs:
|
||||||
|
jobA:
|
||||||
|
env:
|
||||||
|
TEST: 1
|
||||||
|
runs-on: default
|
||||||
|
steps:
|
||||||
|
- run: echo "test"
|
||||||
|
`
|
||||||
|
|
||||||
|
workflow, _ := FromYamlBytes([]byte(sample))
|
||||||
|
|
||||||
|
env := workflow.GetEnv("jobA", 0)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(env, map[string]string{"TEST": "1"}) {
|
||||||
|
t.Errorf("Unexpected env: %#v", env)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepEnv(t *testing.T) {
|
||||||
|
sample := `
|
||||||
|
jobs:
|
||||||
|
jobA:
|
||||||
|
|
||||||
|
runs-on: default
|
||||||
|
steps:
|
||||||
|
- run: echo "test"
|
||||||
|
env:
|
||||||
|
TEST: 1
|
||||||
|
`
|
||||||
|
|
||||||
|
workflow, _ := FromYamlBytes([]byte(sample))
|
||||||
|
|
||||||
|
env := workflow.GetEnv("jobA", 0)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(env, map[string]string{"TEST": "1"}) {
|
||||||
|
t.Errorf("Unexpected env: %#v", env)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJobEnvOverwritesWorkflowEnv(t *testing.T) {
|
||||||
|
sample := `
|
||||||
|
env:
|
||||||
|
TEST: 1
|
||||||
|
jobs:
|
||||||
|
jobA:
|
||||||
|
env:
|
||||||
|
TEST: 2
|
||||||
|
runs-on: default
|
||||||
|
steps:
|
||||||
|
- run: echo "test"
|
||||||
|
`
|
||||||
|
|
||||||
|
workflow, _ := FromYamlBytes([]byte(sample))
|
||||||
|
|
||||||
|
env := workflow.GetEnv("jobA", 0)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(env, map[string]string{"TEST": "2"}) {
|
||||||
|
t.Errorf("Unexpected env: %#v", env)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepEnvOverwritesWorkflowEnv(t *testing.T) {
|
||||||
|
sample := `
|
||||||
|
env:
|
||||||
|
TEST: 1
|
||||||
|
jobs:
|
||||||
|
jobA:
|
||||||
|
runs-on: default
|
||||||
|
steps:
|
||||||
|
- run: echo "test"
|
||||||
|
env:
|
||||||
|
TEST: 2
|
||||||
|
`
|
||||||
|
|
||||||
|
workflow, _ := FromYamlBytes([]byte(sample))
|
||||||
|
|
||||||
|
env := workflow.GetEnv("jobA", 0)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(env, map[string]string{"TEST": "2"}) {
|
||||||
|
t.Errorf("Unexpected env: %#v", env)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStepEnvOverwritesJobEnv(t *testing.T) {
|
||||||
|
sample := `
|
||||||
|
jobs:
|
||||||
|
jobA:
|
||||||
|
env:
|
||||||
|
TEST: 1
|
||||||
|
runs-on: default
|
||||||
|
steps:
|
||||||
|
- run: echo "test"
|
||||||
|
env:
|
||||||
|
TEST: 2
|
||||||
|
`
|
||||||
|
|
||||||
|
workflow, _ := FromYamlBytes([]byte(sample))
|
||||||
|
|
||||||
|
env := workflow.GetEnv("jobA", 0)
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(env, map[string]string{"TEST": "2"}) {
|
||||||
|
t.Errorf("Unexpected env: %#v", env)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue