Skip to content

Commit 7479625

Browse files
chore(relay): deploy with the Alchemy CLI and publish client config through an Action (#12401)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent e3c85ea commit 7479625

10 files changed

Lines changed: 369 additions & 755 deletions

File tree

‎.github/workflows/deploy-relay.yml‎

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,11 @@ jobs:
5454
- --filter=t3code-relay...
5555
5656
- name: Deploy production relay stage
57-
id: deploy
58-
run: vp run --filter t3code-relay deploy --stage prod --yes --github-output
57+
run: vp run --filter t3code-relay deploy --stage prod --yes --no-input
5958
env:
59+
# The PublishClientConfig action writes the client env here instead
60+
# of the repo-root .env; nothing on the runner reads it.
61+
T3CODE_RELAY_CLIENT_CONFIG_ENV: ${{ runner.temp }}/relay-client-config.env
6062
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
6163
PLANETSCALE_API_TOKEN_ID: ${{ secrets.PLANETSCALE_API_TOKEN_ID }}
6264
PLANETSCALE_API_TOKEN: ${{ secrets.PLANETSCALE_API_TOKEN }}
@@ -69,20 +71,12 @@ jobs:
6971
uses: actions/github-script@v8
7072
with:
7173
script: |
72-
const result = "${{ steps.deploy.outputs.result }}";
73-
const changed = "${{ steps.deploy.outputs.changed }}" === "true";
74-
const description = changed
75-
? "Relay production deploy applied infrastructure changes."
76-
: result === "noop"
77-
? "Relay production deploy was a no-op."
78-
: `Relay production deploy completed with result: ${result}.`;
79-
8074
await github.rest.repos.createCommitStatus({
8175
owner: context.repo.owner,
8276
repo: context.repo.repo,
8377
sha: context.sha,
8478
state: "success",
8579
context: "Relay deploy / production",
86-
description,
80+
description: "Relay production deploy completed.",
8781
target_url: `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`,
8882
});

‎.github/workflows/release.yml‎

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -311,15 +311,28 @@ jobs:
311311
args:
312312
- --filter=t3code-relay...
313313
314-
- id: relay_state
315-
name: Read production relay tracing config
314+
# The deployed stack's outputs, read from Alchemy's state store without
315+
# planning or applying. Redacted values are persisted as
316+
# {"__redacted__": "<value>"}; the token is masked before it is written.
317+
- name: Read production relay tracing config
316318
shell: bash
319+
working-directory: infra/relay
317320
run: |
318-
vp run --filter t3code-relay deploy \
319-
--stage prod \
320-
--read-state \
321-
--github-output \
322-
--github-env-file "$RUNNER_TEMP/relay-client-tracing.env"
321+
set -euo pipefail
322+
output="$(npx alchemy state read T3CodeRelay/prod/output --no-input)"
323+
field() {
324+
jq -er --arg key "$1" '.[$key] | if type == "object" then .__redacted__ else . end | select(. != null and . != "")' <<<"$output" \
325+
|| { echo "Relay stack output is missing $1" >&2; exit 1; }
326+
}
327+
url="$(field clientTracingUrl)"
328+
dataset="$(field clientTracingDataset)"
329+
token="$(field clientTracingToken)"
330+
echo "::add-mask::$token"
331+
{
332+
echo "T3CODE_RELAY_CLIENT_OTLP_TRACES_URL=$url"
333+
echo "T3CODE_RELAY_CLIENT_OTLP_TRACES_DATASET=$dataset"
334+
echo "T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN=$token"
335+
} > "$RUNNER_TEMP/relay-client-tracing.env"
323336
324337
- name: Upload relay client tracing config
325338
uses: actions/upload-artifact@v7

‎docs/operations/connect-setup.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Bundled servers also accept runtime overrides for operator-managed deployments.
3232

3333
Copy `infra/relay/.env.example` to `infra/relay/.env` for relay deployment settings.
3434
Deploy `prod` before personal stages because it owns the retained database that their branches
35-
depend on. The deploy wrapper writes the resulting relay URL back to the root `.env`.
35+
depend on. The stack's `PublishClientConfig` action writes the resulting relay URL back to the root `.env`.
3636

3737
## CLI OAuth application
3838

‎infra/relay/README.md‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ dependencies represented at their boundary rather than mocking internal behavior
7676

7777
## Deployment
7878

79-
The relay deploys through Alchemy:
79+
The relay deploys with the Alchemy CLI (`vp run --filter t3code-relay deploy` is `alchemy deploy`
80+
in this directory):
8081

8182
```sh
8283
vp run --filter t3code-relay deploy
@@ -109,9 +110,10 @@ DNS-safe sanitization as Alchemy physical resource names, so `prod` uses
109110
`<stage>-<digest>.<RELAY_TUNNEL_ZONE_NAME>`. `RELAY_DOMAIN` remains available as an explicit API
110111
domain override.
111112

112-
After a successful deploy, the wrapper updates the repository-root `.env` file with the derived relay
113-
URL. That makes subsequent source builds point at the relay that was just deployed without copying
114-
the URL manually.
113+
The stack's `PublishClientConfig` action ([`src/clientConfig.ts`](./src/clientConfig.ts)) writes the
114+
deployed relay URL and tracing configuration into the repository-root `.env`, so subsequent source
115+
builds point at the relay that was just deployed without copying values manually. It runs only when
116+
one of those outputs changed, and `T3CODE_RELAY_CLIENT_CONFIG_ENV` redirects it to another file.
115117

116118
### Deployment CI
117119

‎infra/relay/alchemy.run.ts‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
// @effect-diagnostics anyUnknownInErrorContext:off layerMergeAllWithDependencies:off - Alchemy provider helpers expose framework-owned any requirements.
22
import * as Alchemy from "alchemy";
3+
import * as Output from "alchemy/Output";
34
import * as Axiom from "alchemy/Axiom";
45
import * as Cloudflare from "alchemy/Cloudflare";
56
import * as Drizzle from "alchemy/Drizzle";
67
import * as Effect from "effect/Effect";
78
import * as Layer from "effect/Layer";
89
import * as Planetscale from "alchemy/Planetscale";
910

11+
import { PublishClientConfig, tokenDigest } from "./src/clientConfig.ts";
1012
import * as RelayDb from "./src/db.ts";
1113
import { RelayObservability } from "./src/observability.ts";
1214
import { ManagedEndpointZone, RelayApiZone } from "./src/zone.ts";
@@ -30,6 +32,19 @@ export default Alchemy.Stack(
3032
const relayApiZone = yield* RelayApiZone.pipe(Effect.orDie);
3133
const observability = yield* RelayObservability;
3234
const api = yield* Api;
35+
yield* PublishClientConfig({
36+
url: api.url,
37+
mobileTracingUrl: observability.traces.otelTracesEndpoint,
38+
mobileTracingDataset: observability.traces.name,
39+
mobileTracingToken: observability.mobileIngestToken.token,
40+
clientTracingUrl: observability.traces.otelTracesEndpoint,
41+
clientTracingDataset: observability.traces.name,
42+
clientTracingToken: observability.clientIngestToken.token,
43+
tokenDigest: Output.map(
44+
Output.all(observability.mobileIngestToken.token, observability.clientIngestToken.token),
45+
tokenDigest,
46+
),
47+
});
3348

3449
return {
3550
databaseName: db.database.name,

‎infra/relay/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"private": true,
44
"type": "module",
55
"scripts": {
6-
"deploy": "node -- scripts/deploy.ts",
6+
"deploy": "alchemy deploy",
77
"push:android:smoke": "node scripts/android-push-smoke.ts",
88
"push:android:watch": "node scripts/android-push-watch.ts",
99
"destroy": "alchemy destroy",

‎infra/relay/scripts/deploy.test.ts‎

Lines changed: 0 additions & 227 deletions
This file was deleted.

0 commit comments

Comments
 (0)