Skip to content

Support scripted updates: modes and policy flags - #11018

Open
SomeoneWithOptions wants to merge 8 commits into
omacom:quattrofrom
SomeoneWithOptions:feat/update-automation-policies
Open

Support scripted updates: modes and policy flags#11018
SomeoneWithOptions wants to merge 8 commits into
omacom:quattrofrom
SomeoneWithOptions:feat/update-automation-policies

Conversation

@SomeoneWithOptions

@SomeoneWithOptions SomeoneWithOptions commented Sep 9, 2026

Copy link
Copy Markdown

Summary

Scripted omarchy update currently has no supported contract: --yes is absent, and -y leaks prompts under a transcript PTY (orphan gum confirm, reboot gum confirm reachable with a terminal attached), which the prior audit reproduced. This change adds explicit unattended modes plus per-area policy flags with deterministic parsing, a subprocess-scoped nonprompting privilege environment covering the whole descendant tree (including migrations), policy gating for hooks/AUR/mise/orphans/reboot/restarts, failure-closed keyring/inhibitor/git/conflict/migration-deferral semantics, and user plus internal docs. Interactive no-argument behavior is unchanged. This belongs to the #9501 family (scripted-update support).

Flag contract

omarchy update [--yes|-y|--non-interactive] [--hooks=run|skip] [--aur=run|skip] [--mise=run|skip] [--orphans=ask|keep|remove] [--reboot=ask|never|if-needed] [--restarts=run|skip]

Flag Values Notes
-y, --yes (mode) Identical unattended first-party full pipeline.
--non-interactive (mode) Unattended strict mode; hooks/aur/mise default skip.
--hooks run|skip Overrides mode default order-independently.
--aur run|skip Overrides mode default order-independently.
--mise run|skip Overrides mode default order-independently.
--orphans ask|keep|remove remove authorizes removal without confirmation.
--reboot ask|never|if-needed if-needed authorizes automatic reboot on success path only.
--restarts run|skip skip defers service/shell restarts, keeps markers.

Parsing rules: exact --name=value only; repeating the same policy with the same value is allowed; contradictory repeats fail exit 2; -y/--yes and --non-interactive may combine with strict winning regardless of order; ask with either unattended mode fails exit 2 regardless of order; unknown args, missing/invalid values, and positional args fail exit 2 before transcript creation, lock acquisition, or side effects; -h/--help anywhere prints usage with exit 0 without validating other args or running steps.

Behavior defaults per mode

Mode hooks aur mise orphans reboot restarts
No flags (interactive) run run run ask ask run
-y / --yes (full) run run run keep never run
--non-interactive (strict) skip skip skip keep never run

Unattended runs print Unattended update (full): ... (or the (strict) form) with normalized policies, plus Orphan policy: remove -- orphaned packages will be removed without confirmation for --orphans=remove and Reboot policy: if-needed -- system will reboot automatically when required (may close unsaved applications) for --reboot=if-needed. Skipped hooks/AUR/mise print Skipping post-update hooks (--hooks=skip) / Skipping AUR package updates (--aur=skip) / Skipping mise updates (--mise=skip); strict opt-in (--hooks=run, --aur=run, --mise=run) runs but first warns on stderr that the non-interactive guarantee is relaxed for that component. Normalized exports: OMARCHY_UPDATE_UNATTENDED=1 (either unattended mode, unset interactive), OMARCHY_UPDATE_STRICT=1 (strict only, unset otherwise), OMARCHY_UPDATE_HOOKS/OMARCHY_UPDATE_AUR/OMARCHY_UPDATE_MISE (run|skip), OMARCHY_UPDATE_ORPHANS (ask|keep|remove), OMARCHY_UPDATE_REBOOT (ask|never|if-needed), OMARCHY_UPDATE_RESTARTS (run|skip).

Guarantees and explicit boundaries

Guaranteed: deterministic parsing/validation before mutation; subprocess-scoped sudo -n adaptation inherited by plain-sudo descendants (nested bash, xargs/env, migration helpers) with stdin/stdout/stderr and exit-status preservation; pkexec fails closed with no graphical dialog; keyring/inhibitor/reboot/restart/migration/conflict/git failure semantics below; required-migration ordering with deferral staying pending. Explicitly not guaranteed: the adapter directory is subprocess PATH prepend only, not a security boundary and not installed as system sudo; absolute-path sudo, cleared-PATH callers, and custom programs bypass it. User hooks, AUR PKGBUILDs, mise backends, package ALPM hooks, and git credential helpers / custom core.sshCommand programs are cooperative external execution outside the guarantee (strict run opt-in warns per flag). Sudo credential precondition: unattended runs need a cached ticket or NOPASSWD; otherwise sudo -n fails promptly with no password/GUI dialog (keyring precheck exits 1 with omarchy-update-keyring: unattended update cannot authenticate with sudo (sudo -n failed); run interactively or refresh sudo credentials before retrying).

Destructive-policy warnings

--orphans=remove removes the currently detected orphan packages without confirmation (sudo pacman -Rns --noconfirm, existing recursive semantics); it is not authorization to resolve arbitrary package conflicts. --reboot=if-needed reboots automatically via sudo systemctl --no-ask-password reboot --no-wall only after all required work succeeds and update-owned inhibitors are released; it never reboots midway or after failure, keeps the reboot marker when the reboot request fails, and can close unsaved applications.

What changed (file by file)

  • bin/omarchy-update: CLI parse/validate/normalize/export, unattended runner re-exec before transcript/lock/space checks, transcript (script(1)/tmp/omarchy-update.log) and lock order preserved, hooks/AUR/mise gating with skip/warning/summary lines, destructive-policy summaries, preserved order and failure propagation.
  • bin/omarchy-update-run (new): hidden runner installing the scoped privilege environment (OMARCHY_UPDATE_REAL_SUDO capture, update-bin prepend, OMARCHY_UPDATE_ENV_READY=1, OMARCHY_UPDATE_RUN_REEXEC=1 re-entry guard).
  • default/omarchy/update-bin/sudo (new): unattended adapter execing captured real sudo with -n, rejecting -S/--stdin, -A/--askpass, -p/--prompt.
  • default/omarchy/update-bin/pkexec (new): unattended fail-closed adapter, exit 1, no dialog.
  • bin/omarchy-update-orphan-pkgs: OMARCHY_UPDATE_ORPHANS policy (ask interactive gum flow preserved byte-for-byte; keep lists/retains; remove nonconfirm removal with error propagation; unattended/headless ask reports without gum; invalid exits 2).
  • bin/omarchy-update-restart: OMARCHY_UPDATE_REBOOT/OMARCHY_UPDATE_RESTARTS policies, single automatic reboot on final success path only, Reboot required: ... reporting, Skipping service restarts ... / Shell restart deferred for skip, marker retention on failures, no gum unattended even with PTY.
  • bin/omarchy-update-stay-awake: unattended sudo -n runner selection (never pkexec), observed acquisition with Warning: sleep inhibitor failed to start; continuing without sleep inhibition. warn-and-continue, PID/lock-FD/cleanup semantics preserved.
  • bin/omarchy-update-keyring: set -euo pipefail plus unattended sudo -n true precheck described above; Keys are correct only on success.
  • bin/omarchy-update-dev, bin/omarchy-update-available: unattended GIT_TERMINAL_PROMPT=0, empty GIT_ASKPASS=, GIT_SSH_COMMAND="${GIT_SSH_COMMAND:-ssh -oBatchMode=yes}" (explicit env preserved, custom sshCommand never executed, no new-host-key autoaccept); dev pull failure propagates nonzero, available fetch failure keeps quiet cached-state fallback.
  • migrations/1786643346.sh: unattended browser-open deferral with actionable stderr and exit 1, marker absent, interactive unchanged.
  • migrations/1787760281.sh: strict-plus-OMARCHY_UPDATE_MISE!=run explicit deferral before either installer branch (never swallowed by || true), true no-ops preserved.
  • Docs: docs/update-process.md (full flag/mode/env/runner/adapter/keyring/inhibitor/git/exit-code contract, interactive unchanged), manual/30-updates.md (new user-facing Non-interactive updates section with defaults table, examples, prerequisites, warnings, boundaries, restarts/reboot/migration/conflict notes), agents/skills/migrations.md (new Unattended updates authoring subsection citing both migration patterns).
  • Tests: new test/shell.d/update-environment-test.sh, test/shell.d/update-options-test.sh, test/shell.d/update-restart-test.sh, test/shell.d/update-keyring-test.sh, test/shell.d/update-migration-policy-test.sh, test/shell.d/update-unattended-test.sh (35-check mocked PTY/headless end-to-end); rewritten test/shell.d/update-sequence-test.sh; extended test/shell.d/update-orphan-test.sh, test/shell.d/update-lock-test.sh, test/shell.d/update-dev-test.sh, test/shell.d/update-available-test.sh; focused test/shell.d/update-disk-space-test.sh fixture/assertion updates.

Test plan

Focused suites via the sandbox runner (python3 <run-dir>/sandbox.py -- ...): update-options-test.sh, update-sequence-test.sh, update-orphan-test.sh, update-restart-test.sh, update-lock-test.sh, update-keyring-test.sh, update-migration-policy-test.sh, update-dev-test.sh, update-available-test.sh, update-disk-space-test.sh, update-environment-test.sh, plus bash -n on touched bash and git diff --check. Integration: update-unattended-test.sh passes 35 checks (interactive accept/decline, -y/--yes/strict defaults and overrides, PTY plus headless, orphan remove/keep, reboot if-needed success/failure/single-request, restarts skip, conflict stop, keyring no-auth stop, concurrent lock, transcript/summary presence). ./test/cli passes. ./test/all shows only the 4 pre-existing environment failures from baseline (3 from the missing omarchy-pkgs checkout plus the isolated-network QR sandbox artifact). PTY mutation proofs: disposable-copy removal of the orphan unattended guard and the restart unattended no-gum condition makes the matching PTY tests call gum, confirming the tests catch regressions. No live package operations, reboots, removals, or desktop use in tests.

Relation to existing PRs

Related: #9511 (open), #8894, #8992, #9875, #6830. Overlap is expected around scripted-update modes and prompt suppression. What this draft adds beyond that overlap: the full explicit flag contract with per-mode defaults and order-independent parsing/validation rules; a privilege adapter scoped to updater descendants that also covers migrations and transitive first-party sudo callers; keyring unattended precheck and inhibitor acquisition failure semantics; reboot/restarts policies with single-request automatic reboot; git unattended env for dev/available with custom-helper boundaries; migration deferral patterns for unattended and strict-mise cases; plus user and internal docs. Happy to rebase onto whichever related branch lands first and to coordinate rather than duplicate: please point out the preferred base and I will reshape this accordingly.

Limitations

Packaging rule verified for default/** copy into /usr/share/omarchy/default/ (covers the new adapters and preserves executability) from the omarchy-pkgs PKGBUILD, but a full package build was not run and the omarchy-pkgs repo was not checked out here. No live update was executed; end-to-end means the full orchestrator flow with real transcript/lock/helpers and mocked package/system boundaries, not a live upgrade. No timeout is imposed on real package transactions. Upstream package hooks and third-party prompts remain outside the guarantee as documented.

Compliance notes

Docs follow repo AGENTS.md markdown style (full lines, two-space indent). Suggested atomic commits, one per area: CLI/runner/adapters plus environment tests; orphan policy plus tests; restart/reboot policy plus tests; stay-awake/keyring plus tests; migrations plus policy tests; git/dev/available plus tests; unattended integration test; docs (update-process, manual, migrations skill). No commits made here; branch feat/update-automation-policies holds unstaged implementation plus docs for review.

@SomeoneWithOptions
SomeoneWithOptions marked this pull request as ready for review September 9, 2026 18:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant