Bean & Bark: every PR gets a URL

View .md

Tunde's first storefront PR is up: the checkout page now shows the wholesale discount as its own line, so buyers can see what Friday's fix changed. Nadia wants to see it — not a diff, not a promise, the actual checkout page — and she's at the roastery with a phone. What a reviewer needs is a URL. What nobody needs is another permanent environment.

You'll learn
  • Enable previews once in the manifest — and read what each setting means
  • Publish a PR's front end beneath the dev site's distribution: kanject aws preview publish <name>
  • Run the lifecycle: preview ls, url, extend --by, rm / rm --expired
  • Say where a backend preview comes from (your CI, not the CLI) and what keeps real stages out of preview cleanup

The storefront, as a service

Tunde's storefront repository is a Kanject service too, beanandbark-storefront, with two entries: api, the Storefront API that took the Pricing SDK candidate on Monday (a .NET Lambda that prices carts), and web, the checkout front end — a Vite app deployed as a static site behind CloudFront:

json
{  "id": "web",  "projectPath": "web",  "deployTarget": "static-site",  "renderMode": "spa",  "build": { "kind": "static", "mode": "vite", "command": "npm run build" },  "coupledTo": "api"}

coupledTo pairs the two: they deploy and roll back together, and the site's /api/* requests reach the API through the same CloudFront distribution. kanject aws deploy --env dev has been shipping both since Tunde started.

Enable it once

Previews are off by default. Turning them on is a manifest block, committed like everything else:

json
"preview": {  "enabled": true,  "basedOn": "dev",  "ttlDays": 7,  "stackName": "preview-{name}-{service}"}

Read it as policy, not just config. basedOn: "dev" names the deployed stage whose static site hosts the previews — its bucket and CloudFront distribution — and whose region, profile, and Parameter Store path they inherit. ttlDays: 7 gives every preview an expiry date. stackName is where publish looks for a PR's backend, if one exists (more on that below).

Publish the PR

bash
# Publish the PR's checkout page beneath the dev site's distributionkanject aws preview publish pr-12
text
  ⚠  Backend preview stack 'preview-pr-12-beanandbark-storefront' isn't up —publishing the front-end without previewApiUrl.  ●  Building 'web' for /previews/pr-12/ …  ✓  Preview pr-12 live at https://d1q2w3e4r5t6y7.cloudfront.net/previews/pr-12/

The CLI reads the dev site's stack outputs, builds web (npm ci, then the Vite build) with a base path inside /previews/pr-12/, uploads it, updates the routing, records the expiry in the dev stage's ledger, and invalidates that path on CloudFront. Dev's own assets don't move. It needs the dev site already deployed, and Node and npm on the machine that runs it.

The warning is expected. Tunde's PR only touches the page, so there is no backend preview, and the preview's /api/* requests go to dev's Storefront API — which already runs the released Pricing SDK. Nadia opens the link on her phone and sees the new discount line over real dev prices.

The lifecycle

bash
kanject aws preview ls                # what's alive, and when it expireskanject aws preview url pr-12         # print the live URL againkanject aws preview extend pr-12 --by 7dkanject aws preview rm pr-12          # done reviewing? take it downkanject aws preview rm --expired      # or let CI sweep the stragglers
text
── Previews  beanandbark-storefront ────────────────────────────────────────────╭───────┬────────┬────────┬───────────────────────────────╮│ Name  │ Handle │ Status │ Expires                       │├───────┼────────┼────────┼───────────────────────────────┤│ pr-12 │ pr-12  │ active │ 2026-07-23 14:10 UTC  (in 6d) │╰───────┴────────┴────────┴───────────────────────────────╯

Every lifecycle command reads live state from AWS: the preview's record in the dev stage's artifact bucket, and the dev site's stack outputs for its CloudFront domain. preview url prints the address again only while the preview is active, so a CI job that comments the link on the pull request needs AWS credentials with read access.

Nobody remembers to clean up — so nobody has to

The PR merges Friday morning; the preview is now garbage. Maybe you rm it in the merge ritual — but the honest answer is that reviewers forget, which is why every preview carries its TTL. A scheduled preview rm --expired in CI sweeps everything past its date: the "three temporary stages from last year" problem, structurally deleted. Need more time on a genuinely long review? That's what extend --by 7d is for — a deliberate decision to keep one preview alive for seven more days from now, instead of immortality by default.

Recap
  • Previews are enabled by a committed manifest block: basedOn picks the deployed stage whose static site hosts them, and ttlDays gives every preview an expiry.
  • preview publish <name> builds the static-site entry under /previews/<name>/ on that stage's CloudFront distribution; dev's assets never move.
  • No command creates a Lambda or ECS preview: a PR's backend preview is a stack your CI deploys under stackName, and publish wires the page to it when it exists.
  • url, extend --by and rm read live state; tags and ledger records keep real stages outside the lifecycle, and preview rm --expired sweeps whatever outlived its TTL.
Before the merge — check yourself2
A CI job runs kanject aws preview url pr-12 to comment the link on the PR. What AWS permissions does that step need?
Someone runs kanject aws preview extend pr-12 --by 7d. What changes?
Try it yourself 2
1 Wire the weekly sweep
Write the one command a scheduled CI job should run so forgotten previews never outlive their TTL — and say what keeps that job from ever deleting dev.
One flag on preview rm; the safety boundary is the preview inventory, not a name pattern.
Show solution
A scheduled job running kanject aws preview rm --expired removes every preview past its recorded expiry. It can't touch dev: static-site inventory is the preview records in the ledger, and stack inventory requires the kanject:preview tag.
bash
kanject aws preview rm --expired
2 Predict the static preview path
Tunde opens PR 31. Which command publishes it, where does it live beneath the shared distribution, and when is it eligible for cleanup?
The preview name is both the publish argument and the path segment; ttlDays controls eligibility.
Show solution
kanject aws preview publish pr-31 uploads beneath /previews/pr-31/. Its recorded expiry is seven days after publishing, after which the next preview rm --expired removes it.
Was this page helpful?