* ci: run the required workflows for the merge queue main no longer requires a branch to be up to date before it merges, so a PR no longer re-runs CI after every other merge. The Tests, Code Quality and Viewer.js workflows also run on merge_group, so the merge queue can test each PR against the latest main once, in order. The tracked protection record now says strict: false. * test(history): give waits that span a database restore their own ceiling The merge queue's Windows py3.12 job failed test_restore_or_generation_rotation_between_transactions_discards_evidence (restore=True): the restore it waits for, a full verify/migrate/backup/ replace cycle, took over the 10 s BLOCK_SECONDS on a busy runner. The two waits that span a restore now use RESTORE_SECONDS (90 s), which stays under the suite's 120 s hang watchdog, so a real hang still fails with its thread dump. Behavioral waits keep BLOCK_SECONDS. * test(history): drain the writer before every bounded close or dispose The merge queue's Windows py3.12 job failed test_saturated_start_cannot_capture_outputs_or_retain_previous_roots: dispose(2) ran while the writer still worked through 1,024 queued commands on a busy runner. Like #370, the test now drains first; the 2 s bound is not what it tests. Four more closes that follow a released block get the same drain, so the next slow runner can't fail them one at a time. * ci(comfyui): run the real-host job in the pinned Playwright image The real ComfyUI compatibility job installed Chromium with `playwright install --with-deps`, which pulls the browser's system packages from Ubuntu's apt mirror on every run. Three runs (#349 run 40367579020, #378, #382 job 122287230985: "Fetched 19.7 MB in 12min 23s (26.5 kB/s)") spent 11-12 minutes there and were cancelled at the 24-minute limit after all 89 tests had passed. The job now runs inside mcr.microsoft.com/playwright:v1.64.1-noble, the same image as the frontend coverage and page workflow jobs, which ships Chromium and its libraries, so the job has no apt step at all. The whole job moves rather than only the browser part: the Playwright suites launch the managed ComfyUI host themselves (PM_TEST_PYTHON), so splitting the browser off would mean running the host on the runner and the browser in a container across a network boundary, a larger change than moving one job whose host is plain Python and pip wheels. Python follows the other image jobs: the image's own Python 3.12 with a user-space venv bootstrapped from a checksum-pinned pip wheel (toolcache setup-python builds do not run in the container). A step fails the job when the image Python differs from pythonVersion in tools/vendor/comfyui-runtime.json, so the pinned host stays honest; torch, torchvision and ComfyUI's requirements install from the same pins as before. pip and npm download caches replace setup-python's and setup-node's. tests/vendor/browser-image.test.mjs now checks this job like the other browser jobs: image tag equals @playwright/test in the lockfile, no browser install or apt, no setup-python/setup-node, checksum-pinned pip, Node 24, and the runtime Python check. The 24-minute limit is unchanged.
240 lines
12 KiB
YAML
240 lines
12 KiB
YAML
name: Viewer.js vendor and compatibility
|
|
|
|
on:
|
|
merge_group:
|
|
pull_request:
|
|
branches: [main]
|
|
push:
|
|
branches: [main]
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
# Superseded PR pushes cancel their older run. Every main push gets its own
|
|
# group (run_id), so no main commit's run is ever cancelled or dropped as pending.
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.run_id }}
|
|
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
|
|
# Timeouts are the measured job time plus at least 50%, and never under 20 minutes for a browser job,
|
|
# because runner speed varies. Before sharding (main 2cecfcf, one worker): frontend coverage 32m10s,
|
|
# page workflows firefox 18m59s, webkit 19m26s. First sharded run (PR #274 run 39712909047, 3 workers,
|
|
# no npm cache, where npm ci stalled for 5m in six of eight jobs): coverage shards 11m19s/10m47s/10m49s
|
|
# (tests 5m20s/4m42s/4m46s), merge 5m52s (report and check 4s), firefox shards 7m53s/9m27s (tests
|
|
# 4m41s/3m29s), webkit shards 12m25s+ (cancelled at the old 12-minute limit after its 267 tests passed in
|
|
# 6m29s) and 4m53s (tests 3m08s). With the npm cache hit (run 39725176320 at cbc8a61, npm ci 0-2s):
|
|
# coverage shards 7m34s/6m04s/5m22s (tests 6m38s/4m53s/4m04s), merge 54s, firefox 7m57s/5m21s (tests
|
|
# 7m08s/4m34s), webkit 8m16s/5m03s (tests 7m26s/3m58s); a cache miss adds up to 5m to npm ci, which
|
|
# the 20-minute limits absorb.
|
|
jobs:
|
|
frontend-coverage:
|
|
name: frontend coverage (shard ${{ matrix.shard }}/3)
|
|
runs-on: ubuntu-latest
|
|
# Layout comparisons depend on the fonts each browser falls back to, so browser jobs run in the
|
|
# same pinned Playwright image as local runs (tag = @playwright/test in tools/vendor/package-lock.json;
|
|
# tests/vendor/browser-image.test.mjs checks it). Browsers and fonts come from the image.
|
|
container:
|
|
image: mcr.microsoft.com/playwright:v1.64.1-noble
|
|
options: --user 1001
|
|
timeout-minutes: 20
|
|
# Public-repo ubuntu-latest runners have 4 vCPUs: three Playwright workers, each with its own test
|
|
# server, leave one CPU for the servers and the runner.
|
|
env:
|
|
PM_TEST_WORKERS: 3
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
shard: [1, 2, 3]
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
# The image's own Python 3.12 and Node 24: setup-python/setup-node install the runner's toolcache
|
|
# builds, whose entry points name host paths that do not exist in the container. The image has no
|
|
# ensurepip and the job has no root, so a checksum-pinned pip wheel bootstraps a user-space venv.
|
|
- name: Python environment
|
|
env:
|
|
PIP_WHEEL: https://files.pythonhosted.org/packages/f3/6e/1736e5b4ae2b778ef2f81c47d797de9f891d4d8acb047a24ca37a60294dd/pip-26.2.1-py3-none-any.whl
|
|
PIP_SHA256: 71138adf1f4ca900cdb7d289c21b7494329f2332b6d85f0e1c42108c0384ed3e
|
|
run: |
|
|
venv="$RUNNER_TEMP/venv" wheel="$RUNNER_TEMP/pip-26.2.1-py3-none-any.whl"
|
|
python3 -m venv --without-pip "$venv"
|
|
curl -fsSL --retry 3 -o "$wheel" "$PIP_WHEEL"
|
|
echo "$PIP_SHA256 $wheel" | sha256sum -c -
|
|
"$venv/bin/python" "$wheel/pip" install --no-index --no-cache-dir "$wheel"
|
|
"$venv/bin/python" -m pip install --no-cache-dir -r requirements-dev.txt
|
|
echo "$venv/bin" >> "$GITHUB_PATH"
|
|
- run: python --version && node --version && node --version | grep -q '^v24\.'
|
|
# npm's download cache, keyed on the lockfile; inside the container ~ is the job's HOME (/github/home),
|
|
# which npm also uses. node_modules itself is still built fresh by npm ci.
|
|
- name: Cache npm downloads
|
|
uses: actions/cache@v6
|
|
with:
|
|
path: ~/.npm
|
|
key: npm-${{ runner.os }}-playwright-v1.64.1-${{ hashFiles('tools/vendor/package-lock.json') }}
|
|
- run: npm ci --prefix tools/vendor --ignore-scripts
|
|
# Shard 1 also runs the node units and the viewer suite, so the vendor pins and the tooling tests
|
|
# run on the lightest shard instead.
|
|
- if: matrix.shard == 3
|
|
run: npm run --prefix tools/vendor check
|
|
- if: matrix.shard == 3
|
|
run: npm run --prefix tools/vendor test
|
|
- name: Page workflows with coverage, shard ${{ matrix.shard }}/3
|
|
run: npm run --prefix tools/vendor test:coverage:shard -- ${{ matrix.shard }}/3
|
|
- name: Upload raw coverage
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: coverage-raw-${{ matrix.shard }}
|
|
path: coverage/js/raw/
|
|
if-no-files-found: error
|
|
retention-days: 3
|
|
- name: Upload Chromium and touch traces
|
|
if: always()
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: frontend-traces-shard-${{ matrix.shard }}
|
|
path: tools/vendor/test-results/
|
|
retention-days: 14
|
|
|
|
frontend-coverage-merge:
|
|
name: frontend coverage (merge)
|
|
needs: frontend-coverage
|
|
runs-on: ubuntu-latest
|
|
# Same image as the shards: the raw files name sources by the container's workspace path.
|
|
container:
|
|
image: mcr.microsoft.com/playwright:v1.64.1-noble
|
|
options: --user 1001
|
|
timeout-minutes: 10
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- run: node --version && node --version | grep -q '^v24\.'
|
|
# npm's download cache, keyed on the lockfile; inside the container ~ is the job's HOME (/github/home),
|
|
# which npm also uses. node_modules itself is still built fresh by npm ci.
|
|
- name: Cache npm downloads
|
|
uses: actions/cache@v6
|
|
with:
|
|
path: ~/.npm
|
|
key: npm-${{ runner.os }}-playwright-v1.64.1-${{ hashFiles('tools/vendor/package-lock.json') }}
|
|
- run: npm ci --prefix tools/vendor --ignore-scripts
|
|
# Each artifact lands in its own coverage-raw-N directory, so the next step can count them.
|
|
- uses: actions/download-artifact@v8
|
|
with:
|
|
pattern: coverage-raw-*
|
|
path: ${{ runner.temp }}/coverage-raw
|
|
# coverage:merge only checks that raw files exist; merging two of three shards would report
|
|
# partial coverage, so a missing or empty shard artifact fails here.
|
|
- name: Verify every shard's raw coverage arrived
|
|
shell: bash
|
|
env:
|
|
STAGED_RAW: ${{ runner.temp }}/coverage-raw
|
|
EXPECTED_SHARDS: 3
|
|
run: |
|
|
found=0
|
|
for shard in $(seq 1 "$EXPECTED_SHARDS"); do
|
|
directory="$STAGED_RAW/coverage-raw-$shard"
|
|
count=0
|
|
if [ -d "$directory" ]; then count=$(find "$directory" -maxdepth 1 -type f -name '*.json' | wc -l); fi
|
|
echo "coverage-raw-$shard: $count raw files"
|
|
if [ "$count" -gt 0 ]; then found=$((found + 1)); fi
|
|
done
|
|
echo "shard artifacts with raw coverage: $found of $EXPECTED_SHARDS"
|
|
if [ "$found" -ne "$EXPECTED_SHARDS" ]; then
|
|
echo "::error::Expected raw coverage from $EXPECTED_SHARDS shards, got $found of $EXPECTED_SHARDS"
|
|
exit 1
|
|
fi
|
|
staged=$(find "$STAGED_RAW" -mindepth 2 -maxdepth 2 -type f -name '*.json' | wc -l)
|
|
mkdir -p coverage/js/raw
|
|
# cp -n never overwrites; a clash shows up as fewer merged files than staged ones.
|
|
find "$STAGED_RAW" -mindepth 2 -maxdepth 2 -type f -name '*.json' -exec cp -n -t coverage/js/raw {} + || true
|
|
merged=$(find coverage/js/raw -maxdepth 1 -type f -name '*.json' | wc -l)
|
|
if [ "$merged" -ne "$staged" ]; then
|
|
echo "::error::Shards wrote clashing raw file names: $staged staged, $merged merged"
|
|
exit 1
|
|
fi
|
|
echo "merged $merged raw files"
|
|
- name: Frontend coverage report and baseline check
|
|
run: npm run --prefix tools/vendor coverage:merge
|
|
- name: Upload frontend coverage report
|
|
if: always()
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: frontend-test-reports
|
|
path: |
|
|
coverage/js/
|
|
!coverage/js/raw/**
|
|
retention-days: 14
|
|
|
|
browser-workflows:
|
|
name: page workflows (${{ matrix.browser }}, shard ${{ matrix.shard }}/2)
|
|
runs-on: ubuntu-latest
|
|
# Layout comparisons depend on the fonts each browser falls back to, so browser jobs run in the
|
|
# same pinned Playwright image as local runs (tag = @playwright/test in tools/vendor/package-lock.json;
|
|
# tests/vendor/browser-image.test.mjs checks it). Browsers and fonts come from the image.
|
|
container:
|
|
image: mcr.microsoft.com/playwright:v1.64.1-noble
|
|
options: --user 1001
|
|
timeout-minutes: 20
|
|
# Public-repo ubuntu-latest runners have 4 vCPUs: three Playwright workers, each with its own test
|
|
# server, leave one CPU for the servers and the runner.
|
|
env:
|
|
PM_TEST_WORKERS: 3
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
browser: [firefox, webkit]
|
|
shard: [1, 2]
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
# The image's own Python 3.12 and Node 24: setup-python/setup-node install the runner's toolcache
|
|
# builds, whose entry points name host paths that do not exist in the container. The image has no
|
|
# ensurepip and the job has no root, so a checksum-pinned pip wheel bootstraps a user-space venv.
|
|
- name: Python environment
|
|
env:
|
|
PIP_WHEEL: https://files.pythonhosted.org/packages/f3/6e/1736e5b4ae2b778ef2f81c47d797de9f891d4d8acb047a24ca37a60294dd/pip-26.2.1-py3-none-any.whl
|
|
PIP_SHA256: 71138adf1f4ca900cdb7d289c21b7494329f2332b6d85f0e1c42108c0384ed3e
|
|
run: |
|
|
venv="$RUNNER_TEMP/venv" wheel="$RUNNER_TEMP/pip-26.2.1-py3-none-any.whl"
|
|
python3 -m venv --without-pip "$venv"
|
|
curl -fsSL --retry 3 -o "$wheel" "$PIP_WHEEL"
|
|
echo "$PIP_SHA256 $wheel" | sha256sum -c -
|
|
"$venv/bin/python" "$wheel/pip" install --no-index --no-cache-dir "$wheel"
|
|
"$venv/bin/python" -m pip install --no-cache-dir -r requirements-dev.txt
|
|
echo "$venv/bin" >> "$GITHUB_PATH"
|
|
- run: python --version && node --version && node --version | grep -q '^v24\.'
|
|
# npm's download cache, keyed on the lockfile; inside the container ~ is the job's HOME (/github/home),
|
|
# which npm also uses. node_modules itself is still built fresh by npm ci.
|
|
- name: Cache npm downloads
|
|
uses: actions/cache@v6
|
|
with:
|
|
path: ~/.npm
|
|
key: npm-${{ runner.os }}-playwright-v1.64.1-${{ hashFiles('tools/vendor/package-lock.json') }}
|
|
- run: npm ci --prefix tools/vendor --ignore-scripts
|
|
- name: Page workflows in ${{ matrix.browser }}, shard ${{ matrix.shard }}/2
|
|
run: npm run --prefix tools/vendor test:workflows -- --project ${{ matrix.browser }} --shard ${{ matrix.shard }}/2
|
|
- name: Upload browser traces
|
|
if: always()
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: browser-traces-${{ matrix.browser }}-shard-${{ matrix.shard }}
|
|
path: tools/vendor/test-results/
|
|
retention-days: 14
|
|
|
|
# Branch protection requires the exact context "viewerjs". This job keeps that
|
|
# name and fails unless every frontend job above succeeded (every shard, the
|
|
# coverage merge and every browser shard), including when one was cancelled or skipped.
|
|
viewerjs:
|
|
needs: [frontend-coverage, frontend-coverage-merge, browser-workflows]
|
|
if: always()
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
steps:
|
|
- name: Require every frontend job to succeed
|
|
env:
|
|
COVERAGE_RESULT: ${{ needs.frontend-coverage.result }}
|
|
MERGE_RESULT: ${{ needs.frontend-coverage-merge.result }}
|
|
WORKFLOWS_RESULT: ${{ needs.browser-workflows.result }}
|
|
run: |
|
|
echo "frontend-coverage: $COVERAGE_RESULT"
|
|
echo "frontend-coverage-merge: $MERGE_RESULT"
|
|
echo "browser-workflows: $WORKFLOWS_RESULT"
|
|
test "$COVERAGE_RESULT" = success && test "$MERGE_RESULT" = success && test "$WORKFLOWS_RESULT" = success
|