Template tests / tests (pull_request) Failing after 31s
Phase 3 of the improvement plan (PR 8 of the sequence): the apt/X11 packaging half of Linux support, in separate Linux-specific files. Replaces the old scripts/package-linux.sh, which the audit flagged as "not production packaging" (it copied the whole dev node_modules — including vulnerable build deps — plus docs/prompts/examples/audit files, hardcoded amd64, declared only xinput, lacked desktop/icon/MIME integration, and could build without node_modules). Production builder (packaging/linux/debian/package.sh): - Stages ONLY runtime files: app code, a fixed Electron runtime, and the production npm deps (enumerated via npm ls --omit=dev). Never copies the development node_modules; guards against electron-builder/app-builder-lib leaking in. Fails if node_modules is absent instead of shipping an unusable artifact. - Detects architecture (dpkg --print-architecture, x64/arm64) rather than hardcoding amd64. Generates DEBIAN/control from control.in with proper runtime Depends, real maintainer, and homepage. - Installs a desktop entry, hicolor icons (16–512), .sfgz/.sfglt MIME registration, the launcher, and the license. postinst makes chrome-sandbox setuid and refreshes desktop/MIME/icon caches; postrm cleans them. - Emits a .deb, a portable tarball that now INCLUDES /usr/bin/stepforge (the old tarball omitted it), and a sha256 sums file. Launcher (packaging/linux/common/launcher.sh): - Runs sandboxed; prefers the user-namespace sandbox, accepts a root-owned setuid helper, and otherwise refuses to launch with an actionable message. --no-sandbox requires an explicit STEPFORGE_ALLOW_NO_SANDBOX opt-in. Never installs anything at runtime. Setup (separate build vs runtime, apt only): - scripts/linux/apt/install-runtime-deps.sh (Chromium/Electron libs, X11 tools, portal/PipeWire) and install-build-deps.sh (dpkg-dev, fakeroot, xvfb). Runtime script installs no build tools. Assets: original StepForge icon — packaging/assets/stepforge.svg plus a generator (scripts/make-icons.js) that renders the PNG set with the repo's own rasterizer/PNG writer (no third-party art). npm run icons regenerates them. Wiring: package.json gains package:linux:deb / package:linux:rpm / icons; build-release.sh uses the production builder and requires node_modules; README points at the apt/dnf guides. Tests: tests/unit/packaging-linux.test.js (structural: files present in their separate locations, old script gone, valid desktop entry, templated arch + runtime Depends, launcher gates --no-sandbox, builder requires node_modules and guards dev-dep leaks, apt build/runtime dep separation, original icon set generates a valid PNG) runs in the normal suite; tests/integration/linux/package-deb.test.sh builds a real .deb and asserts the right files present and the dev tree / build tooling / app docs absent (honest skip only when dpkg-deb/node_modules are genuinely missing). Verified locally: 276 unit tests pass; the integration test builds and validates stepforge_0.3.2_amd64.deb; build-release E2E passes with the new production package. Co-Authored-By: Claude Fable 5 <[email protected]>
151 lines
5.1 KiB
Bash
151 lines
5.1 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$ROOT_DIR"
|
|
BUILD_ROOT="${STEPFORGE_BUILD_DIR:-$ROOT_DIR/build}"
|
|
EXAMPLES_ROOT="${STEPFORGE_EXAMPLES_DIR:-$ROOT_DIR/examples}"
|
|
ARTIFACT_DIR="$BUILD_ROOT/artifacts"
|
|
REPORT_FILE="$BUILD_ROOT/build_report.md"
|
|
MANIFEST_FILE="$BUILD_ROOT/artifacts_manifest.json"
|
|
|
|
mkdir -p "$BUILD_ROOT"
|
|
|
|
bash "$ROOT_DIR/scripts/bootstrap-offline.sh"
|
|
node "$ROOT_DIR/scripts/make-sample-guide.js" --root "$EXAMPLES_ROOT"
|
|
# Production Linux package: a pruned runtime tree with real desktop
|
|
# integration. Requires node_modules (fails otherwise); never installs at
|
|
# build time. Skipped only when the Electron runtime is genuinely absent.
|
|
if [ -d "$ROOT_DIR/node_modules/electron/dist" ]; then
|
|
STEPFORGE_PACKAGE_DIR="$ARTIFACT_DIR" bash "$ROOT_DIR/packaging/linux/debian/package.sh" >/dev/null
|
|
else
|
|
echo "[build-release] skipping Linux .deb: node_modules/electron missing (run npm ci)" >&2
|
|
fi
|
|
|
|
BUILD_ROOT="$BUILD_ROOT" \
|
|
ARTIFACT_DIR="$ARTIFACT_DIR" \
|
|
EXAMPLES_ROOT="$EXAMPLES_ROOT" \
|
|
REPORT_FILE="$REPORT_FILE" \
|
|
MANIFEST_FILE="$MANIFEST_FILE" \
|
|
ROOT_DIR="$ROOT_DIR" \
|
|
node - <<'NODE'
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const crypto = require('node:crypto');
|
|
|
|
const buildRoot = process.env.BUILD_ROOT;
|
|
const artifactDir = process.env.ARTIFACT_DIR;
|
|
const examplesRoot = process.env.EXAMPLES_ROOT;
|
|
const reportFile = process.env.REPORT_FILE;
|
|
const manifestFile = process.env.MANIFEST_FILE;
|
|
const rootDir = process.env.ROOT_DIR;
|
|
|
|
function walk(dir, base = dir, out = []) {
|
|
if (!fs.existsSync(dir)) return out;
|
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
const abs = path.join(dir, entry.name);
|
|
if (entry.isDirectory()) walk(abs, base, out);
|
|
else out.push(path.relative(base, abs));
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function sha256(file) {
|
|
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
}
|
|
|
|
const files = [];
|
|
for (const rel of walk(artifactDir, artifactDir)) {
|
|
const abs = path.join(artifactDir, rel);
|
|
files.push({
|
|
kind: 'artifact',
|
|
path: path.relative(buildRoot, abs),
|
|
size: fs.statSync(abs).size,
|
|
sha256: sha256(abs),
|
|
});
|
|
}
|
|
for (const rel of walk(examplesRoot, examplesRoot)) {
|
|
if (!rel.startsWith('sample-')) continue;
|
|
const abs = path.join(examplesRoot, rel);
|
|
files.push({
|
|
kind: 'sample',
|
|
path: path.relative(buildRoot, abs),
|
|
size: fs.statSync(abs).size,
|
|
sha256: sha256(abs),
|
|
});
|
|
}
|
|
|
|
const pkg = require(path.join(rootDir, 'package.json'));
|
|
const buildVersion = pkg.buildVersion || pkg.version;
|
|
|
|
const { execSync } = require('node:child_process');
|
|
function toolAvailable(cmd) {
|
|
try { execSync(`command -v ${cmd}`, { stdio: 'pipe', shell: '/bin/bash' }); return true; } catch { return false; }
|
|
}
|
|
const tools = {
|
|
'dpkg-deb (Linux .deb)': toolAvailable('dpkg-deb'),
|
|
'rpmbuild (Linux .rpm)': toolAvailable('rpmbuild'),
|
|
'appimagetool (Linux AppImage)': toolAvailable('appimagetool'),
|
|
'makensis (Windows installer .exe)': toolAvailable('makensis'),
|
|
'wixl / WiX (Windows .msi)': toolAvailable('wixl'),
|
|
};
|
|
const toolRows = Object.entries(tools)
|
|
.map(([name, ok]) => `| ${name} | ${ok ? 'available' : '**missing**'} |`)
|
|
.join('\n');
|
|
|
|
const report = `# StepForge Build Report
|
|
|
|
Build version: ${buildVersion}
|
|
Package version: ${pkg.version}
|
|
Generated: ${new Date().toISOString()}
|
|
Host: ${process.platform} ${process.arch} (node ${process.version})
|
|
|
|
## Outputs
|
|
|
|
- Portable tarball: ${files.find((f) => f.path.endsWith('.tar.gz'))?.path || 'not generated'}
|
|
- Debian package: ${files.find((f) => f.path.endsWith('.deb'))?.path || 'not generated'}
|
|
- Sample guide archive: ${files.find((f) => f.path.endsWith('sample-guide.sfgz'))?.path || 'not generated'}
|
|
- Sample exports (10 formats): see examples/sample-exports/
|
|
- Full artifact list with sha256 checksums: artifacts_manifest.json
|
|
|
|
## Packaging tool availability
|
|
|
|
| Tool | Status |
|
|
|---|---|
|
|
${toolRows}
|
|
|
|
Fallback policy: when a packaging tool is missing the build still produces
|
|
the runnable app (portable tarball with launcher) plus whatever package
|
|
formats the available tools allow. Windows artifacts are produced by
|
|
\`npm run package:windows\` (electron-builder, installer .exe); .msi/.rpm/
|
|
AppImage require the tools listed above and are skipped on this host.
|
|
|
|
## Offline guarantee
|
|
|
|
- The shipped app opens no sockets: no telemetry, update checks, license
|
|
checks, cloud sync, or remote AI. See docs/SECURITY.md.
|
|
- All exporters (PNG/GIF/PDF/DOCX/PPTX/ZIP) are implemented in-repo with
|
|
Node built-ins; Electron is the only third-party dependency
|
|
(dev-time fetch recorded in build/agent_audit.md).
|
|
|
|
## Verification
|
|
|
|
- \`bash tests/run_test.sh\` runs the workflow suites (node --test), a
|
|
startup smoke test of the Electron launcher, the sample-artifact
|
|
pipeline, and this release build.
|
|
`;
|
|
|
|
fs.writeFileSync(reportFile, report);
|
|
fs.writeFileSync(manifestFile, JSON.stringify({
|
|
format: 'stepforge-artifacts-manifest',
|
|
version: 1,
|
|
generatedAt: new Date().toISOString(),
|
|
packageVersion: pkg.version,
|
|
buildVersion,
|
|
files,
|
|
}, null, 2) + '\n');
|
|
NODE
|
|
|
|
echo "Build artifacts written to $BUILD_ROOT"
|