Publish Forma Engine 0.3.0 source with documentation and CI
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { promises as fs } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { unpackProject, decodeData } from "../engine/archive.ts";
|
||||
import { BuildManager } from "../server/builds.ts";
|
||||
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const arg = (n: string) => {
|
||||
const i = process.argv.indexOf(n);
|
||||
return i < 0 ? undefined : process.argv[i + 1];
|
||||
};
|
||||
const input = path.resolve(arg("--project") || "projects/MyGame");
|
||||
let manager: BuildManager | undefined;
|
||||
try {
|
||||
const isDir = (await fs.stat(input)).isDirectory();
|
||||
const project = unpackProject(
|
||||
new Uint8Array(
|
||||
await fs.readFile(isDir ? path.join(input, "project.forma.json") : input),
|
||||
),
|
||||
);
|
||||
const base = isDir ? input : path.dirname(input);
|
||||
const read = async (uri: string) => {
|
||||
if (uri.startsWith("data:")) return decodeData(uri);
|
||||
const clean = uri.replace(/^\/+/, "").replace(/^\.\//, "");
|
||||
if (!/^(assets|engine)\/[a-zA-Z0-9_.-]+$/.test(clean))
|
||||
throw Error("Invalid asset path");
|
||||
const safe = async (folder: string) => {
|
||||
const file = await fs.realpath(path.join(folder, clean));
|
||||
if (!file.startsWith((await fs.realpath(folder)) + path.sep))
|
||||
throw Error("Asset escapes project");
|
||||
return new Uint8Array(await fs.readFile(file));
|
||||
};
|
||||
if (clean.startsWith("assets/"))
|
||||
try {
|
||||
return await safe(base);
|
||||
} catch (e: any) {
|
||||
if (e.code !== "ENOENT") throw e;
|
||||
}
|
||||
return safe(path.join(root, "public"));
|
||||
};
|
||||
manager = new BuildManager(base, read);
|
||||
await manager.init();
|
||||
const options = {
|
||||
target: arg("--target") || "linux",
|
||||
name: arg("--name") || project.name,
|
||||
appId: arg("--app-id") || "games.forma.mygame",
|
||||
version: arg("--version") || "1.0.0",
|
||||
versionCode: Number(arg("--version-code") || 1),
|
||||
mode: arg("--mode") || "debug",
|
||||
fullscreen: process.argv.includes("--fullscreen"),
|
||||
};
|
||||
const job = await manager.start(project, options, project.revision);
|
||||
let shown = 0;
|
||||
const stop = () => void manager!.cancel(job.id);
|
||||
process.once("SIGINT", stop);
|
||||
process.once("SIGTERM", stop);
|
||||
for (;;) {
|
||||
const j = manager.get(job.id);
|
||||
for (const line of j.logs.slice(shown)) console.log(line);
|
||||
shown = j.logs.length;
|
||||
if (!["queued", "building"].includes(j.status)) {
|
||||
if (j.status !== "succeeded") throw Error(j.error || j.status);
|
||||
for (const file of j.artifacts || [])
|
||||
console.log(await manager.artifact(j.id, file.name));
|
||||
break;
|
||||
}
|
||||
await new Promise((r) => setTimeout(r, 750));
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(String(e));
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
await manager?.close();
|
||||
}
|
||||
Reference in New Issue
Block a user