Post deploy smoke
Skill jcdavis131/cursor-agent-skills/skills/post-deploy-smoke
After a deploy, confirm the live URL actually serves the expected content (not just that the API returned 200) by polling it with a bounded retry loop that greps for an expected string per URL. Use after any deploy to verify propagation, after a rebrand/rename to confirm the new content is live, or before telling the user a deploy is "done".From its SKILL.md
npx -y skills add jcdavis131/cursor-agent-skills --skill post-deploy-smokeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
4.5 KB, ~1.1k tokens by cl100k_base, as published. Nobody here has run it
Post-Deploy Smoke
A deploy isn't "done" when the build succeeds or the API returns 200. Vercel/edge deploys propagate with a delay; a 200 can come from a cached old version. Done is when the live URL serves the expected content. Confirm it with a bounded retry loop that greps for an expected string.
The shape
export TERM=dumb
for i in 1 2 3 4 5 6 7 8; do
ok=1
for u in "https://hq.example.com|Headquarters" "https://www.storefront.example.com|Storefront"; do
url="${u%%|*}"; want="${u##*|}"
hit=$(curl -sL --max-time 20 "$url" | grep -c "$want")
[ "$hit" -ge 1 ] || ok=0
done
[ "$ok" = 1 ] && break
sleep 5
done
Why each piece earns its place:
export TERM=dumb— stop terminal control sequences from polluting the scripted output.- Bounded retry (
for i in 1..8) — a deploy that never propagates shouldn't loop forever. 8 × (curl + sleep) is a reasonable propagation window. url|wantpairs — each URL has an expected string ("Headquarters" on hq.example.com, "Storefront" on storefront.example.com). The expected string is content only the new deploy would serve, so a match confirms propagation, not a cached 200.curl -sL --max-time 20— silent, follow redirects, bound the request so a hung edge doesn't hang the loop.grep -c "$want"— count matches;[ "$hit" -ge 1 ]confirms the expected content is present.- All URLs must match (
okflag) — partial propagation (one URL live, one stale) is not done; require all. breakon success — stop as soon as all match; don't run all 8 iterations when it propagated on iteration 2.
Choosing the expected string
Pick a string that:
- Is only in the new deploy — so a match rules out the cached old version.
- Is stable — not a timestamp, not a random id.
- Is content, not boilerplate — a page title, a hero headline, a nav label. "Headquarters" / "Storefront" are good;
<html>is bad (every deploy has it).
For a rebrand, the expected string is the new brand name or label — confirming the rename propagated to the live site.
When to run
- After every deploy you'll tell the user is "done".
- After a rebrand/rename — confirm the new content is live.
- Before a review handoff — so the URLs you hand over actually serve the new content.
When NOT to run
- A deploy to a preview URL with no propagation delay — a single curl is enough.
- A backend API deploy with no user-facing content — check the health endpoint instead.
The backend variant: poll a health endpoint
For a backend deploy (API, worker, service), the "expected content" is the health endpoint appearing. Instead of grepping for a string, poll /readyz (or /health, /healthz) until it returns 200 — signaling the new image is serving:
Watching for the new image to go live (/readyz appearing = current build)
- The health endpoint returning 200 confirms the new image is up and ready to serve, not just that the deploy API accepted the push.
- A deploy that's "ready" per the platform but whose
/readyznever appears is a stuck deploy — escalate todiagnose-before-retry. - This is the event-driven form of the smoke loop: arm a monitor on
/readyzappearing, with a timeout, rather than polling on a fixed cadence (seeevent-driven-wait).
Anti-patterns
- "Deploy returned 200, we're done." A cached 200 from the old deploy isn't propagation.
- Unbounded retry. A deploy that never propagates loops forever.
- Expected string that's in every deploy.
<html>matches the old version too. - One URL checked, declared done. Partial propagation is not done.
- No
--max-timeon curl. A hung edge hangs the whole loop.
Pair with
use-available-integrations— the smoke confirms the integration's deploy actually served.event-driven-wait— a smoke loop is a bounded event-driven wait on "expected content appeared".diagnose-before-retry— if the smoke fails after 8 iterations, diagnose (check the deploy logs, the build output) before re-deploying.close-the-loop— the smoke is the proof that "deployed" = "live and serving expected content" before the closure.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.