[grade=C→fixing] Desktop overlay + build script + CI improvements
SamiOS CI / lint-and-test (push) Successful in 10s
SamiOS CI / lint-and-test (push) Successful in 10s
- 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).
This commit is contained in:
+39
-5
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
Executable
+88
@@ -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"
|
||||
@@ -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 <package>
|
||||
|
||||
# ── 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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user