Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
126 lines
3.3 KiB
JavaScript
126 lines
3.3 KiB
JavaScript
import { spawn } from "node:child_process";
|
|
import { access, readdir, stat } from "node:fs/promises";
|
|
import { createRequire } from "node:module";
|
|
import { join } from "node:path";
|
|
|
|
const ignoredDirectories = new Set([
|
|
".git",
|
|
"build",
|
|
"dist",
|
|
"node_modules",
|
|
"out"
|
|
]);
|
|
|
|
export function isDesktopBundleFresh({ outputMtimeMs, sourceMtimeMs }) {
|
|
return (
|
|
Number.isFinite(outputMtimeMs) &&
|
|
Number.isFinite(sourceMtimeMs) &&
|
|
outputMtimeMs >= sourceMtimeMs
|
|
);
|
|
}
|
|
|
|
export async function ensurePreparedDesktopLaunch({ workspaceRoot, log }) {
|
|
const desktopRoot = join(workspaceRoot, "apps", "desktop");
|
|
const outputFiles = [
|
|
join(desktopRoot, "out", "main", "index.js"),
|
|
join(desktopRoot, "out", "preload", "index.cjs"),
|
|
join(desktopRoot, "out", "renderer", "index.html")
|
|
];
|
|
const sourceMtimeMs = await latestSourceMtimeMs(workspaceRoot);
|
|
const outputMtimeMs = await oldestMtimeMs(outputFiles);
|
|
|
|
if (!isDesktopBundleFresh({ outputMtimeMs, sourceMtimeMs })) {
|
|
log("building prepared Desktop bundle");
|
|
await runCommand(
|
|
"pnpm",
|
|
["--filter", "@tutti-os/desktop", "build"],
|
|
workspaceRoot
|
|
);
|
|
} else {
|
|
log("reusing prepared Desktop bundle");
|
|
}
|
|
|
|
const electronExecutable = createRequire(join(desktopRoot, "package.json"))(
|
|
"electron"
|
|
);
|
|
return {
|
|
args: [desktopRoot],
|
|
command: electronExecutable,
|
|
launchMode: "prebuilt"
|
|
};
|
|
}
|
|
|
|
async function latestSourceMtimeMs(workspaceRoot) {
|
|
const sourceRoots = [
|
|
join(workspaceRoot, "apps", "desktop"),
|
|
join(workspaceRoot, "packages"),
|
|
join(workspaceRoot, "config"),
|
|
join(workspaceRoot, "package.json"),
|
|
join(workspaceRoot, "pnpm-lock.yaml")
|
|
];
|
|
let latest = 0;
|
|
for (const sourceRoot of sourceRoots) {
|
|
latest = Math.max(latest, await treeMtimeMs(sourceRoot));
|
|
}
|
|
return latest;
|
|
}
|
|
|
|
async function treeMtimeMs(path) {
|
|
let entry;
|
|
try {
|
|
entry = await stat(path);
|
|
} catch (error) {
|
|
if (error?.code === "ENOENT") return 0;
|
|
throw error;
|
|
}
|
|
if (!entry.isDirectory()) return entry.mtimeMs;
|
|
|
|
let latest = entry.mtimeMs;
|
|
for (const child of await readdir(path, { withFileTypes: true })) {
|
|
if (child.isDirectory() && ignoredDirectories.has(child.name)) continue;
|
|
latest = Math.max(latest, await treeMtimeMs(join(path, child.name)));
|
|
}
|
|
return latest;
|
|
}
|
|
|
|
async function oldestMtimeMs(paths) {
|
|
let oldest = Number.POSITIVE_INFINITY;
|
|
for (const path of paths) {
|
|
try {
|
|
await access(path);
|
|
oldest = Math.min(oldest, (await stat(path)).mtimeMs);
|
|
} catch (error) {
|
|
if (error?.code === "ENOENT") return 0;
|
|
throw error;
|
|
}
|
|
}
|
|
return oldest;
|
|
}
|
|
|
|
function runCommand(command, args, cwd) {
|
|
return new Promise((resolve, reject) => {
|
|
const child = spawn(command, args, {
|
|
cwd,
|
|
env: process.env,
|
|
stdio: ["ignore", "pipe", "pipe"]
|
|
});
|
|
child.stdout.resume();
|
|
let stderr = "";
|
|
child.stderr.on("data", (chunk) => {
|
|
stderr += String(chunk);
|
|
});
|
|
child.once("error", reject);
|
|
child.once("exit", (code, signal) => {
|
|
if (code === 0) {
|
|
resolve();
|
|
return;
|
|
}
|
|
reject(
|
|
new Error(
|
|
`${command} ${args.join(" ")} failed (${code ?? signal ?? "unknown"})${stderr.trim() ? `: ${stderr.trim()}` : ""}`
|
|
)
|
|
);
|
|
});
|
|
});
|
|
}
|