From 57291e25ff7369417469fc53cce50c0e91f5fd04 Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Wed, 19 Aug 2026 07:13:40 +0200 Subject: [PATCH] =?UTF-8?q?feat(cef):=20real=20ldd-based=20Linux=20runtime?= =?UTF-8?q?=20linkage=20check=20(roadmap=20=C2=A744.3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing check-linux-runtime-deps.mjs only confirms dpkg *package* presence — a package can be installed and still not satisfy a binary's exact SONAME/version requirement, which dpkg presence alone doesn't prove. Both native-readiness.md and linux-runtime-notes.md have flagged this specific gap ("not ldd against the actual shipped .so files") since Wave 2's first spike. New check-linux-runtime-linkage.mjs runs ldd against the real, already-built worldscript_host executable and the real libcef.so CEF shipped into the same output directory (not a hypothetical package list), reporting any "=> not found" line as a genuine unresolved runtime dependency. Deliberately non-fatal by default, matching check-linux-runtime-deps.mjs's own reasoning — an honest inventory data point, not a gate, until a real compatibility floor is proven. Wired into cef-learning-harness.yml right after the build step. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/cef-learning-harness.yml | 4 + scripts/cef/check-linux-runtime-linkage.mjs | 94 +++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 scripts/cef/check-linux-runtime-linkage.mjs diff --git a/.github/workflows/cef-learning-harness.yml b/.github/workflows/cef-learning-harness.yml index 100a5c4fd..17294ac9b 100644 --- a/.github/workflows/cef-learning-harness.yml +++ b/.github/workflows/cef-learning-harness.yml @@ -108,6 +108,9 @@ jobs: - name: List worldscript_host output directory (diagnostic) run: ls -la build/worldscript_host/ + - name: Linux runtime linkage check (ldd against shipped .so files) + run: node scripts/cef/check-linux-runtime-linkage.mjs build/worldscript_host + - name: Serve production bundle + repeated launch/close proof run: | python3 -m http.server 8080 --directory dist & @@ -139,5 +142,6 @@ jobs: echo "- Pinned SDK: \`$(node -e "console.log(require('./scripts/cef/cef-version.json').cefVersion)")\`" >> "$GITHUB_STEP_SUMMARY" echo "- Cache hit: \`${{ steps.cef-cache.outputs.cache-hit }}\`" >> "$GITHUB_STEP_SUMMARY" echo "- worldscript_host built and repeated launch/close cycles proven against the real production bundle (dist/), under Xvfb." >> "$GITHUB_STEP_SUMMARY" + echo "- Linux runtime linkage (ldd against the shipped worldscript_host + libcef.so, not just dpkg package presence): see the \"Linux runtime linkage check\" step above." >> "$GITHUB_STEP_SUMMARY" echo "- Wayland smoke (best-effort, roadmap §44.2): \`${{ steps.wayland-smoke.outcome }}\`" >> "$GITHUB_STEP_SUMMARY" echo "- Not yet in scope: X11/Wayland matrix beyond this one runner, sandbox posture, accessibility smoke." >> "$GITHUB_STEP_SUMMARY" diff --git a/scripts/cef/check-linux-runtime-linkage.mjs b/scripts/cef/check-linux-runtime-linkage.mjs new file mode 100644 index 000000000..8e9065d9f --- /dev/null +++ b/scripts/cef/check-linux-runtime-linkage.mjs @@ -0,0 +1,94 @@ +#!/usr/bin/env node +/** + * CEF Linux runtime *linkage* check (docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md §44.3) — + * the specific gap docs/architecture/native-readiness.md and + * docs/cef/knowledge/linux-runtime-notes.md have both flagged as open since Wave 2's + * first spike: check-linux-runtime-deps.mjs only confirms a *package* is installed via + * dpkg, never that the *actual shipped* .so files this build produced can resolve their + * real runtime dependencies. A package can be installed and still not satisfy a binary's + * exact SONAME/version requirement; dpkg presence alone doesn't prove that. + * + * Runs `ldd` against the real, already-built worldscript_host executable and the real + * libcef.so CEF shipped into the same output directory (COPY_FILES, apps/desktop-cef/ + * CMakeLists.txt) — not a hypothetical package list. Reports any `=> not found` line as + * a genuine unresolved runtime dependency. + * + * Deliberately non-fatal by default, matching check-linux-runtime-deps.mjs's own + * reasoning: an honest inventory data point, not a gate on a specific distro's package + * set, until a real compatibility floor (§44.1) has been proven. Pass --strict once it + * has. + * + * Run: node scripts/cef/check-linux-runtime-linkage.mjs [--strict] + */ +import { execFileSync } from 'node:child_process'; +import path from 'node:path'; + +const [outputDir] = process.argv.slice(2); +const strict = process.argv.includes('--strict'); + +if (!outputDir) { + console.error( + '[check-linux-linkage] Usage: node scripts/cef/check-linux-runtime-linkage.mjs [--strict]', + ); + process.exit(1); +} + +// QNBS-v3: the two artifacts whose actual runtime linkage matters most — the host executable itself, and libcef.so, CEF's own largest and most dependency-heavy shared library (apps/desktop-cef/CMakeLists.txt's CEF_BINARY_FILES COPY_FILES step puts both in the same output directory). +const TARGETS = ['worldscript_host', 'libcef.so']; + +/** @param {string} target */ +function checkLinkage(target) { + const targetPath = path.join(outputDir, target); + let out; + try { + // QNBS-v3: ldd's own exit code is 0 even when a dependency is unresolved (it prints "=> not found" and still exits cleanly) — the unresolved-dependency signal is in stdout text, not the process exit code, so it must be parsed rather than trusted from execFileSync alone. + out = execFileSync('ldd', [targetPath], { stdio: ['ignore', 'pipe', 'pipe'] }).toString(); + } catch (err) { + // A genuinely non-dynamic-executable or missing file is itself a real finding, not a script bug. + return { + target, + ok: false, + notFound: [], + error: err instanceof Error ? err.message : String(err), + }; + } + const notFound = out + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('=> not found') || /^\S+\s+not found/.test(line)); + return { target, ok: notFound.length === 0, notFound, error: null }; +} + +const results = TARGETS.map(checkLinkage); + +console.log( + '[check-linux-linkage] CEF Linux runtime *linkage* check (ldd against shipped .so files):', +); +for (const { target, ok, notFound, error } of results) { + if (error) { + console.log(` ✗ ${target} — could not run ldd: ${error}`); + continue; + } + console.log( + ` ${ok ? '✓' : '✗'} ${target}${ok ? '' : ` — ${notFound.length} unresolved dependency line(s):`}`, + ); + for (const line of notFound) { + console.log(` ${line}`); + } +} + +const anyFailed = results.some((r) => r.error || !r.ok); +if (anyFailed) { + console.log('\n[check-linux-linkage] One or more targets have unresolved runtime dependencies.'); +} else { + console.log( + `\n[check-linux-linkage] All ${results.length} target(s) fully resolved on this runner.`, + ); +} + +if (strict && anyFailed) { + console.error( + '[check-linux-linkage] --strict requested and unresolved dependencies found — failing.', + ); + process.exit(1); +}