Review findings on the change-detection steps and the shared build artefact. - A failed fetch or diff in "Detect client changes" used to fail the job: the fail-safe only covered an output that was absent, not a step that errored. Both workflows now exit 0 without writing client=false on any failure, so an undecidable change set runs the suite instead of blocking the pull request. - .gitea/scripts/ joins the change set in both workflows: it carries the spec list (cypress-specs.sh) and the runner gate (check-runner-memory.sh), so a change to what the suite runs, or to the gate that lets it run, must run it. - A skipped job now says so in a named step and a notice annotation, rather than reporting success from a list of greyed-out steps. - The dist upload sets if-no-files-found: error, so an empty artefact fails the producing job instead of surfacing later as "webSourcePath ... doesn't exist". - lighthouserc.js records the cost of numberOfRuns: 1 - no median, so one slow navigation is a re-run rather than a fix.
201 lines
8.1 KiB
YAML
201 lines
8.1 KiB
YAML
name: Lighthouse Checks
|
|
run-name: Running Lighthouse Performance and Accessibility Checks on Pull Request
|
|
on: [pull_request]
|
|
|
|
env:
|
|
# the server the mocked services and app tests run against - bump deliberately
|
|
SASJS_SERVER_VERSION: '3.6.2'
|
|
NODE_VERSION: '24.15.0'
|
|
|
|
jobs:
|
|
lighthouse:
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Detect client changes
|
|
id: changes
|
|
# The audit measures the client, served from the mocked estate. A pull
|
|
# request that changes none of what it reads cannot move a score, so it
|
|
# does not need nineteen minutes of runner (measured median) to prove it.
|
|
# 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.
|
|
run: |
|
|
set -euo pipefail
|
|
BASE="${{ github.base_ref }}"
|
|
if [ -z "$BASE" ]; then
|
|
echo "no base ref (not a pull request) - running the audit"
|
|
exit 0
|
|
fi
|
|
|
|
if ! git fetch --no-tags --depth=1 origin "+refs/heads/${BASE}:refs/remotes/origin/${BASE}"; then
|
|
echo "::warning::could not fetch ${BASE} - running the audit rather than deciding"
|
|
exit 0
|
|
fi
|
|
|
|
if ! CHANGED="$(git diff --name-only "refs/remotes/origin/${BASE}" HEAD)"; then
|
|
echo "::warning::could not diff against ${BASE} - running the audit rather than deciding"
|
|
exit 0
|
|
fi
|
|
|
|
# .gitea/scripts/ carries the runner-memory gate this job runs before
|
|
# it starts, so a change there has to be exercised too.
|
|
if printf '%s\n' "$CHANGED" | grep -qE '^(client/|sas/mocks/|\.gitea/scripts/|package-lock\.json|\.gitea/workflows/lighthouse\.yaml)'; then
|
|
echo "client=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "client=false" >> "$GITHUB_OUTPUT"
|
|
echo "::notice::Lighthouse audit skipped - nothing under client/, sas/mocks/ or .gitea/scripts/ changed"
|
|
printf 'Skipping the audit - nothing it reads changed:\n%s\n' "$CHANGED"
|
|
fi
|
|
|
|
- name: Lighthouse audit not run - no client changes
|
|
if: steps.changes.outputs.client == 'false'
|
|
run: |
|
|
echo "No client, sas/mocks or .gitea/scripts change in this pull request - the audit was skipped by design."
|
|
echo "The detection step above lists the files it saw, and warns instead if it could not decide (in which case the audit runs)."
|
|
|
|
- name: Check runner size
|
|
if: steps.changes.outputs.client != 'false'
|
|
# This job runs a production build, then a SASjs server, a dev server
|
|
# and Chrome. An undersized runner dies inside the build with exitcode
|
|
# 137; fail here in seconds instead - see the script.
|
|
run: bash .gitea/scripts/check-runner-memory.sh
|
|
|
|
- name: Use Node.js ${{ env.NODE_VERSION }}
|
|
if: steps.changes.outputs.client != 'false'
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION }}
|
|
|
|
- name: Install Google Chrome
|
|
if: steps.changes.outputs.client != 'false'
|
|
run: |
|
|
apt-get update
|
|
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
|
apt install -y ./google-chrome*.deb
|
|
|
|
- name: Install global packages
|
|
if: steps.changes.outputs.client != 'false'
|
|
run: npm i -g pm2 @sasjs/cli wait-on
|
|
|
|
- name: Cache the npm downloads
|
|
if: steps.changes.outputs.client != 'false'
|
|
# The server is installed from npm at a pinned version, so the tarball is
|
|
# immutable and the key never needs invalidating by hand: a hit skips
|
|
# the 10.7MB download, a miss costs exactly what the zip cost before.
|
|
# continue-on-error, because a runner without cache support should still
|
|
# run the pipeline - this step only ever saves time.
|
|
continue-on-error: true
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ~/.npm
|
|
key: npm-sasjs-server-${{ env.SASJS_SERVER_VERSION }}
|
|
|
|
- name: Setup and start SASjs server
|
|
if: steps.changes.outputs.client != 'false'
|
|
run: |
|
|
touch .env
|
|
echo RUN_TIMES=js >> .env
|
|
echo NODE_PATH=node >> .env
|
|
echo CORS=enable >> .env
|
|
echo WHITELIST=http://localhost:4200 >> .env
|
|
echo HTTP_PORT=5000 >> .env
|
|
# The server refuses to run as root unless RUN_AS names an account
|
|
# (a security change in the recent releases). This job runs as root
|
|
# in a throwaway container, so opt in explicitly.
|
|
echo RUN_AS=root >> .env
|
|
cat .env
|
|
pm2 start npx --name sasjs-server -- --yes @sasjs/server@${{ env.SASJS_SERVER_VERSION }}
|
|
# `pm2 start` returns before the app is listening, and the next step
|
|
# hits :5000 immediately. Wait for the server to answer, and dump its
|
|
# log if it never does.
|
|
for i in $(seq 1 60); do
|
|
if curl -sf -o /dev/null http://localhost:5000/; then
|
|
echo "SASjs server answered on attempt ${i}"
|
|
exit 0
|
|
fi
|
|
sleep 2
|
|
done
|
|
echo "SASjs server did not come up within 120s"
|
|
pm2 list
|
|
pm2 logs sasjs-server --lines 100 --nostream
|
|
exit 1
|
|
|
|
- name: Write .npmrc file
|
|
if: steps.changes.outputs.client != 'false'
|
|
run: |
|
|
# the registry secret stays outside the repo: client/.npmrc is
|
|
# tracked now, and one git add -A would commit a live token
|
|
echo "$NPMRC" > "$RUNNER_TEMP/npmrc"
|
|
echo "NPM_CONFIG_USERCONFIG=$RUNNER_TEMP/npmrc" >> "$GITHUB_ENV"
|
|
shell: bash
|
|
env:
|
|
NPMRC: ${{ secrets.NPMRC}}
|
|
|
|
- name: Install npm dependencies
|
|
if: steps.changes.outputs.client != 'false'
|
|
run: |
|
|
cd client
|
|
# Decrypt and Install sheet
|
|
echo "${{ secrets.SHEET_PWD }}" | \
|
|
gpg --batch --yes --passphrase-fd 0 \
|
|
--output ./libraries/sheet-crypto.tgz \
|
|
--decrypt ./libraries/sheet-crypto.tgz.gpg
|
|
npm ci
|
|
# client install scripts are off (client/.npmrc); the generator that
|
|
# creates src/environments/version.ts runs explicitly
|
|
npm run postinstall
|
|
npm install -g replace-in-files-cli
|
|
|
|
- name: Update appLoc in index.html
|
|
if: steps.changes.outputs.client != 'false'
|
|
run: |
|
|
cd client
|
|
replace-in-files --regex='appLoc=".*?"' --replacement='appLoc="/proj/sasjs/genesis-mocks"' ./src/index.html
|
|
|
|
- name: Build Frontend
|
|
if: steps.changes.outputs.client != 'false'
|
|
run: |
|
|
cd client
|
|
npm run build
|
|
|
|
- name: Deploy JS mocked services and frontend to the local SASjs Server instance
|
|
if: steps.changes.outputs.client != 'false'
|
|
run: |
|
|
cd sas/mocks
|
|
npm ci
|
|
sasjs cbd -t server-ci
|
|
|
|
- name: Start frontend server
|
|
if: steps.changes.outputs.client != 'false'
|
|
run: |
|
|
cd client
|
|
npx ng serve --host 0.0.0.0 --port 4200 &
|
|
wait-on http://localhost:4200
|
|
|
|
- name: Run Lighthouse CI
|
|
if: steps.changes.outputs.client != 'false'
|
|
# One run per URL: the assertions aggregate by median, so three runs
|
|
# bought a steadier median at three times the navigations (13.6 minutes
|
|
# of this job, measured). A pull request gate only needs to know the
|
|
# page still clears the thresholds it cleared before; a regression that
|
|
# only shows in a median of three is a flake to chase separately.
|
|
run: |
|
|
cd client
|
|
npx lhci autorun
|
|
|
|
- name: Lighthouse Result Artifacts
|
|
if: always() && steps.changes.outputs.client != 'false'
|
|
uses: actions/upload-artifact@v3
|
|
with:
|
|
name: Lighthouse results
|
|
path: client/lighthouse-reports
|
|
include-hidden-files: true
|
|
|
|
- name: Remove registry config
|
|
# written earlier in this job; nothing deletes it on the way out
|
|
if: always()
|
|
run: rm -f "$RUNNER_TEMP/npmrc"
|