Files
sencho/docs
Anso bb85e08b36 feat(gitops): add rollout lifecycle controls and rollback recovery (#1965)
* feat(gitops): capture a recovery point before Git-managed rollout applies

A rollout rollback needs a recovery point that names the generation the node
was running before the deploy, and Git-managed rollout applies did not create
one. applyLocalUnderLock now captures the pre-deploy project (with the
hub-supplied GitOps binding) before the incoming compose reaches disk, owns
the recovery lifecycle from acquire through verified, and compensates from
the recovery point when the apply fails. A remote target does the same over
apply-local, and the request carries the binding because a leaf has no GitOps
rows of its own.

The binding resolves the deployed pointer for Direct mode and the ack's
applied pointer for Blueprint mode: nothing writes the deploy-bound pointer
for a Blueprint-managed stack. A re-capture that cannot resolve the owning
application (a leaf backup, for example) carries the previous binding
forward, since re-capturing does not change what the node is running.

* feat(gitops): add rollout lifecycle controls and rollback coordination

Adds the operator lifecycle routes for a Git-managed Blueprint rollout:
pause and resume (application or one target), replan (re-derives placement
and reopens review instead of invalidating an accurate one), supersede
(withdraws the live authorization and reports the abandoned rollout), and
rollback.

Rollback restores an explicit prior application generation on one target, the
failed targets, or every changed target in the frozen set. Selectable
generations come from the application's rollout-generation history; targets
already running the selected generation are out of scope; every target is
authorized before any work starts; and the abandoned generation is superseded
first so nothing can dispatch it mid-restore. Each target is restored from
its own captured recovery point, in process for the local node and through the
node's stack rollback route for a remote, where the node must confirm it
restored the requested generation. Every exit records a terminal per-target
result, and a partial failure lifts the application out of recovery rather
than leaving a restore nobody drives.

The application detail read and the Blueprint summary now carry the prior
generations a rollback can select, plus the bound Blueprint's enabled state so
the surfaces can withhold execution controls.

* feat(gitops): add rollout controls to the GitOps application surfaces

The GitOps application view and the Blueprint sheet's GitOps section gain a
rollout controls row: pause and resume as first-row actions, with replan,
supersede, and rollback behind the overflow menu and each destructive choice
behind a confirmation. Rollback selects a prior generation and a target scope
(one target, the failed targets, or every changed target) and states the
configuration-versus-data boundary before anything runs.

Controls are offered only from the projection's own statuses and gated by the
permission the route requires, and execution controls are withheld while the
bound Blueprint is disabled. The superseded rollout copy now covers an
operator withdrawal as well as replacement.

* docs(gitops): document rollout lifecycle controls and rollback

Covers pausing, resuming, replanning, and superseding a rollout, the rollback
generation and scope selection with its configuration-versus-data caveat, and
the drift-enforce interaction that re-applies the current revision after a
rollback. The GitOps portfolio page names the in-place actions on a Git-managed
application, and What's New gains an entry for the lifecycle controls.

* fix(gitops): sanitize logged error messages on rollout recovery paths

CodeQL flagged log injection on the new rollout lifecycle and recovery
handlers: a remote node's response body, a recovery row, or a candidate
payload can reach the logged Error object, so the logs now record the
sanitized message string instead. No behavior change.

* fix(gitops): sanitize the logged target id on rollout recovery paths

The target node id on these paths can come from the rollback request's scope
selector, so CodeQL reads it as a user-provided value flowing into the log
entry even though it is a number. Pass it through sanitizeForLog, the same
barrier the other logged values use. No behavior change.

* fix(gitops): hold rollout dispatch while it is paused

Pause was recorded and rendered but no execution path honored it: boot-time
reconstruction re-dispatched a paused rollout, and the sequential dispatch
loop kept deploying targets the operator had paused. The adapter now blocks
while the application is paused, re-reads the pause before every target, skips
individually paused targets, and reports a hold instead of progress when
nothing was dispatched because every remaining target is paused;
reconstruction skips paused applications outright. A dispatch that pauses
during the preflight window no longer mints or supersedes the authorization it
is holding.

The continuation a target resume triggers is covered by the route test: the
queue is dispatched again and any still-paused target is skipped.

* fix(gitops): continue the queue on target resume and refuse empty pauses

A per-target resume cleared the pause but never continued the rollout, so the
target it released waited for the next restart to be retried; the route now
runs the same continuation as an application resume, and the adapter skips
targets that are still individually paused.

Pausing an application with no authorized rollout and no partial record is now
refused with ROLLOUT_PAUSE_REFUSED: recording a hold on nothing makes the
surface read as held when no rollout ever ran.

* docs(gitops): state the per-node restore limit for rollout rollback

A node keeps only the recovery generation captured immediately before its
latest rollout, so an older selectable generation has nothing to restore from
and is reported as a failed target. The rollback dialog and the feature page
now say so instead of implying every listed generation is restorable.
2026-09-23 18:52:22 -04:00
..