Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions .github/workflows/deploy-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,47 @@ jobs:
- name: Build site
run: bun run build

- name: Upload preview version
# Worker Previews (`wrangler preview`, GA 2026-09-22) in place of
# `wrangler versions upload`. Previews give a branch a stable URL that
# every push updates, plus isolated config/state/observability, where
# Version URLs only ever pointed at production resources.
# The CLI still labels the command "private beta"; this account has
# access, so treat that label as stale rather than as a blocker.
# Preview names must be hostname-safe, so slashes in branch names are
# flattened rather than relying on the git-branch default.
- name: Deploy branch preview
id: upload
run: |
set -o pipefail
bunx wrangler versions upload 2>&1 | tee upload_output.txt
PREVIEW_URL=$(grep -o 'https://[a-zA-Z0-9.-]*\.workers\.dev' upload_output.txt | head -1 || echo "")
PREVIEW_NAME=$(printf '%s' "$BRANCH" | tr -c 'a-zA-Z0-9' '-' | sed 's/--*/-/g; s/^-//; s/-$//')
echo "Preview name: $PREVIEW_NAME"
# --json is parsed first; the human output is kept for the log and
# used as a fallback, since the JSON shape is not pinned down yet.
bunx wrangler preview --name "$PREVIEW_NAME" --json > preview_output.json 2> preview_stderr.txt || STATUS=$?
cat preview_stderr.txt
cat preview_output.json
if [ -n "${STATUS:-}" ]; then
echo "::error::wrangler preview exited with status $STATUS"
exit "$STATUS"
fi
PREVIEW_URL=$(jq -r '[.. | strings | select(startswith("https://") and contains("workers.dev"))] | first // empty' preview_output.json 2>/dev/null || true)
if [ -z "$PREVIEW_URL" ]; then
PREVIEW_URL=$(grep -oh 'https://[a-zA-Z0-9.-]*\.workers\.dev' preview_output.json preview_stderr.txt | head -1 || true)
fi
if [ -z "$PREVIEW_URL" ]; then
echo "::error::wrangler preview produced no URL, so there is nothing to preview. See the output above for the shape it actually returned."
exit 1
fi
echo "PREVIEW_URL=$PREVIEW_URL" >> "$GITHUB_OUTPUT"
env:
BRANCH: ${{ github.ref_name }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

# No `if:` guard here on purpose: the upload step now fails outright when
# no URL is found, so an empty PREVIEW_URL can no longer skip this step
# and leave the job green with nothing to preview.
- name: Add preview URL to summary
if: steps.upload.outputs.PREVIEW_URL
run: |
echo "## 🚀 Preview Deployment" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
Expand Down
14 changes: 7 additions & 7 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ async function getServerEntry(): Promise<ServerEntry> {
return serverEntryPromise;
}

// The legacy Jekyll site (docs, blog, and not-yet-ported pages) stays deployed
// on Render. Any GET/HEAD request this app 404s on is retried against that
// origin, so all pre-existing appmap.io URLs keep working unchanged.
// Preview deploys can override the origin (e.g. to a Render branch preview)
// The legacy Jekyll site (docs, blog, and not-yet-ported pages) is deployed on
// Cloudflare Pages. Any GET/HEAD request this app 404s on is retried against
// that origin, so all pre-existing appmap.io URLs keep working unchanged.
// Preview deploys can override the origin (e.g. to a Pages branch preview)
// with a LEGACY_ORIGIN var; production uses the default.
const DEFAULT_LEGACY_ORIGIN = "https://appland-com.onrender.com";
const DEFAULT_LEGACY_ORIGIN = "https://applandinc-github-io.pages.dev";

function legacyOrigin(env: unknown): string {
// The nitro cloudflare entry does not pass `env` through to this handler
Expand Down Expand Up @@ -142,8 +142,8 @@ function shouldTryLegacyFallback(request: Request, response: Response): boolean
async function fetchFromLegacySite(request: Request, origin: string): Promise<Response | undefined> {
const url = new URL(request.url);
try {
// redirect: "manual" passes the legacy site's redirects (e.g. Render's
// .html-stripping 301s) through to the browser on our own domain.
// redirect: "manual" passes the legacy site's redirects (e.g. Pages'
// .html-stripping 308s) through to the browser on our own domain.
const response = await fetch(new Request(origin + url.pathname + url.search, request), {
redirect: "manual",
});
Expand Down
19 changes: 16 additions & 3 deletions wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,25 @@
"compatibility_flags": ["nodejs_compat"],
"main": "src/server.ts",
"routes": [{ "pattern": "appmap.io/*", "zone_name": "appmap.io" }],
// Paired with `wrangler preview` in deploy-preview.yml. Worker Previews
// require this block to exist; it may be empty when the Preview needs no
// settings of its own. If a Preview should ever point at a branch build of
// the legacy site rather than its production deploy, a
// "vars": { "LEGACY_ORIGIN": "..." } here is the hook for it (see
// legacyOrigin() in src/server.ts).
//
// Unlike environments, "previews" is accepted in the redirected config
// nitro writes to .output/server/wrangler.json — verified by a passing
// deploy-preview run, not assumed.
"previews": {},
"vars": {
"SUPABASE_URL": "https://ulrbnkrvxzbtqancxmtn.supabase.co",
// Legacy docs/blog proxy origin: no LEGACY_ORIGIN var here means the
// worker uses its default, the Render build of applandinc.github.io
// master (see DEFAULT_LEGACY_ORIGIN in src/server.ts). PR #1583 merged
// 2026-09-04, so master is the post-cutover old site.
// worker uses its default, the Cloudflare Pages build of
// applandinc.github.io master (see DEFAULT_LEGACY_ORIGIN in
// src/server.ts). PR #1583 merged 2026-09-04, so master is the
// post-cutover old site. Render was retired as the legacy origin
// 2026-10-15.
},
// Preview deploys use wrangler.preview.jsonc (a standalone config), NOT a
// wrangler environment: nitro copies this file into the build output as a
Expand Down
Loading