Update CI pipeline, documentation, and config for Bun/Electrobun.
Some checks failed
farmcontrol/farmcontrol-server/pipeline/head There was a failure building this commit

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Tom Butcher 2026-07-28 22:03:23 +01:00
parent 8453e7c7cf
commit fa389e3eb7
4 changed files with 73 additions and 116 deletions

4
.gitignore vendored
View File

@ -142,3 +142,7 @@ data/*
dist/*
src/buildInfo.json
.electrobun-cache
artifacts
app_dist
build

84
Jenkinsfile vendored
View File

@ -3,16 +3,14 @@ properties([
])
def writeBuildMetadata() {
nodejs(nodeJSInstallationName: 'Node23') {
if (isUnix()) {
sh '''
node -e "const fs = require('fs'); fs.writeFileSync('src/buildInfo.json', JSON.stringify({ buildNumber: process.env.BUILD_NUMBER || 'dev' }, null, 2) + '\\n');"
'''
} else {
bat '''
node -e "const fs = require('fs'); fs.writeFileSync('src/buildInfo.json', JSON.stringify({ buildNumber: process.env.BUILD_NUMBER || 'dev' }, null, 2) + '\\n');"
'''
}
if (isUnix()) {
sh '''
bun --eval "await Bun.write('src/buildInfo.json', JSON.stringify({ buildNumber: process.env.BUILD_NUMBER || 'dev' }, null, 2) + '\\n')"
'''
} else {
bat '''
bun --eval "await Bun.write('src/buildInfo.json', JSON.stringify({ buildNumber: process.env.BUILD_NUMBER || 'dev' }, null, 2) + '\\n')"
'''
}
}
@ -23,17 +21,12 @@ def buildLinux() {
checkout scm
}
stage('Setup Node.js (Ubuntu)') {
nodejs(nodeJSInstallationName: 'Node23') {
sh 'node -v'
sh 'pnpm -v'
}
stage('Setup Bun (Ubuntu)') {
sh 'bun -v'
}
stage('Install Dependencies (Ubuntu)') {
nodejs(nodeJSInstallationName: 'Node23') {
sh 'pnpm install --frozen-lockfile --production=false'
}
sh 'bun install --frozen-lockfile'
}
stage('Write Build Metadata (Ubuntu)') {
@ -41,9 +34,7 @@ def buildLinux() {
}
stage('Build (Ubuntu)') {
nodejs(nodeJSInstallationName: 'Node23') {
sh "VITE_BUILD_NUMBER=${env.BUILD_NUMBER} NODE_ENV=production pnpm build:linux"
}
sh "VITE_BUILD_NUMBER=${env.BUILD_NUMBER} NODE_ENV=production bun run build:linux"
}
stage('Archive Artifacts (Ubuntu)') {
@ -62,25 +53,19 @@ def buildOnLabel(label, buildCommand) {
checkout scm
}
stage("Setup Node.js (${label})") {
nodejs(nodeJSInstallationName: 'Node23') {
if (isUnix()) {
sh 'node -v'
sh 'pnpm -v'
} else {
bat 'node -v'
bat 'pnpm -v'
}
stage("Setup Bun (${label})") {
if (isUnix()) {
sh 'bun -v'
} else {
bat 'bun -v'
}
}
stage("Install Dependencies (${label})") {
nodejs(nodeJSInstallationName: 'Node23') {
if (isUnix()) {
sh 'pnpm install --frozen-lockfile --production=false'
} else {
bat 'pnpm install --frozen-lockfile --production=false'
}
if (isUnix()) {
sh 'bun install --frozen-lockfile'
} else {
bat 'bun install --frozen-lockfile'
}
}
@ -89,17 +74,15 @@ def buildOnLabel(label, buildCommand) {
}
stage("Build (${label})") {
nodejs(nodeJSInstallationName: 'Node23') {
if (isUnix()) {
sh "VITE_BUILD_NUMBER=${env.BUILD_NUMBER} NODE_ENV=production ${buildCommand}"
} else {
bat "set VITE_BUILD_NUMBER=${env.BUILD_NUMBER} && set NODE_ENV=production && ${buildCommand}"
}
if (isUnix()) {
sh "VITE_BUILD_NUMBER=${env.BUILD_NUMBER} NODE_ENV=production ${buildCommand}"
} else {
bat "set VITE_BUILD_NUMBER=${env.BUILD_NUMBER} && set NODE_ENV=production && ${buildCommand}"
}
}
stage("Archive Artifacts (${label})") {
archiveArtifacts artifacts: 'app_dist/**/farmcontrol-server-*.dmg, app_dist/**/farmcontrol-server-*.exe, app_dist/**/farmcontrol-server-*.pkg, app_dist/**/farmcontrol-server-*.msi', fingerprint: true
archiveArtifacts artifacts: 'app_dist/**/farmcontrol-server-*', fingerprint: true
}
}
}
@ -109,13 +92,10 @@ def setBuildNameFromPackageVersion() {
node('ubuntu') {
stage('Set Build Name') {
checkout scm
def version
nodejs(nodeJSInstallationName: 'Node23') {
version = sh(
script: "node -p \"require('./package.json').version\"",
returnStdout: true
).trim()
}
def version = sh(
script: "bun --eval \"console.log(JSON.parse(await Bun.file('package.json').text()).version)\"",
returnStdout: true
).trim()
def buildName = "v${version}-b${env.BUILD_NUMBER}"
currentBuild.displayName = buildName
echo "Build name set to: ${buildName}"
@ -127,8 +107,8 @@ try {
setBuildNameFromPackageVersion()
parallel(
'Windows Build': buildOnLabel('windows', 'pnpm build:electron'),
'MacOS Build': buildOnLabel('macos', 'pnpm build:electron:mac'),
'Windows Build': buildOnLabel('windows', 'bun run build:app'),
'MacOS Build': buildOnLabel('macos', 'bun run build:app:mac'),
'Ubuntu Build': { buildLinux() }
)

View File

@ -4,9 +4,10 @@
"url": "https://dev-wss.tombutcher.work",
"apiUrl": "https://dev.tombutcher.work/api",
"host": {
"id": "6a6654425330570c496f12ee",
"authCode": "VCXo44VxFhJb2pvZDQ0b3QPX7lYMg7SGZ6s4HBU72dJb-PBNk81UfwYhqqYKn1Ez"
}
"id": "691a1db49ce913faf0e51284",
"authCode": "6wdCQPKMr_zuFaJe3-AY99uRuAEZRgU2lLNTuAFPW5hPvzzTrSra3rh3woZLj-eE"
},
"dataDir": "/Users/tombutcher/Projects/farmcontrol-server/data"
},
"production": {
"logLevel": "info",

View File

@ -2,83 +2,55 @@
[![Build Status](https://ci.tombutcher.work/buildStatus/icon?job=farmcontrol%2Ffarmcontrol-server%2Fmain&style=flat-square)](https://ci.tombutcher.work/job/farmcontrol/job/farmcontrol-server/job/main/)
A Node.js application that bridges communication between external websocket clients and a Moonraker-controlled 3D printer.
A Bun application that bridges communication between Farm Control cloud services, local websocket clients, and connected printers.
## Features
- Connects to Moonraker API via websocket
- Provides a websocket server for external clients
- Relays JSON-RPC commands between clients and the printer
- Broadcasts printer status updates to all connected clients
- Handles reconnection if the Moonraker connection is lost
- Connects to the Farm Control websocket API
- Provides a local HTTP server for CLI and integrations
- Desktop UI built with React, Ant Design, and Electrobun
- Headless Linux service packaging for servers and Raspberry Pi hosts
- Relays printer and document printer state to the desktop UI
## Requirements
- [Bun](https://bun.sh) 1.1 or newer
## Installation
1. Clone this repository
2. Install dependencies:
```
npm install
bun install
```
3. Edit the `config.json` file to match your Moonraker setup
3. Edit `config.json` for your environment
4. Start the server:
```
npm start
bun run dev
```
## Development
- Headless server: `bun run dev`
- Desktop app with hot reload: `bun run dev:app`
- Renderer only: `bun run dev:renderer`
## Building
- Desktop app (current platform): `bun run build:app`
- Linux headless packages: `bun run build:linux`
Electrobun handles desktop packaging. Linux headless builds use `bun build --compile`.
## Configuration
The `config.json` file contains the following options:
`config.json` contains `development` and `production` sections. Each section supports:
```json
{
"moonraker": {
"host": "localhost",
"port": 7125,
"protocol": "ws",
"apiKey": null,
"identity": {
"name": "printer-bridge",
"version": "0.1.0",
"type": "external"
}
},
"server": {
"port": 8080
}
}
```
- `moonraker.host`: The hostname or IP address of your Moonraker instance
- `moonraker.port`: The port number of your Moonraker instance
- `moonraker.protocol`: The protocol to use (`ws` or `wss`)
- `moonraker.apiKey`: Your Moonraker API key (if required)
- `server.port`: The port number for the websocket server
## Usage
Connect to the websocket server at `ws://[host]:[port]` and send JSON-RPC formatted messages to control the printer.
Example client-side code:
```javascript
const ws = new WebSocket('ws://localhost:8080');
ws.onopen = () => {
console.log('Connected to printer bridge');
// Send a command to get printer info
ws.send(JSON.stringify({
jsonrpc: "2.0",
method: "printer.info",
id: 1
}));
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
console.log('Received:', message);
};
```
- `logLevel`
- `url` (websocket server)
- `apiUrl`
- `host.id` and `host.authCode`
- `dataDir`
## License