Some checks failed
farmcontrol/farmcontrol-ui/pipeline/head There was a failure building this commit
- Introduced a new function `isPrimaryInstanceReachable` to check the reachability of the primary instance, improving instance handling on Windows. - Updated `forwardDeeplinkToRunningInstance` to include an option for signal fallback, enhancing flexibility in deeplink forwarding. - Implemented `tryRecoverUnresponsivePrimaryLock` to handle unresponsive primary instances by removing stale lock files, improving application reliability. - Refactored `resolveWindowsBinDir` to utilize unique path resolution, ensuring accurate directory handling for Windows applications. - Enhanced the `ensureWindowsWorkingDirectory` function to streamline directory changes, improving application behavior.
370 lines
10 KiB
JavaScript
370 lines
10 KiB
JavaScript
import {
|
|
cpSync,
|
|
existsSync,
|
|
mkdirSync,
|
|
readFileSync,
|
|
writeFileSync
|
|
} from 'node:fs'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
const electrobunDir = path.join(rootDir, 'node_modules/electrobun')
|
|
const sharedSrcDir = path.join(electrobunDir, 'src/shared')
|
|
const sharedDistDir = path.join(electrobunDir, 'dist/api/shared')
|
|
|
|
if (!existsSync(sharedDistDir)) {
|
|
console.error('patch-electrobun-src: electrobun dist/api/shared not found')
|
|
process.exit(1)
|
|
}
|
|
|
|
mkdirSync(sharedSrcDir, { recursive: true })
|
|
|
|
for (const fileName of [
|
|
'cef-version.ts',
|
|
'bun-version.ts',
|
|
'electrobun-version.ts',
|
|
'naming.ts',
|
|
'rpc.ts'
|
|
]) {
|
|
const source = path.join(sharedDistDir, fileName)
|
|
const destination = path.join(sharedSrcDir, fileName)
|
|
if (existsSync(source)) {
|
|
cpSync(source, destination)
|
|
}
|
|
}
|
|
|
|
const platformPatch = `import { platform, arch } from "os";
|
|
|
|
export type SupportedOS = "macos" | "win" | "linux";
|
|
export type SupportedArch = "arm64" | "x64";
|
|
|
|
const platformName = platform();
|
|
const archName = arch();
|
|
|
|
export const OS: SupportedOS = (() => {
|
|
switch (platformName) {
|
|
case "win32":
|
|
return "win";
|
|
case "darwin":
|
|
return "macos";
|
|
case "linux":
|
|
return "linux";
|
|
default:
|
|
throw new Error(\`Unsupported platform: \${platformName}\`);
|
|
}
|
|
})();
|
|
|
|
function normalizeForcedArch(value) {
|
|
if (value === "arm64" || value === "aarch64") {
|
|
return "arm64";
|
|
}
|
|
if (value === "x64" || value === "amd64") {
|
|
return "x64";
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export const ARCH: SupportedArch = (() => {
|
|
const forcedArch = normalizeForcedArch(
|
|
process.env.ELECTROBUN_FORCE_ARCH || process.env.ELECTROBUN_TARGET_ARCH,
|
|
);
|
|
if (forcedArch) {
|
|
return forcedArch;
|
|
}
|
|
|
|
if (OS === "win") {
|
|
return "x64";
|
|
}
|
|
|
|
switch (archName) {
|
|
case "arm64":
|
|
return "arm64";
|
|
case "x64":
|
|
return "x64";
|
|
default:
|
|
throw new Error(\`Unsupported architecture: \${archName}\`);
|
|
}
|
|
})();
|
|
|
|
export const HOST_ARCH: SupportedArch = (() => {
|
|
if (OS === "win") {
|
|
return "x64";
|
|
}
|
|
|
|
switch (archName) {
|
|
case "arm64":
|
|
return "arm64";
|
|
case "x64":
|
|
return "x64";
|
|
default:
|
|
throw new Error(\`Unsupported architecture: \${archName}\`);
|
|
}
|
|
})();
|
|
|
|
export function getPlatformOS(): SupportedOS {
|
|
return OS;
|
|
}
|
|
|
|
export function getPlatformArch(): SupportedArch {
|
|
return ARCH;
|
|
}
|
|
`
|
|
|
|
writeFileSync(path.join(sharedSrcDir, 'platform.ts'), platformPatch)
|
|
|
|
const templatesDir = path.join(electrobunDir, 'src/cli/templates')
|
|
mkdirSync(templatesDir, { recursive: true })
|
|
writeFileSync(
|
|
path.join(templatesDir, 'embedded.ts'),
|
|
`export function getTemplateNames() {
|
|
return [];
|
|
}
|
|
|
|
export function getTemplate(name: string) {
|
|
throw new Error(\`Template not available in patched electrobun CLI: \${name}\`);
|
|
}
|
|
`
|
|
)
|
|
|
|
const RCEDIT_RESOLVER_FN = `function resolveProjectRceditPkgPath(projectRoot) {
|
|
const candidates = [
|
|
join(projectRoot, "node_modules/rcedit/package.json"),
|
|
join(projectRoot, "node_modules/electrobun/node_modules/rcedit/package.json"),
|
|
];
|
|
for (const candidate of candidates) {
|
|
if (existsSync(candidate)) {
|
|
return candidate;
|
|
}
|
|
}
|
|
throw new Error(
|
|
"rcedit not found under " + projectRoot + ". Install rcedit in the project (bun add -d rcedit).",
|
|
);
|
|
}`
|
|
|
|
const cliPath = path.join(electrobunDir, 'src/cli/index.ts')
|
|
let cliSource = readFileSync(cliPath, 'utf8')
|
|
if (!cliSource.includes('HOST_ARCH')) {
|
|
cliSource = cliSource.replace(
|
|
'import { OS, ARCH } from "../shared/platform";',
|
|
'import { OS, ARCH, HOST_ARCH } from "../shared/platform";'
|
|
)
|
|
cliSource = cliSource.replace(
|
|
'const hostPaths = getPlatformPaths(OS, ARCH);',
|
|
'const hostPaths = getPlatformPaths(OS, HOST_ARCH);'
|
|
)
|
|
}
|
|
if (!cliSource.includes('toolPaths')) {
|
|
cliSource = cliSource.replace(
|
|
'const targetPaths = getPlatformPaths(currentTarget.os, currentTarget.arch);',
|
|
'const targetPaths = getPlatformPaths(currentTarget.os, currentTarget.arch);\n\t\tconst toolPaths = getPlatformPaths(currentTarget.os, ARCH !== HOST_ARCH ? HOST_ARCH : ARCH);'
|
|
)
|
|
cliSource = cliSource.replaceAll(
|
|
'const zstdPath = targetPaths.ZSTD;',
|
|
'const zstdPath = toolPaths.ZSTD;'
|
|
)
|
|
cliSource = cliSource.replaceAll(
|
|
'const bsdiffpath = targetPaths.BSDIFF;',
|
|
'const bsdiffpath = toolPaths.BSDIFF;'
|
|
)
|
|
cliSource = cliSource.replace(
|
|
'zigAsarCli = join(targetPaths.BSPATCH).replace("bspatch", "zig-asar");',
|
|
'zigAsarCli = join(toolPaths.BSPATCH).replace("bspatch", "zig-asar");'
|
|
)
|
|
writeFileSync(cliPath, cliSource)
|
|
cliSource = readFileSync(cliPath, 'utf8')
|
|
}
|
|
if (!cliSource.includes('hookBunBinary')) {
|
|
cliSource = cliSource.replace(
|
|
`const hostPaths = getPlatformPaths(OS, HOST_ARCH);
|
|
|
|
const result = Bun.spawnSync([hostPaths.BUN_BINARY, hookScript],`,
|
|
`const hostPaths = getPlatformPaths(OS, HOST_ARCH);
|
|
const hookBunBinary = existsSync(hostPaths.BUN_BINARY)
|
|
? hostPaths.BUN_BINARY
|
|
: process.execPath;
|
|
|
|
const result = Bun.spawnSync([hookBunBinary, hookScript],`
|
|
)
|
|
cliSource = cliSource.replace(
|
|
'console.error("Tried to run with bun at:", hostPaths.BUN_BINARY);',
|
|
'console.error("Tried to run with bun at:", hookBunBinary);'
|
|
)
|
|
writeFileSync(cliPath, cliSource)
|
|
cliSource = readFileSync(cliPath, 'utf8')
|
|
}
|
|
|
|
if (!cliSource.includes('resolveProjectRceditPkgPath')) {
|
|
cliSource = cliSource.replaceAll(
|
|
'const rceditPkgPath = require.resolve("rcedit/package.json");',
|
|
'const rceditPkgPath = resolveProjectRceditPkgPath(projectRoot);'
|
|
)
|
|
cliSource = cliSource.replace(
|
|
'function getPlatformPaths(',
|
|
`${RCEDIT_RESOLVER_FN}
|
|
|
|
function getPlatformPaths(`
|
|
)
|
|
writeFileSync(cliPath, cliSource)
|
|
cliSource = readFileSync(cliPath, 'utf8')
|
|
} else {
|
|
cliSource = cliSource.replace(
|
|
/function resolveProjectRceditPkgPath\(projectRoot\) \{[\s\S]*?\n\}/m,
|
|
RCEDIT_RESOLVER_FN
|
|
)
|
|
writeFileSync(cliPath, cliSource)
|
|
}
|
|
|
|
const electrobunCjsPath = path.join(electrobunDir, 'bin/electrobun.cjs')
|
|
let electrobunCjs = readFileSync(electrobunCjsPath, 'utf8')
|
|
|
|
if (!electrobunCjs.includes('farmcontrol-use-patched-cli')) {
|
|
electrobunCjs = electrobunCjs.replace(
|
|
`async function main() {
|
|
try {
|
|
const args = process.argv.slice(2);
|
|
const cliPath = await ensureCliBinary();
|
|
|
|
// Replace this process with the actual CLI
|
|
const child = spawn(cliPath, args, {
|
|
stdio: 'inherit',
|
|
cwd: process.cwd()
|
|
});`,
|
|
`async function main() {
|
|
try {
|
|
const args = process.argv.slice(2);
|
|
const patchedCliPath = join(electrobunDir, 'src', 'cli', 'index.ts');
|
|
|
|
if (existsSync(patchedCliPath)) {
|
|
// farmcontrol-use-patched-cli: bundled electrobun.exe cannot resolve project rcedit
|
|
const bunBinary = process.env.BUN_INSTALL
|
|
? join(process.env.BUN_INSTALL, 'bin', 'bun' + binExt)
|
|
: 'bun';
|
|
const child = spawn(bunBinary, [patchedCliPath, ...args], {
|
|
stdio: 'inherit',
|
|
cwd: process.cwd(),
|
|
env: process.env,
|
|
shell: platform === 'win',
|
|
});
|
|
|
|
child.on('exit', (code) => {
|
|
process.exit(code || 0);
|
|
});
|
|
|
|
child.on('error', (error) => {
|
|
console.error('Failed to start electrobun patched CLI:', error.message);
|
|
process.exit(1);
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
const cliPath = await ensureCliBinary();
|
|
|
|
// Replace this process with the actual CLI
|
|
const child = spawn(cliPath, args, {
|
|
stdio: 'inherit',
|
|
cwd: process.cwd()
|
|
});`
|
|
)
|
|
writeFileSync(electrobunCjsPath, electrobunCjs)
|
|
}
|
|
|
|
const rceditSrc = path.join(rootDir, 'node_modules/rcedit')
|
|
const rceditDest = path.join(electrobunDir, 'node_modules/rcedit')
|
|
if (existsSync(rceditSrc)) {
|
|
mkdirSync(path.join(electrobunDir, 'node_modules'), { recursive: true })
|
|
cpSync(rceditSrc, rceditDest, { recursive: true, force: true })
|
|
}
|
|
|
|
function patchNativeWrapperPath(nativeTsPath) {
|
|
if (!existsSync(nativeTsPath)) {
|
|
return
|
|
}
|
|
|
|
let source = readFileSync(nativeTsPath, 'utf8')
|
|
|
|
if (source.includes('function resolveNativeWrapperPath()')) {
|
|
source = source.replace(
|
|
/function resolveNativeWrapperPath\(\) \{[\s\S]*?\n\}/m,
|
|
`function resolveNativeWrapperPath() {
|
|
\tconst fileName = \`libNativeWrapper.\${suffix}\`;
|
|
\tconst candidates = [
|
|
\t\tjoin(dirname(process.argv0), fileName),
|
|
\t\tjoin(dirname(process.execPath), fileName),
|
|
\t\tjoin(process.cwd(), fileName),
|
|
\t];
|
|
\tfor (const candidate of candidates) {
|
|
\t\tif (existsSync(candidate)) {
|
|
\t\t\treturn candidate;
|
|
\t\t}
|
|
\t}
|
|
\treturn candidates[0]!;
|
|
}`
|
|
)
|
|
writeFileSync(nativeTsPath, source)
|
|
return
|
|
}
|
|
|
|
if (!source.includes('existsSync')) {
|
|
if (source.includes('import { dirname, join } from "path";')) {
|
|
source = source.replace(
|
|
'import { dirname, join } from "path";',
|
|
'import { existsSync } from "fs";\nimport { dirname, join } from "path";'
|
|
)
|
|
} else {
|
|
source = source.replace(
|
|
'import { join } from "path";',
|
|
'import { existsSync } from "fs";\nimport { dirname, join } from "path";'
|
|
)
|
|
}
|
|
} else if (!source.includes('dirname')) {
|
|
source = source.replace(
|
|
'import { join } from "path";',
|
|
'import { dirname, join } from "path";'
|
|
)
|
|
}
|
|
|
|
const helper = `
|
|
function resolveNativeWrapperPath() {
|
|
\tconst fileName = \`libNativeWrapper.\${suffix}\`;
|
|
\tconst candidates = [
|
|
\t\tjoin(dirname(process.argv0), fileName),
|
|
\t\tjoin(dirname(process.execPath), fileName),
|
|
\t\tjoin(process.cwd(), fileName),
|
|
\t];
|
|
\tfor (const candidate of candidates) {
|
|
\t\tif (existsSync(candidate)) {
|
|
\t\t\treturn candidate;
|
|
\t\t}
|
|
\t}
|
|
\treturn candidates[0]!;
|
|
}
|
|
`
|
|
|
|
source = source.replace(
|
|
'export const native = (() => {',
|
|
`${helper}\nexport const native = (() => {`
|
|
)
|
|
|
|
source = source.replace(
|
|
/const nativeWrapperPath = join\((?:dirname\(process\.execPath\)|process\.cwd\(\)), `libNativeWrapper\.\$\{suffix\}`\);/,
|
|
'const nativeWrapperPath = resolveNativeWrapperPath();'
|
|
)
|
|
|
|
writeFileSync(nativeTsPath, source)
|
|
}
|
|
|
|
for (const distDir of ['dist', 'dist-macos-arm64', 'dist-win-x64']) {
|
|
patchNativeWrapperPath(
|
|
path.join(electrobunDir, distDir, 'api/bun/proc/native.ts')
|
|
)
|
|
}
|
|
|
|
const markerPath = path.join(electrobunDir, '.farmcontrol-electrobun-patched')
|
|
writeFileSync(markerPath, `patched-at=${new Date().toISOString()}\n`)
|
|
|
|
console.log(
|
|
'patch-electrobun-src: electrobun src/shared ready for cross-arch builds'
|
|
)
|