Skip to content

Commit 9b9006f

Browse files
authored
Fix release finalize lockfile refresh (#608)
* Add checkout and Node setup to release updater publish job - Ensure the updater publish job checks out the target ref - Initialize Node using the repository's package.json version file before downloading artifacts * Fix release finalize lockfile refresh
1 parent 42234b8 commit 9b9006f

File tree

5 files changed

+247
-28
lines changed

5 files changed

+247
-28
lines changed

.github/workflows/ci.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,23 @@ jobs:
6969
run: |
7070
test -f apps/desktop/dist-electron/preload.js
7171
grep -nE "desktopBridge|getWsUrl|PICK_FOLDER_CHANNEL|wsUrl" apps/desktop/dist-electron/preload.js
72+
73+
release_smoke:
74+
name: Release Smoke
75+
runs-on: ubuntu-24.04
76+
steps:
77+
- name: Checkout
78+
uses: actions/checkout@v4
79+
80+
- name: Setup Bun
81+
uses: oven-sh/setup-bun@v2
82+
with:
83+
bun-version-file: package.json
84+
85+
- name: Setup Node
86+
uses: actions/setup-node@v4
87+
with:
88+
node-version-file: package.json
89+
90+
- name: Exercise release-only workflow steps
91+
run: node scripts/release-smoke.ts

.github/workflows/release.yml

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -322,41 +322,15 @@ jobs:
322322
name: Update version strings
323323
env:
324324
RELEASE_VERSION: ${{ needs.preflight.outputs.version }}
325-
run: |
326-
node --input-type=module -e '
327-
import { appendFileSync, readFileSync, writeFileSync } from "node:fs";
328-
329-
const files = [
330-
"apps/server/package.json",
331-
"apps/desktop/package.json",
332-
"apps/web/package.json",
333-
"packages/contracts/package.json",
334-
];
335-
336-
let changed = false;
337-
for (const file of files) {
338-
const packageJson = JSON.parse(readFileSync(file, "utf8"));
339-
if (packageJson.version !== process.env.RELEASE_VERSION) {
340-
packageJson.version = process.env.RELEASE_VERSION;
341-
writeFileSync(file, `${JSON.stringify(packageJson, null, 2)}\n`);
342-
changed = true;
343-
}
344-
}
345-
346-
if (!changed) {
347-
console.log("All package.json versions already match release version.");
348-
}
349-
350-
appendFileSync(process.env.GITHUB_OUTPUT, `changed=${changed}\n`);
351-
'
325+
run: node scripts/update-release-package-versions.ts "$RELEASE_VERSION" --github-output
352326

353327
- name: Format package.json files
354328
if: steps.update_versions.outputs.changed == 'true'
355329
run: bunx oxfmt apps/server/package.json apps/desktop/package.json apps/web/package.json packages/contracts/package.json
356330

357331
- name: Refresh lockfile
358332
if: steps.update_versions.outputs.changed == 'true'
359-
run: bun install
333+
run: bun install --lockfile-only --ignore-scripts
360334

361335
- name: Commit and push version bump
362336
if: steps.update_versions.outputs.changed == 'true'

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"dist:desktop:dmg:x64": "node scripts/build-desktop-artifact.ts --platform mac --target dmg --arch x64",
4646
"dist:desktop:linux": "node scripts/build-desktop-artifact.ts --platform linux --target AppImage --arch x64",
4747
"dist:desktop:win": "node scripts/build-desktop-artifact.ts --platform win --target nsis --arch x64",
48+
"release:smoke": "node scripts/release-smoke.ts",
4849
"clean": "rm -rf node_modules apps/*/node_modules packages/*/node_modules apps/*/dist apps/*/dist-electron packages/*/dist .turbo apps/*/.turbo packages/*/.turbo",
4950
"sync:vscode-icons": "node scripts/sync-vscode-icons.mjs"
5051
},

scripts/release-smoke.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import { execFileSync } from "node:child_process";
2+
import { cpSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3+
import { tmpdir } from "node:os";
4+
import { dirname, join, resolve } from "node:path";
5+
import { fileURLToPath } from "node:url";
6+
7+
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
8+
9+
const workspaceFiles = [
10+
"package.json",
11+
"bun.lock",
12+
"apps/server/package.json",
13+
"apps/desktop/package.json",
14+
"apps/web/package.json",
15+
"apps/marketing/package.json",
16+
"packages/contracts/package.json",
17+
"packages/shared/package.json",
18+
"scripts/package.json",
19+
] as const;
20+
21+
function copyWorkspaceManifestFixture(targetRoot: string): void {
22+
for (const relativePath of workspaceFiles) {
23+
const sourcePath = resolve(repoRoot, relativePath);
24+
const destinationPath = resolve(targetRoot, relativePath);
25+
mkdirSync(dirname(destinationPath), { recursive: true });
26+
cpSync(sourcePath, destinationPath);
27+
}
28+
}
29+
30+
function writeMacManifestFixtures(targetRoot: string): { arm64Path: string; x64Path: string } {
31+
const assetDirectory = resolve(targetRoot, "release-assets");
32+
mkdirSync(assetDirectory, { recursive: true });
33+
34+
const arm64Path = resolve(assetDirectory, "latest-mac.yml");
35+
const x64Path = resolve(assetDirectory, "latest-mac-x64.yml");
36+
37+
writeFileSync(
38+
arm64Path,
39+
`version: 9.9.9-smoke.0
40+
files:
41+
- url: T3-Code-9.9.9-smoke.0-arm64.zip
42+
sha512: arm64zip
43+
size: 125621344
44+
- url: T3-Code-9.9.9-smoke.0-arm64.dmg
45+
sha512: arm64dmg
46+
size: 131754935
47+
path: T3-Code-9.9.9-smoke.0-arm64.zip
48+
sha512: arm64zip
49+
releaseDate: '2026-03-08T10:32:14.587Z'
50+
`,
51+
);
52+
53+
writeFileSync(
54+
x64Path,
55+
`version: 9.9.9-smoke.0
56+
files:
57+
- url: T3-Code-9.9.9-smoke.0-x64.zip
58+
sha512: x64zip
59+
size: 132000112
60+
- url: T3-Code-9.9.9-smoke.0-x64.dmg
61+
sha512: x64dmg
62+
size: 138148807
63+
path: T3-Code-9.9.9-smoke.0-x64.zip
64+
sha512: x64zip
65+
releaseDate: '2026-03-08T10:36:07.540Z'
66+
`,
67+
);
68+
69+
return { arm64Path, x64Path };
70+
}
71+
72+
function assertContains(haystack: string, needle: string, message: string): void {
73+
if (!haystack.includes(needle)) {
74+
throw new Error(message);
75+
}
76+
}
77+
78+
const tempRoot = mkdtempSync(join(tmpdir(), "t3-release-smoke-"));
79+
80+
try {
81+
copyWorkspaceManifestFixture(tempRoot);
82+
83+
execFileSync(
84+
process.execPath,
85+
[resolve(repoRoot, "scripts/update-release-package-versions.ts"), "9.9.9-smoke.0", "--root", tempRoot],
86+
{
87+
cwd: repoRoot,
88+
stdio: "inherit",
89+
},
90+
);
91+
92+
execFileSync("bun", ["install", "--lockfile-only", "--ignore-scripts"], {
93+
cwd: tempRoot,
94+
stdio: "inherit",
95+
});
96+
97+
const lockfile = readFileSync(resolve(tempRoot, "bun.lock"), "utf8");
98+
assertContains(lockfile, `"version": "9.9.9-smoke.0"`, "Expected bun.lock to contain the smoke version.");
99+
100+
const { arm64Path, x64Path } = writeMacManifestFixtures(tempRoot);
101+
execFileSync(process.execPath, [resolve(repoRoot, "scripts/merge-mac-update-manifests.ts"), arm64Path, x64Path], {
102+
cwd: repoRoot,
103+
stdio: "inherit",
104+
});
105+
106+
const mergedManifest = readFileSync(arm64Path, "utf8");
107+
assertContains(mergedManifest, "T3-Code-9.9.9-smoke.0-arm64.zip", "Merged manifest is missing the arm64 asset.");
108+
assertContains(mergedManifest, "T3-Code-9.9.9-smoke.0-x64.zip", "Merged manifest is missing the x64 asset.");
109+
110+
console.log("Release smoke checks passed.");
111+
} finally {
112+
rmSync(tempRoot, { recursive: true, force: true });
113+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { appendFileSync, readFileSync, writeFileSync } from "node:fs";
2+
import { resolve } from "node:path";
3+
import { fileURLToPath } from "node:url";
4+
5+
export const releasePackageFiles = [
6+
"apps/server/package.json",
7+
"apps/desktop/package.json",
8+
"apps/web/package.json",
9+
"packages/contracts/package.json",
10+
] as const;
11+
12+
interface UpdateReleasePackageVersionsOptions {
13+
readonly rootDir?: string;
14+
}
15+
16+
interface MutablePackageJson {
17+
version?: string;
18+
[key: string]: unknown;
19+
}
20+
21+
export function updateReleasePackageVersions(
22+
version: string,
23+
options: UpdateReleasePackageVersionsOptions = {},
24+
): { changed: boolean } {
25+
const rootDir = resolve(options.rootDir ?? process.cwd());
26+
let changed = false;
27+
28+
for (const relativePath of releasePackageFiles) {
29+
const filePath = resolve(rootDir, relativePath);
30+
const packageJson = JSON.parse(readFileSync(filePath, "utf8")) as MutablePackageJson;
31+
if (packageJson.version === version) {
32+
continue;
33+
}
34+
35+
packageJson.version = version;
36+
writeFileSync(filePath, `${JSON.stringify(packageJson, null, 2)}\n`);
37+
changed = true;
38+
}
39+
40+
return { changed };
41+
}
42+
43+
function parseArgs(argv: ReadonlyArray<string>): {
44+
version: string;
45+
rootDir: string | undefined;
46+
writeGithubOutput: boolean;
47+
} {
48+
let version: string | undefined;
49+
let rootDir: string | undefined;
50+
let writeGithubOutput = false;
51+
52+
for (let index = 0; index < argv.length; index += 1) {
53+
const argument = argv[index];
54+
if (argument === undefined) {
55+
continue;
56+
}
57+
58+
if (argument === "--github-output") {
59+
writeGithubOutput = true;
60+
continue;
61+
}
62+
63+
if (argument === "--root") {
64+
rootDir = argv[index + 1];
65+
if (!rootDir) {
66+
throw new Error("Missing value for --root.");
67+
}
68+
index += 1;
69+
continue;
70+
}
71+
72+
if (argument.startsWith("--")) {
73+
throw new Error(`Unknown argument: ${argument}`);
74+
}
75+
76+
if (version !== undefined) {
77+
throw new Error("Only one release version can be provided.");
78+
}
79+
version = argument;
80+
}
81+
82+
if (!version) {
83+
throw new Error(
84+
"Usage: node scripts/update-release-package-versions.ts <version> [--root <path>] [--github-output]",
85+
);
86+
}
87+
88+
return { version, rootDir, writeGithubOutput };
89+
}
90+
91+
const isMain = process.argv[1] !== undefined && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
92+
93+
if (isMain) {
94+
const { version, rootDir, writeGithubOutput } = parseArgs(process.argv.slice(2));
95+
const { changed } = updateReleasePackageVersions(
96+
version,
97+
rootDir === undefined ? {} : { rootDir },
98+
);
99+
100+
if (!changed) {
101+
console.log("All package.json versions already match release version.");
102+
}
103+
104+
if (writeGithubOutput) {
105+
const githubOutputPath = process.env.GITHUB_OUTPUT;
106+
if (!githubOutputPath) {
107+
throw new Error("GITHUB_OUTPUT is required when --github-output is set.");
108+
}
109+
appendFileSync(githubOutputPath, `changed=${changed}\n`);
110+
}
111+
}

0 commit comments

Comments
 (0)