aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/justfile
diff options
context:
space:
mode:
authorLibravatar sommerfeld <sommerfeld@sommerfeld.dev>2026-05-13 13:43:21 +0100
committerLibravatar sommerfeld <sommerfeld@sommerfeld.dev>2026-05-13 13:43:21 +0100
commit32be4dc72b09caa91430a18d15b35622f166898e (patch)
tree941731c229af6dcec987623b4a2c7af5a9c53b40 /justfile
parentc4020395efc666b7bd005d4774154cdbebb01b84 (diff)
downloaddotfiles-32be4dc72b09caa91430a18d15b35622f166898e.tar.gz
dotfiles-32be4dc72b09caa91430a18d15b35622f166898e.tar.bz2
dotfiles-32be4dc72b09caa91430a18d15b35622f166898e.zip
refactor(units): split systemd-units into system/ and user/ trees
Move the three existing files into systemd-units/system/ and seed systemd-units/user/ with a .ignore stub. Teach the unit-* recipes a user:/system: group-token prefix (bare names keep system semantics for back-compat). unit-apply and unit-status now walk both scopes; user units go through 'systemctl --user' (no sudo), system units via 'sudo systemctl' as before. Soft-fail per unit preserved for both scopes. Top-level add/forget dispatchers need no changes: the unit-extension sniff already routes anything ending in .service/.timer/etc to unit-*, and user:base passes through as the group token. Docs updated in README.md and .github/copilot-instructions.md.
Diffstat (limited to 'justfile')
-rw-r--r--justfile158
1 files changed, 105 insertions, 53 deletions
diff --git a/justfile b/justfile
index f5725ad..32d831e 100644
--- a/justfile
+++ b/justfile
@@ -397,27 +397,26 @@ dotfiles-status:
# ═══════════════════════════════════════════════════════════════════
# Units domain (systemd)
# ═══════════════════════════════════════════════════════════════════
+# Group tokens: `<name>` == `system:<name>` → systemd-units/system/<name>.txt;
+# `user:<name>` → systemd-units/user/<name>.txt. System units use `sudo systemctl`,
+# user units use `systemctl --user` (no sudo). No-arg unit-list/unit-apply/unit-status
+# walk both trees.
-# List curated systemd units grouped by systemd-units/<group>.txt with their state; pass a group to narrow
+# List curated systemd units grouped by systemd-units/{system,user}/<group>.txt with their state; pass a group (optionally `user:`/`system:` prefixed) to narrow
unit-list group="":
#!/bin/sh
- if [ -n '{{ group }}' ]; then
- files="systemd-units/{{ group }}.txt"
- [ -f "$files" ] || { echo "error: $files does not exist" >&2; exit 1; }
- else
- files="systemd-units/*.txt"
- fi
- for file in $files; do
- [ -f "$file" ] || continue
+ _render() {
+ scope=$1 file=$2
+ sctl="systemctl"; [ "$scope" = user ] && sctl="systemctl --user"
group=$(basename "$file" .txt)
- echo "=== $group ==="
+ echo "=== ${scope}:${group} ==="
sed -E 's/[[:space:]]*#.*$//; /^[[:space:]]*$/d' "$file" | while read -r u; do
- en=$(systemctl is-enabled "$u" 2>/dev/null); en=${en:-unknown}
- ac=$(systemctl is-active "$u" 2>/dev/null); ac=${ac:-unknown}
+ en=$($sctl is-enabled "$u" 2>/dev/null); en=${en:-unknown}
+ ac=$($sctl is-active "$u" 2>/dev/null); ac=${ac:-unknown}
case "$en" in
- enabled|static|alias) c_en=32 ;;
- disabled|masked|not-found) c_en=31 ;;
- *) c_en=33 ;;
+ enabled|enabled-runtime|static|alias|indirect|generated) c_en=32 ;;
+ disabled|masked|not-found) c_en=31 ;;
+ *) c_en=33 ;;
esac
case "$ac" in
active) c_ac=32 ;;
@@ -426,73 +425,121 @@ unit-list group="":
esac
printf ' %-34s \033[%sm%-10s\033[0m \033[%sm%s\033[0m\n' "$u" "$c_en" "$en" "$c_ac" "$ac"
done
- done
+ }
+ g='{{ group }}'
+ if [ -n "$g" ]; then
+ case "$g" in
+ user:*) scope=user; name=${g#user:} ;;
+ system:*) scope=system; name=${g#system:} ;;
+ *) scope=system; name=$g ;;
+ esac
+ file="systemd-units/${scope}/${name}.txt"
+ [ -f "$file" ] || { echo "error: $file does not exist" >&2; exit 1; }
+ _render "$scope" "$file"
+ else
+ for scope in system user; do
+ for file in systemd-units/"$scope"/*.txt; do
+ [ -f "$file" ] || continue
+ _render "$scope" "$file"
+ done
+ done
+ fi
-# Enable all curated systemd units (idempotent, soft-fail per unit)
+# Enable all curated systemd units (idempotent, soft-fail per unit); walks both system/ and user/ trees
unit-apply:
#!/bin/sh
- for file in systemd-units/*.txt; do
+ for file in systemd-units/system/*.txt; do
[ -f "$file" ] || continue
sed -E 's/[[:space:]]*#.*$//; /^[[:space:]]*$/d' "$file" | while read -r u; do
sudo systemctl enable --now "$u" \
- || echo " warn: could not enable $u" >&2
+ || echo " warn: could not enable $u (system)" >&2
+ done
+ done
+ for file in systemd-units/user/*.txt; do
+ [ -f "$file" ] || continue
+ sed -E 's/[[:space:]]*#.*$//; /^[[:space:]]*$/d' "$file" | while read -r u; do
+ systemctl --user enable --now "$u" \
+ || echo " warn: could not enable $u (user)" >&2
done
done
-# Show drift between curated units and actually-enabled systemd units
+# Show drift between curated units and actually-enabled systemd units (system + user)
unit-status:
#!/bin/sh
- echo "=== Unit drift ==="
tmp=$(mktemp -d); trap 'rm -rf "$tmp"' EXIT
- cat systemd-units/*.txt 2>/dev/null \
- | sed -E 's/[[:space:]]*#.*$//; /^[[:space:]]*$/d' | sort -u > "$tmp/curated"
- if [ -f systemd-units/.ignore ]; then
- sed -E 's/[[:space:]]*#.*$//; /^[[:space:]]*$/d' systemd-units/.ignore | sort -u > "$tmp/ignore"
- else
- : > "$tmp/ignore"
- fi
- # Curated units missing from the system: use is-enabled to correctly
- # handle instantiated template units (which list-unit-files does not show).
- while read -r u; do
- [ -z "$u" ] && continue
- state=$(systemctl is-enabled "$u" 2>/dev/null || true)
- case "$state" in
- enabled|enabled-runtime|alias|static|indirect|generated) ;;
- *) echo " not-enabled: $u" ;;
- esac
- done < "$tmp/curated"
- # Enabled unit files not in curated (minus ignore list).
- systemctl list-unit-files --state=enabled --no-legend 2>/dev/null \
- | awk '{print $1}' | grep -vE '@\.' | sort -u > "$tmp/enabled"
- comm -13 "$tmp/curated" "$tmp/enabled" | comm -23 - "$tmp/ignore" | sed 's/^/ uncurated: /'
+ _drift() {
+ scope=$1 label=$2
+ sctl="systemctl"; [ "$scope" = user ] && sctl="systemctl --user"
+ echo "=== ${label} drift ==="
+ cat systemd-units/"$scope"/*.txt 2>/dev/null \
+ | sed -E 's/[[:space:]]*#.*$//; /^[[:space:]]*$/d' | sort -u > "$tmp/curated"
+ if [ -f "systemd-units/$scope/.ignore" ]; then
+ sed -E 's/[[:space:]]*#.*$//; /^[[:space:]]*$/d' "systemd-units/$scope/.ignore" | sort -u > "$tmp/ignore"
+ else
+ : > "$tmp/ignore"
+ fi
+ # Curated units missing from the system: is-enabled correctly handles
+ # instantiated template units (list-unit-files does not show those).
+ while read -r u; do
+ [ -z "$u" ] && continue
+ state=$($sctl is-enabled "$u" 2>/dev/null || true)
+ case "$state" in
+ enabled|enabled-runtime|alias|static|indirect|generated) ;;
+ *) echo " not-enabled: $u" ;;
+ esac
+ done < "$tmp/curated"
+ # Enabled unit files not in curated (minus ignore list).
+ $sctl list-unit-files --state=enabled --no-legend 2>/dev/null \
+ | awk '{print $1}' | grep -vE '@\.' | sort -u > "$tmp/enabled"
+ comm -13 "$tmp/curated" "$tmp/enabled" | comm -23 - "$tmp/ignore" | sed 's/^/ uncurated: /'
+ }
+ _drift system "System unit"
+ _drift user "User unit"
-# Append one or more units to a group list and enable them (e.g. just unit-add base sshd.service)
+# Append one or more units to a group list and enable them (e.g. just unit-add base sshd.service; just unit-add user:graphical kanshi.service)
unit-add group +units:
#!/bin/sh
set -eu
- file="systemd-units/{{ group }}.txt"
+ g='{{ group }}'
+ case "$g" in
+ user:*) scope=user; name=${g#user:} ;;
+ system:*) scope=system; name=${g#system:} ;;
+ *) scope=system; name=$g ;;
+ esac
+ file="systemd-units/${scope}/${name}.txt"
if [ ! -f "$file" ]; then
echo "error: $file does not exist" >&2
exit 1
fi
for u in {{ units }}; do
if grep -qxF "$u" "$file"; then
- echo "$u already in {{ group }}.txt"
+ echo "$u already in ${scope}:${name}"
else
echo "$u" >> "$file"
- echo "added $u to {{ group }}.txt"
+ echo "added $u to ${scope}:${name}"
fi
done
for u in {{ units }}; do
- sudo systemctl enable --now "$u" \
- || echo " warn: could not enable $u" >&2
+ if [ "$scope" = user ]; then
+ systemctl --user enable --now "$u" \
+ || echo " warn: could not enable $u (user)" >&2
+ else
+ sudo systemctl enable --now "$u" \
+ || echo " warn: could not enable $u (system)" >&2
+ fi
done
# Remove one or more units from a group list and disable them
unit-forget group +units:
#!/bin/sh
set -eu
- file="systemd-units/{{ group }}.txt"
+ g='{{ group }}'
+ case "$g" in
+ user:*) scope=user; name=${g#user:} ;;
+ system:*) scope=system; name=${g#system:} ;;
+ *) scope=system; name=$g ;;
+ esac
+ file="systemd-units/${scope}/${name}.txt"
if [ ! -f "$file" ]; then
echo "error: $file does not exist" >&2
exit 1
@@ -500,14 +547,19 @@ unit-forget group +units:
for u in {{ units }}; do
if grep -qxF "$u" "$file"; then
sed -i "/^$(printf '%s' "$u" | sed 's/[]\/$*.^[]/\\&/g')\$/d" "$file"
- echo "removed $u from {{ group }}.txt"
+ echo "removed $u from ${scope}:${name}"
else
- echo "$u not in {{ group }}.txt"
+ echo "$u not in ${scope}:${name}"
fi
done
for u in {{ units }}; do
- sudo systemctl disable --now "$u" \
- || echo " warn: could not disable $u" >&2
+ if [ "$scope" = user ]; then
+ systemctl --user disable --now "$u" \
+ || echo " warn: could not disable $u (user)" >&2
+ else
+ sudo systemctl disable --now "$u" \
+ || echo " warn: could not disable $u (system)" >&2
+ fi
done
# ═══════════════════════════════════════════════════════════════════