Merge remote-tracking branch 'upstream/master' into spark/aarch64-settings-boot-dropins
This commit is contained in:
commit
9d05b3fb06
117 files changed
+18463
-230
No files matched your search
Executable
+532
@@ -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"
|
||||
@@ -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)
|
||||
@@ -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")
|
||||
Executable
+7
@@ -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"
|
||||
Reference in new issue
Block a user