ci(workflows): hand values to the shell as data, not as script text
Build / Build-and-ng-test (pull_request) Successful in 6m40s
Lighthouse Checks / lighthouse (pull_request) Successful in 10m8s
Build / Build-and-test-development (pull_request) Successful in 33m40s

Three values reached `run:` blocks through `${{ }}`, so the runner pasted them
into the script before the shell ever saw it - which makes the value part of the
code rather than an argument to it.

- `BASE="${{ github.base_ref }}"` (build.yaml, lighthouse.yaml) - a ref name in
  the script.
- `echo ${{ secrets.SHEET_PWD }} | gpg --passphrase-fd 0` (all four workflows) -
  a passphrase in the script. A quote in it ends the quoting, and a command
  substitution in it runs.
- two `replace-in-files --replacement=...${{ secrets.CYPRESS_* }}...` lines
  (build.yaml, release.yaml) - the same, inside a replacement string.

Each now arrives as a step `env:` value and is expanded as `"$VAR"`, so the shell
only ever sees data. Checked the replacement lines side by side with the old form:
identical for an ordinary value, and the new form keeps a value containing a space
intact where the old one word-splits it, and survives a value containing a single
quote where the old one is a shell syntax error.

The passphrase pipe also moves from `echo` to `printf '%s\n'`, so the byte the
decryption sees does not depend on how the shell's `echo` treats backslashes.
This commit is contained in:
dc committed 2026-11-04 09:55:22 +00:00
1 parent 514e326be8
commit f0e3117540
4 files changed
+37 -13

No files matched your search

+14 -5
View File
@@ -77,10 +77,12 @@ jobs:
NPMRC: ${{ secrets.NPMRC}}
- name: Install dependencies
env:
SHEET_PWD: ${{ secrets.SHEET_PWD }}
run: |
cd client
# Decrypt and Install sheet
echo "${{ secrets.SHEET_PWD }}" | \
printf '%s\n' "$SHEET_PWD" | \
gpg --batch --yes --passphrase-fd 0 \
--output ./libraries/sheet-crypto.tgz \
--decrypt ./libraries/sheet-crypto.tgz.gpg
@@ -205,9 +207,11 @@ jobs:
# is "slow" is safe; one whose worst case is "silently tests nothing" is
# not, and one whose worst case is "every pull request goes red because a
# fetch failed" is not acceptable either.
env:
BASE_REF: ${{ github.base_ref }}
run: |
set -euo pipefail
BASE="${{ github.base_ref }}"
BASE="${BASE_REF:-}"
if [ -z "$BASE" ]; then
echo "no base ref (not a pull request) - running the suite"
exit 0
@@ -288,10 +292,12 @@ jobs:
- name: Install dependencies
if: steps.changes.outputs.client != 'false'
env:
SHEET_PWD: ${{ secrets.SHEET_PWD }}
run: |
cd client
# Decrypt and Install sheet
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
printf '%s\n' "$SHEET_PWD" | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
# dependency hooks stay off (client/.npmrc); the client's own
# generator runs explicitly, because the app imports the file it makes
npm ci --ignore-scripts
@@ -388,6 +394,9 @@ jobs:
# runner starved the app under test and every failure in that run was a
# timeout, not an assertion.
timeout-minutes: 50
env:
CYPRESS_USERNAME: ${{ secrets.CYPRESS_USERNAME_SASJS }}
CYPRESS_PWD: ${{ secrets.CYPRESS_PWD_SASJS }}
run: |
cd ./client
# The spec list lives in one place, and an empty list is a broken
@@ -399,8 +408,8 @@ jobs:
fi
echo "specs: $SPECS"
mv ./cypress.env.example.json ./cypress.env.json
replace-in-files --regex='"username".*' --replacement='"username":"'${{ secrets.CYPRESS_USERNAME_SASJS }}'",' ./cypress.env.json
replace-in-files --regex='"password".*' --replacement='"password":"'${{ secrets.CYPRESS_PWD_SASJS }}'" ' ./cypress.env.json
replace-in-files --regex='"username".*' --replacement='"username":"'"$CYPRESS_USERNAME"'",' ./cypress.env.json
replace-in-files --regex='"password".*' --replacement='"password":"'"$CYPRESS_PWD"'" ' ./cypress.env.json
cat ./cypress.env.json
npm run postinstall
# Prepare index.html to SASJS local
+6 -2
View File
@@ -29,9 +29,11 @@ jobs:
# As in build.yaml, the gate is `!= 'false'` and every failure exits 0
# without writing client=false, so a decision that cannot be made runs
# the audit rather than failing the job.
env:
BASE_REF: ${{ github.base_ref }}
run: |
set -euo pipefail
BASE="${{ github.base_ref }}"
BASE="${BASE_REF:-}"
if [ -z "$BASE" ]; then
echo "no base ref (not a pull request) - running the audit"
exit 0
@@ -148,10 +150,12 @@ jobs:
- name: Install npm dependencies
if: steps.changes.outputs.client != 'false'
env:
SHEET_PWD: ${{ secrets.SHEET_PWD }}
run: |
cd client
# Decrypt and Install sheet
echo "${{ secrets.SHEET_PWD }}" | \
printf '%s\n' "$SHEET_PWD" | \
gpg --batch --yes --passphrase-fd 0 \
--output ./libraries/sheet-crypto.tgz \
--decrypt ./libraries/sheet-crypto.tgz.gpg
+14 -5
View File
@@ -50,10 +50,12 @@ jobs:
CYPRESS_CREDS: ${{ secrets.CYPRESS_CREDS }}
- name: Install dependencies
env:
SHEET_PWD: ${{ secrets.SHEET_PWD }}
run: |
cd client
# Decrypt and Install sheet
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
printf '%s\n' "$SHEET_PWD" | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
npm ci
# client install scripts are off (client/.npmrc); the generator that
# creates src/environments/version.ts runs explicitly
@@ -131,10 +133,12 @@ jobs:
CYPRESS_CREDS: ${{ secrets.CYPRESS_CREDS }}
- name: Install dependencies
env:
SHEET_PWD: ${{ secrets.SHEET_PWD }}
run: |
cd client
# Decrypt and Install sheet
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
printf '%s\n' "$SHEET_PWD" | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
npm ci
# client install scripts are off (client/.npmrc); the generator that
# creates src/environments/version.ts runs explicitly
@@ -213,11 +217,14 @@ jobs:
sasjs request services/admin/makedata -t server-ci -d deploy/makedata.json -o ./makedata_out.json || true
- name: Prepare and run frontend and cypress
env:
CYPRESS_USERNAME: ${{ secrets.CYPRESS_USERNAME_SASJS }}
CYPRESS_PWD: ${{ secrets.CYPRESS_PWD_SASJS }}
run: |
cd ./client
mv ./cypress.env.example.json ./cypress.env.json
replace-in-files --regex='"username".*' --replacement='"username":"'${{ secrets.CYPRESS_USERNAME_SASJS }}'",' ./cypress.env.json
replace-in-files --regex='"password".*' --replacement='"password":"'${{ secrets.CYPRESS_PWD_SASJS }}'" ' ./cypress.env.json
replace-in-files --regex='"username".*' --replacement='"username":"'"$CYPRESS_USERNAME"'",' ./cypress.env.json
replace-in-files --regex='"password".*' --replacement='"password":"'"$CYPRESS_PWD"'" ' ./cypress.env.json
cat ./cypress.env.json
npm run postinstall
# cypress downloads its binary in a dependency install hook, which the
@@ -295,10 +302,12 @@ jobs:
- name: Frontend Preliminary Build
description: We want to prevent creating empty release if frontend fails
env:
SHEET_PWD: ${{ secrets.SHEET_PWD }}
run: |
cd client
# Decrypt and Install sheet
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
printf '%s\n' "$SHEET_PWD" | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
npm ci
# client install scripts are off (client/.npmrc); the generator that
# creates src/environments/version.ts runs explicitly
+3 -1
View File
@@ -57,10 +57,12 @@ jobs:
NPMRC: ${{ secrets.NPMRC}}
- name: Install client dependencies
env:
SHEET_PWD: ${{ secrets.SHEET_PWD }}
run: |
cd client
# Decrypt and Install sheet
echo ${{ secrets.SHEET_PWD }} | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
printf '%s\n' "$SHEET_PWD" | gpg --batch --yes --passphrase-fd 0 ./libraries/sheet-crypto.tgz.gpg
npm ci
# client install scripts are off (client/.npmrc); the generator that
# creates src/environments/version.ts runs explicitly