From c992acd30896bd31d6253c094210e7ab8c4762cc Mon Sep 17 00:00:00 2001 From: Will Washburn Date: Fri, 22 May 2026 14:49:36 -0400 Subject: [PATCH] fix(web): stabilize preview deployments --- .github/workflows/preview-web.yml | 26 ++++++++++++++++++++++++-- CHANGELOG.md | 4 ++++ web/sst.config.ts | 15 ++++++++++++--- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/.github/workflows/preview-web.yml b/.github/workflows/preview-web.yml index c46e17b73..e70157126 100644 --- a/.github/workflows/preview-web.yml +++ b/.github/workflows/preview-web.yml @@ -54,20 +54,42 @@ jobs: run: bash .github/scripts/assert-aws-account.sh "$AWS_ACCOUNT_ID" "$SST_STAGE" deploy - name: Deploy SST preview + id: deploy working-directory: web env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_DEFAULT_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }} NEXT_PUBLIC_POSTHOG_KEY: ${{ vars.NEXT_PUBLIC_POSTHOG_KEY }} - run: ../node_modules/.bin/sst deploy --stage "$SST_STAGE" + run: | + set -euo pipefail + + deploy_log="$(mktemp)" + ../node_modules/.bin/sst deploy --stage "$SST_STAGE" | tee "$deploy_log" + + preview_url="$(awk '/^[[:space:]]+url: https:\/\// { print $2 }' "$deploy_log" | tail -n 1)" + if [ -z "$preview_url" ]; then + preview_url="$(awk '/^[[:space:]]+Web: https:\/\// { print $2 }' "$deploy_log" | tail -n 1)" + fi + + if [ -z "$preview_url" ]; then + echo "Unable to determine preview URL from SST output" >&2 + exit 1 + fi + + echo "preview_url=$preview_url" >> "$GITHUB_OUTPUT" - name: Comment on PR continue-on-error: true uses: actions/github-script@v7 + env: + PREVIEW_URL: ${{ steps.deploy.outputs.preview_url }} with: script: | const prNum = context.payload.pull_request.number; - const url = `https://pr-${prNum}.agentrelay.net`; + const url = process.env.PREVIEW_URL; + if (!url) { + throw new Error('Missing preview URL from deploy step'); + } const body = `**Preview deployed!**\n\n` + `| Environment | URL |\n` + `|-------------|-----|\n` + diff --git a/CHANGELOG.md b/CHANGELOG.md index 6549ae8d5..0f418692e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Release workflow changelog generation now writes concise Keep a Changelog sections and skips web-only, release-only, trajectory, PR-review, placeholder, and withdrawn-tag entries. +### Fixed + +- `web`: PR preview SST deploys use and comment the generated CloudFront URL and AWS's managed disabled cache policy instead of creating per-preview Cloudflare DNS records, ACM certificates, and custom CloudFront cache policies. + ## [7.0.1] - 2026-05-22 ### Added diff --git a/web/sst.config.ts b/web/sst.config.ts index 3a936a4fd..49f8f4771 100644 --- a/web/sst.config.ts +++ b/web/sst.config.ts @@ -1,3 +1,5 @@ +const AWS_MANAGED_CACHING_DISABLED_CACHE_POLICY_ID = '4135ea2d-6df8-44a3-9df3-4b5a84be39ad'; + export default $config({ app(input) { return { @@ -6,13 +8,14 @@ export default $config({ removal: input?.stage === 'production' ? 'retain' : 'remove', }; }, - run() { + async run() { const isProd = $app.stage === 'production'; + const isPreview = $app.stage.startsWith('pr-'); const domain = isProd ? 'origin.agentrelay.net' : `${$app.stage}.agentrelay.net`; const NEXT_PUBLIC_POSTHOG_HOST = process.env.NEXT_PUBLIC_POSTHOG_HOST ?? 'https://i.agentrelay.com'; const NEXT_PUBLIC_POSTHOG_KEY = process.env.NEXT_PUBLIC_POSTHOG_KEY ?? ''; - new sst.aws.Nextjs('Web', { + const web = new sst.aws.Nextjs('Web', { path: '.', openNextVersion: '3.9.16', environment: { @@ -20,7 +23,13 @@ export default $config({ NEXT_PUBLIC_POSTHOG_KEY, }, // Production deploys land on origin.agentrelay.net; SEO canonicals are set in Next metadata. - domain: { name: domain, dns: sst.cloudflare.dns({ proxy: true }) }, + ...(isPreview ? {} : { domain: { name: domain, dns: sst.cloudflare.dns({ proxy: true }) } }), + // PR previews use CloudFront's generated URL and should not allocate one custom cache policy per stage. + ...(isPreview ? { cachePolicy: AWS_MANAGED_CACHING_DISABLED_CACHE_POLICY_ID } : {}), }); + + return { + url: web.url, + }; }, });