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
46 changes: 45 additions & 1 deletion .github/workflows/build-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,62 @@ jobs:

- name: Smoke test binary
run: |
DIST="./packages/cli/dist/superset-${{ matrix.target }}"
# Resolve dist as an absolute path before changing cwd. The smoke
# tests below run from /tmp instead of the workspace because
# Node's module resolution walks up from cwd before consulting
# NODE_PATH — staying in $GITHUB_WORKSPACE leaks into the host
# repo's node_modules and silently masks bundle issues.
DIST="$(pwd)/packages/cli/dist/superset-${{ matrix.target }}"
"$DIST/bin/superset" --version
"$DIST/bin/superset" --help
"$DIST/lib/node" --version
test -f "$DIST/lib/host-service.js" || (echo "missing host-service.js" >&2; exit 1)
test -f "$DIST/lib/pty-daemon.js" || (echo "missing pty-daemon.js" >&2; exit 1)
if [[ "${{ matrix.target }}" == darwin-* ]]; then
ARCH="${{ matrix.target }}"
ARCH="${ARCH#darwin-}"
HELPER="$DIST/lib/node_modules/node-pty/prebuilds/darwin-$ARCH/spawn-helper"
test -x "$HELPER" || (echo "spawn-helper not executable: $HELPER" >&2; exit 1)
fi
cd /tmp
NODE_PATH="$DIST/lib/node_modules" "$DIST/lib/node" -e '
for (const m of ["better-sqlite3", "node-pty", "@parcel/watcher", "libsql"]) {
require(m);
console.log(m, "OK");
}
'
# Actually spawn a PTY against the bundled Node + bundled prebuild.
# Catches the runtime failures `require()` can't see — spawn-helper
# exec bit, pty.node ABI mismatch, arch mismatch, etc. Asserts
# node-pty resolves to the bundled copy so a host-tree leak
# (described above) doesn't silently make this pass.
NODE_PATH="$DIST/lib/node_modules" DIST="$DIST" "$DIST/lib/node" -e '
const resolved = require.resolve("node-pty/lib/unixTerminal");
if (!resolved.startsWith(process.env.DIST)) {
console.error("node-pty leaked from non-bundled tree:", resolved);
process.exit(1);
}
const pty = require("node-pty");
const term = pty.spawn("/bin/sh", ["-c", "echo SPAWN_OK"], {
name: "xterm", cols: 80, rows: 24,
cwd: process.env.HOME || "/", env: process.env,
});
let got = "";
let exited = null;
const check = () => {
if (got.includes("SPAWN_OK") && exited && exited.exitCode === 0) {
console.log("pty spawn OK"); process.exit(0);
}
console.error("pty spawn FAIL exit=" + (exited && exited.exitCode) + " got=" + JSON.stringify(got));
process.exit(1);
};
term.onData((d) => { got += d.toString(); });
// onExit can fire before the final onData chunk is delivered
// (SIGCHLD and EOF on the pty master are independent events).
// Defer the assertion one tick so any in-flight onData runs first.
term.onExit((e) => { exited = e; setTimeout(check, 100); });
setTimeout(() => { console.error("pty spawn timeout"); process.exit(1); }, 5000);
'

- name: Upload tarball
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
Expand Down
34 changes: 32 additions & 2 deletions packages/cli/scripts/build-dist-linux-docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,47 @@ docker run --rm --platform "$PLATFORM" \
cd packages/cli
bun run build:dist --target="$TARGET"

DIST="./dist/superset-${TARGET}"
DIST="$(pwd)/dist/superset-${TARGET}"
"$DIST/bin/superset" --version
"$DIST/bin/superset" --help | head -5
"$DIST/lib/node" --version
test -f "$DIST/lib/host-service.js" || (echo "missing host-service.js" >&2; exit 1)
test -f "$DIST/lib/pty-daemon.js" || (echo "missing pty-daemon.js" >&2; exit 1)
# Run from /tmp so Node module resolution does not walk up into the
# repo and leak into a non-bundled node-pty (host-tree shadowing).
cd /tmp
NODE_PATH="$DIST/lib/node_modules" "$DIST/lib/node" -e "
for (const m of [\"better-sqlite3\", \"node-pty\", \"@parcel/watcher\", \"libsql\"]) {
require(m);
console.log(m, \"OK\");
}
"
echo "[docker-build] tarball: $(ls -la dist/superset-${TARGET}.tar.gz)"
NODE_PATH="$DIST/lib/node_modules" DIST="$DIST" "$DIST/lib/node" -e "
const resolved = require.resolve(\"node-pty/lib/unixTerminal\");
if (!resolved.startsWith(process.env.DIST)) {
console.error(\"node-pty leaked from non-bundled tree:\", resolved);
process.exit(1);
}
const pty = require(\"node-pty\");
const term = pty.spawn(\"/bin/sh\", [\"-c\", \"echo SPAWN_OK\"], {
name: \"xterm\", cols: 80, rows: 24,
cwd: process.env.HOME || \"/\", env: process.env,
});
let got = \"\";
let exited = null;
const check = () => {
if (got.includes(\"SPAWN_OK\") && exited && exited.exitCode === 0) {
console.log(\"pty spawn OK\"); process.exit(0);
}
console.error(\"pty spawn FAIL exit=\" + (exited && exited.exitCode) + \" got=\" + JSON.stringify(got));
process.exit(1);
};
term.onData((d) => { got += d.toString(); });
// onExit can fire before the final onData chunk is delivered (SIGCHLD
// and EOF on the pty master are independent events). Defer the
// assertion one tick so any in-flight onData callback runs first.
term.onExit((e) => { exited = e; setTimeout(check, 100); });
setTimeout(() => { console.error(\"pty spawn timeout\"); process.exit(1); }, 5000);
"
echo "[docker-build] tarball: $(ls -la "$DIST.tar.gz")"
'
21 changes: 21 additions & 0 deletions packages/cli/scripts/build-dist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,27 @@ async function fixNativeBinariesForNode(
);
rmSync(nodePtyBuild, { recursive: true, force: true });
}

// node-pty's `prebuilds/darwin-{arch}/spawn-helper` ships from npm with
// mode 0644. node-pty posix_spawnp's it as the actual fork helper at
// terminal-open time — without +x the kernel returns EACCES and the
// failure surfaces only as the cryptic "posix_spawnp failed" with no
// errno. The normal install path runs `npm rebuild` which fixes the
// mode; we ship raw prebuilds so we have to fix it ourselves.
if (platform === "darwin") {
const { arch } = targetParts(target);
const spawnHelper = join(
destModules,
"node-pty",
"prebuilds",
`darwin-${arch}`,
"spawn-helper",
);
if (existsSync(spawnHelper)) {
console.log(`[build-dist] chmod +x ${spawnHelper}`);
chmodSync(spawnHelper, 0o755);
}
}
}

async function buildCli(target: Target, outputPath: string): Promise<void> {
Expand Down
Loading