mirror of
https://github.com/Pandipipas/scoreko-electron-dev.git
synced 2026-06-06 05:32:06 +00:00
feat: enhance NodeCG runtime management and packaging
- Update .gitignore and .prettierignore to exclude additional cache and configuration files. - Revise README.md for clarity on build processes and runtime behavior. - Improve architecture documentation to reflect changes in startup flow and module responsibilities. - Modify troubleshooting guide to address common runtime issues and installation steps. - Enhance ESLint configuration to ignore more directories. - Update package.json scripts for better build and distribution processes. - Introduce build-scoreko-bundle.mjs for building the Scoreko bundle. - Implement prepare-nodecg-runtime.mjs for managing NodeCG runtime installation and updates. - Add runtime-provisioner.ts to handle user-specific NodeCG runtime provisioning. - Create tests for runtime provisioning to ensure correct behavior. - Refactor process-manager.ts and main.ts to integrate new runtime management logic.
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/env node
|
||||
import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { spawnSync } from "node:child_process";
|
||||
|
||||
const electronRoot = process.cwd();
|
||||
const bundleRoot = path.resolve(electronRoot, "..");
|
||||
const runtimeRoot = path.join(electronRoot, "lib", "nodecg");
|
||||
const runtimeNodeModules = path.join(runtimeRoot, "node_modules");
|
||||
const bundleName = process.env.NODECG_BUNDLE_NAME?.trim() || "scoreko-dev";
|
||||
const runtimeBundleRoot = path.join(runtimeRoot, "bundles", bundleName);
|
||||
|
||||
const bundleEntries = [
|
||||
"assets",
|
||||
"dashboard",
|
||||
"extension",
|
||||
"graphics",
|
||||
"schemas",
|
||||
"shared",
|
||||
"configschema.json",
|
||||
"LICENSE",
|
||||
"package.json",
|
||||
"README.md",
|
||||
];
|
||||
|
||||
const requiredBundleEntries = ["dashboard", "extension", "graphics", "schemas", "shared", "package.json"];
|
||||
|
||||
function readJson(filePath) {
|
||||
return JSON.parse(readFileSync(filePath, "utf8"));
|
||||
}
|
||||
|
||||
function copyIfExists(source, destination) {
|
||||
if (!existsSync(source)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cpSync(source, destination, {
|
||||
recursive: true,
|
||||
force: true,
|
||||
dereference: true,
|
||||
filter: (sourcePath) => !sourcePath.split(path.sep).includes("node_modules"),
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
function run(command, args, cwd) {
|
||||
const result = spawnSync(command, args, {
|
||||
cwd,
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
env: {
|
||||
...process.env,
|
||||
npm_config_cache: process.env.npm_config_cache ?? path.join(electronRoot, ".npm-runtime-cache"),
|
||||
},
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
throw new Error(`${command} ${args.join(" ")} failed: ${result.error.message}`);
|
||||
}
|
||||
|
||||
if (result.status !== 0) {
|
||||
throw new Error(`${command} ${args.join(" ")} failed with code ${result.status}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getInstalledNodecgVersion() {
|
||||
const nodecgPackagePath = path.join(bundleRoot, "node_modules", "nodecg", "package.json");
|
||||
|
||||
if (!existsSync(nodecgPackagePath)) {
|
||||
throw new Error(
|
||||
[
|
||||
"NodeCG is not installed in the parent project.",
|
||||
`Expected: ${nodecgPackagePath}`,
|
||||
`Run 'pnpm install' from ${bundleRoot} before packaging.`,
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
|
||||
return readJson(nodecgPackagePath).version;
|
||||
}
|
||||
|
||||
function assertBundleBuildExists() {
|
||||
for (const entry of requiredBundleEntries) {
|
||||
const source = path.join(bundleRoot, entry);
|
||||
if (!existsSync(source)) {
|
||||
throw new Error(
|
||||
[
|
||||
`The built Scoreko bundle is missing '${entry}'.`,
|
||||
`Expected: ${source}`,
|
||||
`Run 'pnpm build' from ${bundleRoot} before packaging.`,
|
||||
].join("\n"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createRuntimePackageJson() {
|
||||
const bundlePackageJson = readJson(path.join(bundleRoot, "package.json"));
|
||||
const dependencies = {
|
||||
nodecg: getInstalledNodecgVersion(),
|
||||
...(bundlePackageJson.dependencies ?? {}),
|
||||
};
|
||||
|
||||
writeFileSync(
|
||||
path.join(runtimeRoot, "package.json"),
|
||||
`${JSON.stringify(
|
||||
{
|
||||
private: true,
|
||||
name: "scoreko-nodecg-runtime",
|
||||
version: bundlePackageJson.version ?? "0.0.0",
|
||||
description: "Packaged NodeCG runtime for Scoreko Desktop.",
|
||||
type: "commonjs",
|
||||
scripts: {
|
||||
start: "node index.js",
|
||||
},
|
||||
dependencies,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
);
|
||||
|
||||
writeFileSync(path.join(runtimeRoot, "index.js"), 'require("nodecg");\n');
|
||||
}
|
||||
|
||||
function copyBundle() {
|
||||
mkdirSync(runtimeBundleRoot, { recursive: true });
|
||||
|
||||
for (const entry of bundleEntries) {
|
||||
copyIfExists(path.join(bundleRoot, entry), path.join(runtimeBundleRoot, entry));
|
||||
}
|
||||
}
|
||||
|
||||
function writeManifest() {
|
||||
const bundlePackageJson = readJson(path.join(bundleRoot, "package.json"));
|
||||
const runtimePackageJson = readJson(path.join(runtimeRoot, "package.json"));
|
||||
|
||||
writeFileSync(
|
||||
path.join(runtimeRoot, ".scoreko-runtime.json"),
|
||||
`${JSON.stringify(
|
||||
{
|
||||
bundleName,
|
||||
bundleVersion: bundlePackageJson.version ?? "0.0.0",
|
||||
nodecgVersion: runtimePackageJson.dependencies.nodecg,
|
||||
generatedAt: new Date().toISOString(),
|
||||
},
|
||||
null,
|
||||
2,
|
||||
)}\n`,
|
||||
);
|
||||
}
|
||||
|
||||
function installRuntimeDependencies() {
|
||||
if (process.env.SCOREKO_SKIP_RUNTIME_NPM_INSTALL === "1") {
|
||||
console.log("[prepare-runtime] Skipping runtime npm install by environment request.");
|
||||
return;
|
||||
}
|
||||
|
||||
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
|
||||
run(npmCommand, ["install", "--omit=dev", "--no-audit", "--no-fund"], runtimeRoot);
|
||||
}
|
||||
|
||||
function main() {
|
||||
assertBundleBuildExists();
|
||||
|
||||
rmSync(runtimeRoot, { recursive: true, force: true });
|
||||
mkdirSync(runtimeNodeModules, { recursive: true });
|
||||
mkdirSync(path.join(runtimeRoot, "bundles"), { recursive: true });
|
||||
|
||||
createRuntimePackageJson();
|
||||
copyBundle();
|
||||
installRuntimeDependencies();
|
||||
writeManifest();
|
||||
|
||||
console.log(`[prepare-runtime] NodeCG runtime ready at ${runtimeRoot}`);
|
||||
}
|
||||
|
||||
try {
|
||||
main();
|
||||
} catch (error) {
|
||||
console.error(error instanceof Error ? error.message : error);
|
||||
process.exit(1);
|
||||
}
|
||||
Reference in New Issue
Block a user