Update CI pipeline, documentation, and config for Bun/Electrobun.
Some checks failed
farmcontrol/farmcontrol-server/pipeline/head There was a failure building this commit
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:
parent
8453e7c7cf
commit
fa389e3eb7
4
.gitignore
vendored
4
.gitignore
vendored
@ -142,3 +142,7 @@ data/*
|
|||||||
dist/*
|
dist/*
|
||||||
|
|
||||||
src/buildInfo.json
|
src/buildInfo.json
|
||||||
|
.electrobun-cache
|
||||||
|
artifacts
|
||||||
|
app_dist
|
||||||
|
build
|
||||||
84
Jenkinsfile
vendored
84
Jenkinsfile
vendored
@ -3,16 +3,14 @@ properties([
|
|||||||
])
|
])
|
||||||
|
|
||||||
def writeBuildMetadata() {
|
def writeBuildMetadata() {
|
||||||
nodejs(nodeJSInstallationName: 'Node23') {
|
if (isUnix()) {
|
||||||
if (isUnix()) {
|
sh '''
|
||||||
sh '''
|
bun --eval "await Bun.write('src/buildInfo.json', JSON.stringify({ buildNumber: process.env.BUILD_NUMBER || 'dev' }, null, 2) + '\\n')"
|
||||||
node -e "const fs = require('fs'); fs.writeFileSync('src/buildInfo.json', JSON.stringify({ buildNumber: process.env.BUILD_NUMBER || 'dev' }, null, 2) + '\\n');"
|
'''
|
||||||
'''
|
} else {
|
||||||
} else {
|
bat '''
|
||||||
bat '''
|
bun --eval "await Bun.write('src/buildInfo.json', JSON.stringify({ buildNumber: process.env.BUILD_NUMBER || 'dev' }, null, 2) + '\\n')"
|
||||||
node -e "const fs = require('fs'); fs.writeFileSync('src/buildInfo.json', JSON.stringify({ buildNumber: process.env.BUILD_NUMBER || 'dev' }, null, 2) + '\\n');"
|
'''
|
||||||
'''
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,17 +21,12 @@ def buildLinux() {
|
|||||||
checkout scm
|
checkout scm
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Setup Node.js (Ubuntu)') {
|
stage('Setup Bun (Ubuntu)') {
|
||||||
nodejs(nodeJSInstallationName: 'Node23') {
|
sh 'bun -v'
|
||||||
sh 'node -v'
|
|
||||||
sh 'pnpm -v'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Install Dependencies (Ubuntu)') {
|
stage('Install Dependencies (Ubuntu)') {
|
||||||
nodejs(nodeJSInstallationName: 'Node23') {
|
sh 'bun install --frozen-lockfile'
|
||||||
sh 'pnpm install --frozen-lockfile --production=false'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Write Build Metadata (Ubuntu)') {
|
stage('Write Build Metadata (Ubuntu)') {
|
||||||
@ -41,9 +34,7 @@ def buildLinux() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stage('Build (Ubuntu)') {
|
stage('Build (Ubuntu)') {
|
||||||
nodejs(nodeJSInstallationName: 'Node23') {
|
sh "VITE_BUILD_NUMBER=${env.BUILD_NUMBER} NODE_ENV=production bun run build:linux"
|
||||||
sh "VITE_BUILD_NUMBER=${env.BUILD_NUMBER} NODE_ENV=production pnpm build:linux"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stage('Archive Artifacts (Ubuntu)') {
|
stage('Archive Artifacts (Ubuntu)') {
|
||||||
@ -62,25 +53,19 @@ def buildOnLabel(label, buildCommand) {
|
|||||||
checkout scm
|
checkout scm
|
||||||
}
|
}
|
||||||
|
|
||||||
stage("Setup Node.js (${label})") {
|
stage("Setup Bun (${label})") {
|
||||||
nodejs(nodeJSInstallationName: 'Node23') {
|
if (isUnix()) {
|
||||||
if (isUnix()) {
|
sh 'bun -v'
|
||||||
sh 'node -v'
|
} else {
|
||||||
sh 'pnpm -v'
|
bat 'bun -v'
|
||||||
} else {
|
|
||||||
bat 'node -v'
|
|
||||||
bat 'pnpm -v'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stage("Install Dependencies (${label})") {
|
stage("Install Dependencies (${label})") {
|
||||||
nodejs(nodeJSInstallationName: 'Node23') {
|
if (isUnix()) {
|
||||||
if (isUnix()) {
|
sh 'bun install --frozen-lockfile'
|
||||||
sh 'pnpm install --frozen-lockfile --production=false'
|
} else {
|
||||||
} else {
|
bat 'bun install --frozen-lockfile'
|
||||||
bat 'pnpm install --frozen-lockfile --production=false'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,17 +74,15 @@ def buildOnLabel(label, buildCommand) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stage("Build (${label})") {
|
stage("Build (${label})") {
|
||||||
nodejs(nodeJSInstallationName: 'Node23') {
|
if (isUnix()) {
|
||||||
if (isUnix()) {
|
sh "VITE_BUILD_NUMBER=${env.BUILD_NUMBER} NODE_ENV=production ${buildCommand}"
|
||||||
sh "VITE_BUILD_NUMBER=${env.BUILD_NUMBER} NODE_ENV=production ${buildCommand}"
|
} else {
|
||||||
} else {
|
bat "set VITE_BUILD_NUMBER=${env.BUILD_NUMBER} && set NODE_ENV=production && ${buildCommand}"
|
||||||
bat "set VITE_BUILD_NUMBER=${env.BUILD_NUMBER} && set NODE_ENV=production && ${buildCommand}"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stage("Archive Artifacts (${label})") {
|
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') {
|
node('ubuntu') {
|
||||||
stage('Set Build Name') {
|
stage('Set Build Name') {
|
||||||
checkout scm
|
checkout scm
|
||||||
def version
|
def version = sh(
|
||||||
nodejs(nodeJSInstallationName: 'Node23') {
|
script: "bun --eval \"console.log(JSON.parse(await Bun.file('package.json').text()).version)\"",
|
||||||
version = sh(
|
returnStdout: true
|
||||||
script: "node -p \"require('./package.json').version\"",
|
).trim()
|
||||||
returnStdout: true
|
|
||||||
).trim()
|
|
||||||
}
|
|
||||||
def buildName = "v${version}-b${env.BUILD_NUMBER}"
|
def buildName = "v${version}-b${env.BUILD_NUMBER}"
|
||||||
currentBuild.displayName = buildName
|
currentBuild.displayName = buildName
|
||||||
echo "Build name set to: ${buildName}"
|
echo "Build name set to: ${buildName}"
|
||||||
@ -127,8 +107,8 @@ try {
|
|||||||
setBuildNameFromPackageVersion()
|
setBuildNameFromPackageVersion()
|
||||||
|
|
||||||
parallel(
|
parallel(
|
||||||
'Windows Build': buildOnLabel('windows', 'pnpm build:electron'),
|
'Windows Build': buildOnLabel('windows', 'bun run build:app'),
|
||||||
'MacOS Build': buildOnLabel('macos', 'pnpm build:electron:mac'),
|
'MacOS Build': buildOnLabel('macos', 'bun run build:app:mac'),
|
||||||
'Ubuntu Build': { buildLinux() }
|
'Ubuntu Build': { buildLinux() }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -4,9 +4,10 @@
|
|||||||
"url": "https://dev-wss.tombutcher.work",
|
"url": "https://dev-wss.tombutcher.work",
|
||||||
"apiUrl": "https://dev.tombutcher.work/api",
|
"apiUrl": "https://dev.tombutcher.work/api",
|
||||||
"host": {
|
"host": {
|
||||||
"id": "6a6654425330570c496f12ee",
|
"id": "691a1db49ce913faf0e51284",
|
||||||
"authCode": "VCXo44VxFhJb2pvZDQ0b3QPX7lYMg7SGZ6s4HBU72dJb-PBNk81UfwYhqqYKn1Ez"
|
"authCode": "6wdCQPKMr_zuFaJe3-AY99uRuAEZRgU2lLNTuAFPW5hPvzzTrSra3rh3woZLj-eE"
|
||||||
}
|
},
|
||||||
|
"dataDir": "/Users/tombutcher/Projects/farmcontrol-server/data"
|
||||||
},
|
},
|
||||||
"production": {
|
"production": {
|
||||||
"logLevel": "info",
|
"logLevel": "info",
|
||||||
|
|||||||
92
readme.md
92
readme.md
@ -2,83 +2,55 @@
|
|||||||
|
|
||||||
[](https://ci.tombutcher.work/job/farmcontrol/job/farmcontrol-server/job/main/)
|
[](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
|
## Features
|
||||||
|
|
||||||
- Connects to Moonraker API via websocket
|
- Connects to the Farm Control websocket API
|
||||||
- Provides a websocket server for external clients
|
- Provides a local HTTP server for CLI and integrations
|
||||||
- Relays JSON-RPC commands between clients and the printer
|
- Desktop UI built with React, Ant Design, and Electrobun
|
||||||
- Broadcasts printer status updates to all connected clients
|
- Headless Linux service packaging for servers and Raspberry Pi hosts
|
||||||
- Handles reconnection if the Moonraker connection is lost
|
- Relays printer and document printer state to the desktop UI
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- [Bun](https://bun.sh) 1.1 or newer
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
1. Clone this repository
|
1. Clone this repository
|
||||||
2. Install dependencies:
|
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:
|
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
|
## Configuration
|
||||||
|
|
||||||
The `config.json` file contains the following options:
|
`config.json` contains `development` and `production` sections. Each section supports:
|
||||||
|
|
||||||
```json
|
- `logLevel`
|
||||||
{
|
- `url` (websocket server)
|
||||||
"moonraker": {
|
- `apiUrl`
|
||||||
"host": "localhost",
|
- `host.id` and `host.authCode`
|
||||||
"port": 7125,
|
- `dataDir`
|
||||||
"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);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user