Spaces:
Paused
Paused
File size: 4,660 Bytes
06a5304 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | /**
* Proxy self-update — checks for new commits on GitHub and applies via git pull.
* Only works in CLI mode (where .git exists). Docker/Electron show manual instructions.
*/
import { execFile, execFileSync } from "child_process";
import { existsSync, readFileSync } from "fs";
import { resolve } from "path";
import { promisify } from "util";
import { isEmbedded } from "./paths.js";
const execFileAsync = promisify(execFile);
export interface ProxyInfo {
version: string;
commit: string | null;
}
export interface ProxySelfUpdateResult {
commitsBehind: number;
currentCommit: string | null;
latestCommit: string | null;
}
let _proxyUpdateInProgress = false;
let _gitAvailable: boolean | null = null;
/** Read proxy version from package.json + current git commit hash. */
export function getProxyInfo(): ProxyInfo {
let version = "unknown";
try {
const pkg = JSON.parse(readFileSync(resolve(process.cwd(), "package.json"), "utf-8"));
version = pkg.version ?? "unknown";
} catch { /* ignore */ }
let commit: string | null = null;
if (canSelfUpdate()) {
try {
const out = execFileSync("git", ["rev-parse", "--short", "HEAD"], {
cwd: process.cwd(),
encoding: "utf-8",
timeout: 5000,
});
commit = out.trim() || null;
} catch { /* ignore */ }
}
return { version, commit };
}
/** Whether this environment supports git-based self-update. */
export function canSelfUpdate(): boolean {
if (isEmbedded()) return false;
if (_gitAvailable !== null) return _gitAvailable;
// Check .git directory exists
if (!existsSync(resolve(process.cwd(), ".git"))) {
_gitAvailable = false;
return false;
}
// Check git command is available
try {
execFileSync("git", ["--version"], {
cwd: process.cwd(),
timeout: 5000,
stdio: "ignore",
});
_gitAvailable = true;
} catch {
_gitAvailable = false;
}
return _gitAvailable;
}
/** Whether a proxy self-update is currently in progress. */
export function isProxyUpdateInProgress(): boolean {
return _proxyUpdateInProgress;
}
/** Fetch latest from origin and check how many commits behind. */
export async function checkProxySelfUpdate(): Promise<ProxySelfUpdateResult> {
const cwd = process.cwd();
// Get current commit
let currentCommit: string | null = null;
try {
const { stdout } = await execFileAsync("git", ["rev-parse", "--short", "HEAD"], { cwd, timeout: 5000 });
currentCommit = stdout.trim() || null;
} catch { /* ignore */ }
// Fetch latest
try {
await execFileAsync("git", ["fetch", "origin", "master", "--quiet"], { cwd, timeout: 30000 });
} catch (err) {
console.warn("[SelfUpdate] git fetch failed:", err instanceof Error ? err.message : err);
return { commitsBehind: 0, currentCommit, latestCommit: currentCommit };
}
// Count commits behind
let commitsBehind = 0;
let latestCommit: string | null = null;
try {
const { stdout: countOut } = await execFileAsync(
"git", ["rev-list", "HEAD..origin/master", "--count"], { cwd, timeout: 5000 },
);
commitsBehind = parseInt(countOut.trim(), 10) || 0;
const { stdout: latestOut } = await execFileAsync(
"git", ["rev-parse", "--short", "origin/master"], { cwd, timeout: 5000 },
);
latestCommit = latestOut.trim() || null;
} catch { /* ignore */ }
return { commitsBehind, currentCommit, latestCommit };
}
/**
* Apply proxy self-update: git pull + npm install + npm run build.
* Runs in background. Returns immediately; check isProxyUpdateInProgress() for status.
*/
export async function applyProxySelfUpdate(): Promise<{ started: boolean; error?: string }> {
if (_proxyUpdateInProgress) {
return { started: false, error: "Update already in progress" };
}
_proxyUpdateInProgress = true;
const cwd = process.cwd();
try {
console.log("[SelfUpdate] Pulling latest code...");
await execFileAsync("git", ["pull", "origin", "master"], { cwd, timeout: 60000 });
console.log("[SelfUpdate] Installing dependencies...");
await execFileAsync("npm", ["install"], { cwd, timeout: 120000, shell: true });
console.log("[SelfUpdate] Building...");
await execFileAsync("npm", ["run", "build"], { cwd, timeout: 120000, shell: true });
console.log("[SelfUpdate] Update complete. Server restart required.");
_proxyUpdateInProgress = false;
return { started: true };
} catch (err) {
_proxyUpdateInProgress = false;
const msg = err instanceof Error ? err.message : String(err);
console.error("[SelfUpdate] Update failed:", msg);
return { started: false, error: msg };
}
}
|