feat: block self-stack lifecycle ops with UI and preflight guardrails (#1569)

* feat: block self-stack lifecycle ops with UI and preflight guardrails

Refuse update, deploy, down, stop, and delete when the stack matches Sencho's compose project.

Return 409 self_stack_protected. Expose isSelf on /statuses and disable guarded UI actions.

Add SelfStackProtectedDialog and self-managed-stack preflight warning.

Closes #1564

* fix: add missing stackSelfFlags mock to useSidebarContextMenu test

The production hook now reads stackListState.stackSelfFlags[file], but the
test mock did not include it, causing 6 tests to fail with TypeError:
Cannot read properties of undefined (reading 'web.yml').

* fix: harden self-stack protection during startup

Add a global environment preflight warning when Sencho is managed inside COMPOSE_DIR.

Align status decoration and route guards on Docker label fallback detection.

Block rollback and service-level stop on the protected self stack.

* fix: add self_stack_location to diagnostics-route expected check IDs
This commit is contained in:
Anso
2026-07-06 02:08:16 -04:00
committed by GitHub
parent f30a65ee08
commit 0f9925e04f
30 changed files with 905 additions and 31 deletions
@@ -2,6 +2,7 @@ import BashExecModal from '../BashExecModal';
import { PolicyBlockDialog } from '../stack/PolicyBlockDialog';
import { PreDeployScanDialog } from '../stack/PreDeployScanDialog';
import { UpdateReadinessDialog } from '../stack/UpdateReadinessDialog';
import { SelfStackProtectedDialog } from '../stack/SelfStackProtectedDialog';
import { DeleteStackDialog } from './DeleteStackDialog';
import { UnsavedChangesDialog } from './UnsavedChangesDialog';
import { StackAlertSheet } from '../StackAlertSheet';
@@ -23,6 +24,8 @@ interface ShellOverlaysProps {
stackName: string;
gitSourceOpen: boolean;
setGitSourceOpen: (open: boolean) => void;
canSelfUpdate: boolean;
onOpenFleetNodeUpdates: () => void;
}
export function ShellOverlays({
@@ -35,6 +38,8 @@ export function ShellOverlays({
stackName,
gitSourceOpen,
setGitSourceOpen,
canSelfUpdate,
onOpenFleetNodeUpdates,
}: ShellOverlaysProps) {
const {
deleteDialogOpen, closeDeleteDialog, stackToDelete,
@@ -45,6 +50,7 @@ export function ShellOverlays({
policyBlock, setPolicyBlock, policyBypassing,
updateReadiness, setUpdateReadiness,
preDeployAdvisory,
selfStackProtectedOpen, setSelfStackProtectedOpen,
stackMisconfigScanId, setStackMisconfigScanId,
diffPreview, setDiffPreview, diffPreviewConfirming, setDiffPreviewConfirming,
} = overlayState;
@@ -58,6 +64,13 @@ export function ShellOverlays({
onConfirm={stackActions.deleteStack}
/>
<SelfStackProtectedDialog
open={selfStackProtectedOpen}
onOpenChange={setSelfStackProtectedOpen}
canOpenFleetUpdates={canSelfUpdate}
onOpenFleetUpdates={onOpenFleetNodeUpdates}
/>
<UnsavedChangesDialog
open={!!pendingUnsavedLoad || !!pendingLeaveAction}
onCancel={stackActions.cancelPendingUnsavedLoad}