Make envs available in if conditionals (#225)
* Ignore .idea * Add Env to the RunContext vm so we can Evaluate and Interpolate `env.xx` * Make EvalBool support expressions more in line with the github runner * Turns out Boolean(value) is what github is doing after all * Add test for github context as well
This commit is contained in:
parent
6d6ea7ac04
commit
a149cf8ca2
5 changed files with 155 additions and 18 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -16,3 +16,4 @@ dist/
|
||||||
|
|
||||||
*.nupkg
|
*.nupkg
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
|
@ -119,6 +119,7 @@ func (rc *RunContext) newVM() *otto.Otto {
|
||||||
rc.vmSecrets(),
|
rc.vmSecrets(),
|
||||||
rc.vmStrategy(),
|
rc.vmStrategy(),
|
||||||
rc.vmMatrix(),
|
rc.vmMatrix(),
|
||||||
|
rc.vmEnv(),
|
||||||
}
|
}
|
||||||
vm := otto.New()
|
vm := otto.New()
|
||||||
for _, configer := range configers {
|
for _, configer := range configers {
|
||||||
|
@ -196,18 +197,18 @@ func (rc *RunContext) vmHashFiles() func(*otto.Otto) {
|
||||||
_ = vm.Set("hashFiles", func(path string) string {
|
_ = vm.Set("hashFiles", func(path string) string {
|
||||||
files, _, err := glob.Glob([]string{filepath.Join(rc.Config.Workdir, path)})
|
files, _, err := glob.Glob([]string{filepath.Join(rc.Config.Workdir, path)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Error(err)
|
logrus.Errorf("Unable to glob.Glob: %v", err)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
hasher := sha256.New()
|
hasher := sha256.New()
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
f, err := os.Open(file.Path)
|
f, err := os.Open(file.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Error(err)
|
logrus.Errorf("Unable to os.Open: %v", err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
if _, err := io.Copy(hasher, f); err != nil {
|
if _, err := io.Copy(hasher, f); err != nil {
|
||||||
logrus.Error(err)
|
logrus.Errorf("Unable to io.Copy: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return hex.EncodeToString(hasher.Sum(nil))
|
return hex.EncodeToString(hasher.Sum(nil))
|
||||||
|
@ -251,6 +252,14 @@ func (rc *RunContext) vmGithub() func(*otto.Otto) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rc *RunContext) vmEnv() func(*otto.Otto) {
|
||||||
|
return func(vm *otto.Otto) {
|
||||||
|
env := rc.GetEnv()
|
||||||
|
log.Debugf("context env => %v", env)
|
||||||
|
_ = vm.Set("env", env)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (sc *StepContext) vmEnv() func(*otto.Otto) {
|
func (sc *StepContext) vmEnv() func(*otto.Otto) {
|
||||||
return func(vm *otto.Otto) {
|
return func(vm *otto.Otto) {
|
||||||
log.Debugf("context env => %v", sc.Env)
|
log.Debugf("context env => %v", sc.Env)
|
||||||
|
|
|
@ -4,15 +4,18 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nektos/act/pkg/model"
|
"github.com/nektos/act/pkg/model"
|
||||||
"github.com/stretchr/testify/assert"
|
a "github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEvaluate(t *testing.T) {
|
func TestEvaluate(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := a.New(t)
|
||||||
rc := &RunContext{
|
rc := &RunContext{
|
||||||
Config: &Config{
|
Config: &Config{
|
||||||
Workdir: ".",
|
Workdir: ".",
|
||||||
},
|
},
|
||||||
|
Env: map[string]string{
|
||||||
|
"key": "value",
|
||||||
|
},
|
||||||
Run: &model.Run{
|
Run: &model.Run{
|
||||||
JobID: "job1",
|
JobID: "job1",
|
||||||
Workflow: &model.Workflow{
|
Workflow: &model.Workflow{
|
||||||
|
@ -79,6 +82,8 @@ func TestEvaluate(t *testing.T) {
|
||||||
{"runner.os", "Linux", ""},
|
{"runner.os", "Linux", ""},
|
||||||
{"matrix.os", "Linux", ""},
|
{"matrix.os", "Linux", ""},
|
||||||
{"matrix.foo", "bar", ""},
|
{"matrix.foo", "bar", ""},
|
||||||
|
{"env.key", "value", ""},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, table := range tables {
|
for _, table := range tables {
|
||||||
|
@ -97,11 +102,14 @@ func TestEvaluate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInterpolate(t *testing.T) {
|
func TestInterpolate(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := a.New(t)
|
||||||
rc := &RunContext{
|
rc := &RunContext{
|
||||||
Config: &Config{
|
Config: &Config{
|
||||||
Workdir: ".",
|
Workdir: ".",
|
||||||
},
|
},
|
||||||
|
Env: map[string]string{
|
||||||
|
"key": "value",
|
||||||
|
},
|
||||||
Run: &model.Run{
|
Run: &model.Run{
|
||||||
JobID: "job1",
|
JobID: "job1",
|
||||||
Workflow: &model.Workflow{
|
Workflow: &model.Workflow{
|
||||||
|
@ -113,8 +121,20 @@ func TestInterpolate(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
ee := rc.NewExpressionEvaluator()
|
ee := rc.NewExpressionEvaluator()
|
||||||
|
tables := []struct{
|
||||||
|
in string
|
||||||
|
out string
|
||||||
|
}{
|
||||||
|
{" ${{1}} to ${{2}} ", " 1 to 2 "},
|
||||||
|
{" ${{ env.key }} ", " value "},
|
||||||
|
{"${{ env.unknown }}", ""},
|
||||||
|
}
|
||||||
|
|
||||||
out := ee.Interpolate(" ${{1}} to ${{2}} ")
|
for _, table := range tables {
|
||||||
|
table := table
|
||||||
assert.Equal(" 1 to 2 ", out)
|
t.Run(table.in, func(t *testing.T) {
|
||||||
|
out := ee.Interpolate(table.in)
|
||||||
|
assert.Equal(table.out, out, table.in)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -235,15 +235,15 @@ func (rc *RunContext) platformImage() string {
|
||||||
|
|
||||||
func (rc *RunContext) isEnabled(ctx context.Context) bool {
|
func (rc *RunContext) isEnabled(ctx context.Context) bool {
|
||||||
job := rc.Run.Job()
|
job := rc.Run.Job()
|
||||||
log := common.Logger(ctx)
|
l := common.Logger(ctx)
|
||||||
if !rc.EvalBool(job.If) {
|
if !rc.EvalBool(job.If) {
|
||||||
log.Debugf("Skipping job '%s' due to '%s'", job.Name, job.If)
|
l.Debugf("Skipping job '%s' due to '%s'", job.Name, job.If)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
img := rc.platformImage()
|
img := rc.platformImage()
|
||||||
if img == "" {
|
if img == "" {
|
||||||
log.Infof("\U0001F6A7 Skipping unsupported platform '%+v'", job.RunsOn())
|
l.Infof("\U0001F6A7 Skipping unsupported platform '%+v'", job.RunsOn())
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -252,11 +252,9 @@ func (rc *RunContext) isEnabled(ctx context.Context) bool {
|
||||||
// EvalBool evaluates an expression against current run context
|
// EvalBool evaluates an expression against current run context
|
||||||
func (rc *RunContext) EvalBool(expr string) bool {
|
func (rc *RunContext) EvalBool(expr string) bool {
|
||||||
if expr != "" {
|
if expr != "" {
|
||||||
//v, err := rc.ExprEval.Evaluate(fmt.Sprintf("if (%s) { true } else { false }", expr))
|
expr = fmt.Sprintf("Boolean(%s)", rc.ExprEval.Interpolate(expr))
|
||||||
expr := fmt.Sprintf("Boolean(%s)", expr)
|
|
||||||
v, err := rc.ExprEval.Evaluate(expr)
|
v, err := rc.ExprEval.Evaluate(expr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error evaluating expression '%s' - %v", expr, err)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
log.Debugf("expression '%s' evaluated to '%s'", expr, v)
|
log.Debugf("expression '%s' evaluated to '%s'", expr, v)
|
||||||
|
@ -386,9 +384,11 @@ func (rc *RunContext) getGithubContext() *githubContext {
|
||||||
log.Debugf("using github ref: %s", ref)
|
log.Debugf("using github ref: %s", ref)
|
||||||
ghc.Ref = ref
|
ghc.Ref = ref
|
||||||
}
|
}
|
||||||
err = json.Unmarshal([]byte(rc.EventJSON), &ghc.Event)
|
if rc.EventJSON != "" {
|
||||||
if err != nil {
|
err = json.Unmarshal([]byte(rc.EventJSON), &ghc.Event)
|
||||||
logrus.Error(err)
|
if err != nil {
|
||||||
|
logrus.Errorf("Unable to Unmarshal event '%s': %v", rc.EventJSON, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ghc.EventName == "pull_request" {
|
if ghc.EventName == "pull_request" {
|
||||||
|
|
107
pkg/runner/run_context_test.go
Normal file
107
pkg/runner/run_context_test.go
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
package runner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nektos/act/pkg/model"
|
||||||
|
a "github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus/hooks/test"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRunContext_EvalBool(t *testing.T) {
|
||||||
|
hook := test.NewGlobal()
|
||||||
|
assert := a.New(t)
|
||||||
|
rc := &RunContext{
|
||||||
|
Config: &Config{
|
||||||
|
Workdir: ".",
|
||||||
|
},
|
||||||
|
Env: map[string]string{
|
||||||
|
"TRUE": "true",
|
||||||
|
"FALSE": "false",
|
||||||
|
"SOME_TEXT": "text",
|
||||||
|
},
|
||||||
|
Run: &model.Run{
|
||||||
|
JobID: "job1",
|
||||||
|
Workflow: &model.Workflow{
|
||||||
|
Name: "test-workflow",
|
||||||
|
Jobs: map[string]*model.Job{
|
||||||
|
"job1": {
|
||||||
|
Strategy: &model.Strategy{
|
||||||
|
Matrix: map[string][]interface{}{
|
||||||
|
"os": {"Linux", "Windows"},
|
||||||
|
"foo": {"bar", "baz"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Matrix: map[string]interface{}{
|
||||||
|
"os": "Linux",
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
StepResults: map[string]*stepResult{
|
||||||
|
"id1": {
|
||||||
|
Outputs: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
Success: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rc.ExprEval = rc.NewExpressionEvaluator()
|
||||||
|
|
||||||
|
tables := []struct {
|
||||||
|
in string
|
||||||
|
out bool
|
||||||
|
}{
|
||||||
|
// The basic ones
|
||||||
|
{"true", true},
|
||||||
|
{"false", false},
|
||||||
|
{"1 !== 0", true},
|
||||||
|
{"1 !== 1", false},
|
||||||
|
{"1 == 0", false},
|
||||||
|
{"1 == 1", true},
|
||||||
|
{"1 > 2", false},
|
||||||
|
{"1 < 2", true},
|
||||||
|
{"success()", true},
|
||||||
|
{"failure()", false},
|
||||||
|
// And or
|
||||||
|
{"true && false", false},
|
||||||
|
{"true && 1 < 2", true},
|
||||||
|
{"false || 1 < 2", true},
|
||||||
|
{"false || false", false},
|
||||||
|
// None boolable
|
||||||
|
{"env.SOME_TEXT", true},
|
||||||
|
{"env.UNKNOWN == 'true'", false},
|
||||||
|
{"env.UNKNOWN", false},
|
||||||
|
// Inline expressions
|
||||||
|
{"env.TRUE == 'true'", true},
|
||||||
|
{"env.FALSE == 'true'", false},
|
||||||
|
{"${{env.TRUE == 'true'}}", true},
|
||||||
|
{"${{env.FALSE == 'true'}}", false},
|
||||||
|
{"${{env.FALSE == 'false'}}", true},
|
||||||
|
// All together now
|
||||||
|
{"false || env.TRUE == 'true'", true},
|
||||||
|
{"true || env.FALSE == 'true'", true},
|
||||||
|
{"true && env.TRUE == 'true'", true},
|
||||||
|
{"false && env.TRUE == 'true'", false},
|
||||||
|
{"env.FALSE == 'true' && env.TRUE == 'true'", false},
|
||||||
|
{"env.FALSE == 'true' && true", false},
|
||||||
|
{"${{env.FALSE == 'true'}} && true", false},
|
||||||
|
// Check github context
|
||||||
|
{"github.actor == 'nektos/act'", true},
|
||||||
|
{"github.actor == 'unknown'", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, table := range tables {
|
||||||
|
table := table
|
||||||
|
t.Run(table.in, func(t *testing.T) {
|
||||||
|
defer hook.Reset()
|
||||||
|
b := rc.EvalBool(table.in)
|
||||||
|
|
||||||
|
assert.Equal(table.out, b, table.in)
|
||||||
|
assert.Empty(hook.LastEntry(), table.in)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue