Merge remote-tracking branch 'upstream/master' into spark/aarch64-settings-boot-dropins

This commit is contained in:
Jim Martin committed 2026-09-26 17:38:24 -05:00
commit 9d05b3fb06
117 files changed
+18463 -230

No files matched your search

+532
View File
@@ -0,0 +1,532 @@
#!/bin/bash
# limine-mkinitcpio-hook: the x86_64 package is upstream's as before. On
# aarch64, limine-apple-gate leaves every machine but an Apple Silicon Mac as
# the x86_64 package behaves; a Mac keeps /boot current through mkinitcpio's
# own kernel hook, and Limine waits until Omarchy activates it.
#
# The package() of the PKGBUILD runs over an upstream-shaped source tree
# (LIMINE_ENTRY_TOOL_SRC may name a real limine-entry-tool checkout instead),
# and pacman transactions replay through the packaged hooks on fixture machines.
set -euo pipefail
REPO_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
RECIPE=$REPO_ROOT/pkgbuilds/limine-mkinitcpio-hook
GATE=$RECIPE/limine-apple-gate
TEST_ROOT=$(mktemp -d)
trap 'rm -rf "$TEST_ROOT"' EXIT
fail() {
echo "not ok - $1" >&2
[[ $# -lt 2 ]] || printf '%s\n' "$2" >&2
exit 1
}
pass() {
echo "ok - $1"
}
# pacman hands NeedsTargets to a hook over a socket, and a hook that stops
# reading early fails the write ("unable to write to pipe").
SOCKET_FEED='
import socket, subprocess, sys
parent, child = socket.socketpair()
hook = subprocess.Popen(sys.argv[1:], stdin=child)
child.close()
try:
parent.sendall(sys.stdin.buffer.read())
parent.shutdown(socket.SHUT_WR)
except OSError as error:
print("unable to write to pipe (%s)" % error.strerror, file=sys.stderr)
hook.wait()
sys.exit(141)
sys.exit(hook.wait())
'
socket_feed() { python3 -c "$SOCKET_FEED" "$@"; }
apple_tree() { printf 'apple,j316s\0apple,t6000\0apple,arm-platform\0'; }
live_is_mac() {
local source
for source in /proc/device-tree/compatible /sys/firmware/devicetree/base/compatible; do
[[ -r $source ]] && tr '\0' '\n' <"$source" | grep -q '^apple,' && return 0
done
return 1
}
# The gate trusts fixtures only from an unprivileged caller: as root it reads
# the live machine. Check that, then carry on as nobody.
if ((EUID == 0)); then
if ! live_is_mac; then
mkdir -p "$TEST_ROOT/root-proc/device-tree"
apple_tree >"$TEST_ROOT/root-proc/device-tree/compatible"
out=$(OMARCHY_PROC_ROOT=$TEST_ROOT/root-proc bash "$GATE" echo ran </dev/null)
[[ $out == ran ]] || fail "as root the gate ignores a fixture device tree" "$out"
pass "as root the gate reads the live machine, not fixtures"
fi
rm -rf "$TEST_ROOT"
exec setpriv --reuid=nobody --regid=nobody --clear-groups -- bash "$REPO_ROOT/tests/limine-mkinitcpio-hook.sh"
fi
# ---------------------------------------------------------------------------
# Sources: the local aarch64 sources are pinned, x86_64 has none.
bash -n "$RECIPE/PKGBUILD"
bash -n "$GATE"
mapfile -t sources < <(CARCH=aarch64 bash -c 'source "$1"; printf "%s\n" "${source_aarch64[@]}"' _ "$RECIPE/PKGBUILD")
mapfile -t sums < <(CARCH=aarch64 bash -c 'source "$1"; printf "%s\n" "${sha256sums_aarch64[@]}"' _ "$RECIPE/PKGBUILD")
((${#sources[@]} == ${#sums[@]})) || fail "every aarch64 source has a checksum"
local_sources=0
for i in "${!sources[@]}"; do
[[ ${sources[i]} == *://* ]] && continue
((++local_sources))
[[ $(sha256sum "$RECIPE/${sources[i]}" | cut -d' ' -f1) == "${sums[i]}" ]] ||
fail "the PKGBUILD pins ${sources[i]} by its sha256"
done
((local_sources == 2)) || fail "limine-apple-gate and mkinitcpio-install.hook are the aarch64 local sources"
x86_sources=$(CARCH=x86_64 bash -c 'source "$1"; printf "%s\n" "${source[@]}" "${source_x86_64[@]}"' _ "$RECIPE/PKGBUILD")
[[ $x86_sources != *limine-apple-gate* && $x86_sources != *mkinitcpio-install.hook* ]] ||
fail "x86_64 builds take no Apple Silicon source"
grep -Fxq 'Exec = /usr/share/libalpm/scripts/mkinitcpio install' "$RECIPE/mkinitcpio-install.hook" ||
fail "mkinitcpio-install.hook is mkinitcpio's own kernel hook"
pass "the Apple Silicon sources are aarch64-only and pinned by checksum"
# ---------------------------------------------------------------------------
# package() for both architectures over the same upstream tree.
SRC=$TEST_ROOT/src
upstream=$SRC/limine-entry-tool
mkdir -p "$upstream/build/native/nativeCompile"
if [[ -n ${LIMINE_ENTRY_TOOL_SRC:-} ]]; then
cp -a "$LIMINE_ENTRY_TOOL_SRC/install" "$LIMINE_ENTRY_TOOL_SRC/README.md" "$LIMINE_ENTRY_TOOL_SRC/CHANGELOG.md" "$upstream/"
else
tool=$upstream/install/arch-linux/limine-entry-tool
hook=$upstream/install/arch-linux/limine-mkinitcpio-hook
mkdir -p "$tool/etc/boot/hooks/pre.d" "$tool/usr/bin" "$tool/usr/lib/limine" "$tool/usr/share/libalpm/hooks" \
"$hook/etc/pacman.d/hooks" "$hook/usr/bin" "$hook/usr/local/bin" "$hook/usr/share/libalpm/hooks" "$hook/usr/share/libalpm/scripts"
echo '# README' >"$upstream/README.md"
echo '# CHANGELOG' >"$upstream/CHANGELOG.md"
echo 'ESP_PATH=""' >"$tool/etc/limine-entry-tool.conf"
echo 'ESP_PATH=""' >"$hook/etc/limine-entry-tool.conf"
for name in limine-install limine-entry-tool limine-reset-enroll limine-enroll-config; do
printf '#!/usr/bin/env bash\n' >"$tool/usr/bin/$name"
done
printf '#!/usr/bin/env bash\n' >"$tool/usr/lib/limine/limine-common-functions"
for name in limine-mkinitcpio limine-update; do
printf '#!/usr/bin/env bash\n' >"$hook/usr/bin/$name"
done
for name in limine-mkinitcpio-install limine-mkinitcpio-remove; do
printf '#!/usr/bin/env bash\n' >"$hook/usr/share/libalpm/scripts/$name"
done
cat >"$tool/usr/share/libalpm/hooks/80-limine-efi-deploy.hook" <<'HOOK'
[Trigger]
Operation = Install
Operation = Upgrade
Type = Package
Target = limine
Target = limine-git
Target = limine-dev
[Action]
Description = Deploying Limine after upgrade...
When = PostTransaction
Exec = /usr/bin/limine-install --no-efi-register
HOOK
cat >"$hook/usr/share/libalpm/hooks/80-limine-efi-deploy.hook" <<'HOOK'
[Trigger]
Operation = Install
Operation = Upgrade
Type = Package
Target = limine
Target = limine-git
Target = limine-dev
Target = limine-mkinitcpio-hook
Target = limine-mkinitcpio-hook-git
[Action]
Description = Deploying Limine after upgrade...
When = PostTransaction
Exec = /usr/bin/limine-install
HOOK
cat >"$hook/usr/share/libalpm/hooks/60-limine-mkinitcpio-remove-pre.hook" <<'HOOK'
[Trigger]
Type = Path
Operation = Remove
Target = usr/lib/modules/*/modules.builtin
[Action]
Description = Record kernels marked for removal in Limine
When = PreTransaction
Exec = /usr/share/libalpm/scripts/limine-mkinitcpio-remove pre
NeedsTargets
HOOK
cat >"$hook/usr/share/libalpm/hooks/90-limine-mkinitcpio-remove-post.hook" <<'HOOK'
[Trigger]
Type = Path
Operation = Remove
Target = usr/lib/modules/*/modules.builtin
[Action]
Description = Clean Limine boot entries of removed kernels
When = PostTransaction
Exec = /usr/share/libalpm/scripts/limine-mkinitcpio-remove post
HOOK
cat >"$hook/etc/pacman.d/hooks/90-mkinitcpio-install.hook" <<'HOOK'
[Trigger]
Type = Path
Operation = Install
Operation = Upgrade
Operation = Remove
Target = usr/lib/initcpio/*
Target = usr/lib/firmware/*
Target = usr/lib/modules/*/extramodules/
Target = usr/src/*/dkms.conf
Target = usr/lib/systemd/systemd
Target = usr/bin/cryptsetup
Target = usr/bin/lvm
[Trigger]
Type = Path
Operation = Install
Operation = Upgrade
Target = usr/lib/modules/*/modules.builtin
[Trigger]
Type = Package
Operation = Install
Operation = Upgrade
Target = mkinitcpio
Target = mkinitcpio-git
[Action]
Description = Updating linux initcpios...
When = PostTransaction
Exec = /usr/share/libalpm/scripts/limine-mkinitcpio-install
NeedsTargets
HOOK
cat >"$hook/usr/local/bin/mkinitcpio" <<'WRAPPER'
#!/usr/bin/env bash
/usr/bin/mkinitcpio "$@"
_color_reset=""
_color_yellow=""
colors="$(tput colors 2>/dev/null || echo 0)"
if ((colors >= 8)); then
_color_reset="\033[0m"
_color_yellow="\033[1;33m"
fi
if [[ " $* " == *" -P "* || " $* " == *" --allpresets "* || " $* " == *" -p "* || " $* " == *" --preset "* ]]; then
printf '%b==> WARNING: This does not update Limine boot entries.%b\n' "${_color_yellow}" "${_color_reset}" >&2
printf "%b Use 'limine-mkinitcpio' or 'limine-update' instead.%b\n" "${_color_yellow}" "${_color_reset}" >&2
read -rp "==> Would you like to run 'limine-mkinitcpio' now? [Y/n]: " answer
case "${answer,,}" in
"" | "y" | "yes")
/usr/bin/limine-mkinitcpio
;;
*)
# nothing
;;
esac
fi
WRAPPER
fi
printf 'native image\n' >"$upstream/build/native/nativeCompile/limine-entry-tool"
chmod -R u+w,go+rX "$upstream"
find "$upstream/install" -path '*/bin/*' -type f -exec chmod 755 {} + -o -path '*/scripts/*' -type f -exec chmod 755 {} +
ln -s "$RECIPE/limine-apple-gate" "$RECIPE/mkinitcpio-install.hook" "$SRC/"
# makepkg runs package() with errexit.
build_package() {
local arch=$1 out=$2
mkdir -p "$out"
CARCH=$arch srcdir=$SRC pkgdir=$out bash -euo pipefail -c 'source "$1"; package' _ "$RECIPE/PKGBUILD" ||
fail "package() succeeds for $arch"
}
PKG_X86=$TEST_ROOT/pkg-x86_64
PKG_ARM=$TEST_ROOT/pkg-aarch64
build_package x86_64 "$PKG_X86"
build_package aarch64 "$PKG_ARM"
manifest() {
(cd "$1" && find . \( -type f -o -type l \) -printf '%p %m %l\n' | LC_ALL=C sort) |
while read -r path mode target; do
if [[ -n $target ]]; then
echo "$path $mode -> $target"
else
echo "$path $mode $(sha256sum "$1/$path" | cut -d' ' -f1)"
fi
done
}
scripts=/usr/share/libalpm/scripts
gate=$scripts/limine-apple-gate
upstream_hook=$upstream/install/arch-linux/limine-mkinitcpio-hook
for file in etc/pacman.d/hooks/90-mkinitcpio-install.hook usr/local/bin/mkinitcpio \
usr/share/libalpm/hooks/{60-limine-mkinitcpio-remove-pre,80-limine-efi-deploy,90-limine-mkinitcpio-remove-post}.hook; do
cmp -s "$upstream_hook/$file" "$PKG_X86/$file" || fail "x86_64 ships upstream's $file unchanged"
done
[[ ! -e $PKG_X86$gate && ! -e $PKG_X86/usr/share/libalpm/hooks/90-mkinitcpio-apple-install.hook ]] ||
fail "x86_64 ships no Apple Silicon file"
pass "x86_64 packages upstream's hooks and wrapper unchanged"
differences=$(diff <(manifest "$PKG_X86") <(manifest "$PKG_ARM") || true)
changed=$(sed -n 's/^> \(\S*\) .*/\1/p' <<<"$differences")
expected=$(printf '%s\n' ./etc/pacman.d/hooks/90-mkinitcpio-install.hook \
./usr/local/bin/mkinitcpio \
./usr/share/libalpm/hooks/60-limine-mkinitcpio-remove-pre.hook \
./usr/share/libalpm/hooks/80-limine-efi-deploy.hook \
./usr/share/libalpm/hooks/90-limine-mkinitcpio-remove-post.hook \
./usr/share/libalpm/hooks/90-mkinitcpio-apple-install.hook \
./usr/share/libalpm/scripts/limine-apple-gate)
[[ $changed == "$expected" ]] || fail "aarch64 differs from x86_64 only in the gated hooks, the wrapper and the gate" "$changed"
[[ -z $(sed -n 's/^< \(\S*\) .*/\1/p' <<<"$differences" | grep -vxF "$expected") ]] ||
fail "aarch64 keeps every x86_64 file"
[[ $(stat -c %a "$PKG_ARM$gate") == 755 ]] || fail "the gate is executable"
diff <(grep -v '^Exec = \|^Description = ' "$RECIPE/mkinitcpio-install.hook") \
<(grep -v '^Exec = \|^Description = ' "$PKG_ARM/usr/share/libalpm/hooks/90-mkinitcpio-apple-install.hook") >/dev/null ||
fail "the Mac's kernel hook keeps mkinitcpio's triggers"
[[ $(sed -n 2p "$PKG_ARM/usr/local/bin/mkinitcpio") == "$gate --mac && exec /usr/bin/mkinitcpio \"\$@\"" ]] ||
fail "the aarch64 wrapper is plain mkinitcpio on a Mac"
pass "aarch64 routes Limine's hooks and the wrapper through the gate and adds the Mac's kernel hook"
# ---------------------------------------------------------------------------
# Fixture machines, read through OMARCHY_PROC_ROOT, OMARCHY_LIMINE_GATE and
# OMARCHY_LIMINE_DEFAULT.
machine() {
local name=$1 compatible=$2 dir=$TEST_ROOT/machines/$1
mkdir -p "$dir/proc" "$dir/state"
if [[ -n $compatible ]]; then
mkdir -p "$dir/proc/device-tree"
printf "$compatible" >"$dir/proc/device-tree/compatible"
fi
}
machine x86 ''
machine generic-aarch64 'linux,dummy-virt\0'
machine qualcomm 'lenovo,thinkpad-t14s\0qcom,x1e78100\0qcom,x1e80100\0'
machine mac-dormant 'apple,j316s\0apple,t6000\0apple,arm-platform\0'
machine mac-marker-only 'apple,j316s\0apple,t6000\0apple,arm-platform\0'
machine mac-active 'apple,j316s\0apple,t6000\0apple,arm-platform\0'
: >"$TEST_ROOT/machines/mac-marker-only/state/limine.enabled"
: >"$TEST_ROOT/machines/mac-active/state/limine.enabled"
echo 'ESP_PATH="/boot/efi"' >"$TEST_ROOT/machines/mac-active/state/limine"
on() {
local dir=$TEST_ROOT/machines/$1
shift
(
export OMARCHY_PROC_ROOT=$dir/proc OMARCHY_LIMINE_GATE=$dir/state/limine.enabled OMARCHY_LIMINE_DEFAULT=$dir/state/limine
"$@"
)
}
status() {
local rc=0
"$@" || rc=$?
echo "$rc"
}
for m in x86 generic-aarch64 qualcomm; do
[[ $(on "$m" status bash "$GATE" --mac) == 1 ]] || fail "$m is not a Mac"
[[ $(printf 'a\nb\n' | on "$m" bash "$GATE" cat) == $'a\nb' ]] || fail "$m runs Limine's hooks with their targets"
[[ $(on "$m" status bash "$GATE" bash -c 'exit 7' </dev/null) == 7 ]] || fail "$m keeps the hook's status"
[[ -z $(printf 'a\n' | on "$m" bash "$GATE" --mac cat 2>&1) ]] || fail "$m skips the Mac's hooks quietly"
[[ $(on "$m" bash "$GATE" --not-mac echo ran </dev/null) == ran ]] || fail "$m deploys Limine's EFI binary"
done
for m in mac-dormant mac-marker-only mac-active; do
[[ $(on "$m" status bash "$GATE" --mac) == 0 ]] || fail "$m is a Mac"
[[ $(printf 'a\n' | on "$m" bash "$GATE" --mac cat) == a ]] || fail "$m runs the Mac's hooks with their targets"
[[ -z $(printf 'a\n' | on "$m" bash "$GATE" --not-mac cat 2>&1) ]] || fail "$m leaves Limine's EFI binary to omarchy-mac-boot"
done
for m in mac-dormant mac-marker-only; do
out=$(printf 'usr/lib/modules/7.1.0/modules.builtin\n' | on "$m" bash "$GATE" bash -c 'echo ran; exit 100' 2>&1) ||
fail "$m passes over Limine's hooks successfully"
[[ -z $out ]] || fail "$m passes over Limine's hooks quietly" "$out"
[[ -z $(on "$m" bash "$GATE" echo ran <&- 2>&1) ]] || fail "$m passes over a hook without targets"
done
# A linux-firmware upgrade streams thousands of targets, more than a socket holds.
many_targets() { printf 'usr/lib/firmware/fixture/%s.bin\n' $(seq 60000); }
many_targets | on mac-dormant socket_feed bash "$GATE" true || fail "a dormant Mac reads every target of a hook it passes over"
many_targets | on x86 socket_feed bash "$GATE" --mac true || fail "other machines read every target of the Mac's hooks"
many_targets | on mac-active socket_feed bash "$GATE" --not-mac true || fail "a Mac reads every target of the deploy hook"
many_targets | on mac-dormant bash "$GATE" true || fail "a dormant Mac reads every target piped to a hook it passes over"
[[ $(printf 'a\n' | on mac-active bash "$GATE" bash -c 'echo "ran $1"; cat' _ post) == $'ran post\na' ]] ||
fail "an active Mac runs Limine's hooks with their arguments and targets"
[[ $(on mac-active status bash "$GATE" bash -c 'exit 7' </dev/null) == 7 ]] || fail "an active Mac keeps the hook's status"
[[ $(on x86 status bash "$GATE" </dev/null 2>/dev/null) == 2 ]] || fail "the gate needs a command"
pass "the gate runs Limine's hooks everywhere but a Mac that has not activated Limine"
# ---------------------------------------------------------------------------
# pacman: hooks from /usr/share/libalpm/hooks, overridden by name from
# /etc/pacman.d/hooks, run in name order, each with the targets of the
# transaction its triggers match (sorted, over a socket for NeedsTargets,
# else no stdin).
# Transactions are lines of "<Operation> <Path|Package> <target>".
fake_root() {
local pkg=$1 root=$2 name
cp -a "$pkg" "$root"
cp "$RECIPE/mkinitcpio-install.hook" "$root/usr/share/libalpm/hooks/90-mkinitcpio-install.hook"
for name in usr/share/libalpm/scripts/limine-mkinitcpio-install usr/share/libalpm/scripts/limine-mkinitcpio-remove \
usr/bin/limine-install usr/share/libalpm/scripts/mkinitcpio usr/bin/mkinitcpio usr/bin/limine-mkinitcpio; do
rm -f "$root/$name"
printf '#!/bin/bash\n{ printf %%s %q; if (($#)); then printf " %%s" "$@"; fi; echo; if [[ -S /dev/stdin || -p /dev/stdin ]]; then sed "s/^/ /"; fi; } >>"$SIM_LOG"\n' \
"${name##*/}" >"$root/$name"
chmod 755 "$root/$name"
done
}
run_hook() {
local root=$1 hook=$2 when=$3 transaction=$4
local line section='' hook_when='' exec_line='' needs=0 type='' ops='' targets=''
local -a triggers=()
while IFS= read -r line || [[ -n $line ]]; do
case $line in
'[Trigger]' | '[Action]')
[[ $section != Trigger ]] || triggers+=("$type|$ops|$targets")
section=${line//[][]/} type='' ops=' ' targets=''
;;
NeedsTargets) needs=1 ;;
*' = '*)
case $section/${line%% = *} in
Trigger/Type) type=${line#* = } ;;
Trigger/Operation) ops+="${line#* = } " ;;
Trigger/Target) targets+="${line#* = } " ;;
Action/When) hook_when=${line#* = } ;;
Action/Exec) exec_line=${line#* = } ;;
esac
;;
esac
done <"$hook"
[[ $hook_when == "$when" ]] || return 0
local op kind target trigger t_type t_ops t_targets pattern
local -a matched=() patterns=()
while read -r op kind target; do
for trigger in "${triggers[@]}"; do
IFS='|' read -r t_type t_ops t_targets <<<"$trigger"
[[ $t_type == "$kind" && $t_ops == *" $op "* ]] || continue
read -ra patterns <<<"$t_targets"
for pattern in "${patterns[@]}"; do
# shellcheck disable=SC2053
if [[ $target == $pattern ]]; then
matched+=("$target")
break 2
fi
done
done
done <"$transaction"
((${#matched[@]})) || return 0
local word
local -a argv=() command=()
read -ra argv <<<"$exec_line"
for word in "${argv[@]}"; do
if [[ $word == /* && -e $root$word ]]; then command+=("$root$word"); else command+=("$word"); fi
done
if ((needs)); then
printf '%s\n' "${matched[@]}" | LC_ALL=C sort -u | socket_feed "${command[@]}"
else
"${command[@]}" <&-
fi
}
run_transaction() {
local root=$1 transaction=$2 when name
local -A hooks=()
for name in "$root"/usr/share/libalpm/hooks/*.hook "$root"/etc/pacman.d/hooks/*.hook; do
[[ -e $name ]] && hooks[${name##*/}]=$name
done
for when in PreTransaction PostTransaction; do
while read -r name; do
run_hook "$root" "${hooks[$name.hook]}" "$when" "$transaction"
done < <(printf '%s\n' "${!hooks[@]}" | sed 's/\.hook$//' | LC_ALL=C sort)
done
}
fake_root "$PKG_X86" "$TEST_ROOT/root-x86_64"
fake_root "$PKG_ARM" "$TEST_ROOT/root-aarch64"
transaction() {
local name=$1
shift
printf '%s\n' "$@" >"$TEST_ROOT/tx-$name"
}
transaction kernel \
'Install Path usr/lib/modules/7.1.0-asahi/vmlinuz' \
'Install Path usr/lib/modules/7.1.0-asahi/modules.builtin' \
'Install Path usr/lib/modules/7.1.0-asahi/pkgbase' \
'Remove Path usr/lib/modules/7.0.0-asahi/vmlinuz' \
'Remove Path usr/lib/modules/7.0.0-asahi/modules.builtin' \
'Remove Path usr/lib/modules/7.0.0-asahi/pkgbase' \
'Upgrade Package linux-asahi'
transaction firmware 'Upgrade Path usr/lib/firmware/brcm/fixture.bin' 'Upgrade Package linux-firmware'
transaction modprobe 'Upgrade Path usr/lib/modprobe.d/' 'Install Path usr/lib/modprobe.d/omarchy-mac.conf' 'Upgrade Package omarchy-mac'
transaction limine 'Upgrade Path usr/share/limine/BOOTAA64.EFI' 'Upgrade Package limine'
replay() {
local arch=$1 m=$2 name=$3
export SIM_LOG=$TEST_ROOT/log
: >"$SIM_LOG"
local out
out=$(on "$m" run_transaction "$TEST_ROOT/root-$arch" "$TEST_ROOT/tx-$name" 2>&1) || fail "$name on $arch/$m replays"
[[ -z $out ]] || fail "$name on $arch/$m prints nothing of the gate's" "$out"
cat "$SIM_LOG"
}
limine_kernel=$'limine-mkinitcpio-remove pre\n usr/lib/modules/7.0.0-asahi/modules.builtin\nlimine-mkinitcpio-remove post\nlimine-mkinitcpio-install\n usr/lib/modules/7.1.0-asahi/modules.builtin'
stock_kernel=$'mkinitcpio install\n usr/lib/modules/7.1.0-asahi/vmlinuz'
limine_firmware=$'limine-mkinitcpio-install\n usr/lib/firmware/brcm/fixture.bin'
stock_firmware=$'mkinitcpio install\n usr/lib/firmware/brcm/fixture.bin'
stock_modprobe=$'mkinitcpio install\n usr/lib/modprobe.d/'
declare -A want=(
[kernel/other]=$limine_kernel
[firmware/other]=$limine_firmware
[modprobe/other]=''
[limine/other]='limine-install'
[kernel/mac-dormant]=$stock_kernel
[firmware/mac-dormant]=$stock_firmware
[modprobe/mac-dormant]=$stock_modprobe
[limine/mac-dormant]=''
[kernel/mac-active]=$'limine-mkinitcpio-remove pre\n usr/lib/modules/7.0.0-asahi/modules.builtin\nlimine-mkinitcpio-remove post\n'"$stock_kernel"$'\nlimine-mkinitcpio-install\n usr/lib/modules/7.1.0-asahi/modules.builtin'
[firmware/mac-active]=$stock_firmware$'\n'$limine_firmware
[modprobe/mac-active]=$stock_modprobe
[limine/mac-active]=''
)
want[kernel/mac-marker-only]=${want[kernel/mac-dormant]}
for name in kernel firmware modprobe limine; do
got=$(replay x86_64 x86 "$name")
[[ $got == "${want[$name/other]}" ]] || fail "x86_64: $name transaction runs upstream's hooks" "$got"
for m in generic-aarch64 qualcomm; do
got=$(replay aarch64 "$m" "$name")
[[ $got == "${want[$name/other]}" ]] || fail "aarch64 $m: $name transaction runs as on x86_64" "$got"
done
done
pass "x86_64, generic aarch64 and Snapdragon transactions run Limine's hooks as upstream ships them"
for m in mac-dormant mac-marker-only mac-active; do
for name in kernel firmware modprobe limine; do
[[ -v want[$name/$m] ]] || continue
got=$(replay aarch64 "$m" "$name")
[[ $got == "${want[$name/$m]}" ]] || fail "$m: $name transaction" "$got"
done
done
pass "a dormant Mac defers to mkinitcpio's own hook; an active Mac also writes Limine's entries after it"
# ---------------------------------------------------------------------------
# The wrapper, as sudo mkinitcpio -P and mkinitcpio's hook script call it
# (/usr/local/bin first in PATH, stdin at its end).
wrapper() {
local root=$1 m=$2
sed "s|/usr/|$root/usr/|g" "$root/usr/local/bin/mkinitcpio" >"$TEST_ROOT/wrapper"
export SIM_LOG=$TEST_ROOT/log
: >"$SIM_LOG"
on "$m" bash "$TEST_ROOT/wrapper" -P </dev/null >/dev/null 2>&1 || true
cat "$SIM_LOG"
}
upstream_wrapper=$'mkinitcpio -P\nlimine-mkinitcpio'
[[ $(wrapper "$TEST_ROOT/root-x86_64" x86) == "$upstream_wrapper" ]] || fail "x86_64 keeps upstream's wrapper"
for m in generic-aarch64 qualcomm; do
[[ $(wrapper "$TEST_ROOT/root-aarch64" "$m") == "$upstream_wrapper" ]] || fail "$m keeps upstream's wrapper"
done
for m in mac-dormant mac-active; do
[[ $(wrapper "$TEST_ROOT/root-aarch64" "$m") == 'mkinitcpio -P' ]] || fail "$m runs plain mkinitcpio"
done
pass "the mkinitcpio wrapper is upstream's everywhere but a Mac, where it is plain mkinitcpio"
+148
View File
@@ -0,0 +1,148 @@
#!/usr/bin/env python3
"""Exercise the provider against real, isolated tmux buffers and fake terminals."""
import base64
import fcntl
import json
import os
from pathlib import Path
import pty
import select
import struct
import subprocess
import tempfile
import termios
import time
ROOT = Path(__file__).resolve().parents[1]
PROVIDER = ROOT / "pkgbuilds/omarchy-nvim/lua/config/remote_clipboard.lua"
def drain(fd, seconds=0.1):
data = b""
deadline = time.monotonic() + seconds
while time.monotonic() < deadline:
if select.select([fd], [], [], max(0, deadline - time.monotonic()))[0]:
try:
data += os.read(fd, 65536)
except OSError:
break
return data
with tempfile.TemporaryDirectory(prefix="nvim-tmux-test-") as directory:
root = Path(directory)
command = ["tmux", "-S", str(root / "socket"), "-f", "/dev/null"]
clients = []
def tmux(*args):
return subprocess.check_output(command + list(args), text=True)
driver = root / "driver.lua"
driver.write_text(r'''
local request = vim.json.decode(table.concat(vim.fn.readfile(vim.env.TEST_REQUEST), "\n"))
dofile(vim.env.TEST_PROVIDER).setup()
if request.operation == "copy" then
vim.fn.setreg("+", request.lines, request.regtype or "v")
else
local value = vim.g.clipboard.paste["+"]()
local lines = type(value[1]) == "table" and value[1] or value
io.write(vim.json.encode(lines))
end
''')
def nvim(operation, lines=None, regtype="v"):
request = root / "request.json"
request.write_text(json.dumps(dict(operation=operation, lines=lines, regtype=regtype)))
env = dict(os.environ)
for key in ("WAYLAND_DISPLAY", "DISPLAY", "HERDR_PANE_ID", "SSH_TTY"):
env.pop(key, None)
env.update(
TMUX=tmux("display-message", "-p", "#{socket_path},#{pid},0").strip(),
TMUX_PANE=tmux("display-message", "-p", "-t", "A:0.0", "#{pane_id}").strip(),
SSH_CONNECTION="test", TEST_PROVIDER=str(PROVIDER),
TEST_REQUEST=str(request),
)
result = subprocess.run(
["nvim", "--clean", "-n", "--headless", "-i", "NONE", "-l", str(driver)],
env=env, cwd=root, capture_output=True, text=True, timeout=15,
)
assert result.returncode == 0, result.stderr
if operation == "paste":
return json.loads(result.stdout)
try:
tmux("new-session", "-d", "-s", "A")
tmux("new-session", "-d", "-s", "B")
tmux("set-option", "-s", "terminal-features", "xterm*:clipboard")
tmux("set-option", "-g", "set-clipboard", "off")
# Every nvim() call is a separate instance: clearing must be shared.
nvim("copy", [""])
assert nvim("paste") == [], "empty server clipboard did not stay empty"
tmux("set-buffer", "older history")
nvim("copy", ["private previous value"])
assert nvim("paste") == ["private previous value"]
history = tmux("list-buffers")
for _ in range(2):
nvim("copy", [""])
assert nvim("paste") == [], "clear returned stale clipboard text"
assert tmux("list-buffers") == history, "clear destroyed tmux history"
tmux("set-buffer", "external copy")
assert nvim("paste") == ["external copy"], "new copy did not supersede clear"
nvim("copy", [""])
nvim("copy", ["new Neovim copy"])
assert nvim("paste") == ["new Neovim copy"]
print("ok - shared empty clipboard, preserved history, and subsequent copies")
for lines, regtype in [(["line"], "V"), (["a", "b"], "v"),
(["x" * (1024 * 1024 + 1)], "v"),
(["$(touch NEVER) `id`; quotes ' \"", "λ\u001b\n"], "v")]:
nvim("copy", lines, regtype)
# Neovim represents embedded NUL bytes as newline inside a list item.
expected = list(lines)
if regtype == "V":
expected.append("")
actual = nvim("paste")
assert actual == expected, ("transport changed clipboard bytes", repr(actual)[:200], repr(expected)[:200])
assert not (root / "NEVER").exists(), "clipboard content executed as shell code"
print("ok - real transport: linewise, multiline, large and literal/control payloads")
# Attach three PTYs, never the developer's terminal or clipboard.
for session in ("A", "A", "B"):
master, slave = pty.openpty()
fcntl.ioctl(slave, termios.TIOCSWINSZ, struct.pack("HHHH", 24, 80, 0, 0))
def controlling_tty():
os.setsid()
fcntl.ioctl(0, termios.TIOCSCTTY, 0)
env = dict(os.environ, TERM="xterm-256color")
env.pop("TMUX", None)
env.pop("TMUX_PANE", None)
process = subprocess.Popen(
command + ["attach-session", "-t", session], stdin=slave,
stdout=slave, stderr=slave, env=env, preexec_fn=controlling_tty,
)
os.close(slave)
clients.append((master, process))
drain(master)
for policy in ("off", "on", "external", "off"):
tmux("set-option", "-s", "set-clipboard", policy)
for fd, _ in clients:
drain(fd)
payload = "synthetic-export-" + policy
nvim("copy", [payload])
outputs = [drain(fd, 0.3) for fd, _ in clients]
exports = [b"\x1b]52;" in output for output in outputs]
assert not exports[2], "clipboard escaped into an unrelated session"
if policy == "off":
assert not any(exports), "set-clipboard off leaked OSC 52"
else:
assert sum(exports[:2]) == 1, "expected one selected tmux client"
assert any(base64.b64encode(payload.encode()) in output for output in outputs[:2])
assert nvim("paste") == [payload], "export policy broke internal paste"
print("ok - real clients: export policy, policy changes, selected-client isolation")
finally:
subprocess.run(command + ["kill-server"], capture_output=True)
for fd, process in clients:
process.wait(timeout=5)
os.close(fd)
+131
View File
@@ -0,0 +1,131 @@
local provider = assert(vim.env.TEST_PROVIDER)
local real_system, real_systemlist = vim.fn.system, vim.fn.systemlist
local real_executable, real_readfile = vim.fn.executable, vim.fn.readfile
local writes, reads, emitted, read_result, failed, write_failed
local function setup(env, options)
for _, key in ipairs({ "SSH_CONNECTION", "SSH_TTY", "TMUX", "HERDR_PANE_ID", "WAYLAND_DISPLAY" }) do
vim.env[key] = nil
end
for key, value in pairs(env) do vim.env[key] = value end
vim.g.clipboard = nil
vim.g.omarchy_remote_clipboard_sync = options and options.sync
vim.g.omarchy_remote_clipboard_osc52 = options and options.osc52
vim.opt.clipboard = options and options.clipboard or ""
writes, reads, emitted = {}, {}, {}
read_result, failed, write_failed = nil, false, false
vim.fn.readfile = function(path, ...)
if path:match("^/proc/") then return {} end
return real_readfile(path, ...)
end
vim.fn.executable = function(cmd)
if cmd == "tmux" or cmd == "wl-copy" or cmd == "wl-paste" then return 1 end
return real_executable(cmd)
end
vim.fn.system = function(cmd, lines)
writes[#writes + 1] = { cmd, vim.deepcopy(lines) }
real_system({ "sh", "-c", write_failed and "exit 1" or "exit 0" })
return ""
end
vim.fn.systemlist = function(cmd)
if cmd[2] == "show-options" then
real_system({ "sh", "-c", "exit 0" })
return { "on" }
end
if cmd[2] == "display-message" then
real_system({ "sh", "-c", failed and "exit 1" or "exit 0" })
return { "buffer-test", "" }
end
reads[#reads + 1] = cmd
real_system({ "sh", "-c", failed and "exit 1" or "exit 0" })
return vim.deepcopy(read_result or {})
end
vim.api.nvim_ui_send = function(data) emitted[#emitted + 1] = data end
-- A clipboard read must never reach Neovim's synchronous OSC 52 query.
require("vim.ui.clipboard.osc52").paste = function() error("OSC 52 read attempted") end
dofile(provider).setup()
vim.cmd("unlet! g:loaded_clipboard_provider")
vim.cmd("runtime autoload/provider/clipboard.vim")
end
local function equal(actual, expected)
assert(vim.deep_equal(actual, expected), vim.inspect(actual) .. " != " .. vim.inspect(expected))
end
local function put_yank()
vim.api.nvim_buf_set_lines(0, 0, -1, false, { "probe" })
vim.cmd("normal! gg0yy")
-- Unnamed clipboard writes are deferred until the command loop returns.
vim.cmd("redraw")
local start = vim.uv.hrtime()
vim.cmd("normal! p")
assert((vim.uv.hrtime() - start) / 1e6 < 1000, "paste blocked")
equal(vim.api.nvim_buf_get_lines(0, 0, -1, false), { "probe", "probe" })
end
setup({})
assert(vim.g.clipboard == nil and vim.o.clipboard == "", "local config changed")
for _, env in ipairs({ { SSH_CONNECTION = "test" }, { SSH_TTY = "test" }, { HERDR_PANE_ID = "test" } }) do
setup(env)
assert(vim.o.clipboard == "unnamedplus")
put_yank()
assert(#emitted > 0 and emitted[1]:find("\27]52;c;", 1, true))
assert(#reads == 0)
equal(vim.g.clipboard.paste["*"](), { {}, "v" })
for _, regtype in ipairs({ "v", "V", "\22" .. "3" }) do
vim.g.clipboard.copy["*"]({ "abc" }, regtype)
equal(vim.g.clipboard.paste["*"](), { { "abc" }, regtype })
end
end
print("ok - local, SSH, SSH_TTY and Herdr setup; real yy/p; no OSC 52 reads; register types")
setup({ SSH_CONNECTION = "test" }, { sync = false, clipboard = "unnamed" })
assert(vim.o.clipboard == "unnamed")
setup({ SSH_CONNECTION = "test" }, { clipboard = "unnamed" })
assert(vim.o.clipboard:find("unnamedplus", 1, true) and vim.o.clipboard:find("unnamed,", 1, true))
vim.opt.clipboard = ""
assert(vim.o.clipboard == "", "user override must win")
setup({ SSH_CONNECTION = "test" }, { osc52 = false })
put_yank()
assert(#emitted == 0)
print("ok - sync and OSC 52 opt-outs; existing clipboard flags and subsequent overrides")
setup({ SSH_CONNECTION = "test", TMUX = "test" })
local large = { string.rep("x", 1024 * 1024 + 1) }
vim.g.clipboard.copy["+"](large, "v")
equal(writes[1], { { "tmux", "load-buffer", "-w", "-" }, large })
assert(#emitted == 0)
read_result = { "external buffer" }
equal(vim.g.clipboard.paste["+"](), read_result)
equal(reads[1], { "tmux", "save-buffer", "-b", "buffer-test", "-" })
failed = true
equal(vim.g.clipboard.paste["+"](), { large, "v" })
write_failed = true
vim.g.clipboard.copy["+"]({ "retry" }, "v")
assert(#emitted == 1)
print("ok - tmux large yanks, shared buffer reads, failed read and write fallbacks")
setup({ TMUX = "test" }, { osc52 = false })
put_yank()
assert(#writes == 0 and #reads == 0 and #emitted == 0)
setup({ TMUX = "test" })
failed = true
put_yank()
print("ok - tmux-only yanks and paste; opt-out does not read a stale tmux buffer")
setup({ HERDR_PANE_ID = "test", WAYLAND_DISPLAY = "test" })
vim.g.clipboard.copy["*"]({ "primary" }, "v")
equal(writes[1][1], { "wl-copy", "--sensitive", "--type", "text/plain", "--primary" })
read_result = { "from another app" }
equal(vim.g.clipboard.paste["*"](), read_result)
equal(reads[1], { "wl-paste", "--no-newline", "--primary" })
read_result = {}
equal(vim.g.clipboard.paste["*"](), {})
failed = true
equal(vim.g.clipboard.paste["*"](), { { "primary" }, "v" })
setup({ SSH_CONNECTION = "test", WAYLAND_DISPLAY = "test", TMUX = "test" })
vim.g.clipboard.copy["+"]({ "both" }, "v")
assert(#writes == 2)
read_result = { "both" }
equal(vim.g.clipboard.paste["+"](), { { "both" }, "v" })
equal(reads[1], { "tmux", "save-buffer", "-b", "buffer-test", "-" })
print("ok - Wayland primary, external, empty and failed reads; SSH prefers tmux")
+7
View File
@@ -0,0 +1,7 @@
#!/bin/bash
set -euo pipefail
root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
export TEST_PROVIDER="$root/pkgbuilds/omarchy-nvim/lua/config/remote_clipboard.lua"
export NVIM_LOG_FILE=$(mktemp)
trap 'rm -f "$NVIM_LOG_FILE"' EXIT
nvim --clean -n --headless -i NONE -l "$root/tests/neovim-remote-clipboard.lua"