Skip to content

Commit c1f81d8

Browse files
fix(setup,auth): manage explicitly-confirmed k8s contexts, fail loudly on reset, clear stale post-auth redirect
- lifecycle: detection is now factual — a sim-dev release either exists on the current context or it doesn't. Gating on locality stranded a release the user explicitly confirmed during setup (status/start/stop/down/reset all claimed no k8s install). Locality is recorded instead and surfaced through describeInstall, which every destructive confirm renders, so acting on a non-local cluster is named and defaulted to no rather than silently blocked or silently allowed. - lifecycle: reset no longer discards helm uninstall's exit status. Env files are archived by that point, so claiming 'Reset complete' while the release still runs is the worst outcome — it now throws with retry/inspect commands. - auth: signup clears POST_AUTH_REDIRECT_STORAGE_KEY when it has no callbackUrl, and the verification-disabled path consumes it, so a stale CLI/invite destination can't leak into a later flow in the same tab.
1 parent c5dac2c commit c1f81d8

3 files changed

Lines changed: 37 additions & 8 deletions

File tree

apps/sim/app/(auth)/signup/signup-form.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,10 @@ function SignupFormContent({
346346
sessionStorage.setItem('verificationEmail', emailValue)
347347
if (redirectUrl) {
348348
sessionStorage.setItem(POST_AUTH_REDIRECT_STORAGE_KEY, redirectUrl)
349+
} else {
350+
// Clear any leftover from an earlier signup in this tab — otherwise a
351+
// signup with no callbackUrl inherits the previous CLI/invite destination.
352+
sessionStorage.removeItem(POST_AUTH_REDIRECT_STORAGE_KEY)
349353
}
350354
}
351355

apps/sim/app/(auth)/verify/use-verification.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ export function useVerification({
210210
setStatus('verified')
211211

212212
const destination = resolveRedirectUrl(searchParams.get('redirectAfter'))
213+
// Single-use: consume the stored destination here too, or it survives this
214+
// flow and reapplies to a later sign-in in the same tab.
215+
sessionStorage.removeItem(POST_AUTH_REDIRECT_STORAGE_KEY)
213216

214217
const handleRedirect = async () => {
215218
try {

scripts/setup/lifecycle.ts

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ interface DevInstall {
6969
interface K8sInstall {
7070
kind: 'k8s'
7171
context: string
72+
/** False when the context's API server is outside the local allowlist — flagged before destructive ops. */
73+
local: boolean
7274
}
7375
type Install = ComposeInstall | DevInstall | K8sInstall
7476

@@ -92,19 +94,24 @@ function devInstall(detection: Detection): DevInstall | null {
9294
return { kind: 'dev', postgres, redis }
9395
}
9496

97+
/**
98+
* Detection is factual: a release either exists on the current context or it
99+
* doesn't. Setup lets the user explicitly confirm a context whose API server is
100+
* outside the local allowlist, so gating detection on locality would strand that
101+
* release — `status`/`start`/`stop`/`down`/`reset` would all claim there is no
102+
* Kubernetes install. Instead the locality is recorded and surfaced: every
103+
* destructive path names the target (and flags a non-local one) before acting.
104+
*/
95105
function k8sInstall(detection: Detection): K8sInstall | null {
96106
const context = detection.kubeContext
97-
// Only manage k8s on a verified-local context. The ambient current-context can
98-
// change after setup, and the wizard only ever deploys to a local cluster — so
99-
// gating on locality stops `down`/`reset` from uninstalling a same-named release
100-
// from a remote cluster the user happens to be pointed at.
101-
if (!context || !isLocalKubeContext(context)) return null
107+
if (!context) return null
102108
const status = spawnSync(
103109
'helm',
104110
['status', K8S_RELEASE, '--kube-context', context, '-n', K8S_NAMESPACE],
105111
{ stdio: 'ignore' }
106112
)
107-
return status.status === 0 ? { kind: 'k8s', context } : null
113+
if (status.status !== 0) return null
114+
return { kind: 'k8s', context, local: isLocalKubeContext(context) }
108115
}
109116

110117
function detectInstalls(detection: Detection): Install[] {
@@ -119,7 +126,10 @@ function detectInstalls(detection: Detection): Install[] {
119126
function describeInstall(install: Install): string {
120127
if (install.kind === 'compose') return `Docker Compose (${install.file})`
121128
if (install.kind === 'dev') return 'Local dev (managed Postgres/Redis)'
122-
return `Kubernetes (context ${install.context})`
129+
// Naming a non-local cluster is the guard against acting on the wrong one after
130+
// an ambient context switch — every destructive confirm renders this string.
131+
const scope = install.local ? '' : ' — NOT a verified-local cluster'
132+
return `Kubernetes (context ${install.context}${scope})`
123133
}
124134

125135
/** One install → use it; several → let the user pick; none → null. */
@@ -309,11 +319,23 @@ async function reset(install: Install | null): Promise<void> {
309319
spawnSync('docker', ['volume', 'rm', POSTGRES_VOLUME], { cwd: ROOT, stdio: 'ignore' })
310320
p.log.step('Managed containers and Postgres volume removed')
311321
} else if (install?.kind === 'k8s') {
312-
spawnSync(
322+
const uninstall = spawnSync(
313323
'helm',
314324
['uninstall', K8S_RELEASE, '--kube-context', install.context, '-n', K8S_NAMESPACE],
315325
{ stdio: 'inherit' }
316326
)
327+
// Env files are already archived, so a failed uninstall leaves a live release
328+
// with no local config — the worst thing to do is call that a success.
329+
if (uninstall.status !== 0) {
330+
throw new SetupError(
331+
`env files were archived, but helm uninstall failed — the ${K8S_RELEASE} release is still running.`,
332+
[
333+
`retry: ${theme.command(`helm uninstall ${K8S_RELEASE} --kube-context ${shq(install.context)} -n ${K8S_NAMESPACE}`)}`,
334+
`check the release: ${theme.command(`helm status ${K8S_RELEASE} --kube-context ${shq(install.context)} -n ${K8S_NAMESPACE}`)}`,
335+
]
336+
)
337+
}
338+
p.log.step(`Uninstalled ${K8S_RELEASE}`)
317339
}
318340
p.note(`start fresh with ${theme.command('sim setup')}`, 'Reset complete')
319341
}

0 commit comments

Comments
 (0)