Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions .github/workflows/cef-learning-harness.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 &
Expand Down Expand Up @@ -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"
94 changes: 94 additions & 0 deletions scripts/cef/check-linux-runtime-linkage.mjs
Original file line number Diff line number Diff line change
@@ -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 <build-output-dir> [--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 <build-output-dir> [--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);
}
Loading