* Move the dev sudo grant lifecycle into omarchy-settings-dev The hardened omarchy-sudo-passwordless publishes a grant only when its revocation hook is installed and no package transaction has left /run/omarchy-sudo-passwordless-package-removing behind. The hook sets that marker before every settings upgrade, and the settings scriptlets are what clear it, so shipping the hook alone let the first grant work and then refused every grant after the next update until a reboot. omarchy-settings-dev now ships the command, omarchy-security-functions, the boot cleanup rule and the hook together, with the scriptlets from #257 that revoke before the package changes and clear the marker once no grant remains. omarchy-dev stops shipping the two helpers and requires the settings release that does: without it omarchy-update has no library to source. Co-authored-by: Afonso Oliveira <afonso.oliveira707@gmail.com> * Check that settings-dev registers the scriptlet the lifecycle test exercises The lifecycle test reads the .install file directly, so it kept passing with the recipe's install= line gone, which is the one change that would strand the hook's marker again. Co-Authored-By: Codex XHigh <noreply@openai.com> * Keep omarchy-dev's settings dependency unversioned The dev pair builds each package on its own against the published edge repository, in the PR build and in publish alike, so omarchy-dev cannot require a settings-dev release that only this change produces: the floor failed both omarchy-dev builds on this PR, and an exact pkgver match would fail the same way on every upstream pin bump. Nothing needs it. The upstream watch moves both pins together, publish stops the run before anything is published when one package fails, and a new settings-dev over an old runtime is refused on the files they would both own. * Refuse settings-dev releases that predate the grant helper move With the dependency unversioned, a new omarchy-dev could still install over a settings-dev that is held back or not yet published, removing omarchy-security-functions from both, and omarchy-update cannot start without it. A versioned conflict refuses that pairing at install time without constraining the per-package builds, which resolve only depends. Co-Authored-By: Codex XHigh <noreply@openai.com> * Require the security library as a capability instead of a version floor The conflict named the last settings-dev release without the helpers, a version that moves with every upstream pin bump and settings-only pkgrel while this change waits to merge, and a rebase that kept a stale floor still passed the tests while the published release slipped past it. omarchy-settings-dev now provides omarchy-security-functions and omarchy-dev's package() requires it. makepkg checks only the top-level depends before building, so the per-package builds still resolve against the published repository, while pacman refuses a runtime without a settings release that ships the library, and pulls that release in when only the runtime is asked for. Co-Authored-By: Codex XHigh <noreply@openai.com> --------- Co-authored-by: Spencer Bull <spencer@omarchy.org> Co-authored-by: Afonso Oliveira <afonso.oliveira707@gmail.com> Co-authored-by: Codex XHigh <noreply@openai.com>
298 lines
13 KiB
Bash
Executable File
298 lines
13 KiB
Bash
Executable File
#!/bin/bash
|
|
# Exercise the real package functions with a minimal, synthetic runtime tree.
|
|
set -euo pipefail
|
|
|
|
BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
|
|
scratch=$(mktemp -d)
|
|
trap 'rm -rf "$scratch"' EXIT
|
|
fixture=$scratch/src/omarchy
|
|
|
|
# Every source path the settings recipes install by name.
|
|
mapfile -t files < "$BUILD_ROOT/tests/fixtures/settings-source-files"
|
|
for path in "${files[@]}"; do
|
|
mkdir -p "$(dirname "$fixture/$path")"
|
|
printf 'fixture for %s\n' "$path" > "$fixture/$path"
|
|
done
|
|
|
|
fixtures=$BUILD_ROOT/tests/fixtures/settings-boot
|
|
# Omarchy's HOOKS line, as v4.0.4 ships it (omarchy_hooks-v4.0.4.conf).
|
|
omarchy_hooks='base udev plymouth keyboard autodetect microcode modconf kms keymap consolefont block encrypt filesystems fsck btrfs-overlayfs'
|
|
cp "$fixtures/omarchy_hooks-v4.0.4.conf" "$fixture/etc/mkinitcpio.conf.d/omarchy_hooks.conf"
|
|
|
|
for recipe in omarchy-settings omarchy-settings-dev; do
|
|
for target_arch in aarch64 x86_64; do
|
|
(
|
|
export CARCH=$target_arch OMARCHY_SRC=$fixture
|
|
export srcdir=$scratch/src pkgdir=$scratch/$recipe-$target_arch
|
|
backup=()
|
|
# shellcheck disable=SC1090 # Exercise each recipe's actual package function.
|
|
source "$BUILD_ROOT/pkgbuilds/$recipe/PKGBUILD"
|
|
package
|
|
|
|
for path in etc/mkinitcpio.conf.d/omarchy_hooks.conf \
|
|
etc/limine-entry-tool.d/omarchy-defaults.conf \
|
|
etc/limine-entry-tool.d/omarchy-uki.conf; do
|
|
printf '%s\n' "${backup[@]}" | grep -Fxq "$path"
|
|
done
|
|
for path in etc/limine-entry-tool.d/omarchy-defaults.conf etc/limine-entry-tool.d/omarchy-uki.conf; do
|
|
cmp "$fixture/$path" "$pkgdir/$path"
|
|
done
|
|
hooks_conf=etc/mkinitcpio.conf.d/omarchy_hooks.conf
|
|
if [[ $CARCH == aarch64 ]]; then
|
|
grep -Fxq 'if [[ " ${HOOKS[*]:-} " != *" asahi "* ]]; then' "$pkgdir/$hooks_conf"
|
|
bash -n "$pkgdir/$hooks_conf"
|
|
else
|
|
cmp "$fixture/$hooks_conf" "$pkgdir/$hooks_conf"
|
|
fi
|
|
for template in default.conf limine.conf; do
|
|
cmp "$fixture/default/limine/$template" "$pkgdir/usr/share/omarchy/default/limine/$template"
|
|
done
|
|
if printf '%s\n' "${backup[@]}" | grep -Fxq etc/mkinitcpio.conf.d/00-omarchy-hooks.conf; then
|
|
echo 'FAIL: backup names a 00-omarchy-hooks.conf the source does not ship' >&2
|
|
exit 1
|
|
fi
|
|
# The installer owns the machine-specific live configuration.
|
|
[[ ! -e $pkgdir/etc/default/limine ]]
|
|
if printf '%s\n' "${backup[@]}" | grep -Fxq 'etc/default/limine'; then
|
|
echo 'FAIL: installer-owned Limine configuration is in backup metadata' >&2
|
|
exit 1
|
|
fi
|
|
cmp "$fixture/config/autostart/limine-snapper-notify.desktop" \
|
|
"$pkgdir/etc/skel/.config/autostart/limine-snapper-notify.desktop"
|
|
cmp "$fixture/config/autostart/limine-snapper-notify.desktop" \
|
|
"$pkgdir/usr/share/omarchy/config/autostart/limine-snapper-notify.desktop"
|
|
|
|
thunderbolt=etc/mkinitcpio.conf.d/thunderbolt_module.conf
|
|
if [[ $CARCH == aarch64 ]]; then
|
|
[[ ! -e $pkgdir/$thunderbolt ]]
|
|
if printf '%s\n' "${backup[@]}" | grep -Fxq "$thunderbolt"; then
|
|
echo 'FAIL: removed ARM Thunderbolt config remains in backup metadata' >&2
|
|
exit 1
|
|
fi
|
|
else
|
|
cmp "$fixture/$thunderbolt" "$pkgdir/$thunderbolt"
|
|
printf '%s\n' "${backup[@]}" | grep -Fxq "$thunderbolt"
|
|
fi
|
|
echo "PASS: $recipe $CARCH retains boot configuration and matching backup metadata"
|
|
)
|
|
done
|
|
done
|
|
|
|
# The aarch64 packages also reach Apple Silicon Macs, whose initramfs needs the
|
|
# asahi hook. Source mkinitcpio.conf and the drop-ins in mkinitcpio's order and
|
|
# compare the resulting HOOKS for each kind of aarch64 install. Aurora Macs use
|
|
# omarchy-mac-boot's real 90-94 fragments (omacom/omarchy-mac ff7ce0d4d).
|
|
package_aarch64() {
|
|
local recipe=$1 source_tree=$2 out=$3
|
|
(
|
|
# package() builds from $srcdir/omarchy.
|
|
export CARCH=aarch64 OMARCHY_SRC=$source_tree srcdir=${source_tree%/omarchy} pkgdir=$out
|
|
backup=()
|
|
# shellcheck disable=SC1090 # Exercise the recipe's actual package function.
|
|
source "$BUILD_ROOT/pkgbuilds/$recipe/PKGBUILD"
|
|
package || exit 1
|
|
printf '%s\n' "${backup[@]}" > "$out.backup"
|
|
)
|
|
}
|
|
|
|
# omacom/omarchy#13362 asks omarchy-hw-platform which machine it is on.
|
|
for platform in apple-silicon qualcomm generic-aarch64; do
|
|
mkdir -p "$scratch/detector-$platform"
|
|
printf '#!/bin/sh\necho %s\n' "$platform" > "$scratch/detector-$platform/omarchy-hw-platform"
|
|
chmod +x "$scratch/detector-$platform/omarchy-hw-platform"
|
|
done
|
|
|
|
# effective_hooks ROOT [PLATFORM]
|
|
effective_hooks() {
|
|
local root=$1 platform=${2:-}
|
|
(
|
|
LC_ALL=C
|
|
[[ -z $platform ]] || PATH=$scratch/detector-$platform:$PATH
|
|
HOOKS=() MODULES=() FILES=()
|
|
# shellcheck disable=SC1091
|
|
source "$root/mkinitcpio.conf"
|
|
shopt -s nullglob
|
|
for conf in "$root"/mkinitcpio.conf.d/*.conf; do
|
|
# shellcheck disable=SC1090
|
|
source "$conf"
|
|
done
|
|
echo "${HOOKS[*]}"
|
|
)
|
|
}
|
|
|
|
# machine NAME MKINITCPIO_HOOKS PACKAGED_ETC [mac-boot]
|
|
machine() {
|
|
local root=$scratch/machines/$1
|
|
rm -rf "$root"
|
|
mkdir -p "$root/mkinitcpio.conf.d"
|
|
printf 'HOOKS=(%s)\n' "$2" > "$root/mkinitcpio.conf"
|
|
[[ -z $3 ]] || cp "$3"/*.conf "$root/mkinitcpio.conf.d/"
|
|
[[ ${4:-} != mac-boot ]] || cp "$fixtures"/omarchy-mac-boot/*.conf "$root/mkinitcpio.conf.d/"
|
|
printf '%s\n' "$root"
|
|
}
|
|
|
|
expect() {
|
|
local layout=$1 what=$2 got=$3 want=$4
|
|
[[ $got == "$want" ]] || { echo "FAIL: $layout: $what gets '$got', want '$want'" >&2; exit 1; }
|
|
}
|
|
|
|
arch_default='base udev autodetect microcode modconf kms keyboard keymap consolefont block filesystems fsck'
|
|
snapdragon='base systemd autodetect microcode modconf kms keyboard sd-vconsole block filesystems fsck'
|
|
legacy_mac='base udev autodetect modconf kms keyboard keymap consolefont block asahi encrypt filesystems fsck'
|
|
|
|
# Macs must come out exactly as they would without omarchy-settings' drop-ins.
|
|
check_macs() {
|
|
local layout=$1 packaged=$2 base
|
|
for base in "$arch_default" "$snapdragon"; do
|
|
expect "$layout" "an Aurora Mac" \
|
|
"$(effective_hooks "$(machine aurora "$base" "$packaged" mac-boot)")" \
|
|
"$(effective_hooks "$(machine aurora-bare "$base" "" mac-boot)")"
|
|
done
|
|
expect "$layout" "a legacy GRUB Mac" \
|
|
"$(effective_hooks "$(machine legacy "$legacy_mac" "$packaged")")" "$legacy_mac"
|
|
expect "$layout" "a legacy GRUB Mac with the Apple fragments" \
|
|
"$(effective_hooks "$(machine legacy-boot "$legacy_mac" "$packaged" mac-boot)")" \
|
|
"$(effective_hooks "$(machine legacy-boot-bare "$legacy_mac" "" mac-boot)")"
|
|
}
|
|
|
|
layout="HOOKS in omarchy_hooks.conf (v4.0.4)"
|
|
packaged=$scratch/omarchy-settings-aarch64/etc/mkinitcpio.conf.d
|
|
expect "$layout" Snapdragon "$(effective_hooks "$(machine snapdragon "$snapdragon" "$packaged")")" "$omarchy_hooks"
|
|
expect "$layout" "the DGX Spark" "$(effective_hooks "$(machine spark "$arch_default" "$packaged")")" "$omarchy_hooks"
|
|
check_macs "$layout" "$packaged"
|
|
echo "PASS: $layout: Snapdragon and the Spark get Omarchy's hooks; Macs keep theirs"
|
|
|
|
# omacom/omarchy#13362 decides per platform in 00-omarchy-hooks.conf; the
|
|
# package ships both of its files unchanged.
|
|
split=$scratch/split/omarchy
|
|
mkdir -p "$scratch/split"
|
|
cp -a "$fixture" "$split"
|
|
cp "$fixtures"/omarchy-13362/*.conf "$split/etc/mkinitcpio.conf.d/"
|
|
# Its 00-omarchy-hooks.conf asks the runtime's own detector (omarchy-hw-platform
|
|
# on PATH), so the package builds without a copy of it and ships none.
|
|
for recipe in omarchy-settings omarchy-settings-dev; do
|
|
package_aarch64 "$recipe" "$split" "$scratch/split-$recipe" >/dev/null
|
|
for conf in 00-omarchy-hooks.conf omarchy_hooks.conf; do
|
|
cmp "$fixtures/omarchy-13362/$conf" "$scratch/split-$recipe/etc/mkinitcpio.conf.d/$conf"
|
|
done
|
|
grep -Fxq etc/mkinitcpio.conf.d/00-omarchy-hooks.conf "$scratch/split-$recipe.backup"
|
|
[[ ! -e $scratch/split-$recipe/usr/share/libalpm/scripts/omarchy-hw-platform ]]
|
|
done
|
|
layout="omacom/omarchy#13362 (00-omarchy-hooks.conf)"
|
|
packaged=$scratch/split-omarchy-settings/etc/mkinitcpio.conf.d
|
|
for platform in "" qualcomm generic-aarch64; do
|
|
expect "$layout" "Snapdragon (${platform:-no detector})" \
|
|
"$(effective_hooks "$(machine snapdragon "$snapdragon" "$packaged")" "$platform")" "$omarchy_hooks"
|
|
expect "$layout" "the DGX Spark (${platform:-no detector})" \
|
|
"$(effective_hooks "$(machine spark "$arch_default" "$packaged")" "$platform")" "$omarchy_hooks"
|
|
done
|
|
expect "$layout" "a legacy GRUB Mac" \
|
|
"$(effective_hooks "$(machine legacy "$legacy_mac" "$packaged")" apple-silicon)" "$legacy_mac"
|
|
# An Aurora Mac builds on the systemd baseline and unlocks with sd-encrypt.
|
|
hooks=" $(effective_hooks "$(machine aurora "$arch_default" "$packaged" mac-boot)" apple-silicon) "
|
|
for hook in systemd asahi omarchy-vendorfw omarchy-mac-encrypt sd-encrypt; do
|
|
[[ $hooks == *" $hook "* ]] || { echo "FAIL: $layout: an Aurora Mac lacks $hook:$hooks" >&2; exit 1; }
|
|
done
|
|
for hook in udev encrypt; do
|
|
[[ $hooks != *" $hook "* ]] || { echo "FAIL: $layout: an Aurora Mac keeps $hook:$hooks" >&2; exit 1; }
|
|
done
|
|
echo "PASS: $layout: shipped unchanged and backed up; Snapdragon and the Spark get Omarchy's hooks; Macs keep theirs"
|
|
|
|
# Sources the recipe cannot make safe for Macs stop the aarch64 build, each
|
|
# with its own reason.
|
|
refuse() {
|
|
local what=$1 reason=$2 conf=$3 body=$4 bad=$scratch/bad/omarchy
|
|
rm -rf "$scratch/bad" "$scratch/bad-package"
|
|
mkdir -p "$scratch/bad"
|
|
cp -a "$fixture" "$bad"
|
|
rm -f "$bad"/etc/mkinitcpio.conf.d/{00-omarchy-hooks,omarchy_hooks}.conf
|
|
[[ -z $conf ]] || printf '%s\n' "$body" > "$bad/etc/mkinitcpio.conf.d/$conf"
|
|
if package_aarch64 omarchy-settings "$bad" "$scratch/bad-package" 2>"$scratch/bad.err"; then
|
|
echo "FAIL: the aarch64 package builds with $what" >&2
|
|
exit 1
|
|
fi
|
|
grep -Fq "$reason" "$scratch/bad.err" ||
|
|
{ echo "FAIL: $what stops the build for another reason: $(cat "$scratch/bad.err")" >&2; exit 1; }
|
|
echo "PASS: the aarch64 package refuses $what"
|
|
}
|
|
unsafe="must keep a Mac's asahi line"
|
|
unguardable="cannot guard this HOOKS= line"
|
|
refuse "no hooks file" "$unsafe" "" ""
|
|
refuse "a hooks file that sets no HOOKS" "$unsafe" omarchy_hooks.conf 'FILES+=(/etc/vconsole.conf)'
|
|
refuse "a HOOKS line with a trailing comment" "$unguardable" omarchy_hooks.conf "HOOKS=($omarchy_hooks) # local"
|
|
refuse "a HOOKS line split over lines" "$unguardable" omarchy_hooks.conf "HOOKS=(base udev"$'\n'" block encrypt filesystems)"
|
|
refuse "an indented HOOKS that ignores asahi" "$unsafe" 00-omarchy-hooks.conf "if true; then"$'\n'" HOOKS=($omarchy_hooks)"$'\n'"fi"
|
|
|
|
# Upgrades: pacman replaces an unmodified hooks file, keeps a modified one and
|
|
# leaves the guarded version as .pacnew, and installs it where it was absent.
|
|
# A file restored by hand after the stripped package (as the Spark and Surface
|
|
# owners did) is adopted: it stays in place and the guarded one is .pacnew.
|
|
if ((EUID != 0)) || ! command -v pacman >/dev/null; then
|
|
echo "SKIP: pacman upgrade checks need root"
|
|
exit 0
|
|
fi
|
|
|
|
unguarded=$fixture/etc/mkinitcpio.conf.d/omarchy_hooks.conf
|
|
guarded=$scratch/omarchy-settings-aarch64/etc/mkinitcpio.conf.d/omarchy_hooks.conf
|
|
printf '[options]\nArchitecture = auto\nSigLevel = Never\nLocalFileSigLevel = Never\n' > "$scratch/pacman.conf"
|
|
|
|
make_pkg() {
|
|
local ver=$1 hooks=${2:-} dir=$scratch/pkg-$1
|
|
mkdir -p "$dir/etc/mkinitcpio.conf.d"
|
|
[[ -z $hooks ]] || cp "$hooks" "$dir/etc/mkinitcpio.conf.d/omarchy_hooks.conf"
|
|
cat > "$dir/.PKGINFO" <<EOF
|
|
pkgname = omarchy-settings-upgrade-test
|
|
pkgbase = omarchy-settings-upgrade-test
|
|
pkgver = $ver
|
|
pkgdesc = omarchy-settings upgrade test
|
|
arch = any
|
|
size = 1
|
|
backup = etc/mkinitcpio.conf.d/omarchy_hooks.conf
|
|
EOF
|
|
(cd "$dir" && bsdtar -cf "$scratch/upgrade-test-$ver-any.pkg.tar" .PKGINFO etc)
|
|
printf '%s\n' "$scratch/upgrade-test-$ver-any.pkg.tar"
|
|
}
|
|
|
|
pacman_in() {
|
|
local root=$1
|
|
shift
|
|
mkdir -p "$root/var/lib/pacman" "$root/cache"
|
|
pacman --root "$root" --dbpath "$root/var/lib/pacman" --cachedir "$root/cache" \
|
|
--config "$scratch/pacman.conf" --logfile /dev/null --noconfirm --nodeps --noscriptlet "$@" >/dev/null
|
|
}
|
|
|
|
stripped=$(make_pkg 1-1)
|
|
old=$(make_pkg 2-1 "$unguarded")
|
|
new=$(make_pkg 3-1 "$guarded")
|
|
installed=etc/mkinitcpio.conf.d/omarchy_hooks.conf
|
|
|
|
pacman_in "$scratch/unchanged" -U "$old"
|
|
pacman_in "$scratch/unchanged" -U "$new"
|
|
cmp "$guarded" "$scratch/unchanged/$installed"
|
|
[[ ! -e $scratch/unchanged/$installed.pacnew ]]
|
|
|
|
pacman_in "$scratch/modified" -U "$old"
|
|
echo '# local change' >> "$scratch/modified/$installed"
|
|
pacman_in "$scratch/modified" -U "$new"
|
|
grep -Fxq '# local change' "$scratch/modified/$installed"
|
|
cmp "$guarded" "$scratch/modified/$installed.pacnew"
|
|
|
|
pacman_in "$scratch/absent" -U "$stripped"
|
|
pacman_in "$scratch/absent" -U "$new"
|
|
cmp "$guarded" "$scratch/absent/$installed"
|
|
|
|
pacman_in "$scratch/restored" -U "$stripped"
|
|
mkdir -p "$scratch/restored/etc/mkinitcpio.conf.d"
|
|
cp "$unguarded" "$scratch/restored/$installed"
|
|
pacman_in "$scratch/restored" -U "$new"
|
|
cmp "$unguarded" "$scratch/restored/$installed"
|
|
cmp "$guarded" "$scratch/restored/$installed.pacnew"
|
|
|
|
# Source what the upgrades installed.
|
|
for upgrade in unchanged absent; do
|
|
etc=$scratch/$upgrade/etc/mkinitcpio.conf.d
|
|
expect "$upgrade upgrade" Snapdragon "$(effective_hooks "$(machine "$upgrade-snapdragon" "$snapdragon" "$etc")")" "$omarchy_hooks"
|
|
check_macs "$upgrade upgrade" "$etc"
|
|
done
|
|
echo "PASS: pacman upgrades install the guarded hooks file and keep local changes"
|