Build CLI binaries on-demand when requested via /dist/:file endpoint
This commit is contained in:
parent
c16fdaa2a2
commit
18cf4243fa
|
|
@ -59,9 +59,49 @@ app.all('/api/tools/:tool/:path{.+}', async c => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const CLI_ENTRY = import.meta.dir + '/../cli/index.ts'
|
||||||
const DIST_DIR = import.meta.dir + '/../../dist'
|
const DIST_DIR = import.meta.dir + '/../../dist'
|
||||||
const INSTALL_SCRIPT = await Bun.file(import.meta.dir + '/install.sh').text()
|
const INSTALL_SCRIPT = await Bun.file(import.meta.dir + '/install.sh').text()
|
||||||
|
|
||||||
|
interface BuildTarget {
|
||||||
|
arch: string
|
||||||
|
name: string
|
||||||
|
os: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const BUILD_TARGETS: BuildTarget[] = [
|
||||||
|
{ os: 'darwin', arch: 'arm64', name: 'toes-macos-arm64' },
|
||||||
|
{ os: 'darwin', arch: 'x64', name: 'toes-macos-x64' },
|
||||||
|
{ os: 'linux', arch: 'arm64', name: 'toes-linux-arm64' },
|
||||||
|
{ os: 'linux', arch: 'x64', name: 'toes-linux-x64' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const buildInFlight = new Map<string, Promise<boolean>>()
|
||||||
|
|
||||||
|
async function buildBinary(target: BuildTarget): Promise<boolean> {
|
||||||
|
const existing = buildInFlight.get(target.name)
|
||||||
|
if (existing) return existing
|
||||||
|
|
||||||
|
const promise = (async () => {
|
||||||
|
const { existsSync, mkdirSync } = await import('fs')
|
||||||
|
if (!existsSync(DIST_DIR)) mkdirSync(DIST_DIR, { recursive: true })
|
||||||
|
|
||||||
|
const proc = Bun.spawn([
|
||||||
|
'bun', 'build', CLI_ENTRY, '--compile',
|
||||||
|
'--target', `bun-${target.os}-${target.arch}`,
|
||||||
|
'--minify', '--sourcemap=external',
|
||||||
|
'--outfile', `${DIST_DIR}/${target.name}`,
|
||||||
|
], { stdout: 'inherit', stderr: 'inherit' })
|
||||||
|
|
||||||
|
const exitCode = await proc.exited
|
||||||
|
return exitCode === 0
|
||||||
|
})()
|
||||||
|
|
||||||
|
buildInFlight.set(target.name, promise)
|
||||||
|
promise.finally(() => buildInFlight.delete(target.name))
|
||||||
|
return promise
|
||||||
|
}
|
||||||
|
|
||||||
// Install script: curl -fsSL http://toes.local/install | bash
|
// Install script: curl -fsSL http://toes.local/install | bash
|
||||||
app.get('/install', c => {
|
app.get('/install', c => {
|
||||||
if (!TOES_URL) return c.text('TOES_URL is not configured', 500)
|
if (!TOES_URL) return c.text('TOES_URL is not configured', 500)
|
||||||
|
|
@ -69,7 +109,7 @@ app.get('/install', c => {
|
||||||
return c.text(script, 200, { 'content-type': 'text/plain' })
|
return c.text(script, 200, { 'content-type': 'text/plain' })
|
||||||
})
|
})
|
||||||
|
|
||||||
// Serve built CLI binaries from dist/
|
// Serve built CLI binaries from dist/, building on-demand if needed
|
||||||
app.get('/dist/:file', async c => {
|
app.get('/dist/:file', async c => {
|
||||||
const file = c.req.param('file')
|
const file = c.req.param('file')
|
||||||
if (!file || file.includes('/') || file.includes('..')) {
|
if (!file || file.includes('/') || file.includes('..')) {
|
||||||
|
|
@ -77,9 +117,12 @@ app.get('/dist/:file', async c => {
|
||||||
}
|
}
|
||||||
const bunFile = Bun.file(`${DIST_DIR}/${file}`)
|
const bunFile = Bun.file(`${DIST_DIR}/${file}`)
|
||||||
if (!(await bunFile.exists())) {
|
if (!(await bunFile.exists())) {
|
||||||
return c.text(`Binary "${file}" not found — run cli:build:all on the server`, 404)
|
const target = BUILD_TARGETS.find(t => t.name === file)
|
||||||
|
if (!target) return c.text('Not found', 404)
|
||||||
|
const ok = await buildBinary(target)
|
||||||
|
if (!ok) return c.text(`Failed to build "${file}"`, 500)
|
||||||
}
|
}
|
||||||
return new Response(bunFile, {
|
return new Response(Bun.file(`${DIST_DIR}/${file}`), {
|
||||||
headers: { 'content-type': 'application/octet-stream' },
|
headers: { 'content-type': 'application/octet-stream' },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user