diff --git a/electrobun.config.ts b/electrobun.config.ts index 6eaa547..2069335 100644 --- a/electrobun.config.ts +++ b/electrobun.config.ts @@ -2,6 +2,15 @@ import type { ElectrobunConfig } from "electrobun"; import { readFileSync } from "node:fs"; const packageJson = JSON.parse(readFileSync("./package.json", "utf8")); +const buildEnv = process.env.ELECTROBUN_BUILD_ENV || "dev"; +const isStable = buildEnv === "stable"; + +const nativeExternals = [ + "canvas", + "@napi-rs/canvas", + "@img/sharp-libvips*", + "@napi-rs/*", +]; export default { app: { @@ -9,6 +18,7 @@ export default { identifier: "com.tombutcher.farmcontrolserver", version: packageJson.version, description: "Farm Control Server desktop host application", + urlSchemes: ["farmcontrolserver"], }, runtime: { exitOnLastWindowClosed: false, @@ -16,18 +26,23 @@ export default { build: { buildFolder: "build", artifactFolder: "app_dist", + useAsar: isStable, + asarUnpack: ["*.node", "*.dll", "*.dylib", "*.so"], bun: { entrypoint: "src/bun/index.js", + external: nativeExternals, + packages: "external", }, copy: { - "dist/app/index.html": "views/mainview/index.html", - "dist/app/assets": "views/mainview/assets", + "dist/mainview": "views/mainview", "config.json": "config.json", }, - watch: ["scripts"], - watchIgnore: ["dist/**", "app_dist/**"], + watch: ["scripts", "src/app"], + watchIgnore: ["dist/**", "build/**", "app_dist/**"], mac: { bundleCEF: false, + codesign: isStable, + notarize: isStable, }, linux: { bundleCEF: false, diff --git a/package.json b/package.json index 790e973..7da12b2 100644 --- a/package.json +++ b/package.json @@ -2,23 +2,16 @@ "name": "farmcontrol-server", "version": "1.0.0", "description": "Connects to moonraker and also manages the socket connection to the printer.", - "main": "build/index.js", - "bin": "build/index.js", "type": "module", "scripts": { - "start": "bun run build/server && bun build/index.js", + "start": "bun src/headless.js", "dev": "cross-env NODE_ENV=development bun --watch src/index.js", "dev:app": "concurrently \"cross-env NODE_ENV=development bun run dev:renderer\" \"cross-env NODE_ENV=development electrobun dev\"", "dev:renderer": "vite src/app --port 5287 --no-open", - "build": "bun run cleanBuild && bun run build:server && bun run build:renderer", - "build:server": "bun run scripts/build-server.mjs", - "build:renderer": "vite build src/app", - "build:app": "bun run build && electrobun build --env=stable", + "build:app": "electrobun build --env=stable", "build:app:mac": "bun scripts/build-macos.mjs", - "build:linux": "bun run cleanBuild && bun run build:server && bun run build:linux-binary && bun run build:linux-packages", - "build:linux-binary": "bun scripts/build-linux-binary.mjs", - "build:linux-packages": "bash scripts/build-linux-packages.sh", - "cleanBuild": "bun scripts/clean-build.mjs" + "build:linux": "bun scripts/build-linux.mjs", + "clean": "bun scripts/clean-build.mjs" }, "author": "Tom Butcher", "license": "ISC", diff --git a/readme.md b/readme.md index 3f92c1d..ad5c98a 100644 --- a/readme.md +++ b/readme.md @@ -37,10 +37,19 @@ A Bun application that bridges communication between Farm Control cloud services ## Building -- Desktop app (current platform): `bun run build:app` -- Linux headless packages: `bun run build:linux` +Build configuration lives in `electrobun.config.ts` and follows the [Electrobun build configuration](https://framework.blackboard.sh/electrobun/apis/cli/build-configuration/) model: -Electrobun handles desktop packaging. Linux headless builds use `bun build --compile`. +- **preBuild** — validates sources, writes build metadata, and runs the Vite renderer build +- **postPackage** — normalizes release artifacts (DMG/PKG on macOS, MSI on Windows) + +Commands: + +- Desktop app (current platform): `bun run build:app` +- macOS universal (arm64 + x64): `bun run build:app:mac` +- Linux headless packages: `bun run build:linux` +- Clean build outputs: `bun run clean` + +Desktop artifacts are written to `app_dist/`. Linux headless builds produce `.deb` and `.rpm` packages in the same folder. ## Configuration diff --git a/scripts/build-linux.mjs b/scripts/build-linux.mjs new file mode 100644 index 0000000..1ed5361 --- /dev/null +++ b/scripts/build-linux.mjs @@ -0,0 +1,35 @@ +import { spawnSync } from "node:child_process"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); + +function run(scriptRelativePath) { + const scriptPath = path.join(rootDir, scriptRelativePath); + const result = spawnSync("bun", [scriptPath], { + cwd: rootDir, + stdio: "inherit", + env: process.env, + }); + + if (result.status !== 0) { + process.exit(result.status ?? 1); + } +} + +function runBash(scriptRelativePath) { + const scriptPath = path.join(rootDir, scriptRelativePath); + const result = spawnSync("bash", [scriptPath], { + cwd: rootDir, + stdio: "inherit", + env: process.env, + }); + + if (result.status !== 0) { + process.exit(result.status ?? 1); + } +} + +run("scripts/write-build-info.mjs"); +run("scripts/build-linux-binary.mjs"); +runBash("scripts/build-linux-packages.sh"); diff --git a/scripts/build-macos.mjs b/scripts/build-macos.mjs index 7d9774a..37a6e2c 100644 --- a/scripts/build-macos.mjs +++ b/scripts/build-macos.mjs @@ -41,8 +41,6 @@ function getElectrobunInvocation(targetArch) { return null; } -run("bun", ["run", "build"]); - for (const targetArch of targetArchs) { const invocation = getElectrobunInvocation(targetArch); if (!invocation) { diff --git a/scripts/build-renderer.mjs b/scripts/build-renderer.mjs new file mode 100644 index 0000000..6f982ca --- /dev/null +++ b/scripts/build-renderer.mjs @@ -0,0 +1,22 @@ +import { spawnSync } from "node:child_process"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); + +const result = spawnSync("bun", ["x", "vite", "build", "src/app"], { + cwd: rootDir, + stdio: "inherit", + env: { + ...process.env, + NODE_ENV: process.env.NODE_ENV || "production", + }, +}); + +if (result.status !== 0) { + process.exit(result.status ?? 1); +} + +console.log( + `build-renderer: output at ${path.join(rootDir, "dist/mainview")}`, +); diff --git a/scripts/build-server.mjs b/scripts/build-server.mjs deleted file mode 100644 index d56bd30..0000000 --- a/scripts/build-server.mjs +++ /dev/null @@ -1,15 +0,0 @@ -import { cpSync, mkdirSync, rmSync } from "node:fs"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - -const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); -const buildDir = path.join(rootDir, "build"); - -rmSync(buildDir, { recursive: true, force: true }); -mkdirSync(buildDir, { recursive: true }); - -cpSync(path.join(rootDir, "src"), buildDir, { recursive: true }); -cpSync(path.join(rootDir, "package.json"), path.join(buildDir, "package.json")); -cpSync(path.join(rootDir, "config.json"), path.join(buildDir, "config.json")); - -console.log(`Server sources copied to ${buildDir}`); diff --git a/scripts/clean-build.mjs b/scripts/clean-build.mjs index 967ee6e..519a6ca 100644 --- a/scripts/clean-build.mjs +++ b/scripts/clean-build.mjs @@ -4,6 +4,8 @@ import { fileURLToPath } from "node:url"; const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); -for (const dir of ["build", "dist"]) { +for (const dir of ["build", "dist", "app_dist", "artifacts"]) { rmSync(path.join(rootDir, dir), { recursive: true, force: true }); } + +console.log("clean: removed build, dist, app_dist, and artifacts"); diff --git a/scripts/pre-build.mjs b/scripts/pre-build.mjs index 35b9d80..11c2c69 100644 --- a/scripts/pre-build.mjs +++ b/scripts/pre-build.mjs @@ -1,5 +1,6 @@ import { existsSync } from "node:fs"; import path from "node:path"; +import { spawnSync } from "node:child_process"; import { fileURLToPath } from "node:url"; const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); @@ -7,8 +8,11 @@ const buildEnv = process.env.ELECTROBUN_BUILD_ENV || "dev"; const requiredFiles = [ "src/bun/index.js", + "src/app/index.html", + "src/app/main.jsx", "electrobun.config.ts", "package.json", + "config.json", ]; for (const relativePath of requiredFiles) { @@ -35,4 +39,45 @@ if (buildEnv === "stable" && process.env.ELECTROBUN_OS === "macos") { } } -console.log(`pre-build: validation passed (${buildEnv})`); +const writeBuildInfo = spawnSync( + "bun", + [path.join(rootDir, "scripts/write-build-info.mjs")], + { cwd: rootDir, stdio: "inherit", env: process.env }, +); + +if (writeBuildInfo.status !== 0) { + process.exit(writeBuildInfo.status ?? 1); +} + +if (buildEnv === "dev") { + console.log( + "pre-build: dev environment — skipping production renderer build (use dev:renderer for Vite)", + ); + console.log(`pre-build: validation passed (${buildEnv})`); + process.exit(0); +} + +const buildRenderer = spawnSync( + "bun", + [path.join(rootDir, "scripts/build-renderer.mjs")], + { + cwd: rootDir, + stdio: "inherit", + env: { + ...process.env, + NODE_ENV: "production", + }, + }, +); + +if (buildRenderer.status !== 0) { + process.exit(buildRenderer.status ?? 1); +} + +const rendererIndex = path.join(rootDir, "dist/mainview/index.html"); +if (!existsSync(rendererIndex)) { + console.error(`pre-build: renderer output not found: dist/mainview/index.html`); + process.exit(1); +} + +console.log(`pre-build: validation and renderer build passed (${buildEnv})`); diff --git a/scripts/write-build-info.mjs b/scripts/write-build-info.mjs new file mode 100644 index 0000000..e013764 --- /dev/null +++ b/scripts/write-build-info.mjs @@ -0,0 +1,15 @@ +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const buildNumber = + process.env.BUILD_NUMBER || process.env.VITE_BUILD_NUMBER || "dev"; + +const buildInfoPath = path.join(rootDir, "src/buildInfo.json"); + +await Bun.write( + buildInfoPath, + `${JSON.stringify({ buildNumber }, null, 2)}\n`, +); + +console.log(`write-build-info: ${buildInfoPath} (buildNumber=${buildNumber})`); diff --git a/src/app/index.html b/src/app/index.html index 4bbd666..db279e5 100644 --- a/src/app/index.html +++ b/src/app/index.html @@ -3,7 +3,7 @@
- +