2 Commits
Author SHA1 Message Date
alvinlollo 3b3764573e fix(installer): resolve owner in one chroot call, log ydotool link errors
Tests / tests (push) Failing after 2m28s
Two low-severity review findings on 8501a60:

- owner="$(id -u):$(id -g)" as two substitutions only propagates the
  last status, so a lone id -u failure would build ':1000' and abort at
  the chown with "invalid owner" under the wrong line. Run both ids in
  a single arch-chroot sh -c chained with && and join with paste -sd:;
  pipefail makes any failure abort at the owner= line. Verified both
  paths: existing user yields 1000:1000, missing user trips the ERR
  trap before owner is used.
- the ydotool symlink mkdir/ln wrote stderr to the terminal while the
  failure warn pointed at $LOG, which never recorded it; redirect both
  into the log, matching the bluetooth block.
2026-10-02 17:18:40 +10:00
alvinlollo 8501a60165 fix(installer): chown target files by numeric id, not live-medium name
The end-4 step's chowns ran on the live medium, whose /etc/passwd has
no $USERNAME (the user exists only inside /mnt), so 'chown -R
alvin:alvin' failed with "invalid user" and the ERR trap aborted the
install at line 543. Resolve uid/gid once via arch-chroot id and chown
by number; the three remaining name-based chowns (fork/fish/hypr
restores) would have hit the same wall later.

Replace those three mid-function chowns with one catch-all over the
target home after the last root-side write: sed -i on the presets
rewrites through a temp file, which leaves them root-owned with no
chown after it.

systemctl --user enable ydotool.service could never work inside the
chroot (no /run/user/UID/bus exists before boot; it only ever warned
with "Failed to connect to user scope bus", so ydotool never
autostarted). Write the symlink enable would write:
~/.config/systemd/user/default.target.wants/ydotool.service ->
/usr/lib/systemd/user/ydotool.service.
2026-10-02 17:05:19 +10:00
+30 -7
View File
@@ -539,8 +539,18 @@ install_end4_dots() {
local dots_ok=true local dots_ok=true
local setups_done=true local setups_done=true
# These chowns run on the live medium, whose /etc/passwd has no
# $USERNAME — the user exists only inside $TARGET — so name lookup fails
# ("chown: invalid user"). Resolve the numeric ids from the target and
# chown by number; the catch-all at the end of this function does the same.
# One arch-chroot call chained with && : a plain "a=$(id -u):$(id -g)"
# would only propagate the last substitution's status, so a lone id -u
# failure would silently produce ':1000' and abort at the wrong line.
local owner
owner=$(arch-chroot "$TARGET" sh -c "id -u '$USERNAME' && id -g '$USERNAME'" | paste -sd:)
cp -a /opt/dots-hyprland "$repo_on_host" cp -a /opt/dots-hyprland "$repo_on_host"
chown -R "${USERNAME}:${USERNAME}" "$repo_on_host" chown -R "$owner" "$repo_on_host"
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > "${TARGET}/etc/sudoers.d/99-end4-install" echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > "${TARGET}/etc/sudoers.d/99-end4-install"
chmod 440 "${TARGET}/etc/sudoers.d/99-end4-install" chmod 440 "${TARGET}/etc/sudoers.d/99-end4-install"
@@ -597,10 +607,20 @@ install_end4_dots() {
arch-chroot "$TARGET" systemctl enable bluetooth.service >>"$LOG" 2>&1 \ arch-chroot "$TARGET" systemctl enable bluetooth.service >>"$LOG" 2>&1 \
|| warn "Could not enable bluetooth.service (see $LOG)." || warn "Could not enable bluetooth.service (see $LOG)."
fi fi
# `systemctl --user` cannot work in this chroot either: it needs
# /run/user/UID/bus, which does not exist until the target boots (it only
# ever reports "Failed to connect to user scope bus"). Write the symlink
# `enable` would write — the same absolute link systemctl produces.
if [[ -f "${TARGET}/usr/lib/systemd/user/ydotool.service" ]]; then if [[ -f "${TARGET}/usr/lib/systemd/user/ydotool.service" ]]; then
arch-chroot "$TARGET" runuser -u "$USERNAME" -- env HOME="$home_in_target" \ local yd_wants="${TARGET}${home_in_target}/.config/systemd/user/default.target.wants"
systemctl --user enable ydotool.service >>"$LOG" 2>&1 \ # stderr goes to $LOG so the failure warn below points at something
|| warn "Could not enable ydotool.service for ${USERNAME} (see $LOG)." # that actually records it (as the bluetooth block does).
if { mkdir -p "$yd_wants" \
&& ln -sfn /usr/lib/systemd/user/ydotool.service "$yd_wants/ydotool.service"; } 2>>"$LOG"; then
log "ydotool.service enabled for ${USERNAME} (symlinked in user unit dir)."
else
warn "Could not enable ydotool.service for ${USERNAME} (see $LOG)."
fi
fi fi
# Everything end-4 answered "i" to. systemctl/gsettings are the lines known # Everything end-4 answered "i" to. systemctl/gsettings are the lines known
@@ -640,7 +660,6 @@ install_end4_dots() {
mkdir -p "$(dirname "$fork_dst")" mkdir -p "$(dirname "$fork_dst")"
rm -rf "$fork_dst" rm -rf "$fork_dst"
cp -a "$fork_src" "$fork_dst" cp -a "$fork_src" "$fork_dst"
chown -R "${USERNAME}:${USERNAME}" "$fork_dst"
fi fi
# Same for fish: end-4 syncs ~/.config/fish with rsync --delete, keeping # Same for fish: end-4 syncs ~/.config/fish with rsync --delete, keeping
@@ -659,7 +678,6 @@ install_end4_dots() {
grep -qF "alias q 'qs -c end4-pC'" "${fish_dst}/config.fish" \ grep -qF "alias q 'qs -c end4-pC'" "${fish_dst}/config.fish" \
|| warn "fish 'q' alias still points at the upstream ii config (see $LOG)." || warn "fish 'q' alias still points at the upstream ii config (see $LOG)."
fi fi
chown -R "${USERNAME}:${USERNAME}" "$fish_dst"
fi fi
local configs_ok=true local configs_ok=true
@@ -681,7 +699,6 @@ install_end4_dots() {
if [[ -d /etc/skel/.config/hypr/custom ]]; then if [[ -d /etc/skel/.config/hypr/custom ]]; then
cp -rfT /etc/skel/.config/hypr/custom "${TARGET}${home_in_target}/.config/hypr/custom" cp -rfT /etc/skel/.config/hypr/custom "${TARGET}${home_in_target}/.config/hypr/custom"
chown -R "${USERNAME}:${USERNAME}" "${TARGET}${home_in_target}/.config/hypr/custom"
fi fi
# Presets ship with absolute paths from the build machine; repoint them at # Presets ship with absolute paths from the build machine; repoint them at
@@ -693,6 +710,12 @@ install_end4_dots() {
sed -i "s|/home/alvin/|/home/${USERNAME}/|g" "$preset" sed -i "s|/home/alvin/|/home/${USERNAME}/|g" "$preset"
done done
# Everything above that ran as root from the live medium (repo copy,
# fork/fish/hypr restores, and the presets sed -i just rewrote through a
# temp file) left root-owned files in the home — one pass fixes them all.
# Numeric ids again: this chown also runs on the medium, not in the chroot.
chown -R "$owner" "${TARGET}${home_in_target}"
if $dots_ok && $configs_ok; then if $dots_ok && $configs_ok; then
log "end-4 dotfiles installed for ${USERNAME} (setups_done=${setups_done} skipped=${#ignored[@]})." log "end-4 dotfiles installed for ${USERNAME} (setups_done=${setups_done} skipped=${#ignored[@]})."
ok "end-4 dotfiles installed." ok "end-4 dotfiles installed."