Automate source resource rebuild for SF
This commit is contained in:
parent
6ccce42c62
commit
50975c19e0
4 changed files with 214 additions and 65 deletions
|
|
@ -30,10 +30,14 @@ set -euo pipefail
|
|||
|
||||
SCRIPT_DIR=$(cd -- "$(dirname -- "$(readlink -f "${BASH_SOURCE[0]}")")" &>/dev/null && pwd)
|
||||
SF_SOURCE_ROOT=$(cd -- "$SCRIPT_DIR/.." &>/dev/null && pwd)
|
||||
NODE_BIN=${SF_NODE_BIN:-node}
|
||||
|
||||
export SF_BIN_PATH="$SCRIPT_DIR/sf-from-source"
|
||||
export SF_CLI_PATH="${SF_CLI_PATH:-$SCRIPT_DIR/sf-from-source}"
|
||||
|
||||
exec node \
|
||||
"$NODE_BIN" "$SF_SOURCE_ROOT/scripts/ensure-source-resources.cjs"
|
||||
|
||||
exec "$NODE_BIN" \
|
||||
--import "$SF_SOURCE_ROOT/src/resources/extensions/sf/tests/resolve-ts.mjs" \
|
||||
--experimental-strip-types \
|
||||
--no-warnings \
|
||||
|
|
|
|||
|
|
@ -1,53 +1,70 @@
|
|||
#!/usr/bin/env node
|
||||
const { spawnSync } = require('child_process');
|
||||
const { copyFileSync, mkdirSync, readdirSync, rmSync } = require('fs');
|
||||
const { dirname, join } = require('path');
|
||||
const { spawnSync } = require("node:child_process");
|
||||
const {
|
||||
copyFileSync,
|
||||
mkdirSync,
|
||||
readFileSync,
|
||||
readdirSync,
|
||||
rmSync,
|
||||
writeFileSync,
|
||||
} = require("node:fs");
|
||||
const { dirname, join } = require("node:path");
|
||||
|
||||
function copyNonTsFiles(srcDir, destDir) {
|
||||
for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
|
||||
const srcPath = join(srcDir, entry.name);
|
||||
const destPath = join(destDir, entry.name);
|
||||
for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
|
||||
const srcPath = join(srcDir, entry.name);
|
||||
const destPath = join(destDir, entry.name);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
copyNonTsFiles(srcPath, destPath);
|
||||
continue;
|
||||
}
|
||||
if (entry.isDirectory()) {
|
||||
copyNonTsFiles(srcPath, destPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.name.endsWith('.ts') || entry.name.endsWith('.tsx')) {
|
||||
continue;
|
||||
}
|
||||
if (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mkdirSync(dirname(destPath), { recursive: true });
|
||||
mkdirSync(dirname(destPath), { recursive: true });
|
||||
|
||||
// Rewrite pi.extensions paths from .ts to .js in package.json files
|
||||
// so they match the compiled output (tsc compiles index.ts → index.js
|
||||
// but package.json is copied as-is).
|
||||
if (entry.name === 'package.json') {
|
||||
try {
|
||||
const pkg = JSON.parse(require('fs').readFileSync(srcPath, 'utf-8'));
|
||||
if (Array.isArray(pkg?.pi?.extensions)) {
|
||||
pkg.pi.extensions = pkg.pi.extensions.map(ext =>
|
||||
ext.replace(/\.ts$/, '.js').replace(/\.tsx$/, '.js')
|
||||
);
|
||||
require('fs').writeFileSync(destPath, JSON.stringify(pkg, null, 2) + '\n');
|
||||
continue;
|
||||
}
|
||||
} catch { /* fall through to plain copy */ }
|
||||
}
|
||||
// Rewrite pi.extensions paths from .ts to .js in package.json files
|
||||
// so they match the compiled output (tsc compiles index.ts → index.js
|
||||
// but package.json is copied as-is).
|
||||
if (entry.name === "package.json") {
|
||||
try {
|
||||
const pkg = JSON.parse(readFileSync(srcPath, "utf-8"));
|
||||
if (Array.isArray(pkg?.pi?.extensions)) {
|
||||
pkg.pi.extensions = pkg.pi.extensions.map((ext) =>
|
||||
ext.replace(/\.ts$/, ".js").replace(/\.tsx$/, ".js"),
|
||||
);
|
||||
writeFileSync(destPath, JSON.stringify(pkg, null, 2) + "\n");
|
||||
continue;
|
||||
}
|
||||
} catch {
|
||||
/* fall through to plain copy */
|
||||
}
|
||||
}
|
||||
|
||||
copyFileSync(srcPath, destPath);
|
||||
}
|
||||
copyFileSync(srcPath, destPath);
|
||||
}
|
||||
}
|
||||
|
||||
rmSync('dist/resources', { recursive: true, force: true });
|
||||
rmSync("dist/resources", { recursive: true, force: true });
|
||||
|
||||
const tscBin = require.resolve('typescript/bin/tsc');
|
||||
const compile = spawnSync(process.execPath, [tscBin, '--project', 'tsconfig.resources.json'], {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
const tscBin = require.resolve("typescript/bin/tsc");
|
||||
const compile = spawnSync(
|
||||
process.execPath,
|
||||
[tscBin, "--project", "tsconfig.resources.json"],
|
||||
{
|
||||
stdio: "inherit",
|
||||
},
|
||||
);
|
||||
|
||||
if (compile.status !== 0) {
|
||||
process.exit(compile.status ?? 1);
|
||||
process.exit(compile.status ?? 1);
|
||||
}
|
||||
|
||||
copyNonTsFiles('src/resources', 'dist/resources');
|
||||
copyNonTsFiles("src/resources", "dist/resources");
|
||||
writeFileSync(
|
||||
join("dist", "resources", ".sf-resource-build-stamp"),
|
||||
`${new Date().toISOString()}\n`,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,33 +1,69 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import { spawn } from 'node:child_process'
|
||||
import { dirname, resolve } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { spawn, spawnSync } from "node:child_process";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
const root = resolve(__dirname, '..')
|
||||
const srcLoaderPath = resolve(root, 'src', 'loader.ts')
|
||||
const resolveTsPath = resolve(root, 'src', 'resources', 'extensions', 'sf', 'tests', 'resolve-ts.mjs')
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, "..");
|
||||
const sourceBinPath = resolve(root, "bin", "sf-from-source");
|
||||
const ensureResourcesPath = resolve(
|
||||
root,
|
||||
"scripts",
|
||||
"ensure-source-resources.cjs",
|
||||
);
|
||||
const srcLoaderPath = resolve(root, "src", "loader.ts");
|
||||
const resolveTsPath = resolve(
|
||||
root,
|
||||
"src",
|
||||
"resources",
|
||||
"extensions",
|
||||
"sf",
|
||||
"tests",
|
||||
"resolve-ts.mjs",
|
||||
);
|
||||
|
||||
const resourceBuild = spawnSync(process.execPath, [ensureResourcesPath], {
|
||||
cwd: root,
|
||||
stdio: "inherit",
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
if (resourceBuild.status !== 0) {
|
||||
process.exit(resourceBuild.status ?? 1);
|
||||
}
|
||||
|
||||
const child = spawn(
|
||||
process.execPath,
|
||||
['--import', resolveTsPath, '--experimental-strip-types', srcLoaderPath, ...process.argv.slice(2)],
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
stdio: 'inherit',
|
||||
env: process.env,
|
||||
},
|
||||
)
|
||||
process.execPath,
|
||||
[
|
||||
"--import",
|
||||
resolveTsPath,
|
||||
"--experimental-strip-types",
|
||||
srcLoaderPath,
|
||||
...process.argv.slice(2),
|
||||
],
|
||||
{
|
||||
cwd: process.cwd(),
|
||||
stdio: "inherit",
|
||||
env: {
|
||||
...process.env,
|
||||
SF_BIN_PATH: process.env.SF_BIN_PATH || sourceBinPath,
|
||||
SF_CLI_PATH: process.env.SF_CLI_PATH || sourceBinPath,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
child.on('error', (error) => {
|
||||
console.error(`[forge] Failed to launch local dev CLI: ${error instanceof Error ? error.message : String(error)}`)
|
||||
process.exit(1)
|
||||
})
|
||||
child.on("error", (error) => {
|
||||
console.error(
|
||||
`[forge] Failed to launch local dev CLI: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
child.on('exit', (code, signal) => {
|
||||
if (signal) {
|
||||
process.kill(process.pid, signal)
|
||||
return
|
||||
}
|
||||
process.exit(code ?? 0)
|
||||
})
|
||||
child.on("exit", (code, signal) => {
|
||||
if (signal) {
|
||||
process.kill(process.pid, signal);
|
||||
return;
|
||||
}
|
||||
process.exit(code ?? 0);
|
||||
});
|
||||
|
|
|
|||
92
scripts/ensure-source-resources.cjs
Normal file
92
scripts/ensure-source-resources.cjs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
#!/usr/bin/env node
|
||||
const { spawnSync } = require("node:child_process");
|
||||
const { existsSync, readdirSync, statSync } = require("node:fs");
|
||||
const { join, resolve } = require("node:path");
|
||||
|
||||
const root = resolve(__dirname, "..");
|
||||
const srcResources = join(root, "src", "resources");
|
||||
const distResources = join(root, "dist", "resources");
|
||||
const stampPath = join(distResources, ".sf-resource-build-stamp");
|
||||
const copyResourcesScript = join(root, "scripts", "copy-resources.cjs");
|
||||
|
||||
function latestMtimeMs(path) {
|
||||
let latest = 0;
|
||||
const stack = [path];
|
||||
|
||||
while (stack.length > 0) {
|
||||
const current = stack.pop();
|
||||
if (!current) continue;
|
||||
|
||||
let entries;
|
||||
try {
|
||||
entries = readdirSync(current, { withFileTypes: true });
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const entry of entries) {
|
||||
const entryPath = join(current, entry.name);
|
||||
let stat;
|
||||
try {
|
||||
stat = statSync(entryPath);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
latest = Math.max(latest, stat.mtimeMs);
|
||||
if (entry.isDirectory()) {
|
||||
stack.push(entryPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return latest;
|
||||
}
|
||||
|
||||
function sourceInputsMtimeMs() {
|
||||
return Math.max(
|
||||
latestMtimeMs(srcResources),
|
||||
existsSync(copyResourcesScript) ? statSync(copyResourcesScript).mtimeMs : 0,
|
||||
existsSync(join(root, "tsconfig.resources.json"))
|
||||
? statSync(join(root, "tsconfig.resources.json")).mtimeMs
|
||||
: 0,
|
||||
);
|
||||
}
|
||||
|
||||
function hasCompleteResourceBuild() {
|
||||
return (
|
||||
existsSync(stampPath) &&
|
||||
existsSync(join(distResources, "SF-WORKFLOW.md")) &&
|
||||
existsSync(join(distResources, "agents")) &&
|
||||
existsSync(join(distResources, "extensions"))
|
||||
);
|
||||
}
|
||||
|
||||
function shouldRebuild() {
|
||||
if (process.env.SF_DEV_CLI_SKIP_RESOURCE_BUILD === "1") return false;
|
||||
if (process.env.SF_SKIP_SOURCE_RESOURCE_BUILD === "1") return false;
|
||||
if (!hasCompleteResourceBuild()) return true;
|
||||
|
||||
let stampMtime = 0;
|
||||
try {
|
||||
stampMtime = statSync(stampPath).mtimeMs;
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
|
||||
return sourceInputsMtimeMs() > stampMtime;
|
||||
}
|
||||
|
||||
if (shouldRebuild()) {
|
||||
console.error(
|
||||
"[forge] Source resources changed; rebuilding dist/resources before launch...",
|
||||
);
|
||||
const result = spawnSync(process.execPath, [copyResourcesScript], {
|
||||
cwd: root,
|
||||
stdio: "inherit",
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
process.exit(result.status ?? 1);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue