Skip to content
Open
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
63 changes: 63 additions & 0 deletions .github/workflows/deb.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Debian package

# Builds a Skyscraper .deb via packaging/build-deb.sh on Debian trixie
# (Qt6; avoids libc mismatch with current Debian) and attaches it to
# published releases, next to the AppImages.
#
# Version numbering and shipping stay manual: this only reacts to releases
# and to changes under packaging/ (plus manual dispatch for testing).
#
# For a new upstream version: bump PINNED_VERSION/PINNED_COMMIT in
# packaging/build-deb.sh first (CI always builds the exact triggering
# commit via --pin-commit).

on:
release:
types: [published]
pull_request:
# FIXME: change path packaging -> supplementary/workflows
paths: ['packaging/build-deb.sh', '.github/workflows/deb.yml']
workflow_dispatch:

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-24.04
container:
image: debian:trixie
steps:
- name: Install build dependencies
run: |
apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
ca-certificates git g++ make dpkg-dev \
qmake6 qt6-base-dev qt6-base-dev-tools \
libqt6sql6-sqlite p7zip-full
- name: Checkout sources with git
uses: actions/checkout@v4
- name: Build .deb
run: |
./packaging/build-deb.sh --out-dir dist --pin-commit "$GITHUB_SHA"
- name: Upload .deb artifact
uses: actions/upload-artifact@v4
with:
name: skyscraper-deb
path: dist/*.deb
if-no-files-found: error

attach:
if: github.event_name == 'release'
needs: build
runs-on: ubuntu-24.04
steps:
- name: Download .deb artifact
uses: actions/download-artifact@v4
with:
name: skyscraper-deb
path: dist
- name: Attach .deb to release
uses: softprops/action-gh-release@v3
with:
files: dist/*.deb
15 changes: 5 additions & 10 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,18 @@
object_script.*.Release
object_script.*.Debug
*_plugin_import.cpp
/.qmake.cache
/.qmake.stash
.qmake*
*.pro.user
*.pro.user.*
*.qbs.user
*.qbs.user.*
*.moc
moc_*.cpp
moc_*.h
moc_*
qrc_*.cpp
ui_*.h
*.qmlc
*.jsc
Makefile*
*build-*
*.qm
*.prl

Expand All @@ -45,19 +42,14 @@ target_wrapper.*
# QtCreator CMake
CMakeLists.txt.user*

# QtCreator 4.8< compilation database
compile_commands.json

# QtCreator local machine specific files for imported projects
*creator.user*

*_qmlcache.qrc

moc_*
*.tmp
*.bkp
*.bk
.qmake*
Skyscraper
Skyscraper.app/**/*
packages
Expand All @@ -76,3 +68,6 @@ platforms_idmap_local.csv

# docker config
docker/config.ini

# Debian packaging artifacts (see build-deb.sh)
*.deb
187 changes: 187 additions & 0 deletions packaging/build-deb.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
#!/usr/bin/env bash
#
# Build a Skyscraper .deb for Debian-based systems.
#
# Recipe (per docs, RetroPie scriptmodule, Debian trixie host):
# pinned source -> qmake6 (PREFIX=/usr, SYSCONFDIR=/etc) -> make ->
# make install INSTALL_ROOT=<staging> -> dpkg-shlibdeps Depends ->
# dpkg-deb --build
#
# Usage:
# packaging/build-deb.sh [--out-dir DIR] [--keep-tmp] [--pin-commit SHA]
#
# Output:
# skyscraper_<VERSION>-1_amd64.deb (default: repo root)
#
# Notes:
# - Builds from a clean `git archive HEAD` export, so the working tree stays
# clean. HEAD must match PINNED_COMMIT (or the SHA passed via --pin-commit).
# - All temporary work (build tree, staging dir) lives under $REPO_ROOT/.tmp
# (git-ignored). Nothing is written outside the repo.
# - SYSCONFDIR=/etc is a deliberate FHS choice: upstream default would be
# /usr/etc with PREFIX=/usr. The binary probes SYSCONFDIR/skyscraper at
# runtime and falls back to single-binary mode when absent.
# - libqt6sql6-sqlite and p7zip-full are runtime-only deps (dlopen/exec) that
# dpkg-shlibdeps cannot see; they are appended to Depends manually.
# - No skyscraper.cfg / config.ini is shipped: the app seeds per-user config
# from /etc/skyscraper examples on first run, falling back to built-in
# defaults.

set -euo pipefail

PINNED_COMMIT="ccd49ae99a13ce037476f1d3cc3b646afca2059e"
PKG_NAME="skyscraper"
PKG_REVISION="1"
ARCH="amd64"
# Neutral default; downstream forks override with DEB_MAINTAINER.
MAINTAINER="${DEB_MAINTAINER:-Gemba <[email protected]>}"

OUT_DIR=""
KEEP_TMP=0
PIN_COMMIT=""

while [[ $# -gt 0 ]]; do
case "$1" in
--out-dir)
OUT_DIR="$2"
shift 2
;;
--pin-commit)
PIN_COMMIT="$2"
shift 2
;;
--keep-tmp)
KEEP_TMP=1
shift
;;
-h|--help)
sed -n '2,/^$/p' "$0"
echo "Usage: $0 [--out-dir DIR] [--keep-tmp] [--pin-commit SHA]"
exit 0
;;
*)
echo "Unknown argument: $1" >&2
exit 1
;;
esac
done

REPO_ROOT="$(git rev-parse --show-toplevel)"
cd "$REPO_ROOT"

if [[ -z "$OUT_DIR" ]]; then
OUT_DIR="$REPO_ROOT"
fi
mkdir -p "$OUT_DIR"

# --- Pin check ---------------------------------------------------------------
# Default pin: last locally validated source. CI / releases pass the exact
# commit explicitly via --pin-commit (a hardcoded pin can never survive the
# commit that carries it, so the override is the release mechanism).
HEAD_COMMIT="$(git rev-parse HEAD)"
# expand potential shortened commit
WANT_COMMIT="$(git rev-parse ${PIN_COMMIT:-$PINNED_COMMIT})"
if [[ "$HEAD_COMMIT" != "$WANT_COMMIT" ]]; then
echo "ERROR: HEAD (${HEAD_COMMIT:0:8}) is not the expected commit (${WANT_COMMIT:0:8})." >&2
echo "Check out the expected commit, or pass --pin-commit <sha> to build it deliberately." >&2
exit 1
fi

TREE_VERSION="$(sed -E 's/VERSION="(.+)"/\1/' VERSION.ini | head -n 1)"
TREE_VERSION="$(echo "$TREE_VERSION" | sed -E "s/-dev/-$(git describe --always)/")"
# --- Dependency check ----------------------------------------------------------
for tool in qmake6 make g++ dpkg-shlibdeps dpkg-deb; do
if ! command -v "$tool" >/dev/null 2>&1; then
echo "ERROR: required tool '$tool' not found." >&2
echo "Install build deps: sudo apt-get install qt6-base-dev qmake6 qt6-base-dev-tools libqt6sql6-sqlite p7zip-full" >&2
exit 1
fi
done

DEB_VERSION="${TREE_VERSION}-${PKG_REVISION}"
DEB_FILE="${PKG_NAME}_${DEB_VERSION}_${ARCH}.deb"

TMP_BASE="$REPO_ROOT/build/debpkg"
mkdir -p "$TMP_BASE"
BUILD_DIR="$(mktemp -d "$TMP_BASE/build-XXXXXX")"
STAGING_DIR="$(mktemp -d "$TMP_BASE/staging-XXXXXX")"

cleanup() {
if [[ "$KEEP_TMP" -eq 0 ]]; then
rm -rf "$BUILD_DIR" "$STAGING_DIR" 2>/dev/null || true
else
echo "Kept temp dirs: $BUILD_DIR $STAGING_DIR"
fi
# Remove the shlibdeps stub if we created it inside the export.
rm -rf "$BUILD_DIR/debian" 2>/dev/null || true
}
trap cleanup EXIT

echo "==> Exporting clean tree of $HEAD_COMMIT to $BUILD_DIR"
git archive HEAD | tar -x -C "$BUILD_DIR"

echo "==> Building Skyscraper v$TREE_VERSION"
pushd "$BUILD_DIR" >/dev/null
make --ignore-errors distclean 2>/dev/null || true
PREFIX=/usr SYSCONFDIR=/etc qmake6
make -j"$(nproc)"

echo "==> Staging install to $STAGING_DIR"
make install INSTALL_ROOT="$STAGING_DIR"
popd >/dev/null

if [[ ! -x "$STAGING_DIR/usr/bin/Skyscraper" ]]; then
echo "ERROR: staged binary missing: $STAGING_DIR/usr/bin/Skyscraper" >&2
exit 1
fi
if [[ ! -f "$STAGING_DIR/etc/skyscraper/peas.json" ]]; then
echo "ERROR: staged data files missing under $STAGING_DIR/etc/skyscraper" >&2
exit 1
fi

echo "==> Computing Depends via dpkg-shlibdeps"
# dpkg-shlibdeps requires a debian/control stub in the analysis directory.
mkdir -p "$BUILD_DIR/debian"
printf 'Source: %s\nSection: games\nPriority: optional\n\nPackage: %s\nArchitecture: %s\nDepends: ${shlibs:Depends}\nDescription: stub\n' \
"$PKG_NAME" "$PKG_NAME" "$ARCH" >"$BUILD_DIR/debian/control"
SHLIBS_DEPENDS="$(cd "$BUILD_DIR" && dpkg-shlibdeps -O "$STAGING_DIR/usr/bin/Skyscraper" 2>/dev/null | sed 's/^shlibs:Depends=//')"
rm -rf "$BUILD_DIR/debian"
if [[ -z "$SHLIBS_DEPENDS" ]]; then
echo "ERROR: dpkg-shlibdeps produced empty Depends." >&2
exit 1
fi
# Runtime-only deps invisible to shlibdeps: Qt SQLite driver (dlopened) and
# p7zip-full (shelled out to for archive handling).
DEPENDS="${SHLIBS_DEPENDS}, libqt6sql6-sqlite, p7zip-full"
echo " Depends: $DEPENDS"

echo "==> Writing DEBIAN/control"
mkdir -p "$STAGING_DIR/DEBIAN"
cat >"$STAGING_DIR/DEBIAN/control" <<EOF
Package: $PKG_NAME
Version: $DEB_VERSION
Section: games
Priority: optional
Architecture: $ARCH
Depends: $DEPENDS
Maintainer: $MAINTAINER
Description: Powerful and versatile game data scraper for EmulationStation
Skyscraper gathers game metadata, artwork and videos from online and
local sources and generates EmulationStation gamelists.
.
The package installs the Skyscraper binary to /usr/bin and shared
data files to /etc/skyscraper.
Homepage: https://github.com/Gemba/skyscraper
EOF

echo "==> Building $DEB_FILE"
dpkg-deb --build "$STAGING_DIR" "$OUT_DIR/$DEB_FILE"

echo "==> Verifying"
dpkg-deb --field "$OUT_DIR/$DEB_FILE"
echo "--- contents ---"
# head closes the pipe early (SIGPIPE); without this, `set -o pipefail`
# would fail the build on a successful listing.
dpkg-deb --contents "$OUT_DIR/$DEB_FILE" || true

echo "OK: $OUT_DIR/$DEB_FILE"