From 9aff6e82dddf203e3eaa526b123da4211aa7064e Mon Sep 17 00:00:00 2001 From: Rory Abraham Date: Mon, 30 Aug 2021 14:10:49 -0700 Subject: [PATCH] Properly JSON-decode outputs --- .github/workflows/README.md | 37 ++++++++++++++++++++++++ .github/workflows/cherryPick.yml | 2 +- .github/workflows/finishReleaseCycle.yml | 2 +- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 5e9d5d88f782..be6efb918fb6 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -1,5 +1,42 @@ # [New Expensify](https://new.expensify.com) GitHub Workflows +## Important tip for creating GitHub Workflows +All inputs and outputs to GitHub Actions and any data passed between jobs or workflows is JSON-encoded (AKA, strings). Keep this in mind whenever writing GitHub workflows – you may need to JSON-decode variables to access them accurately. Here's an example of a common way to misuse GitHub Actions data: + +```yaml +name: CI +on: pull_request +jobs: + validate: + runs-on: ubuntu-latest + steps: + - id: myTrueAction + uses: Expensify/my-action-outputs-true@main + + - id: myFalseAction + uses: Expensify/my-action-outputs-false@main + + # This correctly outputs `true`, but it's a string + - run: echo ${{ steps.myTrueAction.outputs.isTrue }} + + # This correctly outputs `false`, but it's a string + - run: echo ${{ steps.myFalseAction.outputs.isFalse }} + + # This correctly outputs `true`, and it's a boolean + - run: echo ${{ true == true }} + + # This correctly outputs `false`, and it's a boolean. + - run: echo ${{ true == false }} + + # Watch out! This seems like it should be true, but it's false! + # What we have here is `'false' || true`, and since the first half is a string the expression resolves to 'false' + - run: echo ${{ steps.myFalseAction.outputs.isFalse || github.actor == 'roryabraham' }} +``` + +We've found that the best way to avoid this pitfall is to always wrap any reference to the output of an action in a call to `fromJSON`. This should force it to resolve to the expected type. + +**Note:** Action inputs and outputs aren't the only thing that's JSON-encoded! Any data passed between jobs via a `needs` parameter is also JSON-encoded! + ## Security Rules 🔐 1. Do **not** use `pull_request_target` trigger unless an external fork needs access to secrets, or a _write_ `GITHUB_TOKEN`. 1. Do **not ever** write a `pull_request_target` trigger with an explicit PR checkout, e.g. using `actions/checkout@v2`. This is [discussed further here](https://securitylab.github.com/research/github-actions-preventing-pwn-requests) diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml index 0ab0188ceda0..14f20f707d2b 100644 --- a/.github/workflows/cherryPick.yml +++ b/.github/workflows/cherryPick.yml @@ -15,7 +15,7 @@ jobs: validateActor: runs-on: ubuntu-latest outputs: - IS_DEPLOYER: ${{ steps.isUserDeployer.outputs.isTeamMember || github.actor == 'OSBotify' }} + IS_DEPLOYER: ${{ fromJSON(steps.isUserDeployer.outputs.isTeamMember) || github.actor == 'OSBotify' }} steps: - id: isUserDeployer uses: tspascoal/get-user-teams-membership@baf2e6adf4c3b897bd65a7e3184305c165aec872 diff --git a/.github/workflows/finishReleaseCycle.yml b/.github/workflows/finishReleaseCycle.yml index d272f1c3de0d..24288e44bb12 100644 --- a/.github/workflows/finishReleaseCycle.yml +++ b/.github/workflows/finishReleaseCycle.yml @@ -11,7 +11,7 @@ jobs: if: contains(github.event.issue.labels.*.name, 'StagingDeployCash') outputs: - isValid: ${{ steps.validateActor.outputs.isTeamMember && steps.checkDeployBlockers.outputs.HAS_DEPLOY_BLOCKERS == 'false' }} + isValid: ${{ fromJSON(steps.validateActor.outputs.isTeamMember) && !fromJSON(steps.checkDeployBlockers.outputs.HAS_DEPLOY_BLOCKERS) }} steps: - name: Validate actor is deployer