Language: English
I Built a Per-PR Environment for Trying Out the Real App
Preview environments that let reviewers exercise the real application on every pull request, built on a trusted workflow and Cloud Run tagged revisions, with a Lite/Full split depending on whether the change touches the database schema.
When you wanted to actually run a PR’s code to verify it, the options were just two: checking out locally, or merging into the dev environment. The former demands setup from whoever wants to verify. The latter contradicts the whole point of verifying before merging.
So I put in place a mechanism that builds an environment you can interact with per PR and posts the URL back to the PR.
How Preview URLs Get Issued
PR (/qa-deploy コメント or 差分による自動起動)
→ trusted workflow (default branch の tooling + PR HEAD の source)
→ build / push (PR source からイメージを作成)
→ Cloud Run へ tagged revision として --no-traffic でデプロイ
→ 共有ロードバランサの wildcard URL に接続
→ PR に Preview URL をコメント
There are two entry points.
A /qa-deploy comment on the PR, or an automatic trigger based on diffs to the application.
The Trusted Workflow Entry Point
The thing to watch out for with preview environments is that you’re executing PR code. You can’t just wave through PRs from forks or launches by actors without write permission.
At the entrance, we check that the PR comes from the same repo or that the actor holds write permission.
Beyond that, the workflow and deploy scripts are taken from the default branch. Only the application source comes from the PR HEAD. This closes off the path where a PR rewrites the workflow and abuses cloud permissions.
Lite and Full
At first, every PR got the same setup. Creating a branch-dedicated DB even for changes that don’t touch the schema is heavy. So I split things into Lite and Full.
| Lite | Full | |
|---|---|---|
| Scope | app-only changes | changes including schema / migrations |
| Cloud Run | tagged revision | tagged revision |
| DB / Secret | shared with dev | branch-dedicated |
| Migration | none | run via Cloud Run Job |
| Trigger | automatic gate for same-repo PRs | /qa-deploy |
Lite only needs a tagged revision added to the existing dev service. Only Full creates a branch-dedicated DB inside the shared Cloud SQL instance, and provisions a Secret and migrations as well.
Tagged revisions are deployed with --no-traffic, so normal dev traffic is unaffected.
They are reachable only via the tagged URLs.
Tag and Revision Cleanup
Environments you create will pile up unless they disappear.
Closing the PR or running /qa-destroy cleans up the tag, revision, DB, and Secret.
This cleanup misbehaved at first.
The destroy workflow lacked pull-requests: write, so processing on PR close kept failing.
There were also cases where digest references left behind prevented deletion; I fixed that by excluding referenced images and turning the scheduled cleaning into a serialized sweep.
Creating environments is easy to verify because you can see the result, but deletions are hard to notice failing since nobody suffers when they do. Resources keep lingering until they turn into cost.
Verifying Behavior Before Merge
We can now touch the actual screens and verify things before merging. Part of reading code in review and imagining its behavior has been replaced with actually interacting with it. For UI changes especially, it’s faster than reading diffs.
This mechanism wasn’t a slimmed-down version of anything pre-existing; it was newly introduced this time. Combining Cloud Run tagged revisions with wildcard URLs on a shared load balancer lets you create per-PR URLs without adding services.
Based on what I presented at our achievements presentation on July 31, 2026.