Skip to content

Commit f939a6a

Browse files
committed
chore(ci): ensure electron runtime on hosted runners
1 parent 5913d06 commit f939a6a

3 files changed

Lines changed: 117 additions & 18 deletions

File tree

.github/workflows/ci.yml

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ on:
88

99
jobs:
1010
quality:
11-
name: Format, Lint, Typecheck, Test, Browser Test, Build
11+
name: Format, Lint, Typecheck, Test, Build
1212
runs-on: ubuntu-24.04
13-
timeout-minutes: 10
13+
timeout-minutes: 30
1414
steps:
1515
- name: Checkout
1616
uses: actions/checkout@v6
@@ -35,17 +35,12 @@ jobs:
3535
restore-keys: |
3636
${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}-
3737
38-
- name: Cache Playwright browsers
39-
uses: actions/cache@v5
40-
with:
41-
path: ~/.cache/ms-playwright
42-
key: ${{ runner.os }}-playwright-${{ hashFiles('bun.lock') }}
43-
restore-keys: |
44-
${{ runner.os }}-playwright-
45-
4638
- name: Install dependencies
4739
run: bun install --frozen-lockfile
4840

41+
- name: Ensure Electron runtime is installed
42+
run: bun scripts/ensure-electron-runtime.mjs
43+
4944
- name: Format
5045
run: bun run fmt:check
5146

@@ -58,14 +53,6 @@ jobs:
5853
- name: Test
5954
run: bun run test
6055

61-
- name: Install browser test runtime
62-
run: |
63-
cd apps/web
64-
bunx playwright install --with-deps chromium
65-
66-
- name: Browser test
67-
run: bun run --cwd apps/web test:browser
68-
6956
- name: Build desktop pipeline
7057
run: bun run build:desktop
7158

.github/workflows/release.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ jobs:
9898
- name: Install dependencies
9999
run: bun install --frozen-lockfile
100100

101+
- name: Ensure Electron runtime is installed
102+
run: bun scripts/ensure-electron-runtime.mjs
103+
101104
- id: release_meta
102105
name: Resolve release version
103106
shell: bash
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
#!/usr/bin/env bun
2+
3+
import { existsSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
4+
import { createRequire } from "node:module";
5+
import { tmpdir } from "node:os";
6+
import { dirname, join } from "node:path";
7+
import { fileURLToPath } from "node:url";
8+
9+
const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
10+
const requireFromRepo = createRequire(join(repoRoot, "package.json"));
11+
const electronPackageJsonPath = requireFromRepo.resolve(
12+
"./apps/desktop/node_modules/electron/package.json",
13+
);
14+
const electronPackageDir = dirname(electronPackageJsonPath);
15+
const requireFromElectron = createRequire(electronPackageJsonPath);
16+
17+
const resolveElectronBinary = () => {
18+
const entry = requireFromRepo.resolve("./apps/desktop/node_modules/electron");
19+
delete requireFromRepo.cache[entry];
20+
return requireFromRepo("./apps/desktop/node_modules/electron");
21+
};
22+
23+
const getInstalledBinary = () => {
24+
try {
25+
const binaryPath = resolveElectronBinary();
26+
return existsSync(binaryPath) ? binaryPath : undefined;
27+
} catch {
28+
return undefined;
29+
}
30+
};
31+
32+
const readElectronInstallState = () => {
33+
const pathTxt = join(electronPackageDir, "path.txt");
34+
const dist = join(electronPackageDir, "dist");
35+
36+
return {
37+
electronPackageDir,
38+
distEntries: existsSync(dist) ? readdirSync(dist).slice(0, 10) : [],
39+
distExists: existsSync(dist),
40+
pathTxt: existsSync(pathTxt) ? readFileSync(pathTxt, "utf8") : undefined,
41+
pathTxtExists: existsSync(pathTxt),
42+
};
43+
};
44+
45+
const getElectronPlatformPath = (platform) => {
46+
switch (platform) {
47+
case "darwin":
48+
case "mas":
49+
return "Electron.app/Contents/MacOS/Electron";
50+
case "freebsd":
51+
case "linux":
52+
case "openbsd":
53+
return "electron";
54+
case "win32":
55+
return "electron.exe";
56+
default:
57+
throw new Error(`Electron builds are not available on platform: ${platform}`);
58+
}
59+
};
60+
61+
const installElectronRuntime = async () => {
62+
const { downloadArtifact } = requireFromElectron("@electron/get");
63+
const extract = requireFromElectron("extract-zip");
64+
const { version } = requireFromElectron("./package.json");
65+
const checksums = requireFromElectron("./checksums.json");
66+
const platform = process.env.npm_config_platform || process.platform;
67+
const arch = process.env.npm_config_arch || process.arch;
68+
const platformPath = getElectronPlatformPath(platform);
69+
const distPath = join(electronPackageDir, "dist");
70+
71+
console.log(`Downloading Electron ${version} for ${platform}/${arch}.`);
72+
const zipPath = await downloadArtifact({
73+
arch,
74+
artifactName: "electron",
75+
cacheRoot: join(tmpdir(), "t3code-electron-cache"),
76+
checksums,
77+
force: true,
78+
platform,
79+
version,
80+
});
81+
82+
await extract(zipPath, { dir: distPath });
83+
84+
const extractedTypeDefinition = join(distPath, "electron.d.ts");
85+
if (existsSync(extractedTypeDefinition)) {
86+
renameSync(extractedTypeDefinition, join(electronPackageDir, "electron.d.ts"));
87+
}
88+
89+
writeFileSync(join(electronPackageDir, "path.txt"), platformPath);
90+
};
91+
92+
let binaryPath = getInstalledBinary();
93+
94+
if (!binaryPath) {
95+
console.log("Electron runtime missing; installing Electron runtime.");
96+
rmSync(join(electronPackageDir, "dist"), { force: true, recursive: true });
97+
rmSync(join(electronPackageDir, "path.txt"), { force: true });
98+
99+
await installElectronRuntime();
100+
binaryPath = getInstalledBinary();
101+
}
102+
103+
if (!binaryPath) {
104+
throw new Error(
105+
`Electron runtime failed to install. State: ${JSON.stringify(readElectronInstallState())}`,
106+
);
107+
}
108+
109+
console.log(`Electron runtime available at ${binaryPath}`);

0 commit comments

Comments
 (0)