diff --git a/.github/scripts/validateActionsAndWorkflows.sh b/.github/scripts/validateActionsAndWorkflows.sh
index 0ff92289b524..e22308c735fd 100755
--- a/.github/scripts/validateActionsAndWorkflows.sh
+++ b/.github/scripts/validateActionsAndWorkflows.sh
@@ -1,6 +1,6 @@
 #!/bin/bash
-#
-# Validates the Github Actions and workflows using the json schemas provided by https://www.schemastore.org/json/
+
+echo 'Validates the Github Actions and workflows using the json schemas provided by (https://www.schemastore.org/json/)'
 
 # Track exit codes separately so we can run a full validation, report errors, and exit with the correct code
 declare EXIT_CODE=0
@@ -14,7 +14,30 @@ curl https://json.schemastore.org/github-workflow.json --output ./tempSchemas/gi
 find ./actions/ -type f -name "*.yml" -print0 | xargs -0 -I file ajv -s ./tempSchemas/github-action.json -d file --strict=false || EXIT_CODE=1
 find ./workflows/ -type f -name "*.yml" -print0 | xargs -0 -I file ajv -s ./tempSchemas/github-workflow.json -d file --strict=false || EXIT_CODE=1
 
+if (( $EXIT_CODE != 0 )); then
+  exit $EXIT_CODE
+fi
+
 # Cleanup after ourselves and delete the schemas
 rm -rf ./tempSchemas
 
+echo
+echo 'Lint Github Actions via actionlint (https://github.com/rhysd/actionlint)'
+
+# If we are running this on a non-CI machine (e.g. locally), install shellcheck
+if [[ -z "${CI}" && -z "$(command -v shellcheck)" ]]; then
+  if [[ "$OSTYPE" != 'darwin'* || -z "$(command -v brew)" ]]; then
+    echo 'This script requires shellcheck to be installed. Please install it and try again'
+    exit 1
+  fi
+
+  brew install shellcheck
+fi
+
+bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
+./actionlint -color || EXIT_CODE=1
+
+# Cleanup after ourselves and delete actionlint
+rm -rf ./actionlint
+
 exit $EXIT_CODE
diff --git a/.github/workflows/cherryPick.yml b/.github/workflows/cherryPick.yml
index c7f6101d5a4f..c97eb147b95e 100644
--- a/.github/workflows/cherryPick.yml
+++ b/.github/workflows/cherryPick.yml
@@ -98,12 +98,14 @@ jobs:
           PULL_REQUEST_NUMBER: ${{ github.event.inputs.PULL_REQUEST_NUMBER }}
 
       - name: Save correct NEW_VERSION to env
+        env:
+          NEW_VERSION: ${{ github.event.inputs.NEW_VERSION }}
         run: |
-          if [ -z ${{ github.event.inputs.NEW_VERSION }} ]; then
-            echo "NEW_VERSION=${{ needs.createNewVersion.outputs.NEW_VERSION }}" >> $GITHUB_ENV
+          if [ -z "$NEW_VERSION" ]; then
+            echo "NEW_VERSION=${{ needs.createNewVersion.outputs.NEW_VERSION }}" >> "$GITHUB_ENV"
             echo "New version is ${{ env.NEW_VERSION }}"
           else
-            echo "NEW_VERSION=${{ github.event.inputs.NEW_VERSION }}" >> $GITHUB_ENV
+            echo "NEW_VERSION=${{ github.event.inputs.NEW_VERSION }}" >> "$GITHUB_ENV"
             echo "New version is ${{ env.NEW_VERSION }}"
           fi;
 
diff --git a/.github/workflows/createNewVersion.yml b/.github/workflows/createNewVersion.yml
index 98f42211840c..5e442002ea4e 100644
--- a/.github/workflows/createNewVersion.yml
+++ b/.github/workflows/createNewVersion.yml
@@ -24,7 +24,7 @@ jobs:
           poll-interval-seconds: 10
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-      
+
       - name: Decrypt GPG key
         run: cd .github/workflows && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output OSBotify-private-key.asc OSBotify-private-key.asc.gpg
         env:
@@ -46,7 +46,7 @@ jobs:
         run: |
           git checkout -b version-${{ github.event.inputs.SEMVER_LEVEL }}-${{ github.sha }}
           git push --set-upstream origin version-${{ github.event.inputs.SEMVER_LEVEL }}-${{ github.sha }}
-          echo "VERSION_BRANCH=version-${{ github.event.inputs.SEMVER_LEVEL }}-${{ github.sha }}" >> $GITHUB_ENV
+          echo "VERSION_BRANCH=version-${{ github.event.inputs.SEMVER_LEVEL }}-${{ github.sha }}" >> "$GITHUB_ENV"
 
       - name: Generate version
         id: bumpVersion
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index f6ff6ab7ab0b..61ddc4d059e6 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -33,7 +33,7 @@ jobs:
         with:
           ref: staging
           token: ${{ secrets.OS_BOTIFY_TOKEN }}
-      
+
       - name: Decrypt Botify GPG key
         run: cd .github/workflows && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output OSBotify-private-key.asc OSBotify-private-key.asc.gpg
         env:
@@ -51,7 +51,7 @@ jobs:
 
 
       - name: Tag version
-        run: git tag $(npm run print-version --silent)
+        run: git tag "$(npm run print-version --silent)"
 
       - name: 🚀 Push tags to trigger staging deploy 🚀
         run: git push --tags
@@ -66,7 +66,7 @@ jobs:
         with:
           fetch-depth: 0
           token: ${{ secrets.OS_BOTIFY_TOKEN }}
-      
+
       - name: Decrypt Botify GPG key
         run: cd .github/workflows && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output OSBotify-private-key.asc OSBotify-private-key.asc.gpg
         env:
@@ -87,7 +87,7 @@ jobs:
         run: git checkout production
 
       - name: Get current app version
-        run: echo "PRODUCTION_VERSION=$(npm run print-version --silent)" >> $GITHUB_ENV
+        run: echo "PRODUCTION_VERSION=$(npm run print-version --silent)" >> "$GITHUB_ENV"
 
       - name: Get Release Pull Request List
         id: getReleasePRList
diff --git a/.github/workflows/deployBlocker.yml b/.github/workflows/deployBlocker.yml
index 46f54ac2ae60..015ed9a98007 100644
--- a/.github/workflows/deployBlocker.yml
+++ b/.github/workflows/deployBlocker.yml
@@ -18,10 +18,12 @@ jobs:
 
       - name: Get URL, title, & number of new deploy blocker (issue)
         if: ${{ github.event_name == 'issues' }}
+        env:
+          TITLE: ${{ github.event.issue.title }}
         run: |
-          echo "DEPLOY_BLOCKER_URL=${{ github.event.issue.html_url }}" >> $GITHUB_ENV
-          echo "DEPLOY_BLOCKER_NUMBER=${{ github.event.issue.number }}" >> $GITHUB_ENV
-          echo "DEPLOY_BLOCKER_TITLE=$(sed -e "s/'/'\\\\''/g; s/\`/\\\\\`/g; 1s/^/'/; \$s/\$/'/" <<< "'${{ github.event.issue.title }}'")" >> $GITHUB_ENV
+          { echo "DEPLOY_BLOCKER_URL=${{ github.event.issue.html_url }}";
+          echo "DEPLOY_BLOCKER_NUMBER=${{ github.event.issue.number }}";
+          echo "DEPLOY_BLOCKER_TITLE=$(sed -e "s/'/'\\\\''/g; s/\`/\\\\\`/g; 1s/^/'/; \$s/\$/'/" <<< "$TITLE")";} >> "$GITHUB_ENV"
 
       - name: Update StagingDeployCash with new deploy blocker
         uses: Expensify/App/.github/actions/createOrUpdateStagingDeploy@main
diff --git a/.github/workflows/finishReleaseCycle.yml b/.github/workflows/finishReleaseCycle.yml
index eb9b251a0504..fe0eb8aff88a 100644
--- a/.github/workflows/finishReleaseCycle.yml
+++ b/.github/workflows/finishReleaseCycle.yml
@@ -105,7 +105,7 @@ jobs:
         run: |
           git checkout staging
           git pull origin staging
-          echo "NEW_VERSION=$(npm run print-version --silent)" >> $GITHUB_ENV
+          echo "NEW_VERSION=$(npm run print-version --silent)" >> "$GITHUB_ENV"
           echo "New version is ${{ env.NEW_VERSION }}"
 
         # Create a local git tag so that GitUtils.getPullRequestsMergedBetween can use `git log` to generate a
diff --git a/.github/workflows/lockDeploys.yml b/.github/workflows/lockDeploys.yml
index 0ed24ced5589..f1b8d44d97fe 100644
--- a/.github/workflows/lockDeploys.yml
+++ b/.github/workflows/lockDeploys.yml
@@ -24,7 +24,7 @@ jobs:
       - name: Comment in StagingDeployCash to give Applause the 🟢 to begin QA
         run: |
           gh issue comment \
-          $(gh issue list --label StagingDeployCash --json number --jq '.[0].number') \
+          "$(gh issue list --label StagingDeployCash --json number --jq '.[0].number')" \
           --body ":rocket: All staging deploys are complete, @Expensify/applauseleads please begin QA on version https://github.com/Expensify/App/releases/tag/$(< package.json jq -r .version) :rocket:"
         env:
           GITHUB_TOKEN: ${{ secrets.OS_BOTIFY_TOKEN }}
diff --git a/.github/workflows/platformDeploy.yml b/.github/workflows/platformDeploy.yml
index 598c8fe2e602..d5b66fecafb7 100644
--- a/.github/workflows/platformDeploy.yml
+++ b/.github/workflows/platformDeploy.yml
@@ -77,7 +77,7 @@ jobs:
           LARGE_SECRET_PASSPHRASE: ${{ secrets.LARGE_SECRET_PASSPHRASE }}
 
       - name: Set version in ENV
-        run: echo "VERSION_CODE=$(grep -o 'versionCode\s\+[0-9]\+' android/app/build.gradle | awk '{ print $2 }')" >> $GITHUB_ENV
+        run: echo "VERSION_CODE=$(grep -o 'versionCode\s\+[0-9]\+' android/app/build.gradle | awk '{ print $2 }')" >> "$GITHUB_ENV"
 
       - name: Run Fastlane beta
         if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }}
@@ -243,7 +243,7 @@ jobs:
 
       - name: Set iOS version in ENV
         if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }}
-        run: echo "IOS_VERSION=$(echo '${{ github.event.release.tag_name }}' | tr '-' '.')" >> $GITHUB_ENV
+        run: echo "IOS_VERSION=$(echo '${{ github.event.release.tag_name }}' | tr '-' '.')" >> "$GITHUB_ENV"
 
       - name: Run Fastlane for App Store release
         if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }}
@@ -329,11 +329,11 @@ jobs:
 
       - name: Deploy production to S3
         if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }}
-        run: aws s3 cp --recursive --acl public-read $GITHUB_WORKSPACE/dist s3://expensify-cash/
+        run: aws s3 cp --recursive --acl public-read "$GITHUB_WORKSPACE"/dist s3://expensify-cash/
 
       - name: Deploy staging to S3
         if: ${{ !fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }}
-        run: aws s3 cp --recursive --acl public-read $GITHUB_WORKSPACE/dist s3://staging-expensify-cash/
+        run: aws s3 cp --recursive --acl public-read "$GITHUB_WORKSPACE"/dist s3://staging-expensify-cash/
 
       - name: Purge production Cloudflare cache
         if: ${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) }}
@@ -387,7 +387,7 @@ jobs:
           node-version: '14.x'
 
       - name: Set version
-        run: echo "VERSION=$(npm run print-version --silent)" >> $GITHUB_ENV
+        run: echo "VERSION=$(npm run print-version --silent)" >> "$GITHUB_ENV"
 
       - name: Get Release Pull Request List
         id: getReleasePRList
diff --git a/.github/workflows/preDeploy.yml b/.github/workflows/preDeploy.yml
index 12e1402b7e4d..8d5f1c1a5eb5 100644
--- a/.github/workflows/preDeploy.yml
+++ b/.github/workflows/preDeploy.yml
@@ -122,7 +122,7 @@ jobs:
       - name: Pull main to get the new version
         run: |
           git pull origin main
-          echo "NEW_VERSION=$(npm run print-version --silent)" >> $GITHUB_ENV
+          echo "NEW_VERSION=$(npm run print-version --silent)" >> "$GITHUB_ENV"
           echo "New version is ${{ env.NEW_VERSION }}"
 
       - name: Update staging branch from main
@@ -195,7 +195,7 @@ jobs:
           github_token: ${{ secrets.GITHUB_TOKEN }}
 
       - name: Get PR count for ${{ steps.getMergedPullRequest.outputs.author }}
-        run: echo "PR_COUNT=$(gh pr list --author ${{ steps.getMergedPullRequest.outputs.author }} --state any | grep -c '')" >> $GITHUB_ENV
+        run: echo "PR_COUNT=$(gh pr list --author ${{ steps.getMergedPullRequest.outputs.author }} --state any | grep -c '')" >> "$GITHUB_ENV"
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
diff --git a/.github/workflows/updateProtectedBranch.yml b/.github/workflows/updateProtectedBranch.yml
index fb925c524fa2..61f5aeada49b 100644
--- a/.github/workflows/updateProtectedBranch.yml
+++ b/.github/workflows/updateProtectedBranch.yml
@@ -35,11 +35,11 @@ jobs:
       - name: Set source branch
         run: |
           if [[ ${{ github.event.inputs.TARGET_BRANCH }} == 'staging' ]]; then
-            echo "SOURCE_BRANCH=main" >> $GITHUB_ENV
+            echo "SOURCE_BRANCH=main" >> "$GITHUB_ENV"
           elif [[ ${{ github.event.inputs.TARGET_BRANCH }} == 'production' ]]; then
-            echo "SOURCE_BRANCH=staging" >> $GITHUB_ENV
+            echo "SOURCE_BRANCH=staging" >> "$GITHUB_ENV"
           else
-            echo "SOURCE_BRANCH=${{ github.event.inputs.SOURCE_BRANCH }}" >> $GITHUB_ENV
+            echo "SOURCE_BRANCH=${{ github.event.inputs.SOURCE_BRANCH }}" >> "$GITHUB_ENV"
           fi
 
       # Version: 2.3.4
@@ -52,7 +52,7 @@ jobs:
         run: git checkout ${{ env.SOURCE_BRANCH }}
 
       - name: Set New Version
-        run: echo "NEW_VERSION=$(npm run print-version --silent)" >> $GITHUB_ENV
+        run: echo "NEW_VERSION=$(npm run print-version --silent)" >> "$GITHUB_ENV"
 
       - name: Decrypt OSBotify GPG key
         run: cd .github/workflows && gpg --quiet --batch --yes --decrypt --passphrase="$LARGE_SECRET_PASSPHRASE" --output OSBotify-private-key.asc OSBotify-private-key.asc.gpg
@@ -77,7 +77,7 @@ jobs:
           git checkout -b update-${{ github.event.inputs.TARGET_BRANCH }}-from-${{ env.SOURCE_BRANCH }}
           git merge -Xtheirs ${{ env.SOURCE_BRANCH }}
           git push --set-upstream origin update-${{ github.event.inputs.TARGET_BRANCH }}-from-${{ env.SOURCE_BRANCH }}
-          echo "SOURCE_BRANCH=update-${{ github.event.inputs.TARGET_BRANCH }}-from-${{ env.SOURCE_BRANCH }}" >> $GITHUB_ENV
+          echo "SOURCE_BRANCH=update-${{ github.event.inputs.TARGET_BRANCH }}-from-${{ env.SOURCE_BRANCH }}" >> "$GITHUB_ENV"
 
       - name: Create Pull Request
         id: createPullRequest
@@ -125,8 +125,8 @@ jobs:
           github_token: ${{ secrets.OS_BOTIFY_TOKEN }}
           number: ${{ steps.createPullRequest.outputs.pr_number }}
           body: |
-            :bell: @Expensify/mobile-deployers :bell: - The Update Protected Branch workflow has failed because this PR was not mergable. 
-            If you are the deployer this week, please resolve the error and merge this PR to continue the deploy process. 
+            :bell: @Expensify/mobile-deployers :bell: - The Update Protected Branch workflow has failed because this PR was not mergable.
+            If you are the deployer this week, please resolve the error and merge this PR to continue the deploy process.
 
       - name: Fail workflow if PR is not mergeable
         if: ${{ steps.isPullRequestMergeable.outputs.IS_MERGEABLE == 'false' }}
