git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commited0c87a0d4318850fe3c557ee7724eb7f9564f9d
parent5e9671441c
authorLucas Galante <[email protected]>
date2026-08-24 10:29
ccebuild: per-crate restart form

`restart [--all] [PKG...]` now scopes the sweep to units whose unit
files live in the named crates, via the same crate_selected /
file_crate_dir mapping the filtered install uses. This is the
multi-session-safe form: several agents work in sibling crates
concurrently, and the bare sweep bounces services another session has
installed but is not ready to restart. Bare `restart` and `--all` are
unchanged; `--all PKG...` composes (every running unit of those crates,
replaced binary or not). Unknown packages die via assert_packages, and
a crate that ships no units reports that rather than silently matching
nothing.

Verified live: bogus package rejected; scoped no-op messages for a
crate with current binaries and for one with no units; bare form
unchanged; `restart --all cce-grid` restarted exactly cce-grid.service
while sibling services kept their PIDs.

WORKSPACE.md "Concurrent sessions" now points at the scoped form as the
required restart path.

Co-Authored-By: Claude Fable 5 <[email protected]>

 WORKSPACE.md     |  8 +++++---
 scripts/ccebuild | 40 ++++++++++++++++++++++++++++++++++++----
 2 files changed, 41 insertions(+), 7 deletions(-)

diff --git a/WORKSPACE.md b/WORKSPACE.md
index 5b45058..b13d52d 100644
--- a/WORKSPACE.md
+++ b/WORKSPACE.md
@@ -256,9 +256,11 @@ the others. The rules:
   including another session's half-finished work. A concurrent build blocking
   on cargo's build-directory lock ("Blocking waiting for file lock") is
   normal — wait it out; don't kill it or conclude the build is broken.
-- **Never `ccebuild restart`.** It is unscoped: it restarts every user service
-  running a replaced binary, including apps another session has installed but
-  is not ready to restart. Restart only your own app by name
+- **Restart with `ccebuild restart <crate>`, never the bare form.** Bare
+  `ccebuild restart` is unscoped: it restarts every user service running a
+  replaced binary, including apps another session has installed but is not
+  ready to restart. The per-crate form restarts only units shipped by the
+  named crate(s). Apps that are not services restart by name
   (`pkill -x <bin>`, relaunch detached).
 - **Shared crates are exclusive.** Before editing `cce-ui`,
   `cce-window-manager`, or `cce-icons`, run `git status` there. Foreign dirt
diff --git a/scripts/ccebuild b/scripts/ccebuild
index e7d30ed..cac2115 100755
--- a/scripts/ccebuild
+++ b/scripts/ccebuild
@@ -527,9 +527,28 @@ cmd_prune() {
 #
 # The compositor is deliberately unreachable here: it is not a user unit (it is
 # started by startcce), and restarting it would tear down the session.
+#
+# `restart PKG...` scopes the sweep to units whose unit files live in the named
+# crates (same crate_selected/file_crate_dir mapping as the filtered install).
+# This is the multi-session-safe form: several agents work in sibling crates
+# concurrently, and an unscoped restart bounces services another session has
+# installed but is not ready to restart (WORKSPACE.md "Concurrent sessions").
 cmd_restart() {
     local all=0
-    [ "${1:-}" = --all ] && all=1
+    [ "${1:-}" = --all ] && { all=1; shift; }
+    local pkgs=("$@")
+    [ ${#pkgs[@]} -gt 0 ] && assert_packages "${pkgs[@]}"
+
+    # Crate scope: unit-file basenames belonging to the named packages.
+    local scoped="" u
+    if [ ${#pkgs[@]} -gt 0 ]; then
+        while read -r u; do
+            [ -n "$u" ] || continue
+            crate_selected "$(file_crate_dir "$u")" "${pkgs[@]}" || continue
+            scoped="$scoped $(basename "$u")"
+        done < <(user_units)
+        [ -n "$scoped" ] || { printf 'ccebuild: no user units belong to: %s\n' "${pkgs[*]}"; return; }
+    fi
 
     systemctl --user daemon-reload 2>/dev/null || true
 
@@ -539,6 +558,12 @@ cmd_restart() {
     [ -n "$units" ] || { printf 'ccebuild: no cce user services running\n'; return; }
 
     for unit in $units; do
+        if [ -n "$scoped" ]; then
+            case " $scoped " in
+                *" $unit "*) ;;
+                *) continue ;;
+            esac
+        fi
         if [ "$all" -eq 1 ]; then
             want+=("$unit")
             continue
@@ -550,7 +575,11 @@ cmd_restart() {
     done
 
     if [ ${#want[@]} -eq 0 ]; then
-        printf 'ccebuild: every running cce service is already on its current binary\n'
+        if [ ${#pkgs[@]} -gt 0 ]; then
+            printf 'ccebuild: no running service of %s is on a replaced binary\n' "${pkgs[*]}"
+        else
+            printf 'ccebuild: every running cce service is already on its current binary\n'
+        fi
         return
     fi
 
@@ -635,8 +664,11 @@ usage: ccebuild <command>
                         and user unit (derived from cargo metadata)
   status                show built-vs-installed drift, and running processes
                         that are not on their installed binary
-  restart [--all]       restart user services still running a replaced binary
-                        (--all: every running cce service). Never the compositor.
+  restart [--all] [PKG...]
+                        restart user services still running a replaced binary
+                        (--all: regardless of binary state; PKG...: only units
+                        belonging to those crates — the safe form when other
+                        sessions are working). Never the compositor.
   prune [--apply]       delete target/ artifacts of crates cargo no longer
                         knows about (renamed/retired); dry-run by default
   install-system        update the root-owned artifacts: binaries, system