fix: enforce workspace deletion blockers
This commit is contained in:
@@ -91,7 +91,6 @@ export type WorkspaceDeletionPreflightResponse = {
|
||||
*/
|
||||
expected_revision: string;
|
||||
can_delete: boolean;
|
||||
force_delete_dirty_workdirs_available: boolean;
|
||||
resources: WorkspaceDeletionResourceCounts;
|
||||
blockers: Array<WorkspaceDeletionBlocker>;
|
||||
};
|
||||
@@ -100,7 +99,6 @@ export type WorkspaceDeletionRequest = {
|
||||
operation_id: string;
|
||||
expected_revision: string;
|
||||
confirmation: string;
|
||||
force_delete_dirty_workdirs: boolean;
|
||||
};
|
||||
|
||||
export type WorkspaceDeletionOperationResponse = {
|
||||
@@ -108,7 +106,6 @@ export type WorkspaceDeletionOperationResponse = {
|
||||
workspace_id: string;
|
||||
display_name: string;
|
||||
state: WorkspaceDeletionState;
|
||||
force_delete_dirty_workdirs: boolean;
|
||||
resources: WorkspaceDeletionResourceCounts;
|
||||
child_operation_ids: Array<string>;
|
||||
blockers: Array<WorkspaceDeletionBlocker>;
|
||||
|
||||
@@ -665,7 +665,6 @@ export function parseWorkspaceDeletionPreflightResponse(
|
||||
"display_name",
|
||||
"expected_revision",
|
||||
"can_delete",
|
||||
"force_delete_dirty_workdirs_available",
|
||||
"resources",
|
||||
"blockers",
|
||||
], "Workspace deletion preflight");
|
||||
@@ -686,10 +685,6 @@ export function parseWorkspaceDeletionPreflightResponse(
|
||||
item.can_delete,
|
||||
"Workspace deletion preflight.can_delete",
|
||||
),
|
||||
force_delete_dirty_workdirs_available: boolean(
|
||||
item.force_delete_dirty_workdirs_available,
|
||||
"Workspace deletion preflight.force_delete_dirty_workdirs_available",
|
||||
),
|
||||
resources: deletionResourceCounts(
|
||||
item.resources,
|
||||
"Workspace deletion preflight.resources",
|
||||
@@ -713,7 +708,6 @@ export function parseWorkspaceDeletionOperationResponse(
|
||||
"workspace_id",
|
||||
"display_name",
|
||||
"state",
|
||||
"force_delete_dirty_workdirs",
|
||||
"resources",
|
||||
"child_operation_ids",
|
||||
"blockers",
|
||||
@@ -736,10 +730,6 @@ export function parseWorkspaceDeletionOperationResponse(
|
||||
"Workspace deletion operation.display_name",
|
||||
),
|
||||
state: deletionState(item.state, "Workspace deletion operation.state"),
|
||||
force_delete_dirty_workdirs: boolean(
|
||||
item.force_delete_dirty_workdirs,
|
||||
"Workspace deletion operation.force_delete_dirty_workdirs",
|
||||
),
|
||||
resources: deletionResourceCounts(
|
||||
item.resources,
|
||||
"Workspace deletion operation.resources",
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
let deletionLoading = $state(false);
|
||||
let deletionSubmitting = $state(false);
|
||||
let deletionConfirmation = $state('');
|
||||
let forceDeleteDirtyWorkdirs = $state(false);
|
||||
let deletionPreflight = $state<WorkspaceDeletionPreflightResponse | null>(null);
|
||||
let deletionOperation = $state<WorkspaceDeletionOperationResponse | null>(null);
|
||||
let deletionOperationId = $state('');
|
||||
@@ -95,7 +94,6 @@
|
||||
deletionOperation = null;
|
||||
deletionOperationId = crypto.randomUUID();
|
||||
deletionConfirmation = '';
|
||||
forceDeleteDirtyWorkdirs = false;
|
||||
try {
|
||||
deletionPreflight = await preflightWorkspaceDeletion(workspaceId);
|
||||
} catch (err) {
|
||||
@@ -114,7 +112,6 @@
|
||||
operation_id: deletionOperationId,
|
||||
expected_revision: deletionPreflight.expected_revision,
|
||||
confirmation: deletionConfirmation,
|
||||
force_delete_dirty_workdirs: forceDeleteDirtyWorkdirs,
|
||||
});
|
||||
deletionOperation = operation;
|
||||
while (operation.state === 'queued' || operation.state === 'running') {
|
||||
@@ -207,15 +204,9 @@
|
||||
<p class="status-message error">{blocker.message}</p>
|
||||
{/each}
|
||||
<label>
|
||||
<span>Type <strong>delete {deletionPreflight.display_name}</strong> to confirm</span>
|
||||
<span>Type <strong>{deletionPreflight.display_name}</strong> to confirm</span>
|
||||
<input bind:value={deletionConfirmation} autocomplete="off" />
|
||||
</label>
|
||||
{#if deletionPreflight.force_delete_dirty_workdirs_available}
|
||||
<label class="force-delete-option">
|
||||
<input type="checkbox" bind:checked={forceDeleteDirtyWorkdirs} disabled={deletionOperation !== null} />
|
||||
<span>Force-delete dirty Workdirs after reviewing the impact</span>
|
||||
</label>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if deletionOperation}
|
||||
<p class="status-message">Deletion state: {deletionOperation.state}</p>
|
||||
@@ -230,7 +221,7 @@
|
||||
class="danger-button"
|
||||
type="button"
|
||||
onclick={() => void deleteWorkspace()}
|
||||
disabled={deletionSubmitting || !deletionPreflight?.can_delete || deletionConfirmation !== `delete ${deletionPreflight?.display_name ?? ''}`}
|
||||
disabled={deletionSubmitting || !deletionPreflight?.can_delete || deletionConfirmation !== (deletionPreflight?.display_name ?? '')}
|
||||
>{deletionSubmitting ? 'Deleting…' : 'Delete Workspace'}</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -244,6 +235,5 @@
|
||||
.modal-backdrop { position: fixed; inset: 0; z-index: 100; display: grid; place-items: center; padding: var(--space-4); background: rgb(0 0 0 / 0.55); }
|
||||
.deletion-dialog { width: min(34rem, 100%); max-height: calc(100vh - 2rem); overflow: auto; padding: var(--space-5); background: var(--color-surface, white); border: 1px solid var(--color-border); }
|
||||
.deletion-dialog label { display: grid; gap: var(--space-2); margin-block: var(--space-4); }
|
||||
.force-delete-option { grid-template-columns: auto 1fr !important; align-items: start; }
|
||||
.dialog-actions { display: flex; justify-content: flex-end; gap: var(--space-2); margin-top: var(--space-5); }
|
||||
</style>
|
||||
|
||||
@@ -117,7 +117,6 @@ Deno.test("Workspace deletion DTOs fail closed and preserve durable operation st
|
||||
display_name: "Alpha",
|
||||
expected_revision: "2026-01-01T00:00:00Z",
|
||||
can_delete: true,
|
||||
force_delete_dirty_workdirs_available: true,
|
||||
resources: {
|
||||
workers: 2,
|
||||
workdirs: 1,
|
||||
@@ -137,7 +136,6 @@ Deno.test("Workspace deletion DTOs fail closed and preserve durable operation st
|
||||
workspace_id: "workspace-a",
|
||||
display_name: "Alpha",
|
||||
state: "blocked",
|
||||
force_delete_dirty_workdirs: false,
|
||||
resources: preflight.resources,
|
||||
child_operation_ids: ["worker-remove:arcadia/7"],
|
||||
blockers: [{
|
||||
@@ -185,8 +183,7 @@ Deno.test("Workspace settings exposes owner-gated typed destructive confirmation
|
||||
"permissions.delete_workspace",
|
||||
"preflightWorkspaceDeletion",
|
||||
"startWorkspaceDeletion",
|
||||
"delete ${",
|
||||
"force_delete_dirty_workdirs",
|
||||
"deletionConfirmation",
|
||||
"disposeWorkspaceMultiplexer(workspaceId)",
|
||||
]
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user