authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-18 17:44:23-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-18 17:44:23-07:00
loge74c668d786ffc14f40a9540cd3aede02ba5d62e
treef238bf92a017fbdee1d679f7649682303a2f81ab
parent4931d73af3e68d8706d7568b9af37c8397caffdb
signature Signed by PGP key B5690EEEBB952194

fix(release): strip Alchemy progress before parsing relay state (#12519)


3 files changed, 85 insertions(+), 2 deletions(-)

.github/scripts/relay-state-output.test.cjs created+76
......@@ -0,0 +1,76 @@
1const assert = require("node:assert/strict");
2const { spawnSync } = require("node:child_process");
3const { mkdtempSync, readFileSync, rmSync, existsSync } = require("node:fs");
4const { tmpdir } = require("node:os");
5const { join } = require("node:path");
6const { test } = require("node:test");
7
8const workflow = readFileSync(join(__dirname, "../workflows/release.yml"), "utf8");
9const step = workflow.match(
10 /- name: Read production relay tracing config\n[\s\S]*? run: \|\n((?: .*\n|\n)+)/,
11);
12assert.ok(step, "Could not find the relay state workflow step");
13const script = step[1].replace(/^ /gm, "");
14const config = {
15 clientTracingUrl: "https://example.invalid/traces",
16 clientTracingDataset: "fixture-dataset",
17 clientTracingToken: { __redacted__: "fixture-token" },
18};
19const json = JSON.stringify(config, null, 2);
20
21function runStep(stdout, exitCode = 0) {
22 const runnerTemp = mkdtempSync(join(tmpdir(), "t3-relay-state-test-"));
23 try {
24 const result = spawnSync(
25 "bash",
26 ["-c", 'npx() { printf "%s\\n" "$FIXTURE_STDOUT"; return "$FIXTURE_EXIT"; }\n' + script],
27 {
28 encoding: "utf8",
29 env: {
30 PATH: process.env.PATH,
31 RUNNER_TEMP: runnerTemp,
32 FIXTURE_STDOUT: stdout,
33 FIXTURE_EXIT: String(exitCode),
34 },
35 },
36 );
37 assert.ifError(result.error);
38 const envPath = join(runnerTemp, "relay-client-tracing.env");
39 return {
40 ...result,
41 envFile: existsSync(envPath) ? readFileSync(envPath, "utf8") : undefined,
42 };
43 } finally {
44 rmSync(runnerTemp, { recursive: true, force: true });
45 }
46}
47
48for (const prefix of [
49 "",
50 "• Refreshing Cloudflare State Store credentials\n✓ Refreshing Cloudflare State Store credentials\n",
51]) {
52 test(`extracts tracing config ${prefix ? "after progress output" : "from plain JSON"}`, () => {
53 const result = runStep(prefix + json);
54 assert.equal(result.status, 0, result.stderr);
55 assert.equal(result.stdout, "::add-mask::fixture-token\n");
56 assert.equal(
57 result.envFile,
58 "T3CODE_RELAY_CLIENT_OTLP_TRACES_URL=https://example.invalid/traces\n" +
59 "T3CODE_RELAY_CLIENT_OTLP_TRACES_DATASET=fixture-dataset\n" +
60 "T3CODE_RELAY_CLIENT_OTLP_TRACES_TOKEN=fixture-token\n",
61 );
62 });
63}
64
65for (const [name, stdout, exitCode] of [
66 ["failed CLI even with valid JSON", json, 1],
67 ["missing JSON", "Refreshing credentials...", 0],
68 ["malformed JSON", "{not JSON", 0],
69 ["missing token", JSON.stringify({ ...config, clientTracingToken: null }, null, 2), 0],
70]) {
71 test(`rejects ${name} without writing config`, () => {
72 const result = runStep(stdout, exitCode);
73 assert.notEqual(result.status, 0);
74 assert.equal(result.envFile, undefined);
75 });
76}
.github/workflows/release.yml+3-2
......@@ -314,14 +314,15 @@ jobs:
314314 # The deployed stack's outputs, read from Alchemy's state store without
315315 # planning or applying. Select the backend explicitly to avoid loading
316316 # the stack's unrelated provider credentials; suppress informational logs
317 # so stdout stays valid JSON. Redacted values are persisted as
317 # and strip the credential-refresh progress prefix before parsing JSON.
318 # Redacted values are persisted as
318319 # {"__redacted__": "<value>"}; the token is masked before it is written.
319320 - name: Read production relay tracing config
320321 shell: bash
321322 working-directory: infra/relay
322323 run: |
323324 set -euo pipefail
324 output="$(npx alchemy state read T3CodeRelay/prod/output --backend cloudflare --log-level error --no-input)"
325 output="$(npx alchemy state read T3CodeRelay/prod/output --backend cloudflare --log-level error --no-input | sed -n '/^{/,$p')"
325326 field() {
326327 jq -er --arg key "$1" '.[$key] | if type == "object" then .__redacted__ else . end | select(. != null and . != "")' <<<"$output" \
327328 || { echo "Relay stack output is missing $1" >&2; exit 1; }
scripts/release-smoke.ts+6
......@@ -188,6 +188,12 @@ function assertMissing(path: string, message: string): void {
188188const tempRoot = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-release-smoke-"));
189189
190190try {
191 NodeChildProcess.execFileSync(
192 process.execPath,
193 ["--test", NodePath.resolve(repoRoot, ".github/scripts/relay-state-output.test.cjs")],
194 { stdio: "inherit" },
195 );
196
191197 copyWorkspaceManifestFixture(tempRoot);
192198
193199 NodeChildProcess.execFileSync(