Auto-download Go binary on startup and validate setup

The postinstall script doesn't run on toes (package.json is
transformed during deploy), so the binary was never downloaded.
Now the server downloads it from GitHub releases if missing.
Added validate() to catch missing DATA_DIR and non-executable
binary with clear error messages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Corey Johnson 2026-03-10 17:16:05 -07:00
parent 387f746f8c
commit 84ce472c87

View File

@ -1,10 +1,10 @@
import { join } from 'path'
import { unlinkSync } from 'fs'
import { accessSync, chmodSync, constants, mkdirSync, unlinkSync } from 'fs'
import type { ServerWebSocket, Subprocess } from 'bun'
const DATA_DIR = process.env.DATA_DIR!
const DATA_DIR = process.env.DATA_DIR
const PORT = Number(process.env.PORT) || 3000
const SOCKET_PATH = join(DATA_DIR, 'tronbyt.sock')
const SOCKET_PATH = DATA_DIR ? join(DATA_DIR, 'tronbyt.sock') : ''
const BIN_DIR = join(import.meta.dir, '..', 'bin')
let goProcess: Subprocess | undefined
@ -107,13 +107,52 @@ async function waitForHealthy(maxAttempts = 60): Promise<boolean> {
return false
}
async function downloadBinary(binPath: string): Promise<boolean> {
const name = getBinaryName()
const url = `https://github.com/tronbyt/server/releases/latest/download/${name}`
console.log(`Downloading ${name}...`)
try {
const resp = await fetch(url, { redirect: 'follow' })
if (!resp.ok) {
console.error(`Download failed: ${resp.status} ${resp.statusText}`)
return false
}
mkdirSync(BIN_DIR, { recursive: true })
await Bun.write(binPath, resp)
chmodSync(binPath, 0o755)
console.log('Download complete')
return true
} catch (e) {
console.error('Download failed:', e)
return false
}
}
function validate(): string | undefined {
if (!DATA_DIR) return 'DATA_DIR env var is not set — toes should provide this automatically'
const binPath = join(BIN_DIR, getBinaryName())
try {
accessSync(binPath, constants.X_OK)
} catch {
return `Binary not found or not executable: ${binPath}`
}
}
async function spawnGoServer() {
const binPath = join(BIN_DIR, getBinaryName())
if (!(await Bun.file(binPath).exists())) {
console.error(`Binary not found: ${binPath}`)
console.error(`Download from https://github.com/tronbyt/server/releases`)
console.error(`Expected: ${getBinaryName()}`)
if (!(await downloadBinary(binPath))) return
}
const error = validate()
if (error) {
console.error(`Setup error: ${error}`)
return
}
@ -126,7 +165,7 @@ async function spawnGoServer() {
...process.env,
TRONBYT_UNIX_SOCKET: SOCKET_PATH,
DATA_DIR,
DB_DSN: join(DATA_DIR, 'tronbyt.db'),
DB_DSN: join(DATA_DIR!, 'tronbyt.db'),
PRODUCTION: process.env.PRODUCTION ?? 'false',
SINGLE_USER_AUTO_LOGIN: process.env.SINGLE_USER_AUTO_LOGIN ?? 'true',
SYSTEM_APPS_AUTO_REFRESH: process.env.SYSTEM_APPS_AUTO_REFRESH ?? 'true',