#!/usr/bin/env bash # Row 233 (I3, #1735): root's upgrade and rollback for a stack whose units are root's system units # (stack-release.sh --units external, row 232). r6's activate A1-A5, A2f, R1-R4 and rollback # (agents/darkwing/work/queue-195/DESIGN.md), as the I3 brief changes them # (docs/plans/2026-10-20_installer-rows.md). Deviations and decisions: # agents/darkwing/work/queue-233/BUILD.md. # # stack-upgrade.sh upgrade --instance dev --bundle FILE --sha SHA [--migrate] [--no-rehearsal] # [--rehearse-port N] [--next-args] [--timeout S] # stack-upgrade.sh rollback --instance dev --to ID [--restore ACT [--events N]] [--next-args] # [--timeout S] # # Root runs it from a root-owned checkout of the reviewed commit, never from a copy, because the # stack's user can write the copies. The unit values come from /etc/mosaic-stack/.args, # which I2's install writes: one case2-unit.sh argument per line, the serve flags after `--`. Only # --copy changes, and the file changes only after confirm passes. --org is the same option as # --business in each of the file's two scopes, case2-unit.sh's own flags before `--` and the serve # flags after it: each scope takes each spelling once, a second spelling only with the same value, # and both scopes must name the business, the same one (rename design r2). The stack's user runs # its steps from its own clone, /src/mosaic-stack, through sudo. Root reads none of that user's # output but exit codes and the rehearsal's BACKUP_STORE line (sage-1653), which it copies into its # step log after a match against a fixed pattern and decides nothing on. # # Before anything stops, case2-unit.sh --check renders the installed copy from the args file and # compares the installed units with it, then checks the target's render the same way; either # refusal is render-refused, with the units left running (#1873 M3). # # --next-args changes the serve flags, which a re-render otherwise refuses (row 195's installed-unit # match): /etc/mosaic-stack/.args.next, root's with no group or other write, must equal # the args file before `--` but for --copy's value, and differ from it after `--`. The target # renders from it through case2-unit.sh --check-new before anything stops. After A3, root moves the # installed unit files into a new .replaced.XXXXXX directory beside the args file, and A4 # renders from the next args as a first install. A failure from there moves the replaced files back # unchanged, restarts them on the old copy and gates it, with no render. After confirm, the next # args become the args file, and the replaced directory keeps the old unit files and args file for # a later rollback with --next-args (docs/guides/host-run-sheet.md section 7). # # --no-rehearsal is for a stack whose log records no backup. The rehearsal runs either way: exit 3 # from it (no-backup) goes on only with the flag, and exit 0 with the flag refuses # rehearsal-required (sage-1650, sage-1652). # # The lock /run/mosaic-stack/upgrade-.lock is held from before A1 to the end of the run, # rollback included; every child runs with the lock's descriptor closed. The step log # /var/log/mosaic-stack/upgrade-.log gets one line per step, `