diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e35b6b07f..a1fb49d057 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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: @@ -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: @@ -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() @@ -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 @@ -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" diff --git a/docs/development.md b/docs/development.md index 45c92691b8..04d1771105 100644 --- a/docs/development.md +++ b/docs/development.md @@ -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/