Files
insomnia/.github/workflows/insomnia-test.yml
T
Bob QiuandClaude Sonnet 5.5 b340ba53fd fix(insomnia-test): address review feedback on deps, coverage and video
- align dependency versions with the rest of the monorepo (express ^4,
  zod ^3, graphql ^16, @types/node ^24, @modelcontextprotocol/sdk ^1.17.5)
  and regenerate package-lock.json
- make CI coverage on-demand: always on for develop pushes, opt-in via the
  workflow_dispatch `coverage` input otherwise
- drop video recording (and the ffmpeg install); start Electron window
  tracing explicitly and attach trace.zip on failure instead

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-05 09:59:51 +08:00

447 lines
18 KiB
YAML

name: Insomnia Test
on:
merge_group:
workflow_dispatch:
inputs:
coverage:
description: Collect and merge V8 coverage (slower)
type: boolean
default: false
push:
branches:
- develop
pull_request:
types:
- opened
- synchronize
env:
# Coverage is on-demand: always on for develop pushes (the baseline), and
# for manual runs only when the `coverage` input is ticked. PRs and merge
# queue runs skip it.
COVERAGE: ${{ (github.event_name == 'push' || inputs.coverage) && 'true' || 'false' }}
concurrency:
group: insomnia-test-nonblocking-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
# ---------------------------------------------------------------------------
# Build the Insomnia binary once and upload it as an artifact. All shards
# download the same binary instead of each building it.
# ---------------------------------------------------------------------------
build:
timeout-minutes: 30
runs-on: ubuntu-24.04
permissions:
contents: read
packages: read
steps:
- name: Harden Runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
- name: Checkout repo
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
node-version-file: .nvmrc
registry-url: 'https://npm.pkg.github.com'
scope: '@kong'
- name: Expand swap space
run: |
sudo dd if=/dev/zero of=/mnt/swapfile bs=1M count=8192 status=none
sudo chmod 600 /mnt/swapfile
sudo mkswap /mnt/swapfile
sudo swapon /mnt/swapfile
# node_modules is installed once here and cached by lockfile hash; the
# test shards and result job restore it instead of running `npm ci`.
# Restore must come before install; the save runs right after install so
# later build steps can't alter what gets cached.
- name: Restore node_modules
id: node-modules-cache
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5
with:
path: |
node_modules
packages/*/node_modules
key: node-modules-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
- name: Install dependencies
if: steps.node-modules-cache.outputs.cache-hit != 'true'
run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Save node_modules
if: steps.node-modules-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@caa296126883cff596d87d8935842f9db880ef25 # v5
with:
path: |
node_modules
packages/*/node_modules
key: node-modules-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
- name: Type-check insomnia-test
run: npm run type-check -w insomnia-test
- name: Build Insomnia
working-directory: packages/insomnia
env:
NODE_OPTIONS: --max-old-space-size=8192
run: |
npm run build:react-router -- --minify=false
npx esr --cache ./scripts/build.ts --noErrorTruncation
echo "=== build/ contents ==="
ls -la build/ 2>/dev/null || echo "ERROR: build/ not found"
test -f build/entry.main.min.js || { echo "ERROR: entry.main.min.js missing after build"; exit 1; }
- name: Package Insomnia (Linux unpacked)
working-directory: packages/insomnia
env:
USE_HARD_LINKS: 'false'
# Package .map files so coverage reports show original (pre-compile) sources
KEEP_SOURCEMAPS: 'true'
run: npx electron-builder build --config ../insomnia-test/electron-builder.config.ts --linux --dir --publish never
- name: Upload Insomnia binary
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: insomnia-linux-unpacked
path: packages/insomnia/dist/linux-unpacked
retention-days: 1
# ---------------------------------------------------------------------------
# Counts the test cases with `playwright test --list` and derives the shard
# count as ceil(total / TESTS_PER_SHARD), capped at MAX_SHARDS, then generates
# the matrix indices (1..N). Needs `build` only for the cached node_modules.
# ---------------------------------------------------------------------------
shards:
name: Shard Matrix
needs: build
runs-on: ubuntu-24.04
permissions:
contents: read
packages: read
env:
TESTS_PER_SHARD: 10
MAX_SHARDS: 200
outputs:
indices: ${{ steps.matrix.outputs.indices }}
total: ${{ steps.matrix.outputs.total }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
- name: Checkout repo
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
node-version-file: .nvmrc
- name: Restore node_modules
id: node-modules-cache
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5
with:
path: |
node_modules
packages/*/node_modules
key: node-modules-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
# The build job's cache save can lose a reservation race (or be left
# reserved by a cancelled run), so fall back to a fresh install.
- name: Install dependencies
if: steps.node-modules-cache.outputs.cache-hit != 'true'
run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- id: matrix
working-directory: packages/insomnia-test
run: |
list_output=$(npx playwright test --list 2>&1) || true
count=$(echo "$list_output" | grep -oE '^Total: [0-9]+' | grep -oE '[0-9]+' || true)
if [ -z "$count" ] || [ "$count" -lt 1 ]; then
echo "$list_output"
echo "ERROR: could not determine test count"; exit 1
fi
total=$(( (count + TESTS_PER_SHARD - 1) / TESTS_PER_SHARD ))
if [ "$total" -gt "$MAX_SHARDS" ]; then total=$MAX_SHARDS; fi
echo "$count tests -> $total shards ($TESTS_PER_SHARD tests per shard, max $MAX_SHARDS)"
echo "total=$total" >> "$GITHUB_OUTPUT"
echo "indices=$(seq 1 $total | jq -sc .)" >> "$GITHUB_OUTPUT"
# ---------------------------------------------------------------------------
# Run the whole suite across the shards computed by the `shards` job (about
# TESTS_PER_SHARD tests each, at most MAX_SHARDS) in parallel. Each shard restores the
# downloaded binary and runs a slice of the suite, retrying failures in-process
# (see playwright.config.ts `retries`).
# ---------------------------------------------------------------------------
parallel-test:
name: Parallel Test
needs: [build, shards]
timeout-minutes: 60
runs-on: ubuntu-24.04
permissions:
contents: read
packages: read
strategy:
fail-fast: false
matrix:
shardIndex: ${{ fromJSON(needs.shards.outputs.indices) }}
shardTotal: ${{ fromJSON(format('[{0}]', needs.shards.outputs.total)) }}
steps:
- name: Harden Runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
- name: Checkout repo
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
node-version-file: .nvmrc
- name: Download Insomnia binary
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: insomnia-linux-unpacked
path: packages/insomnia/dist/linux-unpacked
# upload/download-artifact go through zip and drop Unix file modes, so
# the binary and its helper executables (e.g. chrome_crashpad_handler)
# would otherwise fail to spawn with EACCES.
- name: Make Insomnia binary executable
run: |
cd packages/insomnia/dist/linux-unpacked
chmod +x insomnia
for f in chrome_crashpad_handler chrome-sandbox; do
if [ -f "$f" ]; then chmod +x "$f"; fi
done
- name: Restore node_modules
id: node-modules-cache
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5
with:
path: |
node_modules
packages/*/node_modules
key: node-modules-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
- name: Install dependencies
if: steps.node-modules-cache.outputs.cache-hit != 'true'
run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install Electron system dependencies
working-directory: packages/insomnia-test
run: npx playwright install-deps chromium
- name: Run Playwright tests (shard ${{ matrix.shardIndex }}/${{ matrix.shardTotal }})
working-directory: packages/insomnia-test
env:
INSOMNIA_BINARY: ${{ github.workspace }}/packages/insomnia/dist/linux-unpacked/insomnia
PLAYWRIGHT_BLOB_OUTPUT_NAME: blob-report-${{ matrix.shardIndex }}.zip
run: |
xvfb-run --auto-servernum npx playwright test --reporter=list,html,blob --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
# Two paths here (not just coverage/raw/) because v8-report.json (see
# misc/coverage.ts) must live as a sibling of raw/, not inside it — the
# upload computes a common ancestor across both, so raw/'s own
# contents now land one level deeper in the artifact, under `raw/`.
- name: Upload coverage (raw)
if: ${{ !cancelled() && env.COVERAGE == 'true' }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: coverage-raw-${{ matrix.shardIndex }}
path: |
packages/insomnia-test/coverage/raw/
packages/insomnia-test/coverage/v8-report.json
retention-days: 1
if-no-files-found: ignore
- name: Upload Playwright report
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
if: ${{ !cancelled() }}
with:
name: playwright-report-${{ matrix.shardIndex }}
path: packages/insomnia-test/playwright-report/
retention-days: 1
if-no-files-found: ignore
- name: Upload blob report
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
if: ${{ !cancelled() }}
with:
name: blob-report-${{ matrix.shardIndex }}
path: packages/insomnia-test/blob-report/blob-report-${{ matrix.shardIndex }}.zip
retention-days: 1
if-no-files-found: ignore
# ---------------------------------------------------------------------------
# This job doesn't gate the workflow — parallel-test (no continue-on-error)
# already fails the run on its own if any shard fails. This job only
# merges every shard's raw V8 coverage data (collected only when the
# workflow-level COVERAGE env is 'true') into one coverage
# report, and every shard's blob report into one full-suite Playwright
# report — both run on every trigger this workflow has (push and
# pull_request are the only two, so no `if` gate is needed on either).
# Each merge step is its own `continue-on-error: true` so a merge failure
# never fails this job. It finishes by deleting every per-shard
# intermediate (coverage-raw-*, playwright-report-*, blob-report-*),
# leaving only the merged artifact(s).
# ---------------------------------------------------------------------------
insomnia-test-result:
name: Insomnia Test Result
needs: parallel-test
if: ${{ !cancelled() }}
runs-on: ubuntu-24.04
permissions:
contents: read
packages: read
actions: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: audit
- name: Checkout repo
continue-on-error: true
uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
persist-credentials: false
- name: Setup Node.js
continue-on-error: true
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
with:
node-version-file: .nvmrc
# Full install (not just monocart-coverage-reports) so `npx playwright
# merge-reports` below is the exact same Playwright version the shards
# generated their blob reports with.
- name: Restore node_modules
continue-on-error: true
uses: actions/cache/restore@caa296126883cff596d87d8935842f9db880ef25 # v5
with:
path: |
node_modules
packages/*/node_modules
key: node-modules-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
fail-on-cache-miss: true
# Coverage raw data can embed `file:` sourcemap URLs pointing into this
# build output (see parallel-test's own restore step). Without it
# here, sourcemap resolution silently fails for those
# scripts during the merge below, deflating line/statement/branch
# coverage relative to what each shard reports locally.
- name: Download Insomnia binary
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: insomnia-linux-unpacked
path: packages/insomnia/dist/linux-unpacked
- name: Download raw coverage from all shards
if: ${{ env.COVERAGE == 'true' }}
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: coverage-raw
pattern: coverage-raw-*
# Uses our own line/statement/branch/function union across each shard's
# own (already-correct) v8-json report, instead of monocart's built-in
# multi-shard raw-coverage merge (misc/merge-coverage.ts) — that merge
# has a bug where combining a script's coverage across shards with very
# different coverage levels can produce a result *lower* than any
# individual shard reported on its own. See misc/merge-coverage-union.ts
# for the full writeup.
- name: Merge coverage reports
id: merge-coverage
if: ${{ env.COVERAGE == 'true' }}
continue-on-error: true
working-directory: packages/insomnia-test
run: node misc/merge-coverage-union.ts ../../coverage-raw
- name: Upload merged coverage report
if: ${{ env.COVERAGE == 'true' }}
continue-on-error: true
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: coverage-report
path: packages/insomnia-test/coverage-merged/
retention-days: 30
- name: Download blob reports from all shards
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
path: all-blob-reports
pattern: blob-report-*
merge-multiple: true
- name: Merge Playwright reports
id: merge-report
continue-on-error: true
working-directory: packages/insomnia-test
run: npx playwright merge-reports --reporter=html ../../all-blob-reports
- name: Upload merged Playwright report
continue-on-error: true
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: playwright-report
path: packages/insomnia-test/playwright-report/
retention-days: 30
# Runs only after every shard passed: a failed run keeps all its
# intermediates so "Re-run failed jobs" still has the binary and the
# other shards' blob/coverage data to merge with. `gh api` instead of
# a third-party action keeps this within the repo's pinned-action set.
- name: Delete per-shard intermediate artifacts
if: ${{ always() && needs.parallel-test.result == 'success' }}
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
run: |
gh api --paginate "repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/artifacts" \
--jq '.artifacts[] | select(.name | test("^(coverage-raw-|playwright-report-|blob-report-)") or . == "insomnia-linux-unpacked") | "\(.id) \(.name)"' |
while read -r id name; do
echo "Deleting artifact $name ($id)"
gh api -X DELETE "repos/${{ github.repository }}/actions/artifacts/$id" || echo "::warning::Failed to delete $name"
done
- name: Warn if a merge step failed
if: ${{ always() && (steps.merge-coverage.outcome == 'failure' || steps.merge-report.outcome == 'failure') }}
run: |
{
echo "### :warning: Insomnia Test report merge failed"
echo "- Merge coverage reports: ${{ steps.merge-coverage.outcome }}"
echo "- Merge Playwright reports: ${{ steps.merge-report.outcome }}"
echo "The merged artifacts may be missing; per-shard artifacts are still available."
} >> "$GITHUB_STEP_SUMMARY"