Skip to content
Development

Testing a deploy gate accidentally deployed the thing I was testing

By Victor Da Luz
astrocloudflarecidev-logsite

The imperfectsystems.com deploy pipeline had exactly one gate: “did it compile.” That was true back when GitHub Actions ran it, and it stayed true after I moved the deploy to Cloudflare Workers Builds a couple weeks ago. Nothing checked types, nothing checked formatting, and the dependabot auto-merge that used to ride on that same weak signal had just been switched off along with the rest of Actions. Time to actually add gates that mean something: astro check and Prettier, run before every deploy.

The setup was the easy part

Copy vdaluz.com’s .prettierrc verbatim, add @astrojs/check and prettier-plugin-astro as devDependencies, wire up check / format / format:check scripts. No ESLint this time; the repo’s small enough that it wasn’t worth the setup cost, same call the spike that scoped this issue already made.

Then I actually ran astro check instead of assuming it would pass, because “very likely passes today” in an issue description is a guess, not a result.

The gate found something real on its first run

One error: astro.config.mjs:12:5 - Type '{ themes: { light: string; dark: string; }; defaultColor: false; }' is not assignable to type 'Partial<ShikiConfig>'. Coming from the shared shikiConfig object both this site and vdaluz.com import from @vdaluz/astro-blog.

My first guess was version drift. This repo was still pinned to an older Astro; vdaluz.com runs a newer one. Bumped the version, reinstalled, re-ran the check. Same error, word for word. Wrong theory.

So I went and actually looked at how vdaluz.com’s own astro.config.mjs handles the same import, instead of continuing to guess. It already had the fix, with a comment explaining it:

// The package types `themes` values as plain strings, so cast to satisfy astro check.
shikiConfig: /** @type {Partial<import('astro').ShikiConfig>} */ (shikiConfig),

The shared package types themes.light/themes.dark as plain string, not the literal theme-name union Astro’s own type expects. Not a version problem at all, just a type that was never quite right, silently accepted because nothing here had ever run astro check before. Copied the same cast. Error gone. I kept the Astro version bump anyway since it reduces drift between the two sites and nothing broke, but it never was the fix.

Verifying the gate meant testing it against something, which is where this got interesting

I didn’t want to just configure the new build command in Cloudflare’s dashboard and assume it worked. I wanted to see it actually run. The Workers Builds API has an endpoint to manually fire a build against any branch you name, so I pointed one at my feature branch to check the new gated command before merging anything to main.

It came back green. Good. Except the log had one more line than I expected:

Executing user deploy command:  npx wrangler deploy --config wrangler.toml dist/server/entry.mjs
✨ Success! Uploaded 7 files (23 already uploaded) (0.91 sec)
Success: Deploy command completed

The trigger’s deploy step doesn’t check which branch you told it to build. It just runs, unconditionally, after any successful build. Pointing the production trigger at a feature branch to “test the build command” also deployed that feature branch straight to production, ahead of any merge, ahead of any review. wrangler deployments list confirmed it: a fresh deployment, timestamped to my manual API call, not a git push.

The actual damage here was close to zero. The branch only touched tooling and formatting, nothing a visitor would ever notice, and I’d already diff-reviewed every formatting change before that point. But the mechanism is the part worth remembering: a “let me just test this” action reached production with no confirmation step in between, because I assumed a manually-triggered build was safer than it was. It wasn’t a dry run. There’s no dry run.

Once I knew, the fix was just to catch main up to what was already live: merge the branch, verify the real push-triggered build (not a manual one) also went green, done. Which it did - commit hash and all, tied to an actual git push, gates passing for real this time.

What I’d do differently

Test build-command changes by merging first, not by pointing the production deploy trigger at unmerged code and hoping the “branch” parameter means what it sounds like it means. If an API lets you specify a target, that’s not the same as it respecting a boundary. Ask what happens on success before you find out by watching it happen.

Related reading