mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-25 07:16:02 +00:00
chore(windows): track bundled helper manifests
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"version": 1,
|
||||
"platform": "win32",
|
||||
"arch": "x64",
|
||||
"helpers": {
|
||||
"wgc-capture": {
|
||||
"binaryName": "wgc-capture.exe",
|
||||
"binarySha256": "72d8f0703a5360a0e911a95bf8839354c877df5fa51294af75ded83a17c0c32e",
|
||||
"sourceDir": "electron/native/wgc-capture",
|
||||
"sourceFingerprint": "f30dee6b4956a5b57f4fc4d8d586f3470c793c8f7e3242f4160394debbb3f3be",
|
||||
"updatedAt": "2026-03-28T05:17:10.449Z"
|
||||
},
|
||||
"cursor-monitor": {
|
||||
"binaryName": "cursor-monitor.exe",
|
||||
"binarySha256": "916ed064c12a3b861d7721df8b9209d10ec0fdb8078564a2ba3ea8cf4f4354c4",
|
||||
"sourceDir": "electron/native/cursor-monitor",
|
||||
"sourceFingerprint": "6ad1b8b50bb336f2a48937b06f5ec56d90b6ab4a3e56a4bca278cf67a5d3e52e",
|
||||
"updatedAt": "2026-03-28T05:17:17.697Z"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,12 @@ import { execSync } from "node:child_process";
|
||||
import { copyFileSync, existsSync, mkdirSync, rmSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import {
|
||||
formatNativeHelperManifestWarning,
|
||||
updateNativeHelperManifest,
|
||||
verifyNativeHelperManifest,
|
||||
} from "./native-helper-manifest.mjs";
|
||||
|
||||
const projectRoot = process.cwd();
|
||||
const sourceDir = path.join(projectRoot, "electron", "native", "cursor-monitor");
|
||||
const buildDir = path.join(sourceDir, "build");
|
||||
@@ -13,6 +19,7 @@ const bundledDir = path.join(
|
||||
process.arch === "arm64" ? "win32-arm64" : "win32-x64",
|
||||
);
|
||||
const bundledExePath = path.join(bundledDir, "cursor-monitor.exe");
|
||||
const helperId = "cursor-monitor";
|
||||
|
||||
if (process.platform !== "win32") {
|
||||
console.log("[build-cursor-monitor] Skipping: host platform is not Windows.");
|
||||
@@ -79,6 +86,16 @@ function findCmake() {
|
||||
const cmake = findCmake();
|
||||
if (!cmake) {
|
||||
if (existsSync(bundledExePath)) {
|
||||
const verification = verifyNativeHelperManifest({
|
||||
projectRoot,
|
||||
helperId,
|
||||
sourceDir,
|
||||
binaryPath: bundledExePath,
|
||||
binaryName: "cursor-monitor.exe",
|
||||
});
|
||||
if (!verification.ok) {
|
||||
console.warn(formatNativeHelperManifestWarning("build-cursor-monitor", verification));
|
||||
}
|
||||
console.log(`[build-cursor-monitor] Using bundled helper: ${bundledExePath}`);
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -139,6 +156,14 @@ if (existsSync(exePath)) {
|
||||
mkdirSync(bundledDir, { recursive: true });
|
||||
copyFileSync(exePath, bundledExePath);
|
||||
console.log(`[build-cursor-monitor] Staged bundled helper: ${bundledExePath}`);
|
||||
const manifestPath = updateNativeHelperManifest({
|
||||
projectRoot,
|
||||
helperId,
|
||||
sourceDir,
|
||||
binaryPath: bundledExePath,
|
||||
binaryName: "cursor-monitor.exe",
|
||||
});
|
||||
console.log(`[build-cursor-monitor] Updated helper manifest: ${manifestPath}`);
|
||||
} else {
|
||||
console.error("[build-cursor-monitor] Expected exe not found at", exePath);
|
||||
process.exit(1);
|
||||
|
||||
@@ -2,6 +2,12 @@ import { execSync } from "node:child_process";
|
||||
import { copyFileSync, mkdirSync, existsSync, rmSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import {
|
||||
formatNativeHelperManifestWarning,
|
||||
updateNativeHelperManifest,
|
||||
verifyNativeHelperManifest,
|
||||
} from "./native-helper-manifest.mjs";
|
||||
|
||||
const projectRoot = process.cwd();
|
||||
const sourceDir = path.join(projectRoot, "electron", "native", "wgc-capture");
|
||||
const buildDir = path.join(sourceDir, "build");
|
||||
@@ -13,6 +19,7 @@ const bundledDir = path.join(
|
||||
process.arch === "arm64" ? "win32-arm64" : "win32-x64",
|
||||
);
|
||||
const bundledExePath = path.join(bundledDir, "wgc-capture.exe");
|
||||
const helperId = "wgc-capture";
|
||||
|
||||
if (process.platform !== 'win32') {
|
||||
console.log('[build-windows-capture] Skipping native Windows capture build: host platform is not Windows.');
|
||||
@@ -79,6 +86,16 @@ function findCmake() {
|
||||
const cmake = findCmake();
|
||||
if (!cmake) {
|
||||
if (existsSync(bundledExePath)) {
|
||||
const verification = verifyNativeHelperManifest({
|
||||
projectRoot,
|
||||
helperId,
|
||||
sourceDir,
|
||||
binaryPath: bundledExePath,
|
||||
binaryName: "wgc-capture.exe",
|
||||
});
|
||||
if (!verification.ok) {
|
||||
console.warn(formatNativeHelperManifestWarning("build-windows-capture", verification));
|
||||
}
|
||||
console.log(`[build-windows-capture] Using bundled helper: ${bundledExePath}`);
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -139,6 +156,14 @@ if (existsSync(exePath)) {
|
||||
mkdirSync(bundledDir, { recursive: true });
|
||||
copyFileSync(exePath, bundledExePath);
|
||||
console.log(`[build-windows-capture] Staged bundled helper: ${bundledExePath}`);
|
||||
const manifestPath = updateNativeHelperManifest({
|
||||
projectRoot,
|
||||
helperId,
|
||||
sourceDir,
|
||||
binaryPath: bundledExePath,
|
||||
binaryName: "wgc-capture.exe",
|
||||
});
|
||||
console.log(`[build-windows-capture] Updated helper manifest: ${manifestPath}`);
|
||||
} else {
|
||||
console.error('[build-windows-capture] Expected exe not found at', exePath);
|
||||
process.exit(1);
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import {
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
readFileSync,
|
||||
readdirSync,
|
||||
statSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const MANIFEST_FILE_NAME = "helpers-manifest.json";
|
||||
|
||||
function getPlatformArchDir(platform, arch) {
|
||||
if (platform !== "win32") {
|
||||
throw new Error(`Unsupported native helper manifest platform: ${platform}`);
|
||||
}
|
||||
|
||||
return arch === "arm64" ? "win32-arm64" : "win32-x64";
|
||||
}
|
||||
|
||||
function hashBuffer(buffer) {
|
||||
return createHash("sha256").update(buffer).digest("hex");
|
||||
}
|
||||
|
||||
function collectSourceFiles(sourceDir, rootDir = sourceDir) {
|
||||
const entries = readdirSync(sourceDir, { withFileTypes: true });
|
||||
const files = [];
|
||||
|
||||
for (const entry of entries) {
|
||||
if (entry.name === "build" || entry.name === "bin") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const absolutePath = path.join(sourceDir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
files.push(...collectSourceFiles(absolutePath, rootDir));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!entry.isFile()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
files.push(path.relative(rootDir, absolutePath).replaceAll("\\", "/"));
|
||||
}
|
||||
|
||||
files.sort((left, right) => left.localeCompare(right));
|
||||
return files;
|
||||
}
|
||||
|
||||
function hashSourceTree(sourceDir) {
|
||||
const hash = createHash("sha256");
|
||||
for (const relativePath of collectSourceFiles(sourceDir)) {
|
||||
const absolutePath = path.join(sourceDir, relativePath);
|
||||
hash.update(relativePath);
|
||||
hash.update("\n");
|
||||
hash.update(readFileSync(absolutePath));
|
||||
hash.update("\n");
|
||||
}
|
||||
|
||||
return hash.digest("hex");
|
||||
}
|
||||
|
||||
function hashFile(filePath) {
|
||||
return hashBuffer(readFileSync(filePath));
|
||||
}
|
||||
|
||||
export function getNativeHelperManifestPath({ projectRoot, platform = process.platform, arch = process.arch }) {
|
||||
return path.join(
|
||||
projectRoot,
|
||||
"electron",
|
||||
"native",
|
||||
"bin",
|
||||
getPlatformArchDir(platform, arch),
|
||||
MANIFEST_FILE_NAME,
|
||||
);
|
||||
}
|
||||
|
||||
function readManifest(manifestPath, platform, arch) {
|
||||
if (!existsSync(manifestPath)) {
|
||||
return {
|
||||
version: 1,
|
||||
platform,
|
||||
arch,
|
||||
helpers: {},
|
||||
};
|
||||
}
|
||||
|
||||
const manifestContent = JSON.parse(readFileSync(manifestPath, "utf8"));
|
||||
return {
|
||||
version: 1,
|
||||
platform,
|
||||
arch,
|
||||
helpers: {},
|
||||
...manifestContent,
|
||||
};
|
||||
}
|
||||
|
||||
export function updateNativeHelperManifest({
|
||||
projectRoot,
|
||||
helperId,
|
||||
sourceDir,
|
||||
binaryPath,
|
||||
binaryName,
|
||||
platform = process.platform,
|
||||
arch = process.arch,
|
||||
}) {
|
||||
const manifestPath = getNativeHelperManifestPath({ projectRoot, platform, arch });
|
||||
const manifestDir = path.dirname(manifestPath);
|
||||
mkdirSync(manifestDir, { recursive: true });
|
||||
|
||||
const manifest = readManifest(manifestPath, platform, arch);
|
||||
manifest.helpers[helperId] = {
|
||||
binaryName,
|
||||
binarySha256: hashFile(binaryPath),
|
||||
sourceDir: path.relative(projectRoot, sourceDir).replaceAll("\\", "/"),
|
||||
sourceFingerprint: hashSourceTree(sourceDir),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
writeFileSync(`${manifestPath}`, `${JSON.stringify(manifest, null, 2)}\n`);
|
||||
return manifestPath;
|
||||
}
|
||||
|
||||
export function verifyNativeHelperManifest({
|
||||
projectRoot,
|
||||
helperId,
|
||||
sourceDir,
|
||||
binaryPath,
|
||||
binaryName,
|
||||
platform = process.platform,
|
||||
arch = process.arch,
|
||||
}) {
|
||||
const manifestPath = getNativeHelperManifestPath({ projectRoot, platform, arch });
|
||||
if (!existsSync(manifestPath)) {
|
||||
return {
|
||||
ok: false,
|
||||
manifestPath,
|
||||
reasons: ["manifest missing"],
|
||||
};
|
||||
}
|
||||
|
||||
if (!existsSync(binaryPath)) {
|
||||
return {
|
||||
ok: false,
|
||||
manifestPath,
|
||||
reasons: ["bundled helper missing"],
|
||||
};
|
||||
}
|
||||
|
||||
const manifest = readManifest(manifestPath, platform, arch);
|
||||
const helperManifest = manifest.helpers?.[helperId];
|
||||
if (!helperManifest) {
|
||||
return {
|
||||
ok: false,
|
||||
manifestPath,
|
||||
reasons: [`${helperId} entry missing`],
|
||||
};
|
||||
}
|
||||
|
||||
const reasons = [];
|
||||
if (helperManifest.binaryName !== binaryName) {
|
||||
reasons.push(`expected binary ${binaryName}, found ${helperManifest.binaryName ?? "unknown"}`);
|
||||
}
|
||||
|
||||
const expectedBinaryHash = helperManifest.binarySha256;
|
||||
const actualBinaryHash = hashFile(binaryPath);
|
||||
if (expectedBinaryHash !== actualBinaryHash) {
|
||||
reasons.push("binary hash mismatch");
|
||||
}
|
||||
|
||||
const expectedSourceFingerprint = helperManifest.sourceFingerprint;
|
||||
const actualSourceFingerprint = hashSourceTree(sourceDir);
|
||||
if (expectedSourceFingerprint !== actualSourceFingerprint) {
|
||||
reasons.push("source fingerprint mismatch");
|
||||
}
|
||||
|
||||
return {
|
||||
ok: reasons.length === 0,
|
||||
manifestPath,
|
||||
reasons,
|
||||
};
|
||||
}
|
||||
|
||||
export function formatNativeHelperManifestWarning(helperLabel, verificationResult) {
|
||||
const reasonText = verificationResult.reasons.join(", ");
|
||||
return `[${helperLabel}] Bundled helper provenance check failed (${reasonText}). Rebuild the helper to refresh ${path.basename(verificationResult.manifestPath)}.`;
|
||||
}
|
||||
Reference in New Issue
Block a user