fix(sysupdate): make hypr config sync atomic

The hypr/ sync removed each live item with `rm -rf` before `cp`. For
hyprland.lua this opened a window where the file was absent; if Hyprland's
live config watcher reloaded during it, it errored ("cannot open
hyprland.lua") and wrote out its fallback hyprland.conf stub, which then
leaked into the repo.

Stage each item into a temp dir on the same filesystem and `mv` it into
place — for files this is an atomic rename(2), so the watcher never sees
hyprland.lua missing. Directories are cleared first (mv can't replace a
non-empty dir), but those aren't watched config files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
main
Amir Alexander Abdelbaki 2026-06-13 22:13:26 +02:00
parent d35d6d17d6
commit dc3ffb68b7
1 changed files with 22 additions and 7 deletions

View File

@ -616,15 +616,30 @@ sync_configs() {
[[ "$skip" == true ]] && continue [[ "$skip" == true ]] && continue
if [[ "$name" == "hypr" ]]; then if [[ "$name" == "hypr" ]]; then
# Copy hypr/ contents but preserve ~/.config/hypr/usr/ # Copy hypr/ contents but preserve ~/.config/hypr/usr/.
# Stage each item into a temp dir on the same filesystem, then mv it
# into place — for files this is an atomic rename(2), so Hyprland's
# live config watcher never sees hyprland.lua momentarily missing
# (which otherwise makes it emit its fallback hyprland.conf stub).
mkdir -p "$target/hypr" mkdir -p "$target/hypr"
local hypr_ok=true local hypr_ok=true
while IFS= read -r -d '' hitem; do local stage; stage="$(mktemp -d "${target}/hypr/.sync.XXXXXX")" || hypr_ok=false
local hname; hname="$(basename "$hitem")" if $hypr_ok; then
[[ "$hname" == "usr" ]] && continue while IFS= read -r -d '' hitem; do
rm -rf "${target}/hypr/${hname}" && cp -r "$hitem" "$target/hypr/" \ local hname; hname="$(basename "$hitem")"
|| hypr_ok=false [[ "$hname" == "usr" ]] && continue
done < <(find "$item" -maxdepth 1 -mindepth 1 -print0 | sort -z) if cp -r "$hitem" "$stage/"; then
# mv can't atomically replace a non-empty dir, so clear
# an existing directory target first; files swap atomically.
[[ -d "$stage/$hname" && -d "${target}/hypr/${hname}" ]] \
&& rm -rf "${target}/hypr/${hname}"
mv -f "$stage/$hname" "${target}/hypr/${hname}" || hypr_ok=false
else
hypr_ok=false
fi
done < <(find "$item" -maxdepth 1 -mindepth 1 -print0 | sort -z)
rm -rf "$stage"
fi
if $hypr_ok; then if $hypr_ok; then
ok "synced hypr ${DI}(usr/ preserved)${RS}" ok "synced hypr ${DI}(usr/ preserved)${RS}"
(( synced++ )) || true (( synced++ )) || true