Skip to content

T7: notify()

T7: notify() #263

Workflow file for this run

name: CI
# Runs the full per-change gate on every push to main and every pull request:
# lint (biome) → typecheck (tsc via turbo) → dependency-cycle check (madge) →
# tests with the per-package coverage gate (vitest via turbo — each package
# enforces its own 100% line/fn/stmt + ≥90% branch threshold).
#
# ⚠️ GITHUB-HOSTED RUNNERS, ON PURPOSE. This repository is PUBLIC. A self-hosted runner must never run
# a public repository's workflows: any fork can open a pull request, and `pull_request` workflows
# execute the PR's own code — on a self-hosted machine that is arbitrary code execution on a host that
# also holds other repositories' checkouts and credentials. The org's runner group refuses public
# repositories for exactly that reason (allows_public_repositories=false), so a self-hosted label here
# would not even start. Standard GitHub-hosted runners are free for public repositories, which also
# removes the old "hosted minutes are exhausted" constraint. Linux runners (ubuntu-latest) are the
# cheapest and most plentiful; `pnpm ci:local` runs the same gate on a developer's Mac.
#
# What follows from hosted runners: every run starts on a clean VM, so there is no leaked-process or
# stale-port hygiene to do between runs (the port steps in the e2e job are kept; they are cheap and
# still guard against a hung harness inside one run).
on:
push:
branches: [main]
pull_request:
# Least privilege: nothing in this workflow writes to the repository. A job that needs more asks for it
# itself.
permissions:
contents: read
# One in-flight run per ref; a new push cancels the previous run.
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
check:
name: Lint · Typecheck · Cycles · Coverage
runs-on: ubuntu-latest
# The runner is shared. Without a bound, a hung vitest worker holds it against
# every other repo's queue until someone notices by hand.
timeout-minutes: 45
steps:
- uses: actions/checkout@v7
# pnpm first (setup-node's pnpm cache needs pnpm on PATH). Version comes from
# the root package.json `packageManager` field (pnpm@11.3.0).
- name: Install pnpm
uses: pnpm/action-setup@v6
# vitest 4 (rolldown) requires Node ≥ 20; the SDK targets Node ≥ 18 at runtime,
# but the toolchain runs on 22.
#
# NO `cache: pnpm` since the move. That option round-trips the pnpm store through
# GitHub's cache service, which only pays off when the machine is thrown away. Here
# the store lives at ~/Library/pnpm/store and survives every run — actions/checkout
# wipes node_modules (`git clean -ffdx`) but never touches the store — so the cache
# would be uploading and re-downloading something already on disk.
- name: Setup Node
uses: actions/setup-node@v7
with:
node-version: 22
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Lint (biome)
run: pnpm lint
- name: Typecheck (tsc)
run: pnpm typecheck
- name: Dependency cycles (madge)
run: pnpm check:cycles
# Every publishable package is public, carries npm's metadata and ships the one version the SDK
# reports about itself — so a release never discovers a drift at publish time.
- name: Publishable packages
run: pnpm check:publishable
# Per-package coverage gate (turbo runs each package's own test:coverage,
# each enforcing 100% line/fn/stmt + ≥90% branch). NOT the root merged
# `pnpm test:coverage`, whose single aggregate threshold the scaffold-only
# stub packages would drag below 100%.
- name: Test + per-package coverage gate (vitest)
run: pnpm exec turbo run test:coverage
# Harness-owned unit tests (e.g. the bundle-assertion library's negative suite). These live in a
# package with no test:coverage script, so `turbo run test:coverage` skipped them silently — the same
# <NONEXISTENT> hole the e2e job exists to close.
- name: Harness unit tests (vitest)
run: pnpm exec turbo run test:unit
# End-to-end harnesses: the REAL SDK booted in real node/bun/deno processes against a
# mock collector, plus the real-framework suites (nuxt / sveltekit / astro / next).
#
# These existed but were OUTSIDE the gate until 2026-07-27: the `check` job runs
# `turbo run test:coverage`, and the four harness packages define only `test:e2e`, so
# turbo reported `<NONEXISTENT>` and skipped them silently. The adversarial review
# (docs/review/e2e-harnesses.md, SEV1 #1) identified that as the root cause of most of
# its ~90 SEV1 findings surviving ~100% unit coverage.
#
# Separate job so it runs alongside `check` and so its extra runtime setup
# (bun, deno) is isolated. Bun and Deno are installed deliberately: the harness SKIPS
# a runtime whose binary is absent (packages/instrumentation-tests/test/runtimes.ts),
# so without them CI would go green while testing strictly less.
#
# Hosted runners are provisioned per job, so the two jobs run in parallel.
e2e:
name: E2E (node · bun · deno · real frameworks)
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v7
- name: Install pnpm
uses: pnpm/action-setup@v6
# No `cache: pnpm` — see the note in the `check` job.
- name: Setup Node
uses: actions/setup-node@v7
with:
node-version: 22
- name: Setup Bun
uses: oven-sh/setup-bun@v2
- name: Setup Deno
uses: denoland/setup-deno@v2
with:
deno-version: v2.x
- name: Install dependencies
run: pnpm install --frozen-lockfile
# Fail loudly if a runtime the harness expects is missing, rather than letting
# runtimes.ts silently skip it and report green.
#
# ⚠️ These three commands were never the thing that could go missing. What the node target
# actually needs is `tsx`, and the harness resolved it only from the WORKSPACE ROOT's
# node_modules/.bin — where pnpm does not put a package's own devDependency, and where
# `git clean -ffdx` removes the stale copy a developer's machine happens to have. So node
# resolved to `undefined` here for as long as this job has existed, its whole instrumentation
# suite was skipped, and the run went green on 133 tests where a developer ran 195. The real
# guard is now inside the harness (instrumentation.e2e.ts THROWS when node does not resolve);
# these version checks remain as a fast, readable failure for a genuinely missing runtime.
- name: Verify the runtime matrix is complete
run: |
node --version
bun --version
deno --version
# Playwright ships a downloader, not the browsers: `pnpm install` does not fetch them, so
# `browser.e2e.ts` fails with "Executable doesn't exist at .../chrome-headless-shell".
# Scoped to chromium — it is the only engine the browser harness drives.
# On a fresh hosted VM this always downloads; it is a few seconds, not a failure.
- name: Install Playwright's Chromium
run: pnpm --filter @bugsee/instrumentation-tests exec playwright install chromium
# The nuxt/sveltekit/astro harnesses each boot a real dev server on a FIXED port. A fresh hosted VM
# starts clean, so this only guards a hung harness within one run (and a developer's machine, where
# the same script is used). The script reads the ports out of the harnesses and fails if it can no
# longer find them, so moving a port cannot quietly disarm it.
- name: Free the harness ports
run: scripts/free-e2e-ports.sh
- name: E2E suites (turbo run test:e2e)
run: pnpm exec turbo run test:e2e
# `always()` so a CANCELLED or failed run cleans up after itself instead of leaving
# the mess for whoever runs next — which is the run that would get blamed for it.
- name: Release the harness ports
if: always()
run: scripts/free-e2e-ports.sh