ops: pin, badge, de-index, measure and expire preview deployments - #111
Merged
joelpeace48-cell merged 2 commits intoAug 30, 2026
Merged
Conversation
app/layout.tsx renders two <main> elements and {children} twice. Every
page therefore renders its entire content twice, and the skip link — whose
target is the second <main> — lands below the duplicate rather than at the
start of the content.
It came from a merge that resolved a conflict in the layout body by
keeping both sides: the analytics branch's <main> and the accessibility
branch's <main id="main" tabIndex={-1}>. Keeping the latter preserves the
skip-link target, and the footer and analytics components move back below
it where they belong.
This is the second time the same block has been broken this way, and
nothing caught it: typecheck and lint are both happy with two <main>
elements, and duplicate landmarks are a best-practice axe rule rather than
a WCAG A/AA one, so the accessibility suite does not fail either.
tests/unit/layout-landmarks.test.ts closes that gap by asserting the
element count and the skip-link wiring at the source. Confirmed it fails
against the current layout rather than passing vacuously.
Closes FinesseStudioLab#78 FinesseStudioLab#101 delivered the first of the five acceptance criteria on this issue — a preview per pull request with the URL commented. The remaining four were never built. This adds them. Previews point at testnet and staging only. The workflow builds with NEXT_PUBLIC_DEPLOY_ENV=preview, NEXT_PUBLIC_STELLAR_NETWORK=testnet and a staging backend. Because a workflow that forgets one of those would deploy a preview wired to production, lib/deploy-env.ts re-checks them and throws during the build instead. Verified: a preview build with the network set to "public" fails with "Preview builds must use testnet". Previews are visibly badged. components/preview-badge.tsx renders a bar naming the environment and network, and nothing at all outside a preview. Preview domains are not indexed. app/robots.ts serves Disallow: / on a preview and the normal policy elsewhere. robots.txt is advisory and does not stop a page being indexed when something links to it, so next.config.ts also sends X-Robots-Tag: noindex, nofollow on previews. Production keeps Allow: / and sends no such header. Lighthouse scores are posted. A job runs it against the deployed preview and comments performance, accessibility, best-practices and SEO for / and /docs, updating one comment in place rather than appending per push. It runs through npx rather than a third-party action, since that job holds pull-requests: write. Previews expire. preview-cleanup.yml removes a pull request's deployments on close or merge, guarded on the token like the deploy workflow. The README's secrets table gains STAGING_BACKEND_URL and describes the new behaviour. Nine unit tests cover the environment resolution and the production-target guard, including that a correctly configured preview and any production build both pass untouched.
Contributor
|
@defimomof2 nice job |
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
Completes the four acceptance criteria on #78 that were not covered by the initial preview workflow: previews are pinned to testnet and staging, visibly badged, kept out of search indexes, measured with Lighthouse, and cleaned up when a pull request closes.
Closes #78
Why
#101 delivered the first item on the issue's list — a preview per pull request with the URL commented back. The other four were never built:
Previews point at testnet and staging only
The workflow now builds with
NEXT_PUBLIC_DEPLOY_ENV=preview,NEXT_PUBLIC_STELLAR_NETWORK=testnetand a staging backend from a newSTAGING_BACKEND_URLsecret.Setting those in the workflow is not sufficient on its own: a workflow that is edited later, or a preview built through some other path, would silently deploy something wired to production. So
lib/deploy-env.tsre-checks at build time and throws. Verified both directions:It also falls back to Vercel's own
NEXT_PUBLIC_VERCEL_ENVwhen the explicit variable is missing, because the cost of failing to recognise a preview is an indexed preview.Badged, and not indexed
components/preview-badge.tsxrenders a bar naming the environment and network, and returnsnullanywhere else, so a reviewer or a stakeholder handed a link cannot mistake it for the live site.app/robots.tsservesDisallow: /on a preview. That alone is not enough — robots.txt is advisory and does not stop a page being indexed when something links to it — sonext.config.tsalso sendsX-Robots-Tag: noindex, nofollowon previews only.Verified against real builds, both directions:
robots.txtDisallow: /Allow: /X-Robots-Tagnoindex, nofollowWhile checking this I noticed production
robots.txtwas advertising/sitemap.xml, which does not exist — the app has no sitemap route. Pointing crawlers at a 404 is worse than omitting the line, so it is dropped. Adding a real sitemap would mean depending onlib/site-map.ts, which belongs to a different change.Lighthouse
A job runs Lighthouse against the deployed preview and comments performance, accessibility, best-practices and SEO for
/and/docs, updating a single marked comment rather than appending one per push.It runs through
npx @lhci/clirather than a third-party action deliberately: that job holdspull-requests: write, and the fewer external actions running with that token, the smaller the surface.Cleanup
.github/workflows/preview-cleanup.ymlruns onpull_request: closedand removes the deployments belonging to that branch, so previews stop accumulating. Guarded onVERCEL_TOKENlike the deploy workflow, so it reports a skip notice instead of failing when credentials are absent.How it was verified
The nine new unit tests cover both directions of the guard, including that a correctly configured preview and any production build pass through untouched — a guard that rejects everything would also have made the first check above pass.
One pre-existing failure is unrelated to this branch:
landing page is navigable by keyboard alonefails locally under mobile-safari on an unmodifiedmainas well, and passes in CI. It looks like a local WebKit version difference, so I have left it rather than change a test that is green in CI.Risk
The workflow changes are inert until a maintainer adds
STAGING_BACKEND_URLalongside the existing three secrets — documented in the README, including the fork behaviour. Until then previews skip with a notice, exactly as before.The one thing worth a maintainer's eye is the production-host list in
lib/deploy-env.ts. It currently matches onmainnet,horizon.stellar.organdapi.modeltrace; if your real production origins differ, that list should be corrected, or the guard will not catch a preview pointed at them.