History

Alias stable releases to the router domain (#2636)

Julius Marminge committed 6 months ago

Commit e64c19f1 parent 6ab8f936 files · +41 −16Browse files

Unified · Split

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