hub-client: UI/UX modernization Phase 1 — component consistency #1672
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: TS Test Suite | |
| on: | |
| workflow_dispatch: | |
| push: | |
| branches: | |
| - main | |
| - kyoto | |
| pull_request: | |
| branches: | |
| - main | |
| - kyoto | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| PANDOC_VERSION: "3.8.3" | |
| # Deno version for engine-host-deno vitest + deno-test + bundle freshness gate. | |
| DENO_VERSION: "2.9.0" | |
| jobs: | |
| test-suite: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest] | |
| runs-on: ${{ matrix.os }} | |
| name: Run test suite | |
| if: github.repository == 'quarto-dev/q2' | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v6 | |
| # Fix mtimes IMMEDIATELY after checkout, before anything else | |
| - name: Restore file modification times | |
| shell: bash | |
| run: | | |
| git ls-files | while read file; do | |
| time=$(git log -1 --format='@%ct' -- "$file" 2>/dev/null || echo '@0') | |
| [ "$time" != "@0" ] && touch -d "$time" "$file" 2>/dev/null || true | |
| done | |
| - name: Set up Homebrew | |
| if: runner.os == 'macOS' | |
| id: set-up-homebrew | |
| uses: Homebrew/actions/setup-homebrew@main | |
| # Consistent Rust setup for both platforms. Use the named `@nightly` | |
| # ref (not `@master`, which hard-errors without an explicit | |
| # `toolchain:` input) — it installs the nightly toolchain; cargo | |
| # then reads the pinned dated nightly from rust-toolchain.toml | |
| # (bd-at72) at build time. Matches the existing pattern on main. | |
| - name: Set up Rust nightly | |
| uses: dtolnay/rust-toolchain@nightly | |
| with: | |
| targets: wasm32-unknown-unknown | |
| components: rust-src | |
| - name: Output rust version | |
| shell: bash | |
| run: rustup --version | |
| # # Cache Rust AFTER toolchain is set up | |
| # - name: Cache Rust dependencies | |
| # uses: Swatinem/rust-cache@v2 | |
| # with: | |
| # cache-on-failure: true | |
| # Cache cargo-nextest and insta separately to avoid reinstalling | |
| - name: Install nextest | |
| uses: taiki-e/install-action@nextest | |
| - name: Install cargo-insta | |
| uses: taiki-e/install-action@v2 | |
| with: | |
| tool: cargo-insta | |
| # Pandoc setup (pinned to match Quarto 1.9) | |
| - name: Set up Pandoc (Linux) | |
| if: runner.os == 'Linux' | |
| run: | | |
| curl -LO "https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/pandoc-${PANDOC_VERSION}-1-amd64.deb" | |
| sudo dpkg -i "pandoc-${PANDOC_VERSION}-1-amd64.deb" | |
| shell: bash | |
| - name: Set up Pandoc (macOS) | |
| if: runner.os == 'macOS' | |
| run: | | |
| curl -LO "https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/pandoc-${PANDOC_VERSION}-arm64-macOS.pkg" | |
| sudo installer -pkg "pandoc-${PANDOC_VERSION}-arm64-macOS.pkg" -target / | |
| shell: bash | |
| # tree-sitter setup | |
| - name: Set up tree-sitter CLI (Linux) | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libc6-dev gcc-multilib | |
| curl -LO https://github.com/tree-sitter/tree-sitter/releases/download/v0.25.8/tree-sitter-linux-x86.gz | |
| gunzip tree-sitter-linux-x86.gz | |
| chmod +x tree-sitter-linux-x86 | |
| sudo mv tree-sitter-linux-x86 /usr/local/bin/tree-sitter | |
| - name: Set up tree-sitter CLI (macOS) | |
| if: runner.os == 'macOS' | |
| run: brew install tree-sitter-cli | |
| # TypeScript workspace build | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| cache: 'npm' | |
| - name: Install npm dependencies | |
| shell: bash | |
| run: npm ci | |
| # Deno — needed for engine-host-deno deno-test and the freshness gate. | |
| # denoland/setup-deno is safe here: this workflow has no "Free disk space" | |
| # step (and no remove_tool_cache invariant to preserve). | |
| - name: Set up Deno | |
| uses: denoland/setup-deno@v2 | |
| with: | |
| deno-version: ${{ env.DENO_VERSION }} | |
| # WASM build for hub-client (must happen before TypeScript build) | |
| - name: Set up Clang (Linux) | |
| if: runner.os == 'Linux' | |
| uses: egor-tensin/setup-clang@v2 | |
| with: | |
| version: latest | |
| platform: x64 | |
| - name: Set up LLVM (macOS) | |
| if: runner.os == 'macOS' | |
| run: brew install llvm | |
| shell: bash | |
| - name: Install wasm-bindgen-cli | |
| shell: bash | |
| run: cargo install wasm-bindgen-cli --version 0.2.108 | |
| # wasm-opt (binaryen) is `build:wasm` step 3 (live-share payload | |
| # plan Phase 1). Pinned to the version local dev uses (Homebrew | |
| # binaryen 132) so CI and dev builds produce the same bytes. | |
| - name: Install binaryen (wasm-opt) | |
| shell: bash | |
| run: npm install -g binaryen@132.0.0 | |
| - name: Build WASM module | |
| shell: bash | |
| run: | | |
| cd hub-client | |
| npm run build:all | |
| - name: Run hub-client tests | |
| shell: bash | |
| run: | | |
| cd hub-client | |
| npm run test:ci | |
| # engine-host-deno: vitest suite (105+ Node-side harness tests) | |
| - name: Run engine-host-deno tests (vitest) | |
| shell: bash | |
| run: npm run test -w @quarto/engine-host-deno | |
| # engine-host-deno: deno-native test (the one leg that requires Deno) | |
| - name: Run engine-host-deno deno test | |
| shell: bash | |
| run: deno test --allow-all ts-packages/quarto-engine-host-deno/src/deno-host.deno-test.ts | |
| # plan1a.6 Phase 2 (Deno dial-back) — seam #8: connectControl round-trip | |
| # over a REAL loopback socket (Deno.listen({ port: 0 }) in-test); no mock | |
| # Deno.Conn. CI-only tier (cannot be a vitest test — the module references | |
| # Deno.*). `--sloppy-imports` lets deno's type-checker resolve | |
| # @quarto/types' `.js` internal specifiers pulled in transitively via | |
| # ./types.ts (same reason the wire-parity step below needs it). | |
| - name: Run engine-host-deno control-transport deno test | |
| shell: bash | |
| run: deno test --allow-all --sloppy-imports ts-packages/quarto-engine-host-deno/src/control-transport.deno-test.ts | |
| # T-Gate-parity: TS↔Rust wire-dual parity (Plan 2 Phase B gate). Reads the | |
| # Rust-serialized fixture (crates/quarto-core/tests/fixtures/ts_wire_parity.json, | |
| # produced by the regen-gated #[test] in ts_protocol.rs) and set-equates each | |
| # instance's keys against a KEYS list pinned to the TS wire type via | |
| # `satisfies`/`_Exhaustive`. `--sloppy-imports` lets deno's type-checker | |
| # resolve @quarto/types' `.js` internal specifiers (so the compile guards | |
| # are checked here, not just the runtime set-equality). @quarto/types is | |
| # mapped in the repo-root deno.jsonc. | |
| - name: Run engine-host-deno wire-parity deno test | |
| shell: bash | |
| run: deno test --allow-all --sloppy-imports ts-packages/quarto-engine-host-deno/src/wire-parity.deno-test.ts | |
| # Bundle freshness gate: rebuild engine-host-deno.js from source and assert | |
| # the committed bytes are unchanged. Fails if someone edited TS but forgot | |
| # to run `npm run bundle`. build-info.json is gitignored (volatile builtAt); | |
| # the diff targets only the bundle itself. | |
| - name: Check engine-host-deno bundle freshness | |
| shell: bash | |
| run: | | |
| npm run bundle -w @quarto/engine-host-deno | |
| git diff --exit-code -- ts-packages/quarto-engine-host-deno/dist/engine-host-deno.js | |
| # preview-renderer's integration tier imports wasm-quarto-hub-client | |
| # (aliased in vitest.integration.config.ts:53-55 to the committed | |
| # hub-client/wasm-quarto-hub-client symlink -> crates/.../pkg). Without it | |
| # 26 of 50 files fail on module resolution, which is why this one suite | |
| # lives here rather than in workspace-ts-suites: it must run below the | |
| # `Build WASM module` step. GH #250. | |
| - name: Run preview-renderer integration tests | |
| shell: bash | |
| run: npm run test:integration -w ts-packages/preview-renderer | |
| # ── Workspace TS suites (GH #250) ──────────────────────────────────────── | |
| # | |
| # A separate job on purpose. These suites need only Node + npm ci + the | |
| # ts-packages dists — no Rust, no WASM, no pandoc/tree-sitter/deno. Keeping | |
| # them out of `test-suite` means a red hub-client test, a deno step or the | |
| # bundle-freshness gate cannot mask them, and they are not serialised behind | |
| # an uncached WASM build on both matrix legs. The one suite that does need | |
| # the WASM (preview-renderer's integration tier) lives in `test-suite`. | |
| workspace-ts-suites: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest] | |
| runs-on: ${{ matrix.os }} | |
| name: Workspace TS suites | |
| if: github.repository == 'quarto-dev/q2' | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v6 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: '24' | |
| cache: 'npm' | |
| - name: Install npm dependencies | |
| shell: bash | |
| run: npm ci | |
| # quarto-sync-client, quarto-hub-mcp and annotated-qmd resolve workspace | |
| # siblings through the `"import": "./dist/index.js"` export condition, and | |
| # hub-client bundles these packages from *source*, so nothing else builds | |
| # them. Build order is irrelevant — types resolve via src/ (see | |
| # crates/xtask/src/ts_packages.rs). Same guarded loop as | |
| # hub-client-e2e.yml:144-153. | |
| - name: Build ts-packages workspaces | |
| shell: bash | |
| run: | | |
| for pkg in ts-packages/*/; do | |
| if [ -f "$pkg/package.json" ]; then | |
| npm run build --workspace "$pkg" --if-present | |
| fi | |
| done | |
| # `--help` exits 0 only after the whole ESM graph links, so a missing or | |
| # stale dependency dist fails here rather than in someone's MCP session. | |
| # This is the same smoke check cargo xtask verify step 6 runs. | |
| - name: Smoke-check quarto-hub-mcp module graph | |
| shell: bash | |
| run: node ts-packages/quarto-hub-mcp/dist/index.js --help | |
| # Suites that are green as-is (GH #250 census, 2026-08-22). Explicit | |
| # `npm test` rather than each package's `test:ci`: kanban's test:ci chains | |
| # a test:wasm leg whose glob matches zero files, and preview-runtime's | |
| # test:wasm names a config file that does not exist. | |
| - name: Run preview-renderer unit tests | |
| shell: bash | |
| run: npm test -w ts-packages/preview-renderer | |
| - name: Run preview-runtime unit tests | |
| shell: bash | |
| run: npm test -w ts-packages/preview-runtime | |
| - name: Run quarto-api tests | |
| shell: bash | |
| run: npm test -w ts-packages/quarto-api | |
| - name: Run quarto-automerge-schema tests | |
| shell: bash | |
| run: npm test -w ts-packages/quarto-automerge-schema | |
| - name: Run wasm-js-bridge tests | |
| shell: bash | |
| run: npm test -w ts-packages/wasm-js-bridge | |
| - name: Run q2-preview-spa tests | |
| shell: bash | |
| run: | | |
| npm test -w q2-preview-spa | |
| npm run test:integration -w q2-preview-spa | |
| - name: Run kanban demo tests | |
| shell: bash | |
| run: | | |
| npm test -w q2-demos/kanban | |
| npm run test:integration -w q2-demos/kanban | |
| - name: Run trace-viewer tests | |
| shell: bash | |
| run: npm test -w trace-viewer | |
| # These two resolve workspace siblings through dist/, so they need the | |
| # "Build ts-packages workspaces" step above. hub-mcp is the heaviest | |
| # suite in this job (~32s: bundle.test.ts runs esbuild in beforeAll). | |
| # | |
| # --retry=2: `src/doc-inventory.test.ts > getDocInventory > reports | |
| # index, text, and binary docs with states and heads` fails | |
| # intermittently on an async peer-connection race — measured at 2 | |
| # failures in 8 local runs (~25%), which across this job's two OS legs | |
| # is a ~44% false-red rate per PR. `--retry=2` brings that to roughly 3% | |
| # while keeping all 137 tests gating. The underlying race is tracked as | |
| # bd-7hd2gzf1. | |
| - name: Run quarto-sync-client tests | |
| shell: bash | |
| run: npm test -w ts-packages/quarto-sync-client -- --retry=2 | |
| - name: Run quarto-hub-mcp tests | |
| shell: bash | |
| run: npm test -w ts-packages/quarto-hub-mcp |