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]>
144 lines
6.2 KiB
Bash
Executable File
144 lines
6.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Build a production StepForge .deb (and a matching portable tarball) from a
|
|
# pruned, runtime-only tree.
|
|
#
|
|
# Unlike the old scripts/package-linux.sh this does NOT copy the development
|
|
# node_modules, docs, prompts, examples, or stale audit files; it stages only
|
|
# the app code plus a runtime dependency set (the fixed Electron runtime and
|
|
# production npm deps), a real desktop entry, icons, MIME registration, and a
|
|
# license. Architecture is detected, not hardcoded.
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
|
|
cd "$ROOT_DIR"
|
|
|
|
VERSION="$(node -p "require('./package.json').version")"
|
|
MAINTAINER="${STEPFORGE_MAINTAINER:-StepForge <[email protected]>}"
|
|
OUT_DIR="${STEPFORGE_PACKAGE_DIR:-$ROOT_DIR/build/artifacts}"
|
|
mkdir -p "$OUT_DIR"
|
|
|
|
# Map dpkg architecture to a Node-style label for the tarball name.
|
|
DEB_ARCH="$(dpkg --print-architecture 2>/dev/null || echo amd64)"
|
|
case "$DEB_ARCH" in
|
|
amd64) NODE_ARCH="x64" ;;
|
|
arm64) NODE_ARCH="arm64" ;;
|
|
*) NODE_ARCH="$DEB_ARCH" ;;
|
|
esac
|
|
|
|
# A packaged app must contain a fixed runtime; never install at build time from
|
|
# within the package step, and never ship without node_modules.
|
|
if [ ! -d "$ROOT_DIR/node_modules/electron/dist" ]; then
|
|
echo "error: node_modules/electron is missing. Run 'npm ci' before packaging." >&2
|
|
exit 1
|
|
fi
|
|
|
|
WORK_DIR="$(mktemp -d "${OUT_DIR%/}/.deb.XXXXXX")"
|
|
trap 'rm -rf "$WORK_DIR"' EXIT
|
|
APP_DIR="$WORK_DIR/opt/stepforge"
|
|
mkdir -p "$APP_DIR" "$WORK_DIR/usr/bin" "$WORK_DIR/DEBIAN"
|
|
mkdir -p "$WORK_DIR/usr/share/applications"
|
|
mkdir -p "$WORK_DIR/usr/share/mime/packages"
|
|
mkdir -p "$WORK_DIR/usr/share/doc/stepforge"
|
|
|
|
# --- application code (runtime only) ----------------------------------------
|
|
for item in app core exporters package.json package-lock.json; do
|
|
cp -a "$ROOT_DIR/$item" "$APP_DIR/$item"
|
|
done
|
|
|
|
# --- runtime node_modules ----------------------------------------------------
|
|
# The fixed Electron runtime (needed at runtime even though it is a dev dep):
|
|
mkdir -p "$APP_DIR/node_modules"
|
|
cp -a "$ROOT_DIR/node_modules/electron" "$APP_DIR/node_modules/electron"
|
|
# Production npm dependencies (tesseract.js + language data + transitive):
|
|
while IFS= read -r dep; do
|
|
[ -n "$dep" ] || continue
|
|
rel="${dep#"$ROOT_DIR"/}"
|
|
[ "$rel" != "$dep" ] || continue # only paths under the repo
|
|
[ -d "$dep" ] || continue
|
|
mkdir -p "$APP_DIR/$(dirname "$rel")"
|
|
cp -a "$dep" "$APP_DIR/$rel"
|
|
done < <(npm ls --omit=dev --all --parseable 2>/dev/null | tail -n +2)
|
|
|
|
# Guard: the development-only packaging toolchain must not have leaked in.
|
|
if [ -d "$APP_DIR/node_modules/electron-builder" ] || [ -d "$APP_DIR/node_modules/app-builder-lib" ]; then
|
|
echo "error: build-only dependency leaked into the package payload." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# --- launcher ----------------------------------------------------------------
|
|
install -m 0755 "$ROOT_DIR/packaging/linux/common/launcher.sh" "$WORK_DIR/usr/bin/stepforge"
|
|
|
|
# --- desktop entry, icons, MIME ---------------------------------------------
|
|
install -m 0644 "$ROOT_DIR/packaging/linux/common/stepforge.desktop" "$WORK_DIR/usr/share/applications/stepforge.desktop"
|
|
install -m 0644 "$ROOT_DIR/packaging/linux/common/stepforge-mime.xml" "$WORK_DIR/usr/share/mime/packages/stepforge.xml"
|
|
for size in 16 32 48 64 128 256 512; do
|
|
icon="$ROOT_DIR/packaging/assets/icons/stepforge-${size}.png"
|
|
[ -f "$icon" ] || continue
|
|
dest="$WORK_DIR/usr/share/icons/hicolor/${size}x${size}/apps"
|
|
mkdir -p "$dest"
|
|
install -m 0644 "$icon" "$dest/stepforge.png"
|
|
done
|
|
|
|
# --- license + docs pointer --------------------------------------------------
|
|
if [ -f "$ROOT_DIR/LICENSE" ]; then
|
|
install -m 0644 "$ROOT_DIR/LICENSE" "$WORK_DIR/usr/share/doc/stepforge/copyright"
|
|
elif [ -f "$ROOT_DIR/docs/LICENSE" ]; then
|
|
install -m 0644 "$ROOT_DIR/docs/LICENSE" "$WORK_DIR/usr/share/doc/stepforge/copyright"
|
|
fi
|
|
|
|
# --- DEBIAN control + maintainer scripts ------------------------------------
|
|
sed -e "s/@VERSION@/$VERSION/" -e "s/@ARCH@/$DEB_ARCH/" -e "s#@MAINTAINER@#$MAINTAINER#" \
|
|
"$ROOT_DIR/packaging/linux/debian/control.in" > "$WORK_DIR/DEBIAN/control"
|
|
|
|
cat > "$WORK_DIR/DEBIAN/postinst" <<'POSTINST'
|
|
#!/bin/sh
|
|
set -e
|
|
# Make the Chromium setuid sandbox helper usable so the app launches sandboxed.
|
|
HELPER=/opt/stepforge/node_modules/electron/dist/chrome-sandbox
|
|
if [ -e "$HELPER" ]; then
|
|
chown root:root "$HELPER" || true
|
|
chmod 4755 "$HELPER" || true
|
|
fi
|
|
# Refresh desktop/MIME/icon caches (best effort).
|
|
if command -v update-desktop-database >/dev/null 2>&1; then update-desktop-database -q /usr/share/applications || true; fi
|
|
if command -v update-mime-database >/dev/null 2>&1; then update-mime-database /usr/share/mime || true; fi
|
|
if command -v gtk-update-icon-cache >/dev/null 2>&1; then gtk-update-icon-cache -q /usr/share/icons/hicolor || true; fi
|
|
exit 0
|
|
POSTINST
|
|
|
|
cat > "$WORK_DIR/DEBIAN/prerm" <<'PRERM'
|
|
#!/bin/sh
|
|
set -e
|
|
exit 0
|
|
PRERM
|
|
|
|
cat > "$WORK_DIR/DEBIAN/postrm" <<'POSTRM'
|
|
#!/bin/sh
|
|
set -e
|
|
if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then
|
|
if command -v update-desktop-database >/dev/null 2>&1; then update-desktop-database -q /usr/share/applications || true; fi
|
|
if command -v update-mime-database >/dev/null 2>&1; then update-mime-database /usr/share/mime || true; fi
|
|
if command -v gtk-update-icon-cache >/dev/null 2>&1; then gtk-update-icon-cache -q /usr/share/icons/hicolor || true; fi
|
|
fi
|
|
exit 0
|
|
POSTRM
|
|
chmod 0755 "$WORK_DIR/DEBIAN/postinst" "$WORK_DIR/DEBIAN/prerm" "$WORK_DIR/DEBIAN/postrm"
|
|
|
|
# --- build the .deb ----------------------------------------------------------
|
|
DEB_FILE="$OUT_DIR/stepforge_${VERSION}_${DEB_ARCH}.deb"
|
|
if command -v fakeroot >/dev/null 2>&1; then
|
|
fakeroot dpkg-deb --build "$WORK_DIR" "$DEB_FILE" >/dev/null
|
|
else
|
|
dpkg-deb --build "$WORK_DIR" "$DEB_FILE" >/dev/null
|
|
fi
|
|
|
|
# --- portable tarball (INCLUDES the launcher, unlike the old script) ---------
|
|
TAR_FILE="$OUT_DIR/stepforge_${VERSION}_linux-${NODE_ARCH}.tar.gz"
|
|
tar -C "$WORK_DIR" -czf "$TAR_FILE" opt usr/bin/stepforge usr/share/applications usr/share/mime usr/share/icons
|
|
|
|
# --- checksums ---------------------------------------------------------------
|
|
( cd "$OUT_DIR" && sha256sum "$(basename "$DEB_FILE")" "$(basename "$TAR_FILE")" > "stepforge_${VERSION}_${DEB_ARCH}.sha256" )
|
|
|
|
echo "$DEB_FILE"
|
|
echo "$TAR_FILE"
|