Skip to content

fix(THU-539): make Preview Destroy idempotent when the Pulumi stack is gone#983

Open
darkbanjo wants to merge 1 commit into
mainfrom
jkab/thu-539-preview-destroy-fails-hard-when-pulumi-stack-is-missing
Open

fix(THU-539): make Preview Destroy idempotent when the Pulumi stack is gone#983
darkbanjo wants to merge 1 commit into
mainfrom
jkab/thu-539-preview-destroy-fails-hard-when-pulumi-stack-is-missing

Conversation

@darkbanjo

@darkbanjo darkbanjo commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Problem

Preview Destroy fails its destroy / destroy step with error: no stack named 'preview-pr-NNN' found whenever the Pulumi stack no longer exists at destroy time — a PR that never deployed, or whose stack was already torn down. The env is actually clean, but every PR close/merge throws a red ✗ in Actions (now constant per the urgency bump), drowning out real CI signal.

Fix

The destroy job uses the pulumi/actions@v7 wrapper (command: destroy), which errors on a missing stack. Added a guard step before it that checks existence via pulumi stack select (exit code) and gates the destroy:

- name: Check whether the stack still exists
  id: stack_check
  run: |
    curl -fsSL https://get.pulumi.com | sh
    export PATH="$HOME/.pulumi/bin:$PATH"
    if pulumi stack select "${{ inputs.stack_name }}" --non-interactive 2>/dev/null; then
      echo "exists=true"  >> "$GITHUB_OUTPUT"
    else
      echo "Stack not found — nothing to destroy."
      echo "exists=false" >> "$GITHUB_OUTPUT"
    fi

- name: Destroy
  if: steps.stack_check.outputs.exists == 'true'
  uses: pulumi/actions@…
  with: { command: destroy, … }

Auth is inherited from the workflow-level PULUMI_ACCESS_TOKEN env. When the stack is gone, the destroy is skipped and the job goes green.

Notes

  • I gate rather than blanket continue-on-error, so a real destroy failure still fails loudly.
  • Adapted from the ticket's shell sketch — the destroy path uses the pulumi/actions wrapper, not a raw pulumi destroy, so the check is a separate gating step.
  • Out of scope (per ticket): why pr-886's stack vanished before destroy.

Testing

YAML validated. Behavioral verification happens on the next PR close (stack-present → destroys as before; stack-absent → skips, green).

🤖 Generated with Claude Code


Note

Low Risk
CI workflow-only change; destroy still runs and fails loudly when the stack exists and teardown fails.

Overview
Makes Preview Destroy in stack-deploy.yml succeed when the Pulumi stack is already gone, instead of failing with no stack named ... found on every PR close (THU-539).

A new Check whether the stack still exists step installs the Pulumi CLI, runs pulumi stack select for the target stack, and sets exists=true|false. The Destroy pulumi/actions step runs only when exists == 'true', so missing stacks are skipped with a green job; actual destroy errors still fail the workflow.

Reviewed by Cursor Bugbot for commit 5edef0e. Bugbot is set up for automated code reviews on this repo. Configure here.

Preview teardown ran `pulumi destroy` unconditionally, so any PR whose
stack was already torn down (manual destroy, a prior failed run, or a PR
that never deployed) failed with "no stack named ... found" and surfaced
a red x on close. Guard destroy behind a stack-existence check so a clean
environment is treated as success.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment on lines +253 to +263
run: |
curl -fsSL https://get.pulumi.com | sh
export PATH="$HOME/.pulumi/bin:$PATH"
if pulumi stack select "${{ inputs.stack_name }}" --non-interactive 2>/dev/null; then
echo "Stack ${{ inputs.stack_name }} exists — proceeding to destroy."
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "Stack ${{ inputs.stack_name }} not found — nothing to destroy."
echo "exists=false" >> "$GITHUB_OUTPUT"
fi

@github-actions

Copy link
Copy Markdown

Semgrep Security Scan

Found 1 issue(s).

# Severity Rule File Line
1 ERROR run-shell-injection .github/workflows/stack-deploy.yml L253
Finding details

run-shell-injection — .github/workflows/stack-deploy.yml:253

Severity: ERROR
Message: Using variable interpolation ${{...}} with github context data in a run: step could allow an attacker to inject their own code into the runner. This would allow them to steal secrets and code. github context data can have arbitrary user input and should be treated as untrusted. Instead, use an intermediate environment variable with env: to store the data and use the environment variable in the run: script. Be sure to use double-quotes the environment variable, like this: "$ENVVAR".

requires login

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 5edef0e. Configure here.

echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "Stack ${{ inputs.stack_name }} not found — nothing to destroy."
echo "exists=false" >> "$GITHUB_OUTPUT"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stack check masks real failures

High Severity

The stack existence step treats any non-zero pulumi stack select exit as “stack missing” because stderr is discarded and only the shell if exit status is used. Auth, network, or permission failures are misread as exists=false, so Destroy is skipped and the job succeeds even when the stack still exists and should be torn down.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5edef0e. Configure here.

@github-actions

Copy link
Copy Markdown

PR Metrics

Metric Value
Lines changed (prod code) +19 / -0
JS bundle size (gzipped) 🟢 737.6 KB → 737.4 KB (-199 B, -0.0%)
Test coverage 🟢 78.13% → 78.13% (+0.0%)
Performance (preview) Preview not ready — Render deploy may have timed out
Accessibility
Best Practices
SEO

Updated Fri, 12 Jun 2026 19:38:19 GMT · run #1882

@github-actions

Copy link
Copy Markdown

Preview environment deployed 🚀

Service URL
Marketing / blog / docs https://thunderbolt-pr-983.preview.thunderbolt.io
App https://app-pr-983.preview.thunderbolt.io
API https://api-pr-983.preview.thunderbolt.io
Keycloak https://auth-pr-983.preview.thunderbolt.io
PowerSync https://powersync-pr-983.preview.thunderbolt.io

Stack: preview-pr-983 · Commit: 5edef0e308ce234da69e4bedfaf05149cabcbca6

Auto-destroys on PR close/merge. Login via the bundled Keycloak realm — demo@thunderbolt.io / demo by default.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants