Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion .github/workflows/cef-learning-harness.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@
# CEF-rendering regression introduced by an unrelated app-source PR is still
# caught promptly (informationally, non-blocking), not silently missed until
# someone next happens to touch a CEF-specific path.
#
# QNBS-v3: docs/cef/** deliberately excluded from the PR path list β€” a docs-only
# change (e.g. updating the competency matrix) can't regress the built host, so it
# doesn't need a ~30min rebuild; code changes under apps/desktop-cef/** or
# scripts/cef/** still trigger this regardless of whether docs are also touched.
# The push-to-main trigger stays deliberately unscoped (no `paths:`) precisely so an
# unrelated app-source change that alters dist/'s real inputs is still caught after
# merge β€” narrowing that trigger to apps/desktop-cef/** only would reintroduce the
# PR #388 gap this design already closed.
#
# QNBS-v3: `fast-gate` (lint+typecheck) runs before `harness` so a trivial code-style
# or type error fails in ~2-3min instead of after the full ~30min SDK+CMake+Rust+Xvfb
# cycle. `Swatinem/rust-cache` caches cargo's registry/git dirs for the rust-core
# crate Corrosion builds via CMake, cutting redundant crates.io re-fetches.
# ============================================================

name: πŸ§ͺ CEF Learning Harness
Expand All @@ -34,7 +48,6 @@ on:
paths:
- 'apps/desktop-cef/**'
- 'scripts/cef/**'
- 'docs/cef/**'
- '.github/workflows/cef-learning-harness.yml'
push:
branches: [main]
Expand All @@ -48,10 +61,29 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
# QNBS-v3: fast, cheap checks (lint/typecheck) gate the ~30min CEF build so a trivial
# code-style or type error doesn't burn the full SDK-fetch+CMake+Rust+Xvfb cycle before
# surfacing. Duplicates a subset of ci.yml's quality job (which runs in parallel anyway) β€”
# deliberate trade: a couple extra lint/typecheck minutes vs. up to 30 wasted on a fail-fast case.
fast-gate:
name: ⚑ Fast gate (lint + typecheck)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
Comment thread
qnbs marked this conversation as resolved.
Outdated
with:
persist-credentials: false
- uses: ./.github/actions/setup
- name: Lint (Biome)
run: pnpm run lint
- name: Typecheck (tsgo)
run: npx tsgo --project tsconfig.tsgo.json --noEmit --checkers 4

harness:
name: πŸ§ͺ CEF host build, dependency inventory, launch-cycle proof
runs-on: ubuntu-latest
timeout-minutes: 30
needs: [fast-gate]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
with:
Expand Down Expand Up @@ -86,6 +118,20 @@ jobs:
with:
toolchain: stable

# QNBS-v3: rust-core (apps/desktop-cef/rust-core) is built via CMake's corrosion_import_crate,
# invoking cargo under the hood β€” this caches the global registry/git dirs cargo uses regardless
# of where Corrosion places its target/ output, so repeat runs skip re-fetching crates.io deps.
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
workspaces: apps/desktop-cef/rust-core -> target

- name: Cache apt packages (CEF host + Wayland build deps)
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: /var/cache/apt/archives/*.deb
Comment thread
qnbs marked this conversation as resolved.
# QNBS-v3: bump the -v suffix if either apt-get install package list in this job changes
key: apt-cef-harness-deps-v1

- name: Install worldscript_host build dependencies
run: |
sudo apt-get update
Expand Down
91 changes: 76 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,43 @@ jobs:
if: github.event_name == 'pull_request'
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5

# ----------------------------------------------------------
# 0b. CHANGE DETECTION: path-scopes rust-tauri so a docs/frontend-only PR
# doesn't pay for a Rust toolchain + apt-get(libgtk/libwebkit) build it can't
# affect. Fails OPEN (tauri=true) on any ambiguity β€” base SHA missing/unreachable
# β€” so a detection error runs the real gate instead of silently skipping it.
# ----------------------------------------------------------
changes:
name: πŸ“‚ Detect changed paths
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
tauri: ${{ steps.filter.outputs.tauri }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
with:
fetch-depth: 0
persist-credentials: false

- name: Detect src-tauri changes
id: filter
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
BASE="${{ github.event.pull_request.base.sha }}"
else
BASE="${{ github.event.before }}"
fi
if [ -z "$BASE" ] || [ "$BASE" = "0000000000000000000000000000000000000000" ] || ! git cat-file -e "$BASE" 2>/dev/null; then
echo "::notice::No usable base SHA to diff against β€” defaulting to tauri=true (fail open)"
echo "tauri=true" >> "$GITHUB_OUTPUT"
exit 0
fi
if git diff --name-only "$BASE" "${{ github.sha }}" | grep -qE '^(src-tauri/|\.github/workflows/ci\.yml$)'; then
echo "tauri=true" >> "$GITHUB_OUTPUT"
else
echo "tauri=false" >> "$GITHUB_OUTPUT"
fi

# ----------------------------------------------------------
# 1. QUALITY GATE: Lint + Typecheck + Tests (parallel matrix)
# ----------------------------------------------------------
Expand Down Expand Up @@ -177,16 +214,15 @@ jobs:
fail_ci_if_error: false

# ----------------------------------------------------------
# 2. BUILD: Production build + Pages artifact upload
# QNBS-v3: Stryker mutation removed from the PR/CI pipeline (2026-06-02) β€” it added noise as a
# flaky, non-gating check. Mutation now runs ONLY via the manual `.github/workflows/
# mutation.yml` (workflow_dispatch). To be re-integrated in a later iteration.
# 1b. RUST-TAURI: Tauri Rust Gate (fmt/check/clippy/test), path-scoped via `changes`
# ----------------------------------------------------------
rust-tauri:
name: πŸ¦€ Tauri Rust Gate
runs-on: ubuntu-latest
timeout-minutes: 20
needs: [security]
needs: [security, changes]
# QNBS-v3: skips for PRs that don't touch src-tauri/** β€” ci-success treats 'skipped' as pass for this job only
if: needs.changes.outputs.tauri == 'true'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
with:
Expand All @@ -195,6 +231,18 @@ jobs:
with:
toolchain: stable
components: rustfmt, clippy
# QNBS-v3: no cargo caching existed before β€” fmt/check/clippy/test each rebuilt the full
# dependency tree from scratch every run. Keyed on Cargo.lock + rustc version, so a toolchain
# bump or lockfile change invalidates cleanly rather than reusing stale artifacts.
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
workspaces: src-tauri -> target
- name: Cache apt packages (Tauri Linux build deps)
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: /var/cache/apt/archives/*.deb
Comment thread
qnbs marked this conversation as resolved.
# QNBS-v3: bump the -v suffix if the apt-get install package list below changes
key: apt-tauri-linux-deps-v1
- name: Install Linux Tauri build dependencies
run: |
sudo apt-get update
Expand All @@ -212,6 +260,12 @@ jobs:
working-directory: src-tauri
run: cargo test --locked

# ----------------------------------------------------------
# 2. BUILD: Production build + Pages artifact upload
# QNBS-v3: Stryker mutation removed from the PR/CI pipeline (2026-06-02) β€” it added noise as a
# flaky, non-gating check. Mutation now runs ONLY via the manual `.github/workflows/
# mutation.yml` (workflow_dispatch). To be re-integrated in a later iteration.
# ----------------------------------------------------------
build:
name: πŸ—οΈ Build
runs-on: ubuntu-latest
Expand Down Expand Up @@ -290,33 +344,40 @@ jobs:
path: ./dist

# ----------------------------------------------------------
# 3. CI SUCCESS: single required-status aggregator (security + quality + build)
# 3. CI SUCCESS: single required-status aggregator (security + quality + changes + rust-tauri + build + e2e + vrt)
# ----------------------------------------------------------
ci-success:
name: βœ… CI Success
runs-on: ubuntu-latest
timeout-minutes: 5
needs: [security, quality, rust-tauri, build, e2e, vrt]
needs: [security, quality, changes, rust-tauri, build, e2e, vrt]
if: always()
steps:
# QNBS-v3: rust-tauri may legitimately be 'skipped' (changes.outputs.tauri == 'false') β€” that's a pass, not a failure
- name: Verify all required jobs succeeded
run: |
if [ "${{ needs.security.result }}" != "success" ] || \
[ "${{ needs.quality.result }}" != "success" ] || \
[ "${{ needs.rust-tauri.result }}" != "success" ] || \
[ "${{ needs.build.result }}" != "success" ] || \
[ "${{ needs.e2e.result }}" != "success" ] || \
[ "${{ needs.vrt.result }}" != "success" ]; then
FAIL=0
[ "${{ needs.security.result }}" = "success" ] || FAIL=1
[ "${{ needs.quality.result }}" = "success" ] || FAIL=1
[ "${{ needs.changes.result }}" = "success" ] || FAIL=1
if [ "${{ needs.rust-tauri.result }}" != "success" ] && [ "${{ needs.rust-tauri.result }}" != "skipped" ]; then
FAIL=1
fi
[ "${{ needs.build.result }}" = "success" ] || FAIL=1
[ "${{ needs.e2e.result }}" = "success" ] || FAIL=1
[ "${{ needs.vrt.result }}" = "success" ] || FAIL=1
if [ "$FAIL" = "1" ]; then
echo "One or more required jobs did not succeed:"
echo " security: ${{ needs.security.result }}"
echo " quality: ${{ needs.quality.result }}"
echo " rust-tauri: ${{ needs.rust-tauri.result }}"
echo " changes: ${{ needs.changes.result }}"
echo " rust-tauri: ${{ needs.rust-tauri.result }} (skipped = OK, src-tauri untouched)"
echo " build: ${{ needs.build.result }}"
echo " e2e: ${{ needs.e2e.result }}"
echo " vrt: ${{ needs.vrt.result }}"
exit 1
fi
echo "All required jobs succeeded."
echo "All required jobs succeeded (or were legitimately skipped)."

# ----------------------------------------------------------
# 4. DEPLOY: GitHub Pages (only on main push)
Expand Down
Loading