Add production .rpm packaging and dnf setup; share runtime staging
Template tests / tests (pull_request) Failing after 33s
Template tests / tests (pull_request) Failing after 33s
Phase 3 of the improvement plan (PR 9 of the sequence): the dnf/Fedora packaging half, in separate Linux-specific files, mirroring the apt/.deb work. - packaging/linux/common/stage-runtime.sh: shared runtime-only payload staging extracted from the .deb builder so the two package formats never drift. It copies app code + a fixed Electron runtime + production npm deps (npm ls --omit=dev), guards against dev-dep leaks, and fails without node_modules. The .deb builder now delegates to it. - packaging/linux/fedora/stepforge.spec: runtime Requires (nss, nspr, gtk3, …), Recommends (xinput, portal, pipewire), %license, and a %post that makes chrome-sandbox setuid and refreshes desktop/MIME/icon caches. No compilation — it packages the prebuilt BuildRoot. - packaging/linux/fedora/package.sh: stages the shared payload, substitutes version/maintainer into the spec, and runs rpmbuild against the prebuilt BuildRoot with detected arch. Requires rpmbuild + node_modules; emits an .rpm + sha256. - scripts/linux/dnf/install-runtime-deps.sh and install-build-deps.sh: separate runtime vs build dependency sets for dnf (runtime installs no build tools). - docs/linux/dnf.md: Fedora/RHEL install + build guide, distinct from apt.md. Tests: packaging-linux.test.js gains Fedora/dnf structural checks (spec Requires + MPL-2.0 + %license + sandbox setup, builder shares staging + requires rpmbuild, dnf build/runtime dep separation, shared staging never copies the whole dev tree). tests/integration/linux/package-rpm.test.sh builds a real .rpm and asserts runtime-only contents (honest skip when rpmbuild is absent, as on this apt host). Verified: 13 packaging unit tests pass; the refactored .deb builder still produces a valid runtime-only package (integration test green); the .rpm integration test skips cleanly where rpmbuild is unavailable. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
Executable
+65
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build a production StepForge .rpm from a pruned, runtime-only tree, mirroring
|
||||
# the .deb builder. Stages the shared payload via common/stage-runtime.sh, then
|
||||
# packages it with rpmbuild against a prebuilt BuildRoot (no compilation).
|
||||
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"
|
||||
|
||||
if ! command -v rpmbuild >/dev/null 2>&1; then
|
||||
echo "error: rpmbuild is not installed. Run scripts/linux/dnf/install-build-deps.sh" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# RPM arch label from the host.
|
||||
RPM_ARCH="$(rpm --eval '%{_arch}' 2>/dev/null || uname -m)"
|
||||
|
||||
BUILD_ROOT="$(mktemp -d "${OUT_DIR%/}/.rpm.XXXXXX")"
|
||||
trap 'rm -rf "$BUILD_ROOT"' EXIT
|
||||
STAGE="$BUILD_ROOT/buildroot"
|
||||
mkdir -p "$STAGE"
|
||||
|
||||
# Shared runtime-only payload (fails if node_modules is missing).
|
||||
ROOT_DIR="$ROOT_DIR" STAGE_ROOT="$STAGE" bash "$ROOT_DIR/packaging/linux/common/stage-runtime.sh"
|
||||
|
||||
# License into the RPM's conventional location.
|
||||
mkdir -p "$STAGE/usr/share/licenses/stepforge"
|
||||
if [ -f "$ROOT_DIR/LICENSE" ]; then
|
||||
install -m 0644 "$ROOT_DIR/LICENSE" "$STAGE/usr/share/licenses/stepforge/LICENSE"
|
||||
elif [ -f "$ROOT_DIR/docs/LICENSE" ]; then
|
||||
install -m 0644 "$ROOT_DIR/docs/LICENSE" "$STAGE/usr/share/licenses/stepforge/LICENSE"
|
||||
else
|
||||
# rpmbuild %license requires the file to exist; write a pointer if absent.
|
||||
echo "See project LICENSE." > "$STAGE/usr/share/licenses/stepforge/LICENSE"
|
||||
fi
|
||||
|
||||
# Materialize the spec with version/maintainer substituted.
|
||||
SPEC="$BUILD_ROOT/stepforge.spec"
|
||||
sed -e "s/@VERSION@/$VERSION/" -e "s#@MAINTAINER@#$MAINTAINER#" \
|
||||
"$ROOT_DIR/packaging/linux/fedora/stepforge.spec" > "$SPEC"
|
||||
|
||||
rpmbuild -bb \
|
||||
--define "_topdir $BUILD_ROOT/rpmbuild" \
|
||||
--define "_rpmdir $OUT_DIR" \
|
||||
--define "_build_id_links none" \
|
||||
--buildroot "$STAGE" \
|
||||
--target "$RPM_ARCH" \
|
||||
"$SPEC" >/dev/null
|
||||
|
||||
# rpmbuild writes to $OUT_DIR/<arch>/<name>.rpm — surface the final path.
|
||||
RPM_FILE="$(find "$OUT_DIR" -name "stepforge-${VERSION}-1*.${RPM_ARCH}.rpm" -newer "$SPEC" | head -1)"
|
||||
if [ -z "$RPM_FILE" ]; then
|
||||
RPM_FILE="$(find "$OUT_DIR" -name "stepforge-${VERSION}-1*.rpm" | head -1)"
|
||||
fi
|
||||
[ -n "$RPM_FILE" ] || { echo "error: rpmbuild did not produce an .rpm" >&2; exit 1; }
|
||||
|
||||
# Checksum.
|
||||
( cd "$(dirname "$RPM_FILE")" && sha256sum "$(basename "$RPM_FILE")" > "$(basename "$RPM_FILE").sha256" )
|
||||
|
||||
echo "$RPM_FILE"
|
||||
@@ -0,0 +1,69 @@
|
||||
# StepForge RPM spec. The payload is prebuilt into a staging BuildRoot by
|
||||
# packaging/linux/fedora/package.sh (which stages a pruned runtime tree), so
|
||||
# this spec only packages and declares metadata — it does not compile.
|
||||
#
|
||||
# Placeholders @VERSION@ / @MAINTAINER@ are substituted by package.sh.
|
||||
|
||||
%global debug_package %{nil}
|
||||
%global __brp_check_rpaths %{nil}
|
||||
%define _build_id_links none
|
||||
|
||||
Name: stepforge
|
||||
Version: @VERSION@
|
||||
Release: 1%{?dist}
|
||||
Summary: Local-first step-by-step guide capture and export tool
|
||||
|
||||
License: MPL-2.0
|
||||
URL: https://github.com/Twest2/StepForge
|
||||
|
||||
# Runtime shared libraries (Chromium/Electron) + capture integration.
|
||||
Requires: nss
|
||||
Requires: nspr
|
||||
Requires: atk
|
||||
Requires: at-spi2-atk
|
||||
Requires: cups-libs
|
||||
Requires: gtk3
|
||||
Requires: mesa-libgbm
|
||||
Requires: alsa-lib
|
||||
Requires: libxkbcommon
|
||||
Recommends: xinput
|
||||
Recommends: xdg-desktop-portal
|
||||
Recommends: pipewire
|
||||
|
||||
# The payload is architecture-specific (bundles the Electron binary).
|
||||
%description
|
||||
StepForge captures step-by-step workflows as screenshots, lets you annotate
|
||||
and describe each step, and exports to Markdown, PDF, DOCX, PPTX, HTML, and
|
||||
more. Local-first: no telemetry, with an optional user-configured local AI
|
||||
integration. This package bundles a fixed Electron runtime and only
|
||||
production dependencies; it does not install anything at runtime.
|
||||
|
||||
%files
|
||||
/opt/stepforge
|
||||
/usr/bin/stepforge
|
||||
/usr/share/applications/stepforge.desktop
|
||||
/usr/share/mime/packages/stepforge.xml
|
||||
/usr/share/icons/hicolor/*/apps/stepforge.png
|
||||
%license /usr/share/licenses/stepforge/LICENSE
|
||||
|
||||
%post
|
||||
# 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
|
||||
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
|
||||
|
||||
%postun
|
||||
if [ "$1" = 0 ]; 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
|
||||
|
||||
%changelog
|
||||
* Fri Jul 03 2026 @MAINTAINER@ - @VERSION@-1
|
||||
- Production runtime-only package (pruned tree, fixed Electron runtime).
|
||||
Reference in New Issue
Block a user