diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 12cd04e..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,114 +0,0 @@ -# Pulsar — Windows build gate. -# -# Runs the same build pipeline release.yml uses, minus packaging, -# minus -Full (no CEF / no obs-browser — keeps the run under -# ~20 min instead of ~40). Produces `pulsar.exe` so the binary-export -# license invariant (#3) can be verified before any code lands on -# main. -# -# Why every PR pays a full Windows build : the binary-export check is -# the only mechanism that catches a plugin starting to publish symbols -# (a __declspec or EXPORT_SYMBOL is the source-side fingerprint, but -# upstream patches or future linker flags could in principle slip a -# symbol through other paths). Static grep + binary dumpbin is the -# defence-in-depth this license posture needs. -# -# Mac (Phase 7) and Linux (Phase 8) join the matrix when those -# targets light up. They will share the same gate structure. - -name: build - -on: - pull_request: - branches: [main] - push: - branches: [main] - -jobs: - windows-x64: - name: build + license gate (windows-x64) - runs-on: windows-2022 - timeout-minutes: 60 - - steps: - - name: Checkout (with submodules) - uses: actions/checkout@v4 - with: - submodules: recursive - fetch-depth: 0 - - - name: License isolation source-tree audit - # Same gate license-isolation.yml runs, repeated here so the - # build job fails fast on a sourcecode-level violation before - # paying the build cost. - shell: bash - run: bash scripts/check-license-isolation.sh - - - name: Cache obs-deps + Qt6 - # build-win.ps1 fetches obs-deps, Qt6, and (with -Full) CEF - # into upstream/.deps/. The downloads dominate cold-cache - # build time. Key on the build-win.ps1 hash (so a deps-list - # change invalidates) + the upstream submodule pointer (so a - # libobs bump invalidates). - uses: actions/cache@v4 - with: - path: upstream/.deps - key: obs-deps-${{ runner.os }}-${{ hashFiles('scripts/build-win.ps1', '.gitmodules') }} - restore-keys: | - obs-deps-${{ runner.os }}- - - - name: Configure git identity - # build-win.ps1 calls `git am` to apply patches/ onto the - # upstream/ submodule, which writes a commit and needs a - # configured user.name / user.email — absent on a fresh runner. - shell: pwsh - run: | - git config --global user.email "ci@pulsar.zablaboratory" - git config --global user.name "Pulsar CI" - - - name: Install CMake 3.28+ - uses: lukka/get-cmake@latest - with: - cmakeVersion: '3.30.0' - - - name: Setup MSVC toolchain - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: x64 - - - name: Build (no -Full, headless only) - # Skipping -Full means no CEF / no obs-browser ; that codepath - # is exercised by release.yml on every tag. For PR validation - # we only need pulsar.exe + the four Pulsar plugins to confirm - # the source compiles + nothing exports. - shell: pwsh - run: ./scripts/build-win.ps1 - - - name: Verify binary export tables (#3) - # LICENSE-INVARIANTS.md (#3) gate. Scans pulsar.exe (must be - # empty), pulsar-browser-page.exe (must be empty), and every - # Pulsar-owned plugin DLL (only OBS module ABI symbols allowed). - # See scripts/check-binary-exports.ps1 for the full policy. - shell: pwsh - run: ./scripts/check-binary-exports.ps1 - - - name: Setup Python (for offline probes) - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install probe deps - shell: bash - run: | - python -m pip install --upgrade pip - pip install websockets - - - name: Run offline probe suite (CTest) - # Spawns pulsar.exe, waits for the PULSAR_READY sentinel, runs - # probe-{websocket,events,multi-stream,adaptive,record}.py - # against it, asserts exit 0. The live Twitch broadcast probe - # is excluded -- it lives in live-test.yml. - # See scripts/run-probes.ps1 + top-level CMakeLists.txt for the - # CTest registration. - shell: pwsh - run: ctest --test-dir build --output-on-failure -C RelWithDebInfo diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index ac84ffe..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,110 +0,0 @@ -# Pulsar — lightweight per-PR checks. -# -# Fast Linux-only sanity checks that don't need the full Windows -# build. License isolation has its own workflow ; these are the -# non-license invariants of repo hygiene. -# -# Two jobs : -# - patch-lint : every patches/*.patch must apply cleanly on -# upstream/ at the pinned submodule SHA. A -# patch that doesn't apply means the next -# release is broken. -# - plugin-metadata : every plugins// must carry CMakeLists.txt -# + README.md. Catches a half-added plugin -# that the build silently ignored. -# -# Mac / Linux build matrices live in build.yml when they light up. - -name: ci - -on: - pull_request: - branches: [main] - push: - branches: [main] - -jobs: - patch-lint: - name: patches/ apply cleanly on upstream pinned SHA - runs-on: ubuntu-latest - steps: - - name: Checkout (with submodules) - uses: actions/checkout@v4 - with: - submodules: recursive - fetch-depth: 0 - - - name: Configure git identity - # `git am` writes commits ; without a configured user identity - # it aborts. Local-only — nothing is pushed. - run: | - git config --global user.email "ci@pulsar.zablaboratory" - git config --global user.name "Pulsar CI" - - - name: Reset upstream to pinned SHA - # build-win.ps1 does this same reset before applying patches. - # We replicate the deterministic state here so a developer - # working in upstream/ with stray commits doesn't poison the - # lint result. - run: | - set -e - pinned=$(git submodule status --cached upstream | awk '{print $1}' | sed 's/^[+-]*//') - if [ -z "$pinned" ]; then - echo "::error::Could not read pinned upstream SHA." - exit 1 - fi - echo "Pinned upstream SHA : $pinned" - cd upstream - git reset --hard "$pinned" - git clean -fdx - - - name: Apply each patch in order - run: | - set -e - shopt -s nullglob - patches=(patches/*.patch) - if [ ${#patches[@]} -eq 0 ]; then - echo "::notice::No patches under patches/ — nothing to apply." - exit 0 - fi - cd upstream - for p in "${patches[@]}"; do - full="../$p" - echo "::group::git am ../$p" - if ! git am --3way "$full"; then - echo "::error::Patch $p does not apply on upstream pinned SHA." - git am --abort || true - exit 1 - fi - echo "::endgroup::" - done - echo "::notice::All ${#patches[@]} patch(es) apply cleanly." - - plugin-metadata: - name: every plugins/* carries CMakeLists.txt + README.md - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Verify metadata - run: | - set -e - fail=0 - shopt -s nullglob - for dir in plugins/*/; do - name=$(basename "$dir") - if [ ! -f "$dir/CMakeLists.txt" ]; then - echo "::error file=$dir::Missing CMakeLists.txt under plugins/$name." - fail=1 - fi - if [ ! -f "$dir/README.md" ]; then - echo "::error file=$dir::Missing README.md under plugins/$name." - fail=1 - fi - done - if [ $fail -ne 0 ]; then - echo "::error::Plugin metadata sanity failed." - exit 1 - fi - count=$(ls -1d plugins/*/ 2>/dev/null | wc -l) - echo "::notice::Plugin metadata sanity passed for $count plugin(s)." diff --git a/.github/workflows/license-isolation.yml b/.github/workflows/license-isolation.yml deleted file mode 100644 index 743414d..0000000 --- a/.github/workflows/license-isolation.yml +++ /dev/null @@ -1,57 +0,0 @@ -# License isolation gate. -# -# Pulsar is GPL-2.0-or-later. Consumers (Prism, future apps) keep their -# own license only because of the four invariants in -# LICENSE-INVARIANTS.md (process boundary, WS-only IPC, no FFI on the -# consumer side, no copy-paste of source). This workflow enforces what -# can be enforced statically on the Pulsar source tree. -# -# Two jobs : -# - source-grep : forbidden FFI / NAPI / node-gyp / consumer-stage -# patterns in plugins/ + packages/ + scripts/ -# - npm-pack-audit : every workspace's `npm pack --dry-run` content -# must not include C/C++ source or node-gyp -# manifests -# -# Both jobs delegate to scripts under scripts/check-*.sh so the same -# logic runs in release.yml + publish-npm.yml on the path that -# actually ships artefacts. Logic lives in one place, multiple -# trigger points. -# -# The harder check (binary `dumpbin /exports pulsar.exe` empty) lives -# in release.yml because it requires a full Windows build — too heavy -# for every PR. release.yml fails early if the binary exports anything, -# blocking the GitHub Release before artefacts are uploaded. - -name: license-isolation - -on: - pull_request: - branches: [main] - push: - branches: [main] - -jobs: - source-grep: - name: source-tree audit (#1, #3, #4) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Run source-tree audit - run: bash scripts/check-license-isolation.sh - - npm-pack-audit: - name: npm tarball audit (#3, #4) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - - name: Install workspaces (skip pulsar.exe download) - env: - PULSAR_BUNDLE_SKIP_POSTINSTALL: '1' - run: npm install --no-audit --no-fund --force - - name: Run npm tarball audit - run: bash scripts/check-npm-pack-audit.sh diff --git a/.github/workflows/live-test.yml b/.github/workflows/live-test.yml deleted file mode 100644 index 560b15b..0000000 --- a/.github/workflows/live-test.yml +++ /dev/null @@ -1,271 +0,0 @@ -# Pulsar — Twitch live broadcast end-to-end test. -# -# Spawns pulsar.exe, points its CEF browser_source at a locally-served -# test scene, pushes a real broadcast to Twitch using -# secrets.TWITCH_STREAM_KEY, polls metrics, asserts thresholds, cleans -# up, then re-encodes the recording into a smaller MP4 for the README -# `