♻️ refactor(pacman): render regional config without root
- 🔒 upstream refresh now gives root only command-scoped copies, so regional files are rendered as the caller and installed with the same four `sudo cp` calls - ✅ exercise refresh only inside the sudo-boundary sandbox; the old host-level test reached real sudo once refresh pinned its PATH Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
87a2931209
commit
d7f17a0424
6 files changed
+93
-36
No files matched your search
@@ -1,18 +1,33 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# omarchy:summary=Restore channel and regional pacman defaults in an installed target
|
# omarchy:summary=Restore channel and regional pacman defaults in an installed target
|
||||||
# omarchy:args=<stable|rc|edge> [target-root]
|
# omarchy:args=<stable|rc|edge> [target-root] | --render <stable|rc|edge> <output-dir> [target-root]
|
||||||
# omarchy:requires-sudo=true
|
# omarchy:requires-sudo=true
|
||||||
# omarchy:hidden=true
|
# omarchy:hidden=true
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
if (( $# < 1 || $# > 2 )); then
|
usage() {
|
||||||
echo "Usage: omarchy-apply-pacman <stable|rc|edge> [target-root]" >&2
|
echo "Usage: omarchy-apply-pacman <stable|rc|edge> [target-root]" >&2
|
||||||
|
echo " omarchy-apply-pacman --render <stable|rc|edge> <output-dir> [target-root]" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# --render only builds the two files, so an unprivileged caller can hand root
|
||||||
|
# nothing but plain copies of the finished result.
|
||||||
|
render_dir=""
|
||||||
|
if [[ ${1:-} == "--render" ]]; then
|
||||||
|
(( $# == 3 || $# == 4 )) || usage
|
||||||
|
channel="$2"
|
||||||
|
render_dir="$3"
|
||||||
|
[[ -d $render_dir ]] || { echo "Render directory not found: $render_dir" >&2; exit 1; }
|
||||||
|
etc="${4:-/}/etc"
|
||||||
|
else
|
||||||
|
(( $# >= 1 && $# <= 2 )) || usage
|
||||||
|
channel="$1"
|
||||||
|
etc="${2:-/}/etc"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
channel="$1"
|
|
||||||
case "$channel" in
|
case "$channel" in
|
||||||
stable|rc|edge) ;;
|
stable|rc|edge) ;;
|
||||||
*) echo "Invalid pacman channel: $channel" >&2; exit 1 ;;
|
*) echo "Invalid pacman channel: $channel" >&2; exit 1 ;;
|
||||||
@@ -20,7 +35,6 @@ esac
|
|||||||
|
|
||||||
# The ISO writes this plain-text marker before target finalization. Missing
|
# The ISO writes this plain-text marker before target finalization. Missing
|
||||||
# markers keep existing installations global; never source it as shell code.
|
# markers keep existing installations global; never source it as shell code.
|
||||||
etc="${2:-/}/etc"
|
|
||||||
region=global
|
region=global
|
||||||
if [[ -e $etc/omarchy/region ]]; then
|
if [[ -e $etc/omarchy/region ]]; then
|
||||||
region=$(<"$etc/omarchy/region")
|
region=$(<"$etc/omarchy/region")
|
||||||
@@ -32,8 +46,12 @@ esac
|
|||||||
|
|
||||||
# Build both files before replacing either. Explicit refreshes retain backups;
|
# Build both files before replacing either. Explicit refreshes retain backups;
|
||||||
# ordinary updates do not call this command or enforce regional defaults.
|
# ordinary updates do not call this command or enforce regional defaults.
|
||||||
staging=$(mktemp -d)
|
if [[ -n $render_dir ]]; then
|
||||||
trap 'rm -rf "$staging"' EXIT
|
staging="$render_dir"
|
||||||
|
else
|
||||||
|
staging=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$staging"' EXIT
|
||||||
|
fi
|
||||||
cp "$OMARCHY_PATH/default/pacman/pacman-$channel.conf" "$staging/pacman.conf"
|
cp "$OMARCHY_PATH/default/pacman/pacman-$channel.conf" "$staging/pacman.conf"
|
||||||
cp "$OMARCHY_PATH/default/pacman/mirrorlist-$channel" "$staging/mirrorlist"
|
cp "$OMARCHY_PATH/default/pacman/mirrorlist-$channel" "$staging/mirrorlist"
|
||||||
if [[ $region != "global" ]]; then
|
if [[ $region != "global" ]]; then
|
||||||
@@ -42,6 +60,7 @@ if [[ $region != "global" ]]; then
|
|||||||
cat "$profile/pacman.conf.append" >> "$staging/pacman.conf"
|
cat "$profile/pacman.conf.append" >> "$staging/pacman.conf"
|
||||||
cat "$profile/mirrorlist.append" >> "$staging/mirrorlist"
|
cat "$profile/mirrorlist.append" >> "$staging/mirrorlist"
|
||||||
fi
|
fi
|
||||||
|
[[ -z $render_dir ]] || exit 0
|
||||||
|
|
||||||
mkdir -p "$etc/pacman.d"
|
mkdir -p "$etc/pacman.d"
|
||||||
for file in pacman.conf pacman.d/mirrorlist; do
|
for file in pacman.conf pacman.d/mirrorlist; do
|
||||||
|
|||||||
@@ -30,10 +30,17 @@ if [[ $channel != "stable" && $channel != "rc" && $channel != "edge" ]]; then
|
|||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Render the channel templates plus this machine's regional additions as the
|
||||||
|
# caller, so root only copies finished files.
|
||||||
|
rendered=$(mktemp -d)
|
||||||
|
"$OMARCHY_PATH/bin/omarchy-apply-pacman" --render "$channel" "$rendered"
|
||||||
|
|
||||||
|
sudo cp -f /etc/pacman.conf /etc/pacman.conf.bak
|
||||||
|
sudo cp -f /etc/pacman.d/mirrorlist /etc/pacman.d/mirrorlist.bak
|
||||||
echo "Setting channel to $channel"
|
echo "Setting channel to $channel"
|
||||||
# Restores the channel templates plus this machine's regional additions, and
|
sudo cp -f "$rendered/pacman.conf" /etc/pacman.conf
|
||||||
# backs up both files first. OMARCHY_PATH was verified as this command's root.
|
sudo cp -f "$rendered/mirrorlist" /etc/pacman.d/mirrorlist
|
||||||
sudo env "OMARCHY_PATH=$OMARCHY_PATH" "$OMARCHY_PATH/bin/omarchy-apply-pacman" "$channel"
|
rm -rf "$rendered"
|
||||||
|
|
||||||
# Allow user customization of /etc/pacman.conf before the upgrade runs, so
|
# Allow user customization of /etc/pacman.conf before the upgrade runs, so
|
||||||
# custom repositories and IgnorePkg entries shape the downgrade-capable
|
# custom repositories and IgnorePkg entries shape the downgrade-capable
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
|||||||
source "$SHELL_TEST_DIR/fixtures/sudo-boundary-test.sh"
|
source "$SHELL_TEST_DIR/fixtures/sudo-boundary-test.sh"
|
||||||
copy_boundary_file bin/omarchy-channel-set
|
copy_boundary_file bin/omarchy-channel-set
|
||||||
copy_boundary_file bin/omarchy-refresh-pacman
|
copy_boundary_file bin/omarchy-refresh-pacman
|
||||||
|
copy_boundary_file bin/omarchy-apply-pacman
|
||||||
copy_boundary_file bin/omarchy-update
|
copy_boundary_file bin/omarchy-update
|
||||||
export OMARCHY_UPDATE_LOGGED=1
|
export OMARCHY_UPDATE_LOGGED=1
|
||||||
|
|
||||||
|
|||||||
@@ -68,35 +68,22 @@ cmp "$scratch/before.conf" "$TARGET_ROOT/etc/pacman.conf"
|
|||||||
cmp "$scratch/before.mirrors" "$TARGET_ROOT/etc/pacman.d/mirrorlist"
|
cmp "$scratch/before.mirrors" "$TARGET_ROOT/etc/pacman.d/mirrorlist"
|
||||||
pass "missing fragment cannot leave a half-generated config"
|
pass "missing fragment cannot leave a half-generated config"
|
||||||
|
|
||||||
# Execute the real refresh and apply commands against the same disposable root.
|
# Refresh renders these files unprivileged and hands root plain copies; its
|
||||||
mkdir -p "$scratch/bin"
|
# privileged path is covered in the sudo-boundary sandbox, never on the host.
|
||||||
cat > "$scratch/bin/sudo" <<'SH'
|
cp "$TARGET_ROOT/etc/pacman.conf" "$scratch/before.conf"
|
||||||
#!/bin/bash
|
mkdir -p "$scratch/rendered"
|
||||||
exec "$@" "$TARGET_ROOT"
|
"$ROOT/bin/omarchy-apply-pacman" --render rc "$scratch/rendered" "$TARGET_ROOT"
|
||||||
SH
|
rg -qxF 'Server = https://pkgs.omarchy.org/rc/$arch' "$scratch/rendered/pacman.conf"
|
||||||
cat > "$scratch/bin/omarchy-hook" <<'SH'
|
rg -qxF '[archlinuxcn]' "$scratch/rendered/pacman.conf"
|
||||||
#!/bin/bash
|
[[ $(<"$scratch/rendered/mirrorlist") == $'Server = https://rc-mirror.omarchy.org/$repo/os/$arch\nServer = https://mirrors.ustc.edu.cn/archlinux/$repo/os/$arch' ]] ||
|
||||||
[[ $1 == "pre-refresh-pacman" ]] || exit 1
|
fail "render keeps the channel mirror first"
|
||||||
printf '# user customization\n' >> "$TARGET_ROOT/etc/pacman.conf"
|
cmp "$scratch/before.conf" "$TARGET_ROOT/etc/pacman.conf"
|
||||||
SH
|
pass "render builds the regional channel files without touching the target"
|
||||||
cat > "$scratch/bin/omarchy-update-pacman" <<'SH'
|
|
||||||
#!/bin/bash
|
|
||||||
[[ $* == "-Syyuu --noconfirm" ]] || exit 1
|
|
||||||
tail -n 1 "$TARGET_ROOT/etc/pacman.conf" > "$TARGET_ROOT/update-observed"
|
|
||||||
SH
|
|
||||||
chmod +x "$scratch/bin/"*
|
|
||||||
PATH="$scratch/bin:$PATH" "$ROOT/bin/omarchy-refresh-pacman" rc
|
|
||||||
[[ $(<"$TARGET_ROOT/update-observed") == '# user customization' ]] || fail "hook runs before update"
|
|
||||||
rg -qxF 'Server = https://pkgs.omarchy.org/rc/$arch' "$TARGET_ROOT/etc/pacman.conf"
|
|
||||||
rg -qxF '[archlinuxcn]' "$TARGET_ROOT/etc/pacman.conf"
|
|
||||||
pass "channel refresh retains region and applies user customization before updating"
|
|
||||||
|
|
||||||
printf 'not-updated\n' > "$TARGET_ROOT/update-observed"
|
if "$ROOT/bin/omarchy-apply-pacman" --render dev "$scratch/rendered" "$TARGET_ROOT" > "$scratch/error" 2>&1; then
|
||||||
if PATH="$scratch/bin:$PATH" "$ROOT/bin/omarchy-refresh-pacman" dev > "$scratch/error" 2>&1; then
|
fail "render accepts a nonexistent dev pacman template"
|
||||||
fail "refresh accepts a nonexistent dev pacman template"
|
|
||||||
fi
|
fi
|
||||||
[[ $(<"$TARGET_ROOT/update-observed") == 'not-updated' ]] || fail "failed generation still updates packages"
|
pass "render rejects channels without pacman templates"
|
||||||
pass "failed refresh does not proceed to a package update"
|
|
||||||
|
|
||||||
printf 'global\n' > "$TARGET_ROOT/etc/omarchy/region"
|
printf 'global\n' > "$TARGET_ROOT/etc/omarchy/region"
|
||||||
apply stable
|
apply stable
|
||||||
|
|||||||
Executable
+42
@@ -0,0 +1,42 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
||||||
|
source "$SHELL_TEST_DIR/fixtures/sudo-boundary-test.sh"
|
||||||
|
copy_boundary_file bin/omarchy-refresh-pacman
|
||||||
|
copy_boundary_file bin/omarchy-apply-pacman
|
||||||
|
|
||||||
|
# Regional rendering must stay unprivileged: root only copies finished files,
|
||||||
|
# exactly as the channel refresh did before regions existed.
|
||||||
|
reset_boundary
|
||||||
|
"$SUDO_TEST_ROOT/bin/omarchy-refresh-pacman" rc >"$boundary_tmp/output" 2>&1 || fail "refresh failed" "$(<"$boundary_tmp/output")"
|
||||||
|
assert_boundary_cold "regional refresh"
|
||||||
|
python3 - "$SUDO_TEST_LOG" "$SUDO_TEST_ROOT" <<'PY'
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
events = open(sys.argv[1]).read().splitlines()
|
||||||
|
root = sys.argv[2]
|
||||||
|
render = [i for i, e in enumerate(events) if e.startswith('step:cp ' + root + '/default/pacman/pacman-rc.conf ')]
|
||||||
|
assert len(render) == 1, events
|
||||||
|
rendered = events[render[0]].split()[-1].rsplit('/', 1)[0]
|
||||||
|
copies = [i for i, e in enumerate(events) if e.startswith('sudo -N cp ')]
|
||||||
|
assert [events[i] for i in copies[2:]] == [
|
||||||
|
f'sudo -N cp -f {rendered}/pacman.conf /etc/pacman.conf',
|
||||||
|
f'sudo -N cp -f {rendered}/mirrorlist /etc/pacman.d/mirrorlist',
|
||||||
|
], events
|
||||||
|
assert not any(e.startswith('sudo ') and 'omarchy-apply-pacman' in e for e in events), events
|
||||||
|
assert render[0] < copies[0], events
|
||||||
|
transaction = next(i for i, e in enumerate(events) if e.startswith('step:pacman '))
|
||||||
|
assert max(copies) < transaction, events
|
||||||
|
PY
|
||||||
|
pass "refresh renders regional config unprivileged and gives root only plain copies"
|
||||||
|
|
||||||
|
reset_boundary
|
||||||
|
if "$SUDO_TEST_ROOT/bin/omarchy-refresh-pacman" dev >"$boundary_tmp/output" 2>&1; then
|
||||||
|
fail "refresh accepts an invalid channel"
|
||||||
|
fi
|
||||||
|
if grep -Eq '^(sudo -N cp |step:pacman )' "$SUDO_TEST_LOG"; then
|
||||||
|
fail "invalid channel reached privileged work" "$(<"$SUDO_TEST_LOG")"
|
||||||
|
fi
|
||||||
|
pass "failed refresh does not proceed to privileged copies or a package update"
|
||||||
@@ -6,6 +6,7 @@ source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
|||||||
source "$SHELL_TEST_DIR/fixtures/sudo-boundary-test.sh"
|
source "$SHELL_TEST_DIR/fixtures/sudo-boundary-test.sh"
|
||||||
copy_boundary_file bin/omarchy-update
|
copy_boundary_file bin/omarchy-update
|
||||||
copy_boundary_file bin/omarchy-refresh-pacman
|
copy_boundary_file bin/omarchy-refresh-pacman
|
||||||
|
copy_boundary_file bin/omarchy-apply-pacman
|
||||||
# Replace the step symlink, preserving the real fixture dispatcher.
|
# Replace the step symlink, preserving the real fixture dispatcher.
|
||||||
rm "$SUDO_TEST_ROOT/bin/omarchy-update-aur-pkgs"
|
rm "$SUDO_TEST_ROOT/bin/omarchy-update-aur-pkgs"
|
||||||
copy_boundary_file bin/omarchy-update-aur-pkgs
|
copy_boundary_file bin/omarchy-update-aur-pkgs
|
||||||
|
|||||||
Reference in new issue
Block a user