|
|
|
@@ -4,166 +4,95 @@ set -euo pipefail
|
|
|
|
|
|
|
|
|
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
|
|
|
|
|
|
|
|
|
config_script="$ROOT/install/config/locate.sh"
|
|
|
|
|
require_command python3
|
|
|
|
|
require_command updatedb
|
|
|
|
|
require_command plocate
|
|
|
|
|
|
|
|
|
|
test_tmp=$(mktemp -d)
|
|
|
|
|
trap 'rm -rf "$test_tmp"' EXIT
|
|
|
|
|
python3 - <<'PY'
|
|
|
|
|
import os
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
import shlex
|
|
|
|
|
import subprocess
|
|
|
|
|
import tempfile
|
|
|
|
|
|
|
|
|
|
stock_conf() {
|
|
|
|
|
cat >"$1" <<'CONF'
|
|
|
|
|
PRUNE_BIND_MOUNTS = "yes"
|
|
|
|
|
PRUNEFS = "9p afs autofs cifs fuse nfs nfs4 proc sysfs tmpfs"
|
|
|
|
|
PRUNENAMES = ".git .hg .svn"
|
|
|
|
|
PRUNEPATHS = "/afs /media /mnt /net /sfs /tmp /udev /var/cache /var/lib/pacman/local /var/lock /var/run /var/spool /var/tmp"
|
|
|
|
|
CONF
|
|
|
|
|
}
|
|
|
|
|
root = Path(os.environ["ROOT"])
|
|
|
|
|
|
|
|
|
|
# updatedb dies on a config that defines a variable twice, so hand every
|
|
|
|
|
# rewritten file to the real parser rather than trusting the greps below.
|
|
|
|
|
empty_tree="$test_tmp/empty-tree"
|
|
|
|
|
mkdir -p "$empty_tree"
|
|
|
|
|
def check(condition, description):
|
|
|
|
|
if not condition:
|
|
|
|
|
raise SystemExit("not ok - " + description)
|
|
|
|
|
print("ok - " + description, flush=True)
|
|
|
|
|
|
|
|
|
|
assert_conf_parses() {
|
|
|
|
|
command -v updatedb >/dev/null || return 0
|
|
|
|
|
drop_in = root / "default/systemd/system/plocate-updatedb.service.d/10-omarchy.conf"
|
|
|
|
|
directives = [line.strip() for line in drop_in.read_text().splitlines() if line.strip() and not line.startswith("#")]
|
|
|
|
|
check(len(directives) == 3 and directives[:2] == ["[Service]", "ExecStart="] and directives[2].startswith("ExecStart="),
|
|
|
|
|
"locate drop-in replaces the command and preserves upstream service restrictions")
|
|
|
|
|
command = shlex.split(directives[2].removeprefix("ExecStart="))
|
|
|
|
|
options = ["--prune-bind-mounts=no", "--add-prunepaths=/.snapshots"]
|
|
|
|
|
check(command == ["/usr/bin/updatedb", *options],
|
|
|
|
|
"locate service runs updatedb directly with fixed Btrfs options")
|
|
|
|
|
check("ConditionACPower=true" in (root / "etc/systemd/system/plocate-updatedb.service.d/ac-only.conf").read_text(),
|
|
|
|
|
"scheduled locate indexing keeps its AC-power condition")
|
|
|
|
|
check(not (root / "install/config/locate.sh").exists() and not (root / "migrations/1784809451.sh").exists(),
|
|
|
|
|
"the retired locate configuration helper and migration are absent")
|
|
|
|
|
for directory in ("bin", "install", "migrations"):
|
|
|
|
|
for path in (root / directory).rglob("*"):
|
|
|
|
|
if path.is_file():
|
|
|
|
|
content = path.read_text()
|
|
|
|
|
if "OMARCHY_UPDATEDB_CONF_PATH" in content or "config/locate.sh" in content:
|
|
|
|
|
raise SystemExit("not ok - retired locate configuration path remains in " + str(path))
|
|
|
|
|
check(True, "runtime and installation no longer reference the configuration rewrite")
|
|
|
|
|
|
|
|
|
|
local errors
|
|
|
|
|
errors=$(updatedb --config-file "$1" -U "$empty_tree" -o "$test_tmp/plocate.db" 2>&1 >/dev/null | grep -F "$1:" || true)
|
|
|
|
|
[[ -z $errors ]] || fail "updatedb accepts the rewritten config" "$errors"
|
|
|
|
|
}
|
|
|
|
|
with tempfile.TemporaryDirectory(prefix="omarchy-locate-") as scratch:
|
|
|
|
|
scratch = Path(scratch)
|
|
|
|
|
fake_bin = scratch / "bin"
|
|
|
|
|
fake_bin.mkdir()
|
|
|
|
|
stubs = {
|
|
|
|
|
"updatedb": 'printf "%s\\n" "$@" >"$TEST_CALLS"',
|
|
|
|
|
"sudo": 'exec "$@"',
|
|
|
|
|
"fzf": 'cat >/dev/null\nprintf "%s\\n" test-package',
|
|
|
|
|
"yay": 'if [[ ${1:-} == "-Slqa" ]]; then printf "%s\\n" test-package; fi',
|
|
|
|
|
"omarchy-sudo-keepalive": ':',
|
|
|
|
|
"omarchy-show-done": ':',
|
|
|
|
|
}
|
|
|
|
|
for name, body in stubs.items():
|
|
|
|
|
path = fake_bin / name
|
|
|
|
|
path.write_text("#!/bin/bash\n" + body + "\n")
|
|
|
|
|
path.chmod(0o755)
|
|
|
|
|
calls = scratch / "updatedb-arguments"
|
|
|
|
|
env = dict(os.environ, PATH=str(fake_bin) + ":" + os.environ["PATH"], TEST_CALLS=str(calls))
|
|
|
|
|
for relative in ("install/post-install/localdb.sh", "bin/omarchy-pkg-aur-install"):
|
|
|
|
|
subprocess.run(["bash", "-euo", "pipefail", str(root / relative)], env=env, check=True)
|
|
|
|
|
check(calls.read_text().splitlines() == options,
|
|
|
|
|
relative + " passes the scheduled service options directly")
|
|
|
|
|
calls.unlink()
|
|
|
|
|
|
|
|
|
|
conf="$test_tmp/updatedb.conf"
|
|
|
|
|
stock_conf "$conf"
|
|
|
|
|
|
|
|
|
|
OMARCHY_UPDATEDB_CONF_PATH="$conf" bash -euo pipefail "$config_script" >/dev/null
|
|
|
|
|
|
|
|
|
|
grep -qFx 'PRUNE_BIND_MOUNTS = "no"' "$conf" || fail "locate config indexes Btrfs subvolume mounts like /home"
|
|
|
|
|
grep -qF 'PRUNEPATHS = "/.snapshots /afs' "$conf" || fail "locate config prunes /.snapshots"
|
|
|
|
|
assert_conf_parses "$conf"
|
|
|
|
|
pass "locate config skips Btrfs snapshots and indexes Btrfs subvolumes"
|
|
|
|
|
|
|
|
|
|
OMARCHY_UPDATEDB_CONF_PATH="$conf" bash -euo pipefail "$config_script" >/dev/null
|
|
|
|
|
|
|
|
|
|
[[ $(grep -o '/\.snapshots' "$conf" | wc -l) -eq 1 ]] || fail "locate config is idempotent"
|
|
|
|
|
assert_conf_parses "$conf"
|
|
|
|
|
pass "locate config leaves an already-configured file alone"
|
|
|
|
|
|
|
|
|
|
OMARCHY_UPDATEDB_CONF_PATH="$test_tmp/missing.conf" bash -euo pipefail "$config_script" >/dev/null
|
|
|
|
|
pass "locate config tolerates a missing updatedb.conf"
|
|
|
|
|
|
|
|
|
|
# A hand-edited updatedb.conf may drop the settings entirely, or write them
|
|
|
|
|
# without the spaces around the "=" or the quotes that the stock Arch file uses.
|
|
|
|
|
conf="$test_tmp/sparse-updatedb.conf"
|
|
|
|
|
printf '%s\n' 'PRUNENAMES = ".git .hg .svn"' >"$conf"
|
|
|
|
|
|
|
|
|
|
OMARCHY_UPDATEDB_CONF_PATH="$conf" bash -euo pipefail "$config_script" >/dev/null
|
|
|
|
|
|
|
|
|
|
grep -qFx 'PRUNE_BIND_MOUNTS = "no"' "$conf" || fail "locate config adds a missing PRUNE_BIND_MOUNTS"
|
|
|
|
|
grep -qFx 'PRUNEPATHS = "/.snapshots"' "$conf" || fail "locate config adds a missing PRUNEPATHS"
|
|
|
|
|
assert_conf_parses "$conf"
|
|
|
|
|
pass "locate config adds settings a hand-edited updatedb.conf is missing"
|
|
|
|
|
|
|
|
|
|
conf="$test_tmp/unspaced-updatedb.conf"
|
|
|
|
|
printf '%s\n' 'PRUNE_BIND_MOUNTS="yes"' 'PRUNEPATHS="/tmp /var/tmp"' >"$conf"
|
|
|
|
|
|
|
|
|
|
OMARCHY_UPDATEDB_CONF_PATH="$conf" bash -euo pipefail "$config_script" >/dev/null
|
|
|
|
|
|
|
|
|
|
grep -qFx 'PRUNE_BIND_MOUNTS = "no"' "$conf" || fail "locate config rewrites an unspaced PRUNE_BIND_MOUNTS"
|
|
|
|
|
grep -qFx 'PRUNEPATHS = "/.snapshots /tmp /var/tmp"' "$conf" || fail "locate config prunes /.snapshots in an unspaced PRUNEPATHS"
|
|
|
|
|
[[ $(grep -c 'PRUNEPATHS' "$conf") -eq 1 ]] || fail "locate config keeps a single PRUNEPATHS setting"
|
|
|
|
|
assert_conf_parses "$conf"
|
|
|
|
|
pass "locate config handles updatedb.conf written without spaces around ="
|
|
|
|
|
|
|
|
|
|
# updatedb allows a comment after a value and indented settings, and defining
|
|
|
|
|
# either setting twice makes it refuse to run at all.
|
|
|
|
|
conf="$test_tmp/commented-updatedb.conf"
|
|
|
|
|
printf '%s\n' ' PRUNE_BIND_MOUNTS = "yes" # subvolumes look like bind mounts' \
|
|
|
|
|
'PRUNEPATHS = "/tmp" # scratch' >"$conf"
|
|
|
|
|
|
|
|
|
|
OMARCHY_UPDATEDB_CONF_PATH="$conf" bash -euo pipefail "$config_script" >/dev/null
|
|
|
|
|
|
|
|
|
|
grep -qFx 'PRUNE_BIND_MOUNTS = "no"' "$conf" || fail "locate config rewrites an indented PRUNE_BIND_MOUNTS"
|
|
|
|
|
grep -qFx 'PRUNEPATHS = "/.snapshots /tmp"' "$conf" || fail "locate config keeps the paths a commented PRUNEPATHS already prunes"
|
|
|
|
|
[[ $(grep -c 'PRUNEPATHS' "$conf") -eq 1 ]] || fail "locate config replaces a commented PRUNEPATHS instead of adding a second one"
|
|
|
|
|
assert_conf_parses "$conf"
|
|
|
|
|
pass "locate config handles indented settings and trailing comments"
|
|
|
|
|
|
|
|
|
|
# A hand-edited file may have dropped the quotes updatedb requires, which
|
|
|
|
|
# leaves it unparseable until something writes the setting out properly.
|
|
|
|
|
conf="$test_tmp/unquoted-updatedb.conf"
|
|
|
|
|
printf '%s\n' 'PRUNEPATHS = /tmp' >"$conf"
|
|
|
|
|
|
|
|
|
|
OMARCHY_UPDATEDB_CONF_PATH="$conf" bash -euo pipefail "$config_script" >/dev/null
|
|
|
|
|
|
|
|
|
|
grep -qFx 'PRUNEPATHS = "/.snapshots"' "$conf" || fail "locate config repairs an unquoted PRUNEPATHS"
|
|
|
|
|
[[ $(grep -c 'PRUNEPATHS' "$conf") -eq 1 ]] || fail "locate config replaces an unquoted PRUNEPATHS instead of adding a second one"
|
|
|
|
|
assert_conf_parses "$conf"
|
|
|
|
|
pass "locate config handles updatedb.conf written without quotes"
|
|
|
|
|
|
|
|
|
|
# A path that merely ends in /.snapshots is not the root snapshot directory.
|
|
|
|
|
conf="$test_tmp/nested-snapshots-updatedb.conf"
|
|
|
|
|
printf '%s\n' 'PRUNEPATHS = "/var/lib/machines/.snapshots"' >"$conf"
|
|
|
|
|
|
|
|
|
|
OMARCHY_UPDATEDB_CONF_PATH="$conf" bash -euo pipefail "$config_script" >/dev/null
|
|
|
|
|
|
|
|
|
|
grep -qFx 'PRUNEPATHS = "/.snapshots /var/lib/machines/.snapshots"' "$conf" || fail "locate config prunes /.snapshots alongside a path that ends in it"
|
|
|
|
|
assert_conf_parses "$conf"
|
|
|
|
|
pass "locate config tells /.snapshots apart from a path that ends in it"
|
|
|
|
|
|
|
|
|
|
locate_migration=$(grep -rl 'Configure locate to skip Btrfs snapshots' "$ROOT/migrations" | head -n 1 || true)
|
|
|
|
|
[[ -n $locate_migration ]] || fail "locate migration exists"
|
|
|
|
|
|
|
|
|
|
fake_bin="$test_tmp/bin"
|
|
|
|
|
mkdir -p "$fake_bin"
|
|
|
|
|
|
|
|
|
|
cat >"$fake_bin/sudo" <<'STUB'
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
exec "$@"
|
|
|
|
|
STUB
|
|
|
|
|
chmod +x "$fake_bin/sudo"
|
|
|
|
|
|
|
|
|
|
cat >"$fake_bin/systemctl" <<'STUB'
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
printf 'systemctl %s\n' "$*" >>"$TEST_LOG"
|
|
|
|
|
STUB
|
|
|
|
|
chmod +x "$fake_bin/systemctl"
|
|
|
|
|
|
|
|
|
|
conf="$test_tmp/migration-updatedb.conf"
|
|
|
|
|
stock_conf "$conf"
|
|
|
|
|
|
|
|
|
|
TEST_LOG="$test_tmp/calls.log" \
|
|
|
|
|
PATH="$fake_bin:$PATH" \
|
|
|
|
|
OMARCHY_PATH="$ROOT" \
|
|
|
|
|
OMARCHY_UPDATEDB_CONF_PATH="$conf" \
|
|
|
|
|
bash -euo pipefail "$locate_migration" >/dev/null
|
|
|
|
|
|
|
|
|
|
grep -qFx 'PRUNE_BIND_MOUNTS = "no"' "$conf" || fail "locate migration rewrites updatedb.conf"
|
|
|
|
|
grep -qF 'PRUNEPATHS = "/.snapshots /afs' "$conf" || fail "locate migration prunes /.snapshots"
|
|
|
|
|
grep -qFx 'systemctl restart --no-block plocate-updatedb.service' "$test_tmp/calls.log" || fail "locate migration replaces an in-flight run and rebuilds the index without blocking"
|
|
|
|
|
pass "locate migration fixes existing installs and rebuilds the index"
|
|
|
|
|
|
|
|
|
|
: >"$test_tmp/calls.log"
|
|
|
|
|
|
|
|
|
|
TEST_LOG="$test_tmp/calls.log" \
|
|
|
|
|
PATH="$fake_bin:$PATH" \
|
|
|
|
|
OMARCHY_PATH="$ROOT" \
|
|
|
|
|
OMARCHY_UPDATEDB_CONF_PATH="$conf" \
|
|
|
|
|
bash -euo pipefail "$locate_migration" >/dev/null
|
|
|
|
|
|
|
|
|
|
[[ ! -s $test_tmp/calls.log ]] || fail "locate migration skips already-configured installs"
|
|
|
|
|
pass "locate migration is a no-op once updatedb.conf is configured"
|
|
|
|
|
|
|
|
|
|
# A dev checkout carries migrations from a release whose install scripts the
|
|
|
|
|
# checked-out tree may not have yet, and omarchy-migrate runs under set -e.
|
|
|
|
|
: >"$test_tmp/calls.log"
|
|
|
|
|
conf="$test_tmp/no-config-script-updatedb.conf"
|
|
|
|
|
stock_conf "$conf"
|
|
|
|
|
|
|
|
|
|
TEST_LOG="$test_tmp/calls.log" \
|
|
|
|
|
PATH="$fake_bin:$PATH" \
|
|
|
|
|
OMARCHY_PATH="$test_tmp/empty" \
|
|
|
|
|
OMARCHY_UPDATEDB_CONF_PATH="$conf" \
|
|
|
|
|
bash -euo pipefail "$locate_migration" >/dev/null ||
|
|
|
|
|
fail "locate migration survives a tree without the locate config script"
|
|
|
|
|
|
|
|
|
|
[[ ! -s $test_tmp/calls.log ]] || fail "locate migration touches nothing without the locate config script"
|
|
|
|
|
pass "locate migration is a no-op when the locate config script is missing"
|
|
|
|
|
tree = scratch / "tree"
|
|
|
|
|
visible = tree / "home/current-file"
|
|
|
|
|
excluded = tree / "private&pipe|directory"
|
|
|
|
|
hidden = excluded / "private-file"
|
|
|
|
|
visible.parent.mkdir(parents=True)
|
|
|
|
|
excluded.mkdir()
|
|
|
|
|
visible.touch()
|
|
|
|
|
hidden.touch()
|
|
|
|
|
conf = scratch / "updatedb.conf"
|
|
|
|
|
conf.write_text('PRUNE_BIND_MOUNTS = "yes"\nPRUNEPATHS = "' + str(excluded) + '"\n')
|
|
|
|
|
conf.chmod(0o640)
|
|
|
|
|
original = conf.read_bytes()
|
|
|
|
|
metadata = conf.stat()
|
|
|
|
|
database = scratch / "plocate.db"
|
|
|
|
|
run = [*command, "--config-file", str(conf), "--database-root", str(tree),
|
|
|
|
|
"--output", str(database), "--require-visibility", "no", "--debug-pruning"]
|
|
|
|
|
result = subprocess.run(run, capture_output=True, text=True, check=True)
|
|
|
|
|
debug = result.stdout + result.stderr
|
|
|
|
|
check("prune_bind_mounts\\000\n0\\000" in debug and "/.snapshots\\000" in debug,
|
|
|
|
|
"real updatedb overrides bind-mount pruning and adds root snapshots to exclusions")
|
|
|
|
|
entries = subprocess.check_output(["plocate", "--database", str(database), ""], text=True).splitlines()
|
|
|
|
|
check(str(visible) in entries and str(hidden) not in entries,
|
|
|
|
|
"real locate indexes current files and preserves literal administrator exclusions")
|
|
|
|
|
check(conf.read_bytes() == original and (conf.stat().st_mode, conf.stat().st_uid, conf.stat().st_gid, conf.stat().st_mtime_ns)
|
|
|
|
|
== (metadata.st_mode, metadata.st_uid, metadata.st_gid, metadata.st_mtime_ns),
|
|
|
|
|
"indexing preserves configuration bytes, permissions, ownership, and modification time")
|
|
|
|
|
subprocess.run(run, capture_output=True, check=True)
|
|
|
|
|
repeated = subprocess.check_output(["plocate", "--database", str(database), ""], text=True).splitlines()
|
|
|
|
|
check(repeated == entries, "repeated indexing retains the same results and exclusions")
|
|
|
|
|
PY
|