Unified · Split
| @@ -625,6 +625,9 @@ jobs: · 624 unmodified lines | ||
| 625 | 625 | router_url="${T3CODE_WEB_ROUTER_URL:-https://app.t3.codes}" |
| 626 | 626 | latest_domain="${T3CODE_WEB_LATEST_DOMAIN:-latest.app.t3.codes}" |
| 627 | 627 | nightly_domain="${T3CODE_WEB_NIGHTLY_DOMAIN:-nightly.app.t3.codes}" |
| 628 | + router_domain="${router_url#http://}" | |
| 629 | + router_domain="${router_domain#https://}" | |
| 630 | + router_domain="${router_domain%%/*}" | |
| 628 | 631 | |
| 629 | 632 | if [[ "${{ needs.preflight.outputs.release_channel }}" == "stable" ]]; then |
| 630 | 633 | channel_domain="$latest_domain" |
| @@ -655,6 +658,13 @@ jobs: · 24 unmodified lines | ||
| 655 | 658 | --token "$VERCEL_TOKEN" \ |
| 656 | 659 | "${vercel_scope_args[@]}" |
| 657 | 660 | |
| 661 | + if [[ "$channel_name" == "latest" && -n "$router_domain" && "$router_domain" != "$channel_domain" ]]; then | |
| 662 | + echo "Aliasing $deployment_url to router domain $router_domain." | |
| 663 | + bunx vercel@53.1.1 alias set "$deployment_url" "$router_domain" \ | |
| 664 | + --token "$VERCEL_TOKEN" \ | |
| 665 | + "${vercel_scope_args[@]}" | |
| 666 | + fi | |
| 667 | + | |
| 658 | 668 | finalize: |
| 659 | 669 | name: Finalize release |
| 660 | 670 | if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.release.result == 'success' && needs.preflight.outputs.release_channel == 'stable' }} |
| @@ -42,6 +42,8 @@ describe("branding", () => { · 41 unmodified lines | ||
| 42 | 42 | |
| 43 | 43 | expect(branding.HOSTED_APP_CHANNEL).toBe("nightly"); |
| 44 | 44 | expect(branding.HOSTED_APP_CHANNEL_LABEL).toBe("Nightly"); |
| 45 | + expect(branding.APP_STAGE_LABEL).toBe("Nightly"); | |
| 46 | + expect(branding.APP_DISPLAY_NAME).toBe("T3 Code (Nightly)"); | |
| 45 | 47 | }); |
| 46 | 48 | |
| 47 | 49 | it("ignores unknown hosted app channels", async () => { |
| @@ -11,13 +11,15 @@ function readInjectedDesktopAppBranding(): DesktopAppBranding | null { · 10 unmodified lines | ||
| 11 | 11 | const injectedDesktopAppBranding = readInjectedDesktopAppBranding(); |
| 12 | 12 | const hostedAppChannel = import.meta.env.VITE_HOSTED_APP_CHANNEL?.trim().toLowerCase(); |
| 13 | 13 | |
| 14 | +export const HOSTED_APP_CHANNEL = | |
| 15 | + hostedAppChannel === "latest" || hostedAppChannel === "nightly" ? hostedAppChannel : null; | |
| 16 | +export const HOSTED_APP_CHANNEL_LABEL = | |
| 17 | + HOSTED_APP_CHANNEL === "nightly" ? "Nightly" : HOSTED_APP_CHANNEL === "latest" ? "Latest" : null; | |
| 14 | 18 | export const APP_BASE_NAME = injectedDesktopAppBranding?.baseName ?? "T3 Code"; |
| 15 | 19 | export const APP_STAGE_LABEL = |
| 16 | − injectedDesktopAppBranding?.stageLabel ?? (import.meta.env.DEV ? "Dev" : "Alpha"); | |
| 20 | + injectedDesktopAppBranding?.stageLabel ?? | |
| 21 | + HOSTED_APP_CHANNEL_LABEL ?? | |
| 22 | + (import.meta.env.DEV ? "Dev" : "Alpha"); | |
| 17 | 23 | export const APP_DISPLAY_NAME = |
| 18 | 24 | injectedDesktopAppBranding?.displayName ?? `${APP_BASE_NAME} (${APP_STAGE_LABEL})`; |
| 19 | 25 | export const APP_VERSION = import.meta.env.APP_VERSION || "0.0.0"; |
| 20 | −export const HOSTED_APP_CHANNEL = | |
| 21 | − hostedAppChannel === "latest" || hostedAppChannel === "nightly" ? hostedAppChannel : null; | |
| 22 | −export const HOSTED_APP_CHANNEL_LABEL = | |
| 23 | − HOSTED_APP_CHANNEL === "nightly" ? "Nightly" : HOSTED_APP_CHANNEL === "latest" ? "Latest" : null; | |
| @@ -892,7 +892,7 @@ export function GeneralSettingsPanel() { · 891 unmodified lines | ||
| 892 | 892 | </SettingsSection> |
| 893 | 893 | |
| 894 | 894 | <SettingsSection title="About"> |
| 895 | − {isElectron ? ( | |
| 895 | + {isElectron || HOSTED_APP_CHANNEL ? ( | |
| 896 | 896 | <AboutVersionSection /> |
| 897 | 897 | ) : ( |
| 898 | 898 | <SettingsRow |
| @@ -1,9 +1,16 @@ | ||
| 1 | −import { matchers, routes, type VercelConfig } from "@vercel/config/v1"; | |
| 1 | +import { matchers, routes, type Transform, type VercelConfig } from "@vercel/config/v1"; | |
| 2 | 2 | |
| 3 | 3 | const ROUTER_HOST = "app.t3.codes"; |
| 4 | 4 | const HOSTED_WEB_CHANNEL_COOKIE = "t3code_web_channel"; |
| 5 | 5 | const LATEST_ORIGIN = "https://latest.app.t3.codes"; |
| 6 | 6 | const NIGHTLY_ORIGIN = "https://nightly.app.t3.codes"; |
| 7 | +const CLEAN_CHANNEL_QUERY_TRANSFORMS = [ | |
| 8 | + { | |
| 9 | + type: "request.query", | |
| 10 | + op: "delete", | |
| 11 | + target: { key: "channel" }, | |
| 12 | + }, | |
| 13 | +] satisfies Transform[]; | |
| 7 | 14 | |
| 8 | 15 | function channelCookie(channel: "latest" | "nightly"): string { |
| 9 | 16 | return [ |
| @@ -28,6 +35,7 @@ export const config: VercelConfig = { · 18 unmodified lines | ||
| 28 | 35 | { |
| 29 | 36 | src: "/__t3code/channel", |
| 30 | 37 | has: [matchers.query("channel", "nightly")], |
| 38 | + transforms: CLEAN_CHANNEL_QUERY_TRANSFORMS, | |
| 31 | 39 | headers: { |
| 32 | 40 | Location: "/", |
| 33 | 41 | "Set-Cookie": channelCookie("nightly"), |
| @@ -36,6 +44,7 @@ export const config: VercelConfig = { · 2 unmodified lines | ||
| 36 | 44 | }, |
| 37 | 45 | { |
| 38 | 46 | src: "/__t3code/channel", |
| 47 | + transforms: CLEAN_CHANNEL_QUERY_TRANSFORMS, | |
| 39 | 48 | headers: { |
| 40 | 49 | Location: "/", |
| 41 | 50 | "Set-Cookie": channelCookie("latest"), |
| @@ -51,7 +51,7 @@ Optional GitHub Actions variables: · 50 unmodified lines | ||
| 51 | 51 | |
| 52 | 52 | Required Vercel domains: |
| 53 | 53 | |
| 54 | −- `app.t3.codes`: the stable router domain users open. | |
| 54 | +- `app.t3.codes`: the router domain users open, updated by stable releases. | |
| 55 | 55 | - `latest.app.t3.codes`: channel alias updated by stable releases. |
| 56 | 56 | - `nightly.app.t3.codes`: channel alias updated by nightly releases. |
| 57 | 57 | |
| @@ -62,11 +62,13 @@ visiting `/__t3code/channel?channel=latest` or · 4 unmodified lines | ||
| 62 | 62 | the matching channel alias. |
| 63 | 63 | |
| 64 | 64 | The release deploy job rewrites release package versions before upload so the |
| 65 | −hosted app's About panel renders the release version. It also passes | |
| 66 | −`VITE_HOSTED_APP_CHANNEL=latest|nightly`, which renders the hosted update track | |
| 67 | −selector in the About panel. Changing the selector navigates through | |
| 68 | −`/__t3code/channel` on the router domain so the user's channel cookie is updated | |
| 69 | −before redirecting to the hosted app root. | |
| 65 | +hosted app's About panel renders the release version. Stable deploys alias the | |
| 66 | +same deployment to both the `latest` channel and the router domain so the router | |
| 67 | +rules stay current. Nightly deploys only alias the `nightly` channel. The job | |
| 68 | +also passes `VITE_HOSTED_APP_CHANNEL=latest|nightly`, which renders the hosted | |
| 69 | +update track selector in the About panel. Changing the selector navigates | |
| 70 | +through `/__t3code/channel` on the router domain so the user's channel cookie is | |
| 71 | +updated before redirecting to the hosted app root. | |
| 70 | 72 | |
| 71 | 73 | One-time Vercel dashboard setup: |
| 72 | 74 | |
| @@ -75,9 +77,9 @@ One-time Vercel dashboard setup: · 2 unmodified lines | ||
| 75 | 77 | 3. Disable automatic Git deployments in the dashboard if desired; the committed |
| 76 | 78 | `vercel.ts` setting is the source-of-truth, but disconnecting Git in the |
| 77 | 79 | dashboard is also safe. |
| 78 | −4. Promote or alias one deployment containing the router rules in `apps/web/vercel.ts` to | |
| 79 | − `app.t3.codes` once. Future release jobs should only update the channel | |
| 80 | − aliases. | |
| 80 | +4. Run one stable release deployment, or manually alias the current stable | |
| 81 | + deployment, so `app.t3.codes` points at a deployment containing the router | |
| 82 | + rules in `apps/web/vercel.ts`. Future stable releases keep this alias current. | |
| 81 | 83 | |
| 82 | 84 | ## Nightly builds |
| 83 | 85 | |