Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 150 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,39 @@ concurrency:
group: validation-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
changes:
name: Detect changes
runs-on: ubuntu-24.04
outputs:
code: ${{ steps.filter.outputs.code }}
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.sha }}
fetch-depth: 0
- id: filter
run: |
if [ "${{ github.event_name }}" = "push" ] && [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "code=true" >> "$GITHUB_OUTPUT"
exit 0
fi
BASE="${{ github.event.pull_request.base.sha }}"
if [ -z "$BASE" ]; then BASE="HEAD~1"; fi
CHANGED=$(git diff --name-only "$BASE" HEAD || echo "")
if [ -z "$CHANGED" ]; then
echo "code=true" >> "$GITHUB_OUTPUT"
exit 0
fi
if echo "$CHANGED" | grep -qvE '^(docs/|\.md$|\.txt$|LICENSE|\.github/ISSUE_TEMPLATE/)'; then
echo "code=true" >> "$GITHUB_OUTPUT"
else
echo "code=false" >> "$GITHUB_OUTPUT"
echo "Only documentation changes detected, skipping heavy tests"
fi
windows-install:
name: Native Windows install
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: windows-latest
timeout-minutes: 10
env:
Expand All @@ -31,6 +62,8 @@ jobs:
- run: bun test ./scripts/windows-plugin.test.ts ./scripts/plugin-runtime.test.ts ./scripts/phone-setup.test.ts ./server/windows-native.test.ts
macos-session:
name: Native macOS session identity
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: macos-15
timeout-minutes: 5
env:
Expand Down Expand Up @@ -61,8 +94,6 @@ jobs:
with:
node-version: '22'
- run: bun install --frozen-lockfile
# scripts/check.ts is what a developer runs too: workflow syntax (checksum-pinned actionlint),
# generated types, typecheck, build, unit tests (no herdr required)
- run: bun run check fast
- name: Preserve check report and verification logs
if: always()
Expand All @@ -78,11 +109,12 @@ jobs:
include-hidden-files: true
if-no-files-found: ignore
integration:
name: Integration and browser
name: Integration tests
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 20
timeout-minutes: 15
env:
# where the run's own herdr keeps its config and state (scripts/check.ts): kept, for the failure logs
CHECK_DIR: ${{ github.workspace }}/.ci
steps:
- uses: actions/checkout@v7
Expand All @@ -102,21 +134,129 @@ jobs:
chmod +x "$RUNNER_TEMP/herdr-bin/herdr"
echo "$RUNNER_TEMP/herdr-bin" >> "$GITHUB_PATH"
- run: bun install --frozen-lockfile
# A build, then the two lanes side by side on a herdr of the run's own, which it stops when
# it ends. The lanes share nothing: the integration files run on herdr sessions of their own.
- name: Integration tests (updater restart and rollback included) beside the browser regressions
run: bun run check integration browser
- name: Integration tests (updater restart and rollback included)
run: bun run check integration
- name: Preserve check report and verification logs
if: always()
uses: actions/upload-artifact@v7
with:
name: herdr-logs-${{ github.run_id }}-${{ github.run_attempt }}
name: integration-logs-${{ github.run_id }}-${{ github.run_attempt }}
path: |
.ci/report.json
.ci/**/*.log
.ci/config/herdr/sessions/*/test-server.log
include-hidden-files: true
if-no-files-found: ignore
browser-main:
name: Browser regression (ui-regression)
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 15
env:
CHECK_DIR: ${{ github.workspace }}/.ci
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.sha }}
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.4.2
- uses: actions/setup-node@v7
with:
node-version: '22'
- name: Install checksum-pinned herdr 0.9.3
run: |
mkdir -p "$RUNNER_TEMP/herdr-bin"
curl --fail --location --retry 3 https://github.com/herdrdev/herdr/releases/download/v0.9.3/herdr-linux-x86_64 -o "$RUNNER_TEMP/herdr-bin/herdr"
echo "18a8dc65f1c2fa485884344356dea1cfd911c6f06cf46fa78e193f4087f4dba7 $RUNNER_TEMP/herdr-bin/herdr" | sha256sum --check
chmod +x "$RUNNER_TEMP/herdr-bin/herdr"
echo "$RUNNER_TEMP/herdr-bin" >> "$GITHUB_PATH"
- run: bun install --frozen-lockfile
- run: bun run build
- name: Browser regression (ui-regression.ts)
env:
BROWSER_SCRIPTS: scripts/ui-regression.ts
run: bun scripts/ci-browser-parallel.ts
- name: Preserve browser logs and evidence
if: always()
uses: actions/upload-artifact@v7
with:
name: browser-main-${{ github.run_id }}-${{ github.run_attempt }}
path: |
.ci/browser-logs/**
.ci/browser-evidence/**/*.png
.ci/browser-evidence/**/*.json
.ci/browser-evidence/**/*.zip
include-hidden-files: true
if-no-files-found: ignore
browser-other:
name: Browser regression (other scripts)
needs: changes
if: needs.changes.outputs.code == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 15
env:
CHECK_DIR: ${{ github.workspace }}/.ci
BROWSER_WORKERS: 4
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.sha }}
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.4.2
- uses: actions/setup-node@v7
with:
node-version: '22'
- name: Install checksum-pinned herdr 0.9.3
run: |
mkdir -p "$RUNNER_TEMP/herdr-bin"
curl --fail --location --retry 3 https://github.com/herdrdev/herdr/releases/download/v0.9.3/herdr-linux-x86_64 -o "$RUNNER_TEMP/herdr-bin/herdr"
echo "18a8dc65f1c2fa485884344356dea1cfd911c6f06cf46fa78e193f4087f4dba7 $RUNNER_TEMP/herdr-bin/herdr" | sha256sum --check
chmod +x "$RUNNER_TEMP/herdr-bin/herdr"
echo "$RUNNER_TEMP/herdr-bin" >> "$GITHUB_PATH"
- run: bun install --frozen-lockfile
- run: bun run build
- name: Browser regression (parallel)
env:
# Every script of scripts/ci-browser.sh except the one browser-main runs.
BROWSER_EXCLUDE: scripts/ui-regression.ts
run: bun scripts/ci-browser-parallel.ts
- name: Preserve browser logs and evidence
if: always()
uses: actions/upload-artifact@v7
with:
name: browser-other-${{ github.run_id }}-${{ github.run_attempt }}
path: |
.ci/browser-logs/**
.ci/browser-evidence/**/*.png
.ci/browser-evidence/**/*.json
.ci/browser-evidence/**/*.zip
include-hidden-files: true
if-no-files-found: ignore
integration-browser:
name: Integration and browser
needs: [changes, integration, browser-main, browser-other]
if: always()
runs-on: ubuntu-24.04
steps:
- name: Check results
run: |
if [ "${{ needs.changes.outputs.code }}" = "false" ]; then
echo "Skipped: only documentation changes"
exit 0
fi
if [ "${{ needs.integration.result }}" != "success" ]; then
echo "Integration tests failed"
exit 1
fi
if [ "${{ needs.browser-main.result }}" != "success" ]; then
echo "Browser regression (ui-regression) failed"
exit 1
fi
if [ "${{ needs.browser-other.result }}" != "success" ]; then
echo "Browser regression (other scripts) failed"
exit 1
fi
echo "All tests passed"
43 changes: 29 additions & 14 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -398,20 +398,35 @@ The [CI workflow](../.github/workflows/ci.yml) runs on every PR and `main` push:
(checksum-pinned actionlint), generated type freshness, application and critical-tools typechecks
(`tsconfig.tools.json`), build, and
`bun run test:unit`. This suite does not start herdr.
- **Integration and browser**: checksum-pinned herdr 0.9.3, Node 22, then
`bun run check integration browser`: a build, `bun run test:integration`, and the browser
scripts of `scripts/ci-browser.sh` with the lockfile's Chromium, on a herdr of the run's own
([Checks](#checks)). The two lanes run at the same time (`scripts/ci-lanes.ts`). The
integration files can run a few at a time, each worker on a herdr session of its own
(`scripts/ci-tests.ts`); they run one by one (`HERDR_TEST_SHARDS`, default 1 in
`scripts/check.ts`) until the timing-bound contract tests hold under load.
Missing herdr fails the run. Its herdr servers are stopped even on failure.
Integration tests have a 15-second default timeout so their bounded process-startup
probes can finish; individual tests can still specify a longer timeout.

The browser lane prints each script's wall seconds and exit code, including the shared demo
build, then a summary on success or failure. The first failing script stops the lane.
Under `CI` or with `CHECK_DIR` set, it creates `UI_EVIDENCE_DIR` at
- **Integration and browser**: a summary job that fails when any of three jobs fails, each with
checksum-pinned herdr 0.9.3, Node 22 and the lockfile's Chromium:
- **Integration tests** runs `bun run check integration`: a build and
`bun run test:integration` on a herdr of the run's own ([Checks](#checks)). The
integration files can run a few at a time, each worker on a herdr session of its own
(`scripts/ci-tests.ts`); they run one by one (`HERDR_TEST_SHARDS`, default 1 in
`scripts/check.ts`) until the timing-bound contract tests hold under load.
Missing herdr fails the run. Its herdr servers are stopped even on failure.
Integration tests have a 15-second default timeout so their bounded process-startup
probes can finish; individual tests can still specify a longer timeout.
- **Browser regression (ui-regression)** runs `scripts/ui-regression.ts` alone.
- **Browser regression (other scripts)** runs every other browser script, four at a time.

Both browser jobs run `scripts/ci-browser-parallel.ts`, which takes its script list from
`scripts/ci-browser.sh` (`BROWSER_SCRIPTS` picks from it, `BROWSER_EXCLUDE` leaves scripts out,
and a name the list does not hold is an error). `scripts/pages-gate.test.ts` fails if the two
jobs together do not run that list exactly once. A new browser script therefore only needs its
`run_script` line in `scripts/ci-browser.sh`.

`bun run check browser` and `bun run check full` still run the serial lane, `scripts/ci-browser.sh`
(`BROWSER_PARALLEL=1` hands it to the parallel runner).
The serial lane prints each script's wall seconds and exit code, including the shared demo
build, then a summary on success or failure. The first failing script stops it.
The parallel runner builds the demo once, prints a line when each script starts and ends (and
every minute a script is still running), and keeps running the other scripts after one fails.
Each script's full output goes to `${CHECK_DIR:-.ci}/browser-logs/<script>.log`, with
`summary.json` beside it; a failure prints the last 400 lines of its log and the log's path, and
the job then fails. CI uploads `browser-logs` with the evidence on success or failure.
Under `CI` or with `CHECK_DIR` set, both runners create `UI_EVIDENCE_DIR` at
`${CHECK_DIR:-.ci}/browser-evidence`. Only the existing sticky-modifier and file-viewer captures
are enabled automatically: the UI suite and key-bar demo have evidence branches that add
assertions, viewport changes or font waits, so those remain disabled in the lane. Outside
Expand Down
Loading
Loading