From 5d735b2987e63408cdcf6560fde8eb396ac4b73b Mon Sep 17 00:00:00 2001 From: Sami Ahmed Date: Wed, 12 Aug 2026 03:41:50 -0700 Subject: [PATCH] =?UTF-8?q?[grade=3DC=E2=86=92fixing]=20Desktop=20overlay?= =?UTF-8?q?=20+=20build=20script=20+=20CI=20improvements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - packages.x86_64.desktop: 30+ packages for desktop variant (Plasma, Firefox, VLC, LibreOffice, PipeWire). No duplicates with base list. - build-desktop.sh: creates temp profile, merges package lists, enables SDDM via preset + display-manager.service symlink. WORK_DIR is mktemp-created (not caller-selected) to prevent symlink races. VERSION copied into temp profile so profiledef.sh resolves correctly. Cleans up both temp dirs via trap. - CI workflow: Python AST validation (sys.argv), VERSION SSOT gate, single make check (no double-lint). - test_packages.sh: 15 assertions for desktop overlay (existence, package count, no dups, Plasma/Firefox exact match, build script structure: mktemp WORK_DIR, VERSION copy, SDDM, cleanup trap). Codex D-grade findings fixed: symlink race (use mktemp), VERSION path (../../VERSION not ../VERSION). --- .gitea/workflows/ci.yml | 44 +++++++++-- Makefile | 1 + packaging/archiso/build-desktop.sh | 88 ++++++++++++++++++++++ packaging/archiso/packages.x86_64.desktop | 62 ++++++++++++++++ tests/test_packages.sh | 89 +++++++++++++++++++++++ 5 files changed, 279 insertions(+), 5 deletions(-) create mode 100755 packaging/archiso/build-desktop.sh create mode 100644 packaging/archiso/packages.x86_64.desktop diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 82e119c..5ce40fd 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -24,9 +24,43 @@ jobs: sudo apt-get install -y -qq make fi - # ── Shellcheck ─────────────────────────────────────────────────── - - name: Run shellcheck on all shell scripts - run: make lint + # ── Python AST validation ──────────────────────────────────────── + - name: Validate Python syntax (AST parse) + run: | + set -e + while IFS= read -r -d '' f; do + echo "Checking $f..." + python3 -c "import ast, sys; ast.parse(open(sys.argv[1]).read())" "$f" + echo " ✓ $f" + done < <(find packaging/scripts -name '*.py' -print0) + + # ── VERSION SSOT consistency ───────────────────────────────────── + - name: Verify VERSION single-source-of-truth + run: | + set -e + VERSION="$(cat VERSION)" + echo "Canonical VERSION: $VERSION" + + # Mirror must match + MIRROR="$(head -n1 packaging/archiso/airootfs/etc/samios-version)" + if [ "$VERSION" != "$MIRROR" ]; then + echo "ERROR: VERSION ($VERSION) != airootfs mirror ($MIRROR)" + echo "Fix: make version-sync" + exit 1 + fi + echo " ✓ airootfs mirror matches VERSION" + + # PKGBUILD pkgver must resolve to VERSION (hyphen → underscore) + PKGVER="$(cd packaging/packages/samios-branding && source PKGBUILD >/dev/null 2>&1 && echo "$pkgver")" + EXPECTED_PKGVER="$(echo "$VERSION" | tr '-' '_')" + if [ "$PKGVER" != "$EXPECTED_PKGVER" ]; then + echo "ERROR: PKGBUILD pkgver ($PKGVER) != expected ($EXPECTED_PKGVER)" + exit 1 + fi + echo " ✓ PKGBUILD pkgver resolves to VERSION" + + echo "" + echo "VERSION SSOT consistency check PASSED ✓" # ── Profile structure validation ───────────────────────────────── - name: Validate archiso profile structure @@ -99,5 +133,5 @@ jobs: echo "Profile structure validation PASSED ✓" # ── Test suite ─────────────────────────────────────────────────── - - name: Run test suite - run: make test + - name: Run full test suite (lint + tests) + run: make check diff --git a/Makefile b/Makefile index 41a13ee..3006ba0 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ VERSION_FILE := $(REPO_ROOT)/VERSION SHELL_SCRIPTS := \ $(PROFILE_DIR)/profiledef.sh \ $(PROFILE_DIR)/build.sh \ + $(PROFILE_DIR)/build-desktop.sh \ $(PROFILE_DIR)/airootfs/usr/local/bin/samios \ $(PROFILE_DIR)/airootfs/usr/local/bin/choose-mirror \ $(PROFILE_DIR)/airootfs/root/.automated_script.sh \ diff --git a/packaging/archiso/build-desktop.sh b/packaging/archiso/build-desktop.sh new file mode 100755 index 0000000..0f97f17 --- /dev/null +++ b/packaging/archiso/build-desktop.sh @@ -0,0 +1,88 @@ +#!/bin/bash +# SamiOS Desktop ISO build script +# +# Builds a desktop ISO by merging the base packages.x86_64 with the +# desktop overlay (packages.x86_64.desktop) into a temporary profile, +# then running mkarchiso on that profile. +# +# Usage: sudo bash packaging/archiso/build-desktop.sh +set -euo pipefail + +PROFILE_DIR="$(cd "$(dirname "$0")" && pwd)" +OUT_DIR="${OUT_DIR:-$PROFILE_DIR/out}" + +# ── Create work dir via mktemp (not caller-selected) ───────────────────── +# We deliberately do NOT honor a caller-provided WORK_DIR for rm -rf +# safety. A caller-selected path is vulnerable to symlink races on +# shared /tmp. Instead, we create a root-owned mktemp directory. +WORK_DIR="$(mktemp -d /tmp/samios-desktop-build.XXXXXX)" +OUT_DIR="$(readlink -f "$OUT_DIR" 2>/dev/null || echo "$OUT_DIR")" + +# ── Create temp profile (BEFORE this, no side effects) ────────────────── +TMP_PROFILE="$(mktemp -d /tmp/samios-desktop-profile.XXXXXX)" + +cleanup() { + rm -rf "$TMP_PROFILE" "$WORK_DIR" +} +trap cleanup EXIT + +echo "Building SamiOS Desktop ISO..." +echo "Base profile: $PROFILE_DIR" +echo "Temp profile: $TMP_PROFILE" +echo "Work directory: $WORK_DIR" +echo "Output: $OUT_DIR" + +# ── Copy base profile (excluding output dir) ───────────────────────────── +for item in "$PROFILE_DIR"/* "$PROFILE_DIR"/.*; do + [ -e "$item" ] || continue + base="$(basename "$item")" + case "$base" in + out|.|..) continue ;; + esac + cp -a "$item" "$TMP_PROFILE/" +done + +# ── Merge package lists ───────────────────────────────────────────────── +cat "$PROFILE_DIR/packages.x86_64" \ + "$PROFILE_DIR/packages.x86_64.desktop" \ + > "$TMP_PROFILE/packages.x86_64" + +# Copy VERSION into the temp profile so profiledef.sh can find it +# (profiledef.sh walks up from its own directory looking for VERSION; +# without this copy, it falls back to 0.0.0-unknown) +# PROFILE_DIR is packaging/archiso, so VERSION is two levels up. +REPO_ROOT="$(cd "$PROFILE_DIR/../.." && pwd)" +if [ -f "$REPO_ROOT/VERSION" ]; then + cp "$REPO_ROOT/VERSION" "$TMP_PROFILE/VERSION" +fi + +# Verify the merged list has more packages than base alone +base_pkgs=$(grep -vE '^\s*#|^\s*$' "$PROFILE_DIR/packages.x86_64" | wc -l) +merged_pkgs=$(grep -vE '^\s*#|^\s*$' "$TMP_PROFILE/packages.x86_64" | wc -l) +if [ "$merged_pkgs" -le "$base_pkgs" ]; then + echo "ERROR: merged package list ($merged_pkgs) not larger than base ($base_pkgs)" + exit 1 +fi +echo "Packages: $base_pkgs (base) + $((merged_pkgs - base_pkgs)) (desktop) = $merged_pkgs (total)" + +# ── Enable SDDM display manager in the TEMP profile only ──────────────── +# This goes into the temp profile, NOT the shared base airootfs, so +# base-only ISO builds are unaffected. +PRESET_DIR="$TMP_PROFILE/airootfs/etc/systemd/system-preset" +mkdir -p "$PRESET_DIR" +echo "enable sddm.service" > "$PRESET_DIR/00-samios-desktop.preset" + +# Create the display-manager.service symlink (standard Arch way to +# enable a display manager — sddm gets activated on boot) +DM_LINK="$TMP_PROFILE/airootfs/etc/systemd/system/display-manager.service" +ln -sf /usr/lib/systemd/system/sddm.service "$DM_LINK" + +# ── Clean previous build state and build ──────────────────────────────── +rm -rf "$WORK_DIR" +mkdir -p "$WORK_DIR" "$OUT_DIR" + +mkarchiso -v -w "$WORK_DIR" -o "$OUT_DIR" "$TMP_PROFILE" + +echo "" +echo "Build complete!" +echo "ISO location: $OUT_DIR/samios-*.iso" diff --git a/packaging/archiso/packages.x86_64.desktop b/packaging/archiso/packages.x86_64.desktop new file mode 100644 index 0000000..3325d73 --- /dev/null +++ b/packaging/archiso/packages.x86_64.desktop @@ -0,0 +1,62 @@ +# SamiOS desktop overlay packages +# This file lists packages for the desktop variant of SamiOS. +# It is used IN ADDITION TO packages.x86_64 (which stays minimal/CLI-only). +# +# To build a desktop ISO, use the build-desktop.sh script: +# sudo bash packaging/archiso/build-desktop.sh +# +# That script creates a temporary profile, merges base + desktop package +# lists into packages.x86_64, then runs mkarchiso on the temp profile. +# +# All packages below are from the standard Arch repos (core/extra) as of +# 2026-08. Verify before building: pacman -Si + +# ── Desktop environment ────────────────────────────────────────────────── +plasma-meta +sddm +sddm-kcm +kdeplasma-addons +plasma-nm +plasma-pa +kmenuedit +kwrite + +# ── Applications ───────────────────────────────────────────────────────── +firefox +dolphin +konsole +kate +gwenview +okular +ark +vlc +libreoffice-fresh +thunderbird + +# ── Audio ──────────────────────────────────────────────────────────────── +pipewire +pipewire-pulse +pipewire-alsa +pipewire-jack +wireplumber + +# ── Network ────────────────────────────────────────────────────────────── +network-manager-applet + +# ── Graphics ───────────────────────────────────────────────────────────── +mesa +vulkan-radeon +vulkan-intel +vulkan-icd-loader + +# ── Fonts (desktop) ────────────────────────────────────────────────────── +# Note: ttf-dejavu and ttf-liberation are already in the base packages.x86_64 +noto-fonts +noto-fonts-emoji + +# ── System utilities ──────────────────────────────────────────────────── +xdg-desktop-portal +xdg-desktop-portal-kde +# Note: xdg-user-dirs is already in the base packages.x86_64 +htop +fastfetch diff --git a/tests/test_packages.sh b/tests/test_packages.sh index 56669bf..6face0a 100755 --- a/tests/test_packages.sh +++ b/tests/test_packages.sh @@ -56,4 +56,93 @@ assert "no duplicate package entries" \ fi ' +# ── Desktop overlay ────────────────────────────────────────────────────── +DESKTOP_PKGLIST="$PROFILE_DIR/packages.x86_64.desktop" +DESKTOP_BUILD="$PROFILE_DIR/build-desktop.sh" +export DESKTOP_PKGLIST DESKTOP_BUILD + +assert_file_exists "desktop overlay packages file exists" "$DESKTOP_PKGLIST" +assert_file_exists "desktop build script exists" "$DESKTOP_BUILD" + +assert "desktop overlay has at least 10 packages" \ + bash -c ' + count="$(grep -vE "^\s*#|^\s*$" "$DESKTOP_PKGLIST" | wc -l)" + if [ "$count" -lt 10 ]; then + echo "expected ≥10 desktop packages, got $count" + exit 1 + fi + ' + +assert "desktop overlay includes Plasma desktop" \ + bash -c "grep -Fqx 'plasma-meta' '$DESKTOP_PKGLIST'" + +assert "desktop overlay includes Firefox" \ + bash -c "grep -Fqx 'firefox' '$DESKTOP_PKGLIST'" + +assert "desktop build script merges base + overlay" \ + bash -c "grep -q 'packages.x86_64.desktop' '$DESKTOP_BUILD'" + +assert "desktop build script uses mkarchiso" \ + bash -c "grep -q 'mkarchiso' '$DESKTOP_BUILD'" + +assert "desktop build script has cleanup trap" \ + bash -c "grep -q 'trap cleanup' '$DESKTOP_BUILD'" + +assert "desktop build script enables SDDM display manager" \ + bash -c "grep -q 'sddm' '$DESKTOP_BUILD'" + +assert "desktop build script creates display-manager symlink" \ + bash -c "grep -q 'display-manager.service' '$DESKTOP_BUILD'" + +assert "desktop build script uses mktemp for WORK_DIR (not caller-selected)" \ + bash -c "grep -q 'WORK_DIR=.*mktemp' '$DESKTOP_BUILD'" + +assert "desktop build script copies VERSION into temp profile" \ + bash -c "grep -q 'VERSION' '$DESKTOP_BUILD' && grep -q 'TMP_PROFILE/VERSION' '$DESKTOP_BUILD'" + +assert "desktop build script cleanup trap removes both temp dirs" \ + bash -c "grep -q 'TMP_PROFILE.*WORK_DIR' '$DESKTOP_BUILD'" + +assert "desktop build script excludes output dir during copy (not after)" \ + bash -c "grep -q 'out|.|..' '$DESKTOP_BUILD'" + +assert "desktop build script passes bash -n syntax check" \ + bash -c "bash -n '$DESKTOP_BUILD'" + +# Desktop overlay must not duplicate base packages +assert "desktop overlay has no duplicates with base package list" \ + bash -c ' + dups="$(grep -hE "^[a-zA-Z0-9]" "$PKGLIST" "$DESKTOP_PKGLIST" | sort | uniq -d)" + if [ -n "$dups" ]; then + echo "packages in both base and desktop overlay: $dups" + exit 1 + fi + ' + +# Regression test: prove the duplicate check actually catches duplicates +assert "duplicate-check regression: a planted duplicate IS detected" \ + bash -c ' + tmp_a="$(mktemp)" + tmp_b="$(mktemp)" + trap "rm -f ${tmp_a:?} ${tmp_b:?}" EXIT + echo -e "base\nlinux\nnetworkmanager" > "$tmp_a" + echo -e "firefox\nbase\nvlc" > "$tmp_b" + dups="$(grep -hE "^[a-zA-Z0-9]" "$tmp_a" "$tmp_b" | sort | uniq -d)" + if [ "$dups" != "base" ]; then + echo "expected to detect duplicate: base, got: $dups" + exit 1 + fi + ' + +# No obsolete packages (khotkeys removed from Arch, neofetch renamed) +assert "desktop overlay does not include known-obsolete packages" \ + bash -c ' + for bad in khotkeys neofetch; do + if grep -q "^$bad$" "$DESKTOP_PKGLIST"; then + echo "obsolete package found: $bad" + exit 1 + fi + done + ' + print_summary