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
@@ -7,6 +7,7 @@ import type { StackMenuCtx } from '@/components/sidebar/sidebar-types';
function makeCtx(overrides: Partial<StackMenuCtx> = {}): StackMenuCtx {
return {
stackStatus: 'running',
isSelfStack: false,
canOpenApp: true,
isBusy: false,
isAdmin: true,
@@ -179,4 +180,10 @@ describe('useStackMenuItems', () => {
const scheduleItem = lifecycle.items.find(i => i.id === 'schedule')!;
expect(scheduleItem.disabled).toBeFalsy();
});
it('disables delete for the self stack', () => {
const { result } = renderHook(() => useStackMenuItems('sencho.yml', makeCtx({ isSelfStack: true })));
const destructive = result.current.find(g => g.id === 'destructive')!;
expect(destructive.items[0].disabled).toBe(true);
});
});