The job installed the same lockfile the build job had just installed and paid the downloads again (92s), and npx cypress install fetched its binary on every run. Both now come from the cache the build job already fills, with the Cypress binary keyed on the lockfile.
560 lines
27 KiB
YAML
560 lines
27 KiB
YAML
name: Build
|
|
run-name: Running Lint Check and Licence checker on Pull Request
|
|
on: [pull_request]
|
|
|
|
# One run per pull request: a newer push supersedes the one in flight for the
|
|
# older head. Both runs test the same thing over the same twenty-six minute
|
|
# suite, the older head can no longer be what gets merged, and this workflow
|
|
# shares two runners with every other pull request in the repo - so leaving the
|
|
# old run queued only delays the head that matters. The group is the base build
|
|
# only: the release workflow is never cancelled by something it does not
|
|
# supersede.
|
|
concurrency:
|
|
group: build-pr-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
env:
|
|
# the server the mocked services and app tests run against - bump deliberately.
|
|
# 3.12.0 is the floor: /ddc-host carries `timing=early` from there, which
|
|
# client/cypress/e2e/va-ddc-host.cy.ts needs. 3.12.2 also carries the
|
|
# packaged-executable fix for the same page, so the version the tests run
|
|
# against is the one that ships.
|
|
#
|
|
# 3.14.0 ships @sasjs/lint 5.0.0 (the studio reports OS commands) and is the
|
|
# release the estate runs. It still declares @sasjs/utils ^4.7.0, so the
|
|
# envelope *formats* (SYSCC as text, DATE7, TIME5, E8601DT26.6 from utils 4.8)
|
|
# are only guaranteed once the next server release raises that floor - a fresh
|
|
# install resolves 4.8.0 today, a cached tree need not. Bump again when it lands.
|
|
#
|
|
# 3.13.0 is the one that matters for licensing: @sasjs/utils 4.7 reports
|
|
# SYSSITE, which %webout takes from &syssite. Before it the prelude wrote an
|
|
# empty string, so the client - which routes expired and wrong-organisation
|
|
# licence keys on that value - never reached those pages on the mock estate.
|
|
#
|
|
# 3.12.3 is on top of that for a different reason: it is the first release whose
|
|
# package.json declares @sasjs/utils ^4.6.0, which is what gives webout() its
|
|
# options argument. getdata, viewdata and getcolvals pass { showmeta }, and a
|
|
# 4.5 install drops it without a word - so the pin is what makes the metadata
|
|
# members guaranteed here rather than whatever a fresh install happens to
|
|
# resolve. requireWeboutOptions in sasjs/mocks/sasjs/services/dcMockUtils.js
|
|
# refuses a runtime without it.
|
|
SASJS_SERVER_VERSION: '3.14.0'
|
|
NODE_VERSION: '24.15.0'
|
|
|
|
jobs:
|
|
Build-and-ng-test:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
# The Cypress job consumes this instead of deciding again: the base
|
|
# branch can move between the two jobs, and a second answer could say
|
|
# "run" after this job had already skipped the build and the upload,
|
|
# which would fail there as a missing artifact. Empty (the decision
|
|
# could not be made) means the Cypress job runs everything.
|
|
client: ${{ steps.changes.outputs.client }}
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Check runner size
|
|
# An undersized runner dies inside `ng build` (exitcode 137) or as a
|
|
# Karma disconnect twenty minutes in. Fail here instead, in seconds,
|
|
# with the measured numbers in the log - see the script.
|
|
run: bash .gitea/scripts/check-runner-memory.sh
|
|
- name: Detect the Cypress job's inputs
|
|
id: changes
|
|
# Decides whether the Cypress job will run at all, so the production
|
|
# build and its artifact upload can be skipped when nothing the suite
|
|
# exercises has changed. Same script the Cypress job itself calls, so
|
|
# the two jobs cannot disagree about which paths matter.
|
|
env:
|
|
BASE_REF: ${{ github.base_ref }}
|
|
run: bash .gitea/scripts/cypress-changes.sh
|
|
- name: Test the changed-path matcher
|
|
# The path list is the whole gate: a dropped alternative silently stops
|
|
# the suite running for a class of change, and the only symptom is a
|
|
# green job that finished sooner. The cases pin the list to what the
|
|
# suite reads.
|
|
run: bash .gitea/scripts/tests/cypress-changes.test.sh
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION }}
|
|
|
|
- name: Scan for secrets (gitleaks)
|
|
# The local pre-commit hook is suppressed entirely when .npmrc sets
|
|
# ignore-scripts=true (the prepare script that would activate the
|
|
# hooks never runs), so CI is the enforcement point: a leaked
|
|
# credential must fail the build, not just a commit.
|
|
# Uses the same @nogoo9/gitleaks binary the pre-commit hook pins - the
|
|
# gitleaks-action requires a license key for organization repos.
|
|
# Runs before the registry config is written, so the scan cannot see
|
|
# the token this job injects.
|
|
# --no-git scans the working tree, which is what a pull request gate
|
|
# is about.
|
|
#
|
|
# The cache steps below deliberately run AFTER this one: the Angular
|
|
# cache holds webpack/babel JSON full of `{ token: ... }` fragments,
|
|
# and a restored copy in the tree is scanned as a pile of generic API
|
|
# keys (it failed the gate once, on run 3174).
|
|
run: |
|
|
npm ci
|
|
node_modules/.bin/gitleaks detect --no-git --source . --redact --no-banner -v
|
|
|
|
- name: Restore the npm download cache
|
|
# The runner's embedded cache server is reachable from job containers
|
|
# (they share a network with the runner), so these entries can actually
|
|
# come back. The key is the client lockfile: a dependency change
|
|
# invalidates it, and the prefix restore-keys keep the previous entries
|
|
# usable while the new ones are fetched.
|
|
#
|
|
# act_runner scopes cache entries to the REPOSITORY, not the ref (the
|
|
# handler stamps each entry with its repo and checks it on read), so
|
|
# the base branch is part of the key: a pull request into release/7.22
|
|
# cannot seed the entry a pull request into main restores. Entries
|
|
# still live on the runner that saved them - each act_runner runs its
|
|
# own cache server - so a box warms its cache from its own first run.
|
|
#
|
|
# An unreachable cache server degrades both the restore and the save to
|
|
# a warning - the action never fails the job - so a box on an older
|
|
# runner config costs a timeout, not a red build.
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ~/.npm
|
|
key: npm-client-${{ github.base_ref }}-${{ hashFiles('client/package-lock.json') }}
|
|
restore-keys: |
|
|
npm-client-${{ github.base_ref }}-
|
|
|
|
- name: Install Google Chrome
|
|
run: |
|
|
apt-get update
|
|
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
|
|
apt install -y ./google-chrome*.deb
|
|
|
|
- name: Write .npmrc file
|
|
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 dependencies
|
|
env:
|
|
SHEET_PWD: ${{ secrets.SHEET_PWD }}
|
|
run: |
|
|
cd client
|
|
# Decrypt and Install sheet
|
|
printf '%s\n' "$SHEET_PWD" | \
|
|
gpg --batch --yes --passphrase-fd 0 \
|
|
--output ./libraries/sheet-crypto.tgz \
|
|
--decrypt ./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
|
|
npm run postinstall
|
|
# cypress's binary is installed in the Cypress job, where it is
|
|
# actually used; nothing in this job starts it, so downloading and
|
|
# unzipping it here only cost time (11s, measured).
|
|
|
|
- name: Restore the Angular build cache
|
|
# `ng build` and both `ng test` runs use this directory, so a restored
|
|
# entry speeds up the production build and the Karma suites alike.
|
|
#
|
|
# This step MUST stay below "Install dependencies". postinstall runs
|
|
# scripts/strip-clr-base64-fonts.mjs, which edits Clarity's CSS inside
|
|
# node_modules and then deletes client/.angular/cache, because the
|
|
# builder treats node_modules as immutable and would otherwise serve
|
|
# styles built from the unstripped file (measured: a marker @font-face
|
|
# injected into clr-ui.min.css is absent from a warm build's CSS and
|
|
# present in a cold one). Every run strips before it builds, so every
|
|
# entry in this cache was produced from the stripped file - restoring
|
|
# after the clear is what keeps that true.
|
|
#
|
|
# Exact key only, no restore-keys: the builder cannot see a change
|
|
# inside node_modules, so an entry compiled against a different
|
|
# Clarity must not be restored. A lockfile bump misses and rebuilds,
|
|
# which is the correct outcome.
|
|
#
|
|
# Angular keeps the cache in memory when CI is set, so client/
|
|
# angular.json sets cli.cache.environment to "all" - without it this
|
|
# step has nothing to save ("Path Validation Error: Path(s) specified
|
|
# in the action for caching do(es) not exist").
|
|
#
|
|
# Saving is skipped for a key that already exists, so this refreshes
|
|
# when client/package-lock.json changes; bump the v1- prefix to force
|
|
# a reset by hand. The base ref keeps a pull request into main from
|
|
# seeding what a pull request into release/7.22 restores.
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: client/.angular/cache
|
|
key: angular-v1-${{ github.base_ref }}-${{ hashFiles('client/package-lock.json') }}
|
|
|
|
- name: Check audit
|
|
# Root and sas fail on moderate and above (npm's default), client on
|
|
# low and above - the flags below are what makes that true, so they have
|
|
# to stay in step with this comment.
|
|
run: |
|
|
npm audit --omit=dev
|
|
cd ./sas
|
|
npm audit --omit=dev
|
|
cd ../client
|
|
npm audit --omit=dev --audit-level=low
|
|
|
|
- name: Check archiver directory walk
|
|
# the readdir-glob minimatch override in client/package.json is a major
|
|
# bump; archiver's directory() is the only runtime caller of that path,
|
|
# so exercise it rather than trusting that the install succeeded
|
|
run: |
|
|
cd client
|
|
npm run check:archiver
|
|
|
|
- name: Lint check
|
|
run: npm run lint:check
|
|
|
|
- name: SAS lint check
|
|
# sas/.sasjslint promotes each rule to "error" as its findings are
|
|
# cleared, and only errors change the exit code - so this gate fails on
|
|
# a regression in a cleared rule and passes while other rules still
|
|
# carry warnings.
|
|
run: |
|
|
cd sas
|
|
npm ci --ignore-scripts
|
|
npm run lint:sas
|
|
|
|
- name: Licence checker
|
|
run: |
|
|
cd client
|
|
npm run license-checker
|
|
|
|
- name: Mock estate service tests
|
|
# The deploy screen's questions (ACTION=DIRLIST / WRITECHECK) are
|
|
# answered by the mock makedata service on the browser-hosted sites.
|
|
# These run that service against a temporary estate, so a browse that
|
|
# seeds the site and deletes the program fails here instead of on the
|
|
# staging site, where it only shows as a wizard that cannot be used.
|
|
run: node --test sas/mocks/test/*.test.js
|
|
|
|
- name: Angular Tests
|
|
run: |
|
|
cd client
|
|
npm run test:headless
|
|
|
|
- name: Angular Tests in other timezones
|
|
# The VA filter clauses are built from local calendar dates, so a
|
|
# UTC-only run cannot see a date that slips a day west of UTC or east of
|
|
# it. Only the VA specs carry that property, so both scripts run that
|
|
# subset rather than the whole suite a second and third time.
|
|
run: |
|
|
cd client
|
|
npm run test:tz-west
|
|
npm run test:tz-east
|
|
|
|
- name: Every Cypress spec is in the CI list
|
|
# The spec list used to be spelled out inline in --spec, and five files
|
|
# quietly fell out of it - among them the VA embed suite. Fails when a
|
|
# spec on disk is not listed.
|
|
run: bash .gitea/scripts/cypress-specs.sh coverage .
|
|
|
|
- name: Production Build
|
|
# The build exists to give the Cypress job the client/dist it streams
|
|
# as the mock estate's frontend. When nothing that job exercises has
|
|
# changed, that job skips and nobody downloads the artifact, so the
|
|
# cold build (109s, measured on run 3132) would be spent on no one.
|
|
if: steps.changes.outputs.client != 'false'
|
|
run: |
|
|
cd client
|
|
npm run build
|
|
|
|
- name: Save the production build for the Cypress job
|
|
# Gated with the build it packages: when the Cypress job skips, this
|
|
# artifact has no consumer.
|
|
if: steps.changes.outputs.client != 'false'
|
|
# The Cypress job needs client/dist: the server-ci target streams it as
|
|
# the 'clickme' web app (see CONTRIBUTING.md). Handing it the build this
|
|
# job has already made saves a second cold `npm run build` (1.6 minutes,
|
|
# measured over thirteen runs) and guarantees both jobs test the same
|
|
# bytes.
|
|
#
|
|
# if-no-files-found: error, because an empty artifact is worse than a
|
|
# failed upload: the Cypress job would download nothing, deploy a service
|
|
# pack with no frontend, and the symptom would reappear there as the
|
|
# "webSourcePath ... doesn't exist" line this artefact exists to remove.
|
|
#
|
|
# v3, not v4: this runner reports "GHESNotSupportedError:
|
|
# @actions/artifact v2.0.0+, upload-artifact@v4+ ... not supported on
|
|
# GHES", so the current major cannot upload here at all - the download in
|
|
# Build-and-test-development is @v3 for the same reason.
|
|
uses: actions/upload-artifact@v3
|
|
with:
|
|
name: client-dist
|
|
path: client/dist
|
|
if-no-files-found: error
|
|
|
|
- name: Remove registry config
|
|
# written earlier in this job; nothing deletes it on the way out
|
|
if: always()
|
|
run: rm -f "$RUNNER_TEMP/npmrc"
|
|
|
|
Build-and-test-development:
|
|
runs-on: ubuntu-latest
|
|
needs: Build-and-ng-test
|
|
env:
|
|
CHROME_BIN: /usr/bin/google-chrome
|
|
# Pin OS locale
|
|
LANG: en_GB.UTF-8
|
|
LC_ALL: en_GB.UTF-8
|
|
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Take the changed-path decision from the build job
|
|
id: changes
|
|
# The build job made this decision before it built (or skipped) the
|
|
# client-dist this job downloads, and published it as a job output.
|
|
# Deciding again here could disagree if the base branch moved in
|
|
# between, and the disagreement would surface as a missing artifact.
|
|
# An empty value means that job could not decide, so the suite runs.
|
|
env:
|
|
CLIENT: ${{ needs['Build-and-ng-test'].outputs.client }}
|
|
run: |
|
|
case "${CLIENT:-}" in
|
|
true) echo "client=true" >> "$GITHUB_OUTPUT" ;;
|
|
false) echo "client=false" >> "$GITHUB_OUTPUT" ;;
|
|
*) echo "the build job published no decision - running the suite" ;;
|
|
esac
|
|
|
|
- name: Cypress suite not run - no client changes
|
|
# A named step rather than a run of greyed-out ones, so the job summary
|
|
# says why it took a minute: this job reports success having tested
|
|
# nothing, which is the intended behaviour for a pull request that
|
|
# changes no client code. If this job is a required check, "green" for
|
|
# such a pull request means "not applicable", and this step is the
|
|
# evidence for it.
|
|
if: steps.changes.outputs.client == 'false'
|
|
run: |
|
|
echo "No client, sas/mocks, sas/package.json or .gitea/scripts change in this pull request - the Cypress suite was skipped by design."
|
|
echo "The build job's 'Detect the Cypress job's inputs' step lists the files it saw, and warns instead if it could not decide (in which case the suite runs)."
|
|
- name: Check runner size
|
|
if: steps.changes.outputs.client != 'false'
|
|
# Each Cypress job compiles the frontend and runs a browser on top; a
|
|
# runner too small for that fails as a flaky-looking disconnect - see
|
|
# the script.
|
|
run: bash .gitea/scripts/check-runner-memory.sh
|
|
- uses: actions/setup-node@v4
|
|
if: steps.changes.outputs.client != 'false'
|
|
with:
|
|
node-version: ${{ env.NODE_VERSION }}
|
|
|
|
- name: Write .npmrc file
|
|
if: steps.changes.outputs.client != 'false'
|
|
run: |
|
|
# the secret arrives through the environment, not interpolated into
|
|
# this script: a quote or $( in the value would otherwise break the
|
|
# step or execute
|
|
printf '%s\n' "$NPMRC" > "$RUNNER_TEMP/npmrc"
|
|
echo "NPM_CONFIG_USERCONFIG=$RUNNER_TEMP/npmrc" >> "$GITHUB_ENV"
|
|
env:
|
|
NPMRC: ${{ secrets.NPMRC}}
|
|
|
|
- name: Install system dependencies
|
|
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
|
|
apt-get -y install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2t64 libxtst6 xauth xvfb jq zip locales
|
|
# Generate the en_GB.UTF-8 locale referenced by LANG/LC_ALL
|
|
locale-gen en_GB.UTF-8
|
|
update-locale LANG=en_GB.UTF-8 LC_ALL=en_GB.UTF-8
|
|
|
|
- name: Write cypress credentials
|
|
if: steps.changes.outputs.client != 'false'
|
|
run: echo "$CYPRESS_CREDS" > ./client/cypress.env.json
|
|
shell: bash
|
|
env:
|
|
CYPRESS_CREDS: ${{ secrets.CYPRESS_CREDS }}
|
|
|
|
- name: Restore the npm download cache
|
|
if: steps.changes.outputs.client != 'false'
|
|
# The build job saves this key with the same lockfile, so npm ci here
|
|
# should not download a tarball that job already fetched.
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ~/.npm
|
|
key: npm-client-${{ github.base_ref }}-${{ hashFiles('client/package-lock.json') }}
|
|
restore-keys: |
|
|
npm-client-${{ github.base_ref }}-
|
|
|
|
- name: Restore the Cypress binary
|
|
if: steps.changes.outputs.client != 'false'
|
|
# npx cypress install fetches a ~50MB binary per run without this; the
|
|
# key follows the lockfile, and a stale hit is re-checked by the installer.
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ~/.cache/Cypress
|
|
key: cypress-binary-${{ hashFiles('client/package-lock.json') }}
|
|
restore-keys: |
|
|
cypress-binary-
|
|
|
|
- name: Install dependencies
|
|
if: steps.changes.outputs.client != 'false'
|
|
env:
|
|
SHEET_PWD: ${{ secrets.SHEET_PWD }}
|
|
run: |
|
|
cd client
|
|
# Decrypt and Install sheet
|
|
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
|
|
npm run postinstall
|
|
# cypress downloads its binary in a dependency install hook, which the
|
|
# policy skips, so it is installed explicitly here
|
|
npx cypress install
|
|
|
|
- name: Fetch the production build
|
|
if: steps.changes.outputs.client != 'false'
|
|
# The server-ci target streams client/dist as the 'clickme' web app (see
|
|
# CONTRIBUTING.md), so the production build has to exist before the mock
|
|
# services are deployed. Without it the deploy logs
|
|
# "webSourcePath: .../client/dist ... doesn't exist" and streams no
|
|
# frontend. It is the artefact Build-and-ng-test published, so this job
|
|
# does not compile the frontend a second time.
|
|
uses: actions/download-artifact@v3
|
|
with:
|
|
name: client-dist
|
|
path: client/dist
|
|
|
|
- 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: |
|
|
npm i -g pm2@7.1.2
|
|
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
|
|
# /ddc-host is a testing tool, so the server answers 404 for it unless
|
|
# this is set. va-ddc-host.cy.ts drives the app through that page with
|
|
# timing=early.
|
|
echo DDC_HOST_ENABLED=true >> .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: Deploy mocked services
|
|
if: steps.changes.outputs.client != 'false'
|
|
run: |
|
|
cd ./sas/mocks/sasjs
|
|
# The CLI comes from the version sas/package.json pins, as docs.yaml and
|
|
# release-assets.yaml already do: an unpinned global install runs
|
|
# whatever release is current, unreviewed.
|
|
CLI_VERSION=$(node -p "require('../../package.json').dependencies['@sasjs/cli']")
|
|
npm install -g "@sasjs/cli@${CLI_VERSION}"
|
|
npm install -g replace-in-files-cli@4.0.0
|
|
# Remove any previous deployment (drive folder delete API) so the
|
|
# deploy and makedata start from a clean appLoc
|
|
curl -sS -X DELETE "http://localhost:5000/SASjsApi/drive/folder/?_folderPath=/Public/app/dc"
|
|
sasjs cbd -t server-ci
|
|
# Seed the DC database (mock data files on the drive).
|
|
# makedata replies with HTML (it is normally called as a URL redirect),
|
|
# which the CLI reports as "invalid Json string" - ignore that.
|
|
# NOTE: -d paths resolve from the sasjs project root (sas/mocks),
|
|
# not the cwd (sas/mocks/sasjs).
|
|
sasjs request services/admin/makedata -t server-ci -d deploy/makedata.json -o ./makedata_out.json || true
|
|
|
|
- name: Prepare and run frontend and cypress
|
|
if: steps.changes.outputs.client != 'false'
|
|
# One job, one SASjs server, one ng serve, one Chrome: four of each on one
|
|
# 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
|
|
# workflow rather than a green run with no tests.
|
|
SPECS="$(bash ../.gitea/scripts/cypress-specs.sh all)"
|
|
if [ -z "$SPECS" ]; then
|
|
echo "the spec list resolved to nothing"
|
|
exit 1
|
|
fi
|
|
echo "specs: $SPECS"
|
|
mv ./cypress.env.example.json ./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
|
|
replace-in-files --regex='serverUrl=".*?"' --replacement='serverUrl="http://localhost:5000"' ./src/index.html
|
|
replace-in-files --regex='appLoc=".*?"' --replacement='appLoc="/Public/app/dc"' ./src/index.html
|
|
replace-in-files --regex='serverType=".*?"' --replacement='serverType="SASJS"' ./src/index.html
|
|
replace-in-files --regex='"hosturl".*' --replacement='hosturl:"http://localhost:4200",' ./cypress.config.ts
|
|
cat ./cypress.config.ts
|
|
# Start frontend and run cypress
|
|
# timeout 2700: SIGTERM after 45 min so Cypress can flush screenshots
|
|
# before the outer timeout-minutes hard-kills the step (avoids silent multi-hour hangs)
|
|
#
|
|
# video=false, screenshotOnRunFailure=true: recording twenty-four
|
|
# specs costs encode time on every run, and the recording is only ever
|
|
# opened after a failure. A screenshot of the failing step carries the
|
|
# same information for a fraction of the cost - and cypress.config.ts
|
|
# sets screenshotOnRunFailure false by default, so both are passed
|
|
# here, as CLI overrides, leaving local runs (and the demo recorder,
|
|
# which sets RECORD_WINDOW_SIZE) on video.
|
|
npx ng serve --host 0.0.0.0 --port 4200 & npx wait-on http://localhost:4200 && timeout 2700 npx cypress run --browser chrome --config video=false,screenshotOnRunFailure=true --spec "$SPECS"
|
|
|
|
- name: Zip Cypress failure evidence
|
|
# Only on a failure: a green run has nothing to look at.
|
|
if: failure() && steps.changes.outputs.client != 'false'
|
|
run: |
|
|
mkdir -p ./client/cypress/screenshots ./client/cypress/videos
|
|
zip -r cypress-artifacts.zip ./client/cypress/screenshots ./client/cypress/videos
|
|
|
|
- name: Add cypress failure evidence as an artifact
|
|
if: failure() && steps.changes.outputs.client != 'false'
|
|
# v3, not v4: this runner reports "GHESNotSupportedError:
|
|
# @actions/artifact v2.0.0+, upload-artifact@v4+ ... not supported on
|
|
# GHES", so the current major cannot upload here at all.
|
|
uses: actions/upload-artifact@v3
|
|
with:
|
|
name: cypress-artifacts.zip
|
|
path: cypress-artifacts.zip
|
|
|
|
- name: Remove registry config
|
|
# written earlier in this job; nothing deletes it on the way out
|
|
if: always()
|
|
run: rm -f "$RUNNER_TEMP/npmrc"
|