EvalBool and Interpolation fixes (#424)
* A new attempt at Interpolation and EvalBool * One small merge fix * Remove some fmt.Printfs
This commit is contained in:
parent
3f4998a4ed
commit
8ba3306aa4
6 changed files with 762 additions and 117 deletions
82
.github/workflows/test-expressions.yml
vendored
Normal file
82
.github/workflows/test-expressions.yml
vendored
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
|
||||||
|
name: "Test how expressions are handled on Github"
|
||||||
|
on: push
|
||||||
|
|
||||||
|
env:
|
||||||
|
KEYWITHNOTHING: valuewithnothing
|
||||||
|
KEY-WITH-HYPHENS: value-with-hyphens
|
||||||
|
KEY_WITH_UNDERSCORES: value_with_underscores
|
||||||
|
SOMETHING_TRUE: true
|
||||||
|
SOMETHING_FALSE: false
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-espressions:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: €{{ 1 }} to €{{ 2 }} -> ${{1}} to ${{2}} should be equal to 1 to 2
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ env.KEYWITHNOTHING }} -> ${{ env.KEYWITHNOTHING }} should be equal to valuewithnothing
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ env.KEY-WITH-HYPHENS }} -> ${{ env.KEY-WITH-HYPHENS }} should be equal to value-with-hyphens
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ env.KEY_WITH_UNDERSCORES }} -> ${{ env.KEY_WITH_UNDERSCORES }} should be equal to value_with_underscores
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ env.UNKNOWN }} -> ${{ env.UNKNOWN }} should be equal to
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ env.SOMETHING_TRUE }} -> ${{ env.SOMETHING_TRUE }} should be equal to true
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ env.SOMETHING_FALSE }} -> ${{ env.SOMETHING_FALSE }} should be equal to false
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ !env.SOMETHING_TRUE }} -> ${{ !env.SOMETHING_TRUE }} should be equal to false
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ !env.SOMETHING_FALSE }} -> ${{ !env.SOMETHING_FALSE }} should be equal to false
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ !env.SOMETHING_TRUE && true }} -> ${{ !env.SOMETHING_TRUE && true }} should be equal to false
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ !env.SOMETHING_FALSE && true }} -> ${{ !env.SOMETHING_FALSE && true }} should be equal to false
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ env.SOMETHING_TRUE && true }} -> ${{ env.SOMETHING_TRUE && true }} should be equal to true
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ env.SOMETHING_FALSE && true }} -> ${{ env.SOMETHING_FALSE && true }} should be equal to true
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ !env.SOMETHING_TRUE || true }} -> ${{ !env.SOMETHING_TRUE || true }} should be equal to true
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ !env.SOMETHING_FALSE || true }} -> ${{ !env.SOMETHING_FALSE || true }} should be equal to true
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ !env.SOMETHING_TRUE && false }} -> ${{ !env.SOMETHING_TRUE && false }} should be equal to false
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ !env.SOMETHING_FALSE && false }} -> ${{ !env.SOMETHING_FALSE && false }} should be equal to false
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ !env.SOMETHING_TRUE || false }} -> ${{ !env.SOMETHING_TRUE || false }} should be equal to false
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ !env.SOMETHING_FALSE || false }} -> ${{ !env.SOMETHING_FALSE || false }} should be equal to false
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ env.SOMETHING_TRUE || false }} -> ${{ env.SOMETHING_TRUE || false }} should be equal to true
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ env.SOMETHING_FALSE || false }} -> ${{ env.SOMETHING_FALSE || false }} should be equal to false
|
||||||
|
run: echo "Done "
|
||||||
|
|
||||||
|
- name: €{{ env.SOMETHING_FALSE }} && €{{ env.SOMETHING_TRUE }} -> ${{ env.SOMETHING_FALSE }} && ${{ env.SOMETHING_TRUE }} should be equal to false && true
|
||||||
|
run: echo "Done "
|
391
.github/workflows/test-if.yml
vendored
Normal file
391
.github/workflows/test-if.yml
vendored
Normal file
|
@ -0,0 +1,391 @@
|
||||||
|
|
||||||
|
name: "Test what expressions result in true and false on Github"
|
||||||
|
on: push
|
||||||
|
|
||||||
|
env:
|
||||||
|
SOMETHING_TRUE: true
|
||||||
|
SOMETHING_FALSE: false
|
||||||
|
SOME_TEXT: text
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-ifs-and-buts:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: failure()"
|
||||||
|
id: step0
|
||||||
|
if: failure()
|
||||||
|
run: echo "failure() should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: success()"
|
||||||
|
id: step1
|
||||||
|
if: success()
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: success()"
|
||||||
|
if: steps.step1.conclusion == 'skipped'
|
||||||
|
run: echo "success() should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: cancelled()"
|
||||||
|
id: step2
|
||||||
|
if: cancelled()
|
||||||
|
run: echo "cancelled() should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: always()"
|
||||||
|
id: step3
|
||||||
|
if: always()
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: always()"
|
||||||
|
if: steps.step3.conclusion == 'skipped'
|
||||||
|
run: echo "always() should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: true"
|
||||||
|
id: step4
|
||||||
|
if: true
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: true"
|
||||||
|
if: steps.step4.conclusion == 'skipped'
|
||||||
|
run: echo "true should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: false"
|
||||||
|
id: step5
|
||||||
|
if: false
|
||||||
|
run: echo "false should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: 1 != 0"
|
||||||
|
id: step8
|
||||||
|
if: 1 != 0
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: 1 != 0"
|
||||||
|
if: steps.step8.conclusion == 'skipped'
|
||||||
|
run: echo "1 != 0 should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: 1 != 1"
|
||||||
|
id: step9
|
||||||
|
if: 1 != 1
|
||||||
|
run: echo "1 != 1 should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: €{{ 1 != 0 }}"
|
||||||
|
id: step10
|
||||||
|
if: ${{ 1 != 0 }}
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: €{{ 1 != 0 }}"
|
||||||
|
if: steps.step10.conclusion == 'skipped'
|
||||||
|
run: echo "${{ 1 != 0 }} should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: €{{ 1 != 1 }}"
|
||||||
|
id: step11
|
||||||
|
if: ${{ 1 != 1 }}
|
||||||
|
run: echo "${{ 1 != 1 }} should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: 1 == 0"
|
||||||
|
id: step12
|
||||||
|
if: 1 == 0
|
||||||
|
run: echo "1 == 0 should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: 1 == 1"
|
||||||
|
id: step13
|
||||||
|
if: 1 == 1
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: 1 == 1"
|
||||||
|
if: steps.step13.conclusion == 'skipped'
|
||||||
|
run: echo "1 == 1 should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: 1 > 2"
|
||||||
|
id: step14
|
||||||
|
if: 1 > 2
|
||||||
|
run: echo "1 > 2 should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: 1 < 2"
|
||||||
|
id: step15
|
||||||
|
if: 1 < 2
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: 1 < 2"
|
||||||
|
if: steps.step15.conclusion == 'skipped'
|
||||||
|
run: echo "1 < 2 should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: true && false"
|
||||||
|
id: step16
|
||||||
|
if: true && false
|
||||||
|
run: echo "true && false should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: true && 1 < 2"
|
||||||
|
id: step17
|
||||||
|
if: true && 1 < 2
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: true && 1 < 2"
|
||||||
|
if: steps.step17.conclusion == 'skipped'
|
||||||
|
run: echo "true && 1 < 2 should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: false || 1 < 2"
|
||||||
|
id: step18
|
||||||
|
if: false || 1 < 2
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: false || 1 < 2"
|
||||||
|
if: steps.step18.conclusion == 'skipped'
|
||||||
|
run: echo "false || 1 < 2 should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: false || false"
|
||||||
|
id: step19
|
||||||
|
if: false || false
|
||||||
|
run: echo "false || false should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: env.UNKNOWN == 'true'"
|
||||||
|
id: step20
|
||||||
|
if: env.UNKNOWN == 'true'
|
||||||
|
run: echo "env.UNKNOWN == 'true' should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: env.UNKNOWN"
|
||||||
|
id: step21
|
||||||
|
if: env.UNKNOWN
|
||||||
|
run: echo "env.UNKNOWN should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: env.SOME_TEXT"
|
||||||
|
id: step22
|
||||||
|
if: env.SOME_TEXT
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: env.SOME_TEXT"
|
||||||
|
if: steps.step22.conclusion == 'skipped'
|
||||||
|
run: echo "env.SOME_TEXT should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: env.SOME_TEXT == 'text'"
|
||||||
|
id: step23
|
||||||
|
if: env.SOME_TEXT == 'text'
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: env.SOME_TEXT == 'text'"
|
||||||
|
if: steps.step23.conclusion == 'skipped'
|
||||||
|
run: echo "env.SOME_TEXT == 'text' should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: env.SOMETHING_TRUE == 'true'"
|
||||||
|
id: step24
|
||||||
|
if: env.SOMETHING_TRUE == 'true'
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: env.SOMETHING_TRUE == 'true'"
|
||||||
|
if: steps.step24.conclusion == 'skipped'
|
||||||
|
run: echo "env.SOMETHING_TRUE == 'true' should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: env.SOMETHING_FALSE == 'true'"
|
||||||
|
id: step25
|
||||||
|
if: env.SOMETHING_FALSE == 'true'
|
||||||
|
run: echo "env.SOMETHING_FALSE == 'true' should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: env.SOMETHING_TRUE"
|
||||||
|
id: step26
|
||||||
|
if: env.SOMETHING_TRUE
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: env.SOMETHING_TRUE"
|
||||||
|
if: steps.step26.conclusion == 'skipped'
|
||||||
|
run: echo "env.SOMETHING_TRUE should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: env.SOMETHING_FALSE"
|
||||||
|
id: step27
|
||||||
|
if: env.SOMETHING_FALSE
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: env.SOMETHING_FALSE"
|
||||||
|
if: steps.step27.conclusion == 'skipped'
|
||||||
|
run: echo "env.SOMETHING_FALSE should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: €{{ !env.SOMETHING_TRUE }}"
|
||||||
|
id: step30
|
||||||
|
if: ${{ !env.SOMETHING_TRUE }}
|
||||||
|
run: echo "${{ !env.SOMETHING_TRUE }} should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: €{{ !env.SOMETHING_FALSE }}"
|
||||||
|
id: step31
|
||||||
|
if: ${{ !env.SOMETHING_FALSE }}
|
||||||
|
run: echo "${{ !env.SOMETHING_FALSE }} should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: €{{ ! env.SOMETHING_TRUE }}"
|
||||||
|
id: step32
|
||||||
|
if: ${{ ! env.SOMETHING_TRUE }}
|
||||||
|
run: echo "${{ ! env.SOMETHING_TRUE }} should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: €{{ ! env.SOMETHING_FALSE }}"
|
||||||
|
id: step33
|
||||||
|
if: ${{ ! env.SOMETHING_FALSE }}
|
||||||
|
run: echo "${{ ! env.SOMETHING_FALSE }} should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: €{{ env.SOMETHING_TRUE }}"
|
||||||
|
id: step34
|
||||||
|
if: ${{ env.SOMETHING_TRUE }}
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: €{{ env.SOMETHING_TRUE }}"
|
||||||
|
if: steps.step34.conclusion == 'skipped'
|
||||||
|
run: echo "${{ env.SOMETHING_TRUE }} should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: €{{ env.SOMETHING_FALSE }}"
|
||||||
|
id: step35
|
||||||
|
if: ${{ env.SOMETHING_FALSE }}
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: €{{ env.SOMETHING_FALSE }}"
|
||||||
|
if: steps.step35.conclusion == 'skipped'
|
||||||
|
run: echo "${{ env.SOMETHING_FALSE }} should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: €{{ !env.SOMETHING_TRUE }}"
|
||||||
|
id: step36
|
||||||
|
if: ${{ !env.SOMETHING_TRUE }}
|
||||||
|
run: echo "${{ !env.SOMETHING_TRUE }} should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: €{{ !env.SOMETHING_FALSE }}"
|
||||||
|
id: step37
|
||||||
|
if: ${{ !env.SOMETHING_FALSE }}
|
||||||
|
run: echo "${{ !env.SOMETHING_FALSE }} should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: €{{ !env.SOMETHING_TRUE && true }}"
|
||||||
|
id: step38
|
||||||
|
if: ${{ !env.SOMETHING_TRUE && true }}
|
||||||
|
run: echo "${{ !env.SOMETHING_TRUE && true }} should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: €{{ !env.SOMETHING_FALSE && true }}"
|
||||||
|
id: step39
|
||||||
|
if: ${{ !env.SOMETHING_FALSE && true }}
|
||||||
|
run: echo "${{ !env.SOMETHING_FALSE && true }} should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: €{{ !env.SOMETHING_TRUE || true }}"
|
||||||
|
id: step40
|
||||||
|
if: ${{ !env.SOMETHING_TRUE || true }}
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: €{{ !env.SOMETHING_TRUE || true }}"
|
||||||
|
if: steps.step40.conclusion == 'skipped'
|
||||||
|
run: echo "${{ !env.SOMETHING_TRUE || true }} should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: €{{ !env.SOMETHING_FALSE || false }}"
|
||||||
|
id: step41
|
||||||
|
if: ${{ !env.SOMETHING_FALSE || false }}
|
||||||
|
run: echo "${{ !env.SOMETHING_FALSE || false }} should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: €{{ env.SOMETHING_TRUE && true }}"
|
||||||
|
id: step42
|
||||||
|
if: ${{ env.SOMETHING_TRUE && true }}
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: €{{ env.SOMETHING_TRUE && true }}"
|
||||||
|
if: steps.step42.conclusion == 'skipped'
|
||||||
|
run: echo "${{ env.SOMETHING_TRUE && true }} should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: €{{ env.SOMETHING_FALSE || true }}"
|
||||||
|
id: step43
|
||||||
|
if: ${{ env.SOMETHING_FALSE || true }}
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: €{{ env.SOMETHING_FALSE || true }}"
|
||||||
|
if: steps.step43.conclusion == 'skipped'
|
||||||
|
run: echo "${{ env.SOMETHING_FALSE || true }} should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: €{{ env.SOMETHING_FALSE || false }}"
|
||||||
|
id: step44
|
||||||
|
if: ${{ env.SOMETHING_FALSE || false }}
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: €{{ env.SOMETHING_FALSE || false }}"
|
||||||
|
if: steps.step44.conclusion == 'skipped'
|
||||||
|
run: echo "${{ env.SOMETHING_FALSE || false }} should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: €{{ env.SOMETHING_TRUE == 'true' }}"
|
||||||
|
id: step46
|
||||||
|
if: ${{ env.SOMETHING_TRUE == 'true'}}
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: €{{ env.SOMETHING_TRUE == 'true' }}"
|
||||||
|
if: steps.step46.conclusion == 'skipped'
|
||||||
|
run: echo "${{ env.SOMETHING_TRUE == 'true'}} should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: €{{ env.SOMETHING_FALSE == 'true' }}"
|
||||||
|
id: step47
|
||||||
|
if: ${{ env.SOMETHING_FALSE == 'true'}}
|
||||||
|
run: echo "${{ env.SOMETHING_FALSE == 'true'}} should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: €{{ env.SOMETHING_FALSE == 'false' }}"
|
||||||
|
id: step48
|
||||||
|
if: ${{ env.SOMETHING_FALSE == 'false'}}
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: €{{ env.SOMETHING_FALSE == 'false' }}"
|
||||||
|
if: steps.step48.conclusion == 'skipped'
|
||||||
|
run: echo "${{ env.SOMETHING_FALSE == 'false'}} should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: €{{ env.SOMETHING_FALSE }} && €{{ env.SOMETHING_TRUE }}"
|
||||||
|
id: step49
|
||||||
|
if: ${{ env.SOMETHING_FALSE }} && ${{ env.SOMETHING_TRUE }}
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: €{{ env.SOMETHING_FALSE }} && €{{ env.SOMETHING_TRUE }}"
|
||||||
|
if: steps.step49.conclusion == 'skipped'
|
||||||
|
run: echo "${{ env.SOMETHING_FALSE }} && ${{ env.SOMETHING_TRUE }} should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: false || env.SOMETHING_TRUE == 'true'"
|
||||||
|
id: step50
|
||||||
|
if: false || env.SOMETHING_TRUE == 'true'
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: false || env.SOMETHING_TRUE == 'true'"
|
||||||
|
if: steps.step50.conclusion == 'skipped'
|
||||||
|
run: echo "false || env.SOMETHING_TRUE == 'true' should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: true || env.SOMETHING_FALSE == 'true'"
|
||||||
|
id: step51
|
||||||
|
if: true || env.SOMETHING_FALSE == 'true'
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: true || env.SOMETHING_FALSE == 'true'"
|
||||||
|
if: steps.step51.conclusion == 'skipped'
|
||||||
|
run: echo "true || env.SOMETHING_FALSE == 'true' should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: true && env.SOMETHING_TRUE == 'true'"
|
||||||
|
id: step52
|
||||||
|
if: true && env.SOMETHING_TRUE == 'true'
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: true && env.SOMETHING_TRUE == 'true'"
|
||||||
|
if: steps.step52.conclusion == 'skipped'
|
||||||
|
run: echo "true && env.SOMETHING_TRUE == 'true' should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: false && env.SOMETHING_TRUE == 'true'"
|
||||||
|
id: step53
|
||||||
|
if: false && env.SOMETHING_TRUE == 'true'
|
||||||
|
run: echo "false && env.SOMETHING_TRUE == 'true' should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: env.SOMETHING_FALSE == 'true' && env.SOMETHING_TRUE == 'true'"
|
||||||
|
id: step54
|
||||||
|
if: env.SOMETHING_FALSE == 'true' && env.SOMETHING_TRUE == 'true'
|
||||||
|
run: echo "env.SOMETHING_FALSE == 'true' && env.SOMETHING_TRUE == 'true' should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "❌ I should not run, expr: env.SOMETHING_FALSE == 'true' && true"
|
||||||
|
id: step55
|
||||||
|
if: env.SOMETHING_FALSE == 'true' && true
|
||||||
|
run: echo "env.SOMETHING_FALSE == 'true' && true should be false, but was evaluated to true;" exit 1;
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: €{{ env.SOMETHING_FALSE == 'true' }} && true"
|
||||||
|
id: step56
|
||||||
|
if: ${{ env.SOMETHING_FALSE == 'true' }} && true
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: €{{ env.SOMETHING_FALSE == 'true' }} && true"
|
||||||
|
if: steps.step56.conclusion == 'skipped'
|
||||||
|
run: echo "${{ env.SOMETHING_FALSE == 'true' }} && true should have been true, but wasn't"
|
||||||
|
|
||||||
|
- name: "✅ I should run, expr: true && €{{ env.SOMETHING_FALSE == 'true' }}"
|
||||||
|
id: step57
|
||||||
|
if: true && ${{ env.SOMETHING_FALSE == 'true' }}
|
||||||
|
run: echo OK
|
||||||
|
|
||||||
|
- name: "Double checking expr: true && €{{ env.SOMETHING_FALSE == 'true' }}"
|
||||||
|
if: steps.step57.conclusion == 'skipped'
|
||||||
|
run: echo "true && ${{ env.SOMETHING_FALSE == 'true' }} should have been true, but wasn't"
|
|
@ -16,11 +16,12 @@ import (
|
||||||
"gopkg.in/godo.v2/glob"
|
"gopkg.in/godo.v2/glob"
|
||||||
)
|
)
|
||||||
|
|
||||||
var contextPattern, expressionPattern *regexp.Regexp
|
var contextPattern, expressionPattern, operatorPattern *regexp.Regexp
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
contextPattern = regexp.MustCompile(`^(\w+(?:\[.+])*)(?:\.([\w-]+))?(.*)$`)
|
contextPattern = regexp.MustCompile(`^([^.]*(?:\[.+])*)(?:\.([\w-]+))?(.*)$`)
|
||||||
expressionPattern = regexp.MustCompile(`\${{\s*(.+?)\s*}}`)
|
expressionPattern = regexp.MustCompile(`\${{\s*(.+?)\s*}}`)
|
||||||
|
operatorPattern = regexp.MustCompile("^[!=><|&]+$")
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewExpressionEvaluator creates a new evaluator
|
// NewExpressionEvaluator creates a new evaluator
|
||||||
|
@ -49,8 +50,9 @@ func (sc *StepContext) NewExpressionEvaluator() ExpressionEvaluator {
|
||||||
|
|
||||||
// ExpressionEvaluator is the interface for evaluating expressions
|
// ExpressionEvaluator is the interface for evaluating expressions
|
||||||
type ExpressionEvaluator interface {
|
type ExpressionEvaluator interface {
|
||||||
Evaluate(string) (string, error)
|
Evaluate(string) (string, bool, error)
|
||||||
Interpolate(string) string
|
Interpolate(string) string
|
||||||
|
InterpolateWithStringCheck(string) (string, bool)
|
||||||
Rewrite(string) string
|
Rewrite(string) string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +60,7 @@ type expressionEvaluator struct {
|
||||||
vm *otto.Otto
|
vm *otto.Otto
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ee *expressionEvaluator) Evaluate(in string) (string, error) {
|
func (ee *expressionEvaluator) Evaluate(in string) (string, bool, error) {
|
||||||
re := ee.Rewrite(in)
|
re := ee.Rewrite(in)
|
||||||
if re != in {
|
if re != in {
|
||||||
log.Debugf("Evaluating '%s' instead of '%s'", re, in)
|
log.Debugf("Evaluating '%s' instead of '%s'", re, in)
|
||||||
|
@ -66,32 +68,40 @@ func (ee *expressionEvaluator) Evaluate(in string) (string, error) {
|
||||||
|
|
||||||
val, err := ee.vm.Run(re)
|
val, err := ee.vm.Run(re)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", false, err
|
||||||
}
|
}
|
||||||
if val.IsNull() || val.IsUndefined() {
|
if val.IsNull() || val.IsUndefined() {
|
||||||
return "", nil
|
return "", false, nil
|
||||||
}
|
}
|
||||||
return val.ToString()
|
valAsString, err := val.ToString()
|
||||||
|
if err != nil {
|
||||||
|
return "", false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return valAsString, val.IsString(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ee *expressionEvaluator) Interpolate(in string) string {
|
func (ee *expressionEvaluator) Interpolate(in string) string{
|
||||||
|
interpolated, _ := ee.InterpolateWithStringCheck(in)
|
||||||
|
return interpolated
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ee *expressionEvaluator) InterpolateWithStringCheck(in string) (string, bool) {
|
||||||
errList := make([]error, 0)
|
errList := make([]error, 0)
|
||||||
|
|
||||||
out := in
|
out := in
|
||||||
|
isString := false
|
||||||
for {
|
for {
|
||||||
out = expressionPattern.ReplaceAllStringFunc(in, func(match string) string {
|
out = expressionPattern.ReplaceAllStringFunc(in, func(match string) string {
|
||||||
// Extract and trim the actual expression inside ${{...}} delimiters
|
// Extract and trim the actual expression inside ${{...}} delimiters
|
||||||
expression := expressionPattern.ReplaceAllString(match, "$1")
|
expression := expressionPattern.ReplaceAllString(match, "$1")
|
||||||
|
|
||||||
// Evaluate the expression and retrieve errors if any
|
// Evaluate the expression and retrieve errors if any
|
||||||
negatedExpression := strings.HasPrefix(expression, "!")
|
evaluated, evaluatedIsString, err := ee.Evaluate(expression)
|
||||||
evaluated, err := ee.Evaluate(strings.ReplaceAll(expression, "!", ""))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errList = append(errList, err)
|
errList = append(errList, err)
|
||||||
}
|
}
|
||||||
if negatedExpression {
|
isString = evaluatedIsString
|
||||||
evaluated = fmt.Sprintf("!%s", evaluated)
|
|
||||||
}
|
|
||||||
return evaluated
|
return evaluated
|
||||||
})
|
})
|
||||||
if len(errList) > 0 {
|
if len(errList) > 0 {
|
||||||
|
@ -104,7 +114,7 @@ func (ee *expressionEvaluator) Interpolate(in string) string {
|
||||||
}
|
}
|
||||||
in = out
|
in = out
|
||||||
}
|
}
|
||||||
return out
|
return out, isString
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rewrite tries to transform any javascript property accessor into its bracket notation.
|
// Rewrite tries to transform any javascript property accessor into its bracket notation.
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package runner
|
package runner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nektos/act/pkg/model"
|
"github.com/nektos/act/pkg/model"
|
||||||
|
@ -105,7 +108,7 @@ func TestEvaluate(t *testing.T) {
|
||||||
table := table
|
table := table
|
||||||
t.Run(table.in, func(t *testing.T) {
|
t.Run(table.in, func(t *testing.T) {
|
||||||
assert := a.New(t)
|
assert := a.New(t)
|
||||||
out, err := ee.Evaluate(table.in)
|
out, _, err := ee.Evaluate(table.in)
|
||||||
if table.errMesg == "" {
|
if table.errMesg == "" {
|
||||||
assert.NoError(err, table.in)
|
assert.NoError(err, table.in)
|
||||||
assert.Equal(table.out, out, table.in)
|
assert.Equal(table.out, out, table.in)
|
||||||
|
@ -126,8 +129,8 @@ func TestInterpolate(t *testing.T) {
|
||||||
"KEYWITHNOTHING": "valuewithnothing",
|
"KEYWITHNOTHING": "valuewithnothing",
|
||||||
"KEY-WITH-HYPHENS": "value-with-hyphens",
|
"KEY-WITH-HYPHENS": "value-with-hyphens",
|
||||||
"KEY_WITH_UNDERSCORES": "value_with_underscores",
|
"KEY_WITH_UNDERSCORES": "value_with_underscores",
|
||||||
"TRUE": "true",
|
"SOMETHING_TRUE": "true",
|
||||||
"FALSE": "false",
|
"SOMETHING_FALSE": "false",
|
||||||
},
|
},
|
||||||
Run: &model.Run{
|
Run: &model.Run{
|
||||||
JobID: "job1",
|
JobID: "job1",
|
||||||
|
@ -149,12 +152,26 @@ func TestInterpolate(t *testing.T) {
|
||||||
{" ${{ env.KEY-WITH-HYPHENS }} ", " value-with-hyphens "},
|
{" ${{ env.KEY-WITH-HYPHENS }} ", " value-with-hyphens "},
|
||||||
{" ${{ env.KEY_WITH_UNDERSCORES }} ", " value_with_underscores "},
|
{" ${{ env.KEY_WITH_UNDERSCORES }} ", " value_with_underscores "},
|
||||||
{"${{ env.UNKNOWN }}", ""},
|
{"${{ env.UNKNOWN }}", ""},
|
||||||
{"${{ env.TRUE }}", "true"},
|
{"${{ env.SOMETHING_TRUE }}", "true"},
|
||||||
{"${{ env.FALSE }}", "false"},
|
{"${{ env.SOMETHING_FALSE }}", "false"},
|
||||||
{"${{ !env.TRUE }}", "!true"},
|
{"${{ !env.SOMETHING_TRUE }}", "false"},
|
||||||
{"${{ !env.FALSE }}", "!false"},
|
{"${{ !env.SOMETHING_FALSE }}", "false"},
|
||||||
|
{"${{ !env.SOMETHING_TRUE && true }}", "false"},
|
||||||
|
{"${{ !env.SOMETHING_FALSE && true }}", "false"},
|
||||||
|
{"${{ env.SOMETHING_TRUE && true }}", "true"},
|
||||||
|
{"${{ env.SOMETHING_FALSE && true }}", "true"},
|
||||||
|
{"${{ !env.SOMETHING_TRUE || true }}", "true"},
|
||||||
|
{"${{ !env.SOMETHING_FALSE || true }}", "true"},
|
||||||
|
{"${{ !env.SOMETHING_TRUE && false }}", "false"},
|
||||||
|
{"${{ !env.SOMETHING_FALSE && false }}", "false"},
|
||||||
|
{"${{ !env.SOMETHING_TRUE || false }}", "false"},
|
||||||
|
{"${{ !env.SOMETHING_FALSE || false }}", "false"},
|
||||||
|
{"${{ env.SOMETHING_TRUE || false }}", "true"},
|
||||||
|
{"${{ env.SOMETHING_FALSE || false }}", "false"},
|
||||||
|
{"${{ env.SOMETHING_FALSE }} && ${{ env.SOMETHING_TRUE }}", "false && true"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateTestExpressionWorkflow(t, tables, rc)
|
||||||
for _, table := range tables {
|
for _, table := range tables {
|
||||||
table := table
|
table := table
|
||||||
t.Run(table.in, func(t *testing.T) {
|
t.Run(table.in, func(t *testing.T) {
|
||||||
|
@ -165,6 +182,56 @@ func TestInterpolate(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateTestExpressionWorkflow(t *testing.T, tables []struct {
|
||||||
|
in string
|
||||||
|
out string
|
||||||
|
//wantErr bool
|
||||||
|
}, rc *RunContext) {
|
||||||
|
|
||||||
|
var envs string
|
||||||
|
for k, v := range rc.Env {
|
||||||
|
envs += fmt.Sprintf(
|
||||||
|
` %s: %s
|
||||||
|
`, k, v)
|
||||||
|
}
|
||||||
|
workflow := fmt.Sprintf(`
|
||||||
|
name: "Test how expressions are handled on Github"
|
||||||
|
on: push
|
||||||
|
|
||||||
|
env:
|
||||||
|
%s
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-espressions:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
`, envs)
|
||||||
|
for _, table := range tables {
|
||||||
|
expressionPattern = regexp.MustCompile(`\${{\s*(.+?)\s*}}`)
|
||||||
|
|
||||||
|
expr := expressionPattern.ReplaceAllStringFunc(table.in, func(match string) string {
|
||||||
|
return fmt.Sprintf("€{{ %s }}", expressionPattern.ReplaceAllString(match, "$1"))
|
||||||
|
})
|
||||||
|
name := fmt.Sprintf(`%s -> %s should be equal to %s`,expr, table.in, table.out)
|
||||||
|
echo := `run: echo "Done "`
|
||||||
|
workflow += fmt.Sprintf(`
|
||||||
|
- name: %s
|
||||||
|
%s
|
||||||
|
`, name, echo)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Create("../../.github/workflows/test-expressions.yml")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = file.WriteString(workflow)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func TestRewrite(t *testing.T) {
|
func TestRewrite(t *testing.T) {
|
||||||
rc := &RunContext{
|
rc := &RunContext{
|
||||||
Config: &Config{},
|
Config: &Config{},
|
||||||
|
|
|
@ -3,6 +3,7 @@ package runner
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -199,13 +200,19 @@ func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor {
|
||||||
_ = sc.setupEnv()(ctx)
|
_ = sc.setupEnv()(ctx)
|
||||||
rc.ExprEval = sc.NewExpressionEvaluator()
|
rc.ExprEval = sc.NewExpressionEvaluator()
|
||||||
|
|
||||||
if !rc.EvalBool(sc.Step.If) {
|
runStep, err := rc.EvalBool(sc.Step.If)
|
||||||
|
if err != nil {
|
||||||
|
common.Logger(ctx).Errorf(" \u274C Error in if: expression - %s", sc.Step)
|
||||||
|
rc.StepResults[rc.CurrentStep].Success = false
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !runStep {
|
||||||
log.Debugf("Skipping step '%s' due to '%s'", sc.Step.String(), sc.Step.If)
|
log.Debugf("Skipping step '%s' due to '%s'", sc.Step.String(), sc.Step.If)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
common.Logger(ctx).Infof("\u2B50 Run %s", sc.Step)
|
common.Logger(ctx).Infof("\u2B50 Run %s", sc.Step)
|
||||||
err := sc.Executor()(ctx)
|
err = sc.Executor()(ctx)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
common.Logger(ctx).Infof(" \u2705 Success - %s", sc.Step)
|
common.Logger(ctx).Infof(" \u2705 Success - %s", sc.Step)
|
||||||
} else {
|
} else {
|
||||||
|
@ -238,7 +245,12 @@ 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()
|
||||||
l := common.Logger(ctx)
|
l := common.Logger(ctx)
|
||||||
if !rc.EvalBool(job.If) {
|
runJob, err := rc.EvalBool(job.If)
|
||||||
|
if err != nil {
|
||||||
|
common.Logger(ctx).Errorf(" \u274C Error in if: expression - %s", job.Name)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !runJob {
|
||||||
l.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
|
||||||
}
|
}
|
||||||
|
@ -252,64 +264,59 @@ 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, error) {
|
||||||
|
if strings.HasPrefix(strings.TrimSpace(expr), "!") {
|
||||||
|
return false, errors.New("expressions starting with ! must be wrapped in ${{ }}")
|
||||||
|
}
|
||||||
if expr != "" {
|
if expr != "" {
|
||||||
interpolated := rc.ExprEval.Interpolate(expr)
|
splitPattern := regexp.MustCompile(fmt.Sprintf(`%s|%s|\S+`, expressionPattern.String(), operatorPattern.String()))
|
||||||
parts := strings.Split(interpolated, " ")
|
|
||||||
|
|
||||||
operatorRe := regexp.MustCompile("^[!=><|&]+$")
|
parts := splitPattern.FindAllString(expr, -1)
|
||||||
var evaluatedParts []string
|
var evaluatedParts []string
|
||||||
for _, part := range parts {
|
for i, part := range parts {
|
||||||
part = fixNegation(part)
|
if operatorPattern.MatchString(part) {
|
||||||
|
|
||||||
if operatorRe.MatchString(part) {
|
|
||||||
evaluatedParts = append(evaluatedParts, part)
|
evaluatedParts = append(evaluatedParts, part)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(part, "!") {
|
interpolatedPart, isString := rc.ExprEval.InterpolateWithStringCheck(part)
|
||||||
withoutNegation, err := rc.ExprEval.Evaluate(strings.ReplaceAll(part, "!", ""))
|
|
||||||
if err != nil {
|
// This peculiar transformation has to be done because the Github parser
|
||||||
return false
|
// treats false retured from contexts as a string, not a boolean.
|
||||||
}
|
// Hence env.SOMETHING will be evaluated to true in an if: expression
|
||||||
evaluatedParts = append(evaluatedParts, fmt.Sprintf("!%s", withoutNegation))
|
// regardless if SOMETHING is set to false, true or any other string.
|
||||||
continue
|
// It also handles some other weirdness that I found by trial and error.
|
||||||
|
if (expressionPattern.MatchString(part) && // it is an expression
|
||||||
|
!strings.Contains(part, "!")) && // but it's not negated
|
||||||
|
interpolatedPart == "false" && // and the interpolated string is false
|
||||||
|
(isString || previousOrNextPartIsAnOperator(i, parts)) { // and it's of type string or has an logical operator before or after
|
||||||
|
|
||||||
|
interpolatedPart = fmt.Sprintf("'%s'", interpolatedPart) // then we have to quote the false expression
|
||||||
}
|
}
|
||||||
// strings with / are misinterpreted as a file path by otto
|
|
||||||
if strings.Contains(part, "/") {
|
evaluatedParts = append(evaluatedParts, interpolatedPart)
|
||||||
evaluatedParts = append(evaluatedParts, part)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
evaluatedPart, err := rc.ExprEval.Evaluate(part)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Unable to evaluate part: %s: %v", part, err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
evaluatedPart = fixQuotingForStrings(evaluatedPart)
|
|
||||||
evaluatedParts = append(evaluatedParts, evaluatedPart)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolExpr := fmt.Sprintf("Boolean(%s)", strings.Join(evaluatedParts, " "))
|
joined := strings.Join(evaluatedParts, " ")
|
||||||
v, err := rc.ExprEval.Evaluate(boolExpr)
|
v, _, err := rc.ExprEval.Evaluate(fmt.Sprintf("Boolean(%s)", joined))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false, nil
|
||||||
}
|
}
|
||||||
log.Debugf("expression '%s' evaluated to '%s'", expr, v)
|
log.Debugf("expression '%s' evaluated to '%s'", expr, v)
|
||||||
return v == "true"
|
return v == "true", nil
|
||||||
}
|
}
|
||||||
return true
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fixNegation(s string) string {
|
func previousOrNextPartIsAnOperator(i int, parts []string) bool {
|
||||||
re := regexp.MustCompile("![ ]+")
|
operator := false
|
||||||
return re.ReplaceAllString(s, "!")
|
if i > 0 {
|
||||||
}
|
operator = operatorPattern.MatchString(parts[i-1])
|
||||||
|
|
||||||
func fixQuotingForStrings(s string) string {
|
|
||||||
if s == "true" || s == "false" {
|
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("'%s'", s)
|
if i+1 < len(parts) {
|
||||||
|
operator = operator || operatorPattern.MatchString(parts[i+1])
|
||||||
|
}
|
||||||
|
return operator
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeMaps(maps ...map[string]string) map[string]string {
|
func mergeMaps(maps ...map[string]string) map[string]string {
|
||||||
|
|
|
@ -4,6 +4,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/nektos/act/pkg/model"
|
"github.com/nektos/act/pkg/model"
|
||||||
a "github.com/stretchr/testify/assert"
|
a "github.com/stretchr/testify/assert"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus/hooks/test"
|
"github.com/sirupsen/logrus/hooks/test"
|
||||||
|
@ -16,9 +19,9 @@ func TestRunContext_EvalBool(t *testing.T) {
|
||||||
Workdir: ".",
|
Workdir: ".",
|
||||||
},
|
},
|
||||||
Env: map[string]string{
|
Env: map[string]string{
|
||||||
"TRUE": "true",
|
"SOMETHING_TRUE": "true",
|
||||||
"FALSE": "false",
|
"SOMETHING_FALSE": "false",
|
||||||
"SOME_TEXT": "text",
|
"SOME_TEXT": "text",
|
||||||
},
|
},
|
||||||
Run: &model.Run{
|
Run: &model.Run{
|
||||||
JobID: "job1",
|
JobID: "job1",
|
||||||
|
@ -52,73 +55,158 @@ func TestRunContext_EvalBool(t *testing.T) {
|
||||||
rc.ExprEval = rc.NewExpressionEvaluator()
|
rc.ExprEval = rc.NewExpressionEvaluator()
|
||||||
|
|
||||||
tables := []struct {
|
tables := []struct {
|
||||||
in string
|
in string
|
||||||
out bool
|
out bool
|
||||||
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
// The basic ones
|
// The basic ones
|
||||||
{"true", true},
|
{in: "failure()", out: false},
|
||||||
{"false", false},
|
{in: "success()", out: true},
|
||||||
{"1 != 0", true},
|
{in: "cancelled()", out: false},
|
||||||
{"1 != 1", false},
|
{in: "always()", out: true},
|
||||||
{"1 == 0", false},
|
{in: "true", out: true},
|
||||||
{"1 == 1", true},
|
{in: "false", out: false},
|
||||||
{"1 > 2", false},
|
{in: "!true", wantErr: true},
|
||||||
{"1 < 2", true},
|
{in: "!false", wantErr: true},
|
||||||
{"success()", true},
|
{in: "1 != 0", out: true},
|
||||||
{"failure()", false},
|
{in: "1 != 1", out: false},
|
||||||
{"always()", true},
|
{in: "${{ 1 != 0 }}", out: true},
|
||||||
{"failure()", false},
|
{in: "${{ 1 != 1 }}", out: false},
|
||||||
|
{in: "1 == 0", out: false},
|
||||||
|
{in: "1 == 1", out: true},
|
||||||
|
{in: "1 > 2", out: false},
|
||||||
|
{in: "1 < 2", out: true},
|
||||||
// And or
|
// And or
|
||||||
{"true && false", false},
|
{in: "true && false", out: false},
|
||||||
{"true && 1 < 2", true},
|
{in: "true && 1 < 2", out: true},
|
||||||
{"false || 1 < 2", true},
|
{in: "false || 1 < 2", out: true},
|
||||||
{"false || false", false},
|
{in: "false || false", out: false},
|
||||||
// None boolable
|
// None boolable
|
||||||
{"env.UNKNOWN == 'true'", false},
|
{in: "env.UNKNOWN == 'true'", out: false},
|
||||||
{"env.UNKNOWN", false},
|
{in: "env.UNKNOWN", out: false},
|
||||||
// Inline expressions
|
// Inline expressions
|
||||||
{"env.SOME_TEXT", true}, // this is because Boolean('text') is true in Javascript
|
{in: "env.SOME_TEXT", out: true}, // this is because Boolean('text') is true in Javascript
|
||||||
{"env.SOME_TEXT == 'text'", true},
|
{in: "env.SOME_TEXT == 'text'", out: true},
|
||||||
{"env.TRUE == 'true'", true},
|
{in: "env.SOMETHING_TRUE == 'true'", out: true},
|
||||||
{"env.FALSE == 'true'", false},
|
{in: "env.SOMETHING_FALSE == 'true'", out: false},
|
||||||
{"env.TRUE", true},
|
{in: "env.SOMETHING_TRUE", out: true},
|
||||||
{"env.FALSE", false},
|
{in: "env.SOMETHING_FALSE", out: true}, // this is because Boolean('text') is true in Javascript
|
||||||
{"!env.TRUE", false},
|
{in: "!env.SOMETHING_TRUE", wantErr: true},
|
||||||
{"!env.FALSE", true},
|
{in: "!env.SOMETHING_FALSE", wantErr: true},
|
||||||
{"${{ env.TRUE }}", true},
|
{in: "${{ !env.SOMETHING_TRUE }}", out: false},
|
||||||
{"${{ env.FALSE }}", false},
|
{in: "${{ !env.SOMETHING_FALSE }}", out: false},
|
||||||
{"${{ !env.TRUE }}", false},
|
{in: "${{ ! env.SOMETHING_TRUE }}", out: false},
|
||||||
{"${{ !env.FALSE }}", true},
|
{in: "${{ ! env.SOMETHING_FALSE }}", out: false},
|
||||||
{"!env.TRUE && true", false},
|
{in: "${{ env.SOMETHING_TRUE }}", out: true},
|
||||||
{"!env.FALSE && true", true},
|
{in: "${{ env.SOMETHING_FALSE }}", out: true},
|
||||||
{"!env.TRUE || true", true},
|
{in: "${{ !env.SOMETHING_TRUE }}", out: false},
|
||||||
{"!env.FALSE || false", true},
|
{in: "${{ !env.SOMETHING_FALSE }}", out: false},
|
||||||
{"${{env.TRUE == 'true'}}", true},
|
{in: "${{ !env.SOMETHING_TRUE && true }}", out: false},
|
||||||
{"${{env.FALSE == 'true'}}", false},
|
{in: "${{ !env.SOMETHING_FALSE && true }}", out: false},
|
||||||
{"${{env.FALSE == 'false'}}", true},
|
{in: "${{ !env.SOMETHING_TRUE || true }}", out: true},
|
||||||
|
{in: "${{ !env.SOMETHING_FALSE || false }}", out: false},
|
||||||
|
{in: "${{ env.SOMETHING_TRUE && true }}", out: true},
|
||||||
|
{in: "${{ env.SOMETHING_FALSE || true }}", out: true},
|
||||||
|
{in: "${{ env.SOMETHING_FALSE || false }}", out: true},
|
||||||
|
{in: "!env.SOMETHING_TRUE || true", wantErr: true},
|
||||||
|
{in: "${{ env.SOMETHING_TRUE == 'true'}}", out: true},
|
||||||
|
{in: "${{ env.SOMETHING_FALSE == 'true'}}", out: false},
|
||||||
|
{in: "${{ env.SOMETHING_FALSE == 'false'}}", out: true},
|
||||||
|
{in: "${{ env.SOMETHING_FALSE }} && ${{ env.SOMETHING_TRUE }}", out: true},
|
||||||
|
|
||||||
// All together now
|
// All together now
|
||||||
{"false || env.TRUE == 'true'", true},
|
{in: "false || env.SOMETHING_TRUE == 'true'", out: true},
|
||||||
{"true || env.FALSE == 'true'", true},
|
{in: "true || env.SOMETHING_FALSE == 'true'", out: true},
|
||||||
{"true && env.TRUE == 'true'", true},
|
{in: "true && env.SOMETHING_TRUE == 'true'", out: true},
|
||||||
{"false && env.TRUE == 'true'", false},
|
{in: "false && env.SOMETHING_TRUE == 'true'", out: false},
|
||||||
{"env.FALSE == 'true' && env.TRUE == 'true'", false},
|
{in: "env.SOMETHING_FALSE == 'true' && env.SOMETHING_TRUE == 'true'", out: false},
|
||||||
{"env.FALSE == 'true' && true", false},
|
{in: "env.SOMETHING_FALSE == 'true' && true", out: false},
|
||||||
{"${{env.FALSE == 'true'}} && true", false},
|
{in: "${{ env.SOMETHING_FALSE == 'true' }} && true", out: true},
|
||||||
|
{in: "true && ${{ env.SOMETHING_FALSE == 'true' }}", out: true},
|
||||||
// Check github context
|
// Check github context
|
||||||
{"github.actor == 'nektos/act'", true},
|
{in: "github.actor == 'nektos/act'", out: true},
|
||||||
{"github.actor == 'unknown'", false},
|
{in: "github.actor == 'unknown'", out: false},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateTestIfWorkflow(t, tables, rc)
|
||||||
for _, table := range tables {
|
for _, table := range tables {
|
||||||
table := table
|
table := table
|
||||||
t.Run(table.in, func(t *testing.T) {
|
t.Run(table.in, func(t *testing.T) {
|
||||||
assert := a.New(t)
|
assert := a.New(t)
|
||||||
defer hook.Reset()
|
defer hook.Reset()
|
||||||
b := rc.EvalBool(table.in)
|
b, err := rc.EvalBool(table.in)
|
||||||
|
if table.wantErr {
|
||||||
|
assert.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
assert.Equal(table.out, b, fmt.Sprintf("Expected %s to be %v, was %v", table.in, table.out, b))
|
assert.Equal(table.out, b, fmt.Sprintf("Expected %s to be %v, was %v", table.in, table.out, b))
|
||||||
assert.Empty(hook.LastEntry(), table.in)
|
assert.Empty(hook.LastEntry(), table.in)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateTestIfWorkflow(t *testing.T, tables []struct {
|
||||||
|
in string
|
||||||
|
out bool
|
||||||
|
wantErr bool
|
||||||
|
}, rc *RunContext) {
|
||||||
|
|
||||||
|
var envs string
|
||||||
|
for k, v := range rc.Env {
|
||||||
|
envs += fmt.Sprintf(
|
||||||
|
` %s: %s
|
||||||
|
`, k, v)
|
||||||
|
}
|
||||||
|
workflow := fmt.Sprintf(`
|
||||||
|
name: "Test what expressions result in true and false on Github"
|
||||||
|
on: push
|
||||||
|
|
||||||
|
env:
|
||||||
|
%s
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-ifs-and-buts:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
`, envs)
|
||||||
|
|
||||||
|
for i, table := range tables {
|
||||||
|
if table.wantErr || strings.HasPrefix(table.in, "github.actor") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
expressionPattern = regexp.MustCompile(`\${{\s*(.+?)\s*}}`)
|
||||||
|
|
||||||
|
expr := expressionPattern.ReplaceAllStringFunc(table.in, func(match string) string {
|
||||||
|
return fmt.Sprintf("€{{ %s }}", expressionPattern.ReplaceAllString(match, "$1"))
|
||||||
|
})
|
||||||
|
echo := fmt.Sprintf(`run: echo "%s should be false, but was evaluated to true;" exit 1;`, table.in)
|
||||||
|
name := fmt.Sprintf(`"❌ I should not run, expr: %s"`, expr)
|
||||||
|
if table.out {
|
||||||
|
echo = `run: echo OK`
|
||||||
|
name = fmt.Sprintf(`"✅ I should run, expr: %s"`, expr)
|
||||||
|
}
|
||||||
|
workflow += fmt.Sprintf(`
|
||||||
|
- name: %s
|
||||||
|
id: step%d
|
||||||
|
if: %s
|
||||||
|
%s
|
||||||
|
`, name, i, table.in, echo)
|
||||||
|
if table.out {
|
||||||
|
workflow += fmt.Sprintf(`
|
||||||
|
- name: "Double checking expr: %s"
|
||||||
|
if: steps.step%d.conclusion == 'skipped'
|
||||||
|
run: echo "%s should have been true, but wasn't"
|
||||||
|
`, expr, i, table.in)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Create("../../.github/workflows/test-if.yml")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = file.WriteString(workflow)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue