Compare commits
2
Commits
0a172b0c37
...
b8eea5063c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8eea5063c | ||
|
|
d84a1d50f4 |
No files matched your search
@@ -1,65 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Which Cypress specs run in which CI shard.
|
||||
#
|
||||
# One list, because the workflow used to spell the specs out in a single
|
||||
# --spec string and five files quietly fell out of it - including the VA embed
|
||||
# suite. The guard step in Build-and-ng-test compares this against the specs on
|
||||
# disk and fails when a file belongs to no shard.
|
||||
#
|
||||
# Balance is on test count AND spec count: each spec pays a few seconds of
|
||||
# startup, so seventeen one-test files cost more than their test count suggests.
|
||||
set -euo pipefail
|
||||
|
||||
shard_specs() {
|
||||
case "$1" in
|
||||
1) echo "editor csv metanav" ;;
|
||||
2) echo "excel filtering download browser-info excel-multi-load hook-programs" ;;
|
||||
3) echo "embed-va viewbox licensing filter-panel liveness" ;;
|
||||
4) echo "viewer-labels row-cell-limits derived-variables ddl-export coltype-delete-record submitted-details startupservice-launcher stage full-table-search csv-limited" ;;
|
||||
*) echo "unknown shard: $1" >&2; return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
all_specs() {
|
||||
local shard
|
||||
for shard in 1 2 3 4; do shard_specs "$shard"; done
|
||||
}
|
||||
|
||||
spec_paths() {
|
||||
local name
|
||||
for name in $1; do printf 'cypress/e2e/%s.cy.ts,' "$name"; done
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
list)
|
||||
# comma separated, the form `cypress run --spec` takes
|
||||
spec_paths "$(shard_specs "$2")" | sed 's/,$//'
|
||||
;;
|
||||
all)
|
||||
spec_paths "$(all_specs)" | sed 's/,$//'
|
||||
;;
|
||||
coverage)
|
||||
# every spec on disk must appear exactly once across the shards
|
||||
on_disk=$(cd "$2/client" && ls cypress/e2e/*.cy.ts | xargs -n1 basename | sed 's/\.cy\.ts$//' | sort)
|
||||
assigned=$(printf '%s\n' $(all_specs) | sort)
|
||||
missing=$(comm -23 <(echo "$on_disk") <(echo "$assigned"))
|
||||
duplicated=$(echo "$assigned" | uniq -d)
|
||||
if [ -n "$missing" ]; then
|
||||
echo "Specs that belong to no shard:"
|
||||
echo "$missing"
|
||||
fi
|
||||
if [ -n "$duplicated" ]; then
|
||||
echo "Specs assigned to more than one shard:"
|
||||
echo "$duplicated"
|
||||
fi
|
||||
if [ -n "$missing$duplicated" ]; then
|
||||
echo "Add them to a shard in .gitea/scripts/cypress-shards.sh"
|
||||
exit 1
|
||||
fi
|
||||
echo "All $(echo "$on_disk" | wc -l) specs are assigned to exactly one shard."
|
||||
;;
|
||||
*)
|
||||
echo "usage: $0 {list <shard>|all|coverage <repo-root>}" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
# The Cypress specs CI runs.
|
||||
#
|
||||
# One list, because the workflow used to spell the specs out in a single --spec
|
||||
# string and five files quietly fell out of it - including the VA embed suite.
|
||||
# `coverage` fails when a spec on disk is not listed here, so the next new spec
|
||||
# cannot go missing in silence.
|
||||
set -euo pipefail
|
||||
|
||||
SPECS="browser-info coltype-delete-record csv csv-limited ddl-export derived-variables download editor embed-va excel excel-multi-load filter-panel filtering full-table-search hook-programs licensing liveness metanav row-cell-limits stage startupservice-launcher submitted-details viewbox viewer-labels"
|
||||
|
||||
case "${1:-}" in
|
||||
all)
|
||||
# comma separated, the form `cypress run --spec` takes
|
||||
for name in $SPECS; do printf 'cypress/e2e/%s.cy.ts,' "$name"; done | sed 's/,$//'
|
||||
;;
|
||||
coverage)
|
||||
on_disk=$(cd "$2/client" && ls cypress/e2e/*.cy.ts | xargs -n1 basename | sed 's/\.cy\.ts$//' | sort)
|
||||
listed=$(printf '%s\n' $SPECS | sort)
|
||||
missing=$(comm -23 <(echo "$on_disk") <(echo "$listed"))
|
||||
if [ -n "$missing" ]; then
|
||||
echo "Specs that are not in the list:"
|
||||
echo "$missing"
|
||||
echo "Add them to SPECS in .gitea/scripts/cypress-specs.sh"
|
||||
exit 1
|
||||
fi
|
||||
echo "All $(echo "$on_disk" | wc -l) specs are listed."
|
||||
;;
|
||||
*)
|
||||
echo "usage: $0 {all|coverage <repo-root>}" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
+23
-92
@@ -85,11 +85,11 @@ jobs:
|
||||
npm run test:tz-west
|
||||
npm run test:tz-east
|
||||
|
||||
- name: Every Cypress spec belongs to a shard
|
||||
# The spec lists used to be spelled out in one --spec string, and five
|
||||
# files quietly fell out of it - among them the VA embed suite. Fails
|
||||
# when a spec on disk appears in no shard, or in more than one.
|
||||
run: bash .gitea/scripts/cypress-shards.sh coverage .
|
||||
- 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
|
||||
run: |
|
||||
@@ -100,8 +100,9 @@ jobs:
|
||||
# Cypress shard 1 of 4. The setup repeats in every shard because each
|
||||
# one gets its own runner and its own SASjs server; that fixed cost is what
|
||||
# limits how far sharding helps. The specs it runs come from
|
||||
# .gitea/scripts/cypress-shards.sh.
|
||||
cypress-shard-1:
|
||||
# .gitea/scripts/cypress-specs.sh.
|
||||
|
||||
Build-and-test-development:
|
||||
runs-on: ubuntu-latest
|
||||
needs: Build-and-ng-test
|
||||
env:
|
||||
@@ -109,9 +110,8 @@ jobs:
|
||||
# Pin OS locale
|
||||
LANG: en_GB.UTF-8
|
||||
LC_ALL: en_GB.UTF-8
|
||||
SHARD: '1'
|
||||
steps: &dev-steps
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
@@ -204,17 +204,20 @@ 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
|
||||
# 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
|
||||
run: |
|
||||
cd ./client
|
||||
# The spec list lives in one place, and a shard that resolves to nothing
|
||||
# is a broken workflow rather than a green run with no tests.
|
||||
SPECS="$(bash ../.gitea/scripts/cypress-shards.sh list "$SHARD")"
|
||||
# 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 "shard $SHARD resolved to no specs"
|
||||
echo "the spec list resolved to nothing"
|
||||
exit 1
|
||||
fi
|
||||
echo "shard $SHARD: $SPECS"
|
||||
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
|
||||
@@ -226,7 +229,7 @@ jobs:
|
||||
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 this shard's specs
|
||||
# Start frontend and run cypress
|
||||
# timeout 2700: SIGTERM after 45 min so Cypress can flush video/screenshots
|
||||
# before the outer timeout-minutes hard-kills the step (avoids silent multi-hour hangs)
|
||||
npx ng serve --host 0.0.0.0 --port 4200 & npx wait-on http://localhost:4200 && timeout 2700 npx cypress run --browser chrome --spec "$SPECS"
|
||||
@@ -242,82 +245,10 @@ jobs:
|
||||
|
||||
- name: Add cypress videos artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
# 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-videos-${{ env.SHARD }}
|
||||
name: cypress-videos.zip
|
||||
path: cypress-videos.zip
|
||||
|
||||
|
||||
# Cypress shard 2 of 4. The setup repeats in every shard because each
|
||||
# one gets its own runner and its own SASjs server; that fixed cost is what
|
||||
# limits how far sharding helps. The specs it runs come from
|
||||
# .gitea/scripts/cypress-shards.sh.
|
||||
cypress-shard-2:
|
||||
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
|
||||
SHARD: '2'
|
||||
steps: *dev-steps
|
||||
|
||||
|
||||
# Cypress shard 3 of 4. The setup repeats in every shard because each
|
||||
# one gets its own runner and its own SASjs server; that fixed cost is what
|
||||
# limits how far sharding helps. The specs it runs come from
|
||||
# .gitea/scripts/cypress-shards.sh.
|
||||
cypress-shard-3:
|
||||
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
|
||||
SHARD: '3'
|
||||
steps: *dev-steps
|
||||
|
||||
|
||||
# Cypress shard 4 of 4. The setup repeats in every shard because each
|
||||
# one gets its own runner and its own SASjs server; that fixed cost is what
|
||||
# limits how far sharding helps. The specs it runs come from
|
||||
# .gitea/scripts/cypress-shards.sh.
|
||||
cypress-shard-4:
|
||||
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
|
||||
SHARD: '4'
|
||||
steps: *dev-steps
|
||||
# The status check that branch protection requires keeps its old name, but it
|
||||
# is produced here now, so a red shard cannot hide behind a green one.
|
||||
#
|
||||
# if: always() is what makes that true: without it a failed dependency skips
|
||||
# this job, and a skipped job is not a failure on the one check that is
|
||||
# watched - the gate would be silent in exactly the case it exists for.
|
||||
Build-and-test-development:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [cypress-shard-1, cypress-shard-2, cypress-shard-3, cypress-shard-4]
|
||||
if: always()
|
||||
steps:
|
||||
- name: Every Cypress shard passed
|
||||
run: |
|
||||
results="${{ needs.cypress-shard-1.result }} ${{ needs.cypress-shard-2.result }} ${{ needs.cypress-shard-3.result }} ${{ needs.cypress-shard-4.result }}"
|
||||
echo "shard results: $results"
|
||||
set -- $results
|
||||
if [ "$#" -ne 4 ]; then
|
||||
echo "expected 4 shard results, got $# - the needs context did not resolve" >&2
|
||||
exit 1
|
||||
fi
|
||||
for result in "$@"; do
|
||||
if [ "$result" != "success" ]; then
|
||||
echo "a Cypress shard did not succeed: $results" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "all 4 shards passed"
|
||||
Reference in new issue
Block a user