From da13bb5a941f54069cd64dc1f0c8b6dd51bdd454 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Mon, 11 May 2026 09:47:31 +0200 Subject: [PATCH 1/3] Alias stable releases to the router domain - Keep `app.t3.codes` pointed at the latest stable deployment - Document the router-domain aliasing and release flow --- .github/workflows/release.yml | 10 ++++++++++ docs/release.md | 20 +++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f9a1d8e5181..71527f9ccf3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -625,6 +625,9 @@ jobs: router_url="${T3CODE_WEB_ROUTER_URL:-https://app.t3.codes}" latest_domain="${T3CODE_WEB_LATEST_DOMAIN:-latest.app.t3.codes}" nightly_domain="${T3CODE_WEB_NIGHTLY_DOMAIN:-nightly.app.t3.codes}" + router_domain="${router_url#http://}" + router_domain="${router_domain#https://}" + router_domain="${router_domain%%/*}" if [[ "${{ needs.preflight.outputs.release_channel }}" == "stable" ]]; then channel_domain="$latest_domain" @@ -657,6 +660,13 @@ jobs: --token "$VERCEL_TOKEN" \ "${vercel_scope_args[@]}" + if [[ "$channel_name" == "latest" && -n "$router_domain" && "$router_domain" != "$channel_domain" ]]; then + echo "Aliasing $deployment_url to router domain $router_domain." + bunx vercel@53.1.1 alias set "$deployment_url" "$router_domain" \ + --token "$VERCEL_TOKEN" \ + "${vercel_scope_args[@]}" + fi + finalize: name: Finalize release if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.release.result == 'success' && needs.preflight.outputs.release_channel == 'stable' }} diff --git a/docs/release.md b/docs/release.md index 640fdc333c9..296f89c6845 100644 --- a/docs/release.md +++ b/docs/release.md @@ -51,7 +51,7 @@ Optional GitHub Actions variables: Required Vercel domains: -- `app.t3.codes`: the stable router domain users open. +- `app.t3.codes`: the router domain users open, updated by stable releases. - `latest.app.t3.codes`: channel alias updated by stable releases. - `nightly.app.t3.codes`: channel alias updated by nightly releases. @@ -62,11 +62,13 @@ visiting `/__t3code/channel?channel=latest` or the matching channel alias. The release deploy job rewrites release package versions before upload so the -hosted app's About panel renders the release version. It also passes -`VITE_HOSTED_APP_CHANNEL=latest|nightly`, which renders the hosted update track -selector in the About panel. Changing the selector navigates through -`/__t3code/channel` on the router domain so the user's channel cookie is updated -before redirecting to the hosted app root. +hosted app's About panel renders the release version. Stable deploys alias the +same deployment to both the `latest` channel and the router domain so the router +rules stay current. Nightly deploys only alias the `nightly` channel. The job +also passes `VITE_HOSTED_APP_CHANNEL=latest|nightly`, which renders the hosted +update track selector in the About panel. Changing the selector navigates +through `/__t3code/channel` on the router domain so the user's channel cookie is +updated before redirecting to the hosted app root. One-time Vercel dashboard setup: @@ -75,9 +77,9 @@ One-time Vercel dashboard setup: 3. Disable automatic Git deployments in the dashboard if desired; the committed `vercel.ts` setting is the source-of-truth, but disconnecting Git in the dashboard is also safe. -4. Promote or alias one deployment containing the router rules in `apps/web/vercel.ts` to - `app.t3.codes` once. Future release jobs should only update the channel - aliases. +4. Run one stable release deployment, or manually alias the current stable + deployment, so `app.t3.codes` points at a deployment containing the router + rules in `apps/web/vercel.ts`. Future stable releases keep this alias current. ## Nightly builds From a5b1511c15a40e8e73d7043a055247752f125ebb Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Mon, 11 May 2026 10:18:40 +0200 Subject: [PATCH 2/3] Show about version on hosted web and clean channel URL - Render AboutVersionSection in hosted web builds - Strip the channel query param after setting the hosted channel cookie --- apps/web/src/components/settings/SettingsPanels.tsx | 2 +- apps/web/vercel.ts | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/settings/SettingsPanels.tsx b/apps/web/src/components/settings/SettingsPanels.tsx index 505be5c73f8..e7f21da4809 100644 --- a/apps/web/src/components/settings/SettingsPanels.tsx +++ b/apps/web/src/components/settings/SettingsPanels.tsx @@ -892,7 +892,7 @@ export function GeneralSettingsPanel() { - {isElectron ? ( + {isElectron || HOSTED_APP_CHANNEL ? ( ) : ( Date: Mon, 11 May 2026 10:25:53 +0200 Subject: [PATCH 3/3] Surface hosted app channel in branding - Use hosted app channel labels for stage and display name - Add coverage for nightly branding defaults --- apps/web/src/branding.test.ts | 2 ++ apps/web/src/branding.ts | 12 +++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/web/src/branding.test.ts b/apps/web/src/branding.test.ts index 764b63cdb4b..096fc16ecc0 100644 --- a/apps/web/src/branding.test.ts +++ b/apps/web/src/branding.test.ts @@ -42,6 +42,8 @@ describe("branding", () => { expect(branding.HOSTED_APP_CHANNEL).toBe("nightly"); expect(branding.HOSTED_APP_CHANNEL_LABEL).toBe("Nightly"); + expect(branding.APP_STAGE_LABEL).toBe("Nightly"); + expect(branding.APP_DISPLAY_NAME).toBe("T3 Code (Nightly)"); }); it("ignores unknown hosted app channels", async () => { diff --git a/apps/web/src/branding.ts b/apps/web/src/branding.ts index d8018f0d924..5c1309ca06b 100644 --- a/apps/web/src/branding.ts +++ b/apps/web/src/branding.ts @@ -11,13 +11,15 @@ function readInjectedDesktopAppBranding(): DesktopAppBranding | null { const injectedDesktopAppBranding = readInjectedDesktopAppBranding(); const hostedAppChannel = import.meta.env.VITE_HOSTED_APP_CHANNEL?.trim().toLowerCase(); +export const HOSTED_APP_CHANNEL = + hostedAppChannel === "latest" || hostedAppChannel === "nightly" ? hostedAppChannel : null; +export const HOSTED_APP_CHANNEL_LABEL = + HOSTED_APP_CHANNEL === "nightly" ? "Nightly" : HOSTED_APP_CHANNEL === "latest" ? "Latest" : null; export const APP_BASE_NAME = injectedDesktopAppBranding?.baseName ?? "T3 Code"; export const APP_STAGE_LABEL = - injectedDesktopAppBranding?.stageLabel ?? (import.meta.env.DEV ? "Dev" : "Alpha"); + injectedDesktopAppBranding?.stageLabel ?? + HOSTED_APP_CHANNEL_LABEL ?? + (import.meta.env.DEV ? "Dev" : "Alpha"); export const APP_DISPLAY_NAME = injectedDesktopAppBranding?.displayName ?? `${APP_BASE_NAME} (${APP_STAGE_LABEL})`; export const APP_VERSION = import.meta.env.APP_VERSION || "0.0.0"; -export const HOSTED_APP_CHANNEL = - hostedAppChannel === "latest" || hostedAppChannel === "nightly" ? hostedAppChannel : null; -export const HOSTED_APP_CHANNEL_LABEL = - HOSTED_APP_CHANNEL === "nightly" ? "Nightly" : HOSTED_APP_CHANNEL === "latest" ? "Latest" : null;