Rebuild deploy process on the project template
- Dockerfile: deps → builder → pb-downloader → runner stages, cross-arch (BUILDPLATFORM node stages, TARGETARCH PocketBase binary), Debian slim runner, VOLUME /app/pb_data, dual healthcheck, docker-entrypoint.sh - docker-entrypoint.sh: bash supervisor replacing scripts/run-all.mjs - Makefile: build-production (multi-arch push from git remote), build-check, build-check-local - deploy.sh [tag]: make build-production then ansible-playbook with tag - drop .env/.env.example (credentials live in ansible/vars/default.yml)
This commit is contained in:
@@ -1,116 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Container entrypoint supervisor.
|
||||
*
|
||||
* Runs PocketBase and the TanStack Start node server side by side:
|
||||
* 1. starts PocketBase (data in /data, migrations from ./pb_migrations)
|
||||
* 2. waits for its health endpoint
|
||||
* 3. bootstraps the superuser account (idempotent upsert)
|
||||
* 4. seeds initial content (idempotent, create-if-missing)
|
||||
* 5. starts the site server on PORT (default 3000)
|
||||
*
|
||||
* If either process dies the container exits non-zero so Docker restarts it.
|
||||
*/
|
||||
import { spawn, spawnSync } from 'node:child_process'
|
||||
|
||||
const PORT = process.env.PORT ?? '3000'
|
||||
const PB_HTTP = process.env.PB_HTTP ?? '0.0.0.0:8090'
|
||||
// PB_DATA_DIR is the canonical variable (set in the deploy .env); PB_DATA kept
|
||||
// as a legacy alias, /data as the image default.
|
||||
const PB_DATA = process.env.PB_DATA_DIR ?? process.env.PB_DATA ?? '/data'
|
||||
|
||||
let stopping = false
|
||||
const children = []
|
||||
|
||||
function start(name, command, args, extraEnv = {}) {
|
||||
const child = spawn(command, args, {
|
||||
stdio: 'inherit',
|
||||
env: { ...process.env, ...extraEnv },
|
||||
})
|
||||
child.name = name
|
||||
children.push(child)
|
||||
child.on('exit', (code) => {
|
||||
if (stopping) return
|
||||
console.error(`[supervisor] ${name} exited with code ${code} — shutting down`)
|
||||
shutdown(code ?? 1)
|
||||
})
|
||||
return child
|
||||
}
|
||||
|
||||
function run(name, command, args, extraEnv = {}) {
|
||||
const result = spawnSync(command, args, {
|
||||
stdio: 'inherit',
|
||||
env: { ...process.env, ...extraEnv },
|
||||
})
|
||||
if (result.status !== 0) {
|
||||
console.warn(`[supervisor] ${name} finished with status ${result.status}`)
|
||||
}
|
||||
return result.status ?? 0
|
||||
}
|
||||
|
||||
function shutdown(exitCode = 0) {
|
||||
if (stopping) return
|
||||
stopping = true
|
||||
for (const child of children) {
|
||||
if (child.exitCode === null && child.signalCode === null) child.kill('SIGTERM')
|
||||
}
|
||||
setTimeout(() => process.exit(exitCode), 2000)
|
||||
}
|
||||
|
||||
process.on('SIGINT', () => shutdown(0))
|
||||
process.on('SIGTERM', () => shutdown(0))
|
||||
|
||||
async function waitForHealth(url, attempts = 120) {
|
||||
for (let i = 0; i < attempts; i++) {
|
||||
try {
|
||||
const res = await fetch(url)
|
||||
if (res.ok) return
|
||||
} catch {
|
||||
// not up yet
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
}
|
||||
throw new Error(`health check at ${url} timed out`)
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('[supervisor] starting PocketBase…')
|
||||
start('pocketbase', './pocketbase', [
|
||||
'serve',
|
||||
`--http=${PB_HTTP}`,
|
||||
`--dir=${PB_DATA}`,
|
||||
'--migrationsDir=./pb_migrations',
|
||||
'--publicDir=./pb_public',
|
||||
])
|
||||
|
||||
await waitForHealth('http://127.0.0.1:8090/api/health')
|
||||
console.log('[supervisor] PocketBase is healthy')
|
||||
|
||||
if (process.env.PB_SUPERUSER_EMAIL && process.env.PB_SUPERUSER_PASSWORD) {
|
||||
run('superuser upsert', './pocketbase', [
|
||||
'superuser',
|
||||
'upsert',
|
||||
process.env.PB_SUPERUSER_EMAIL,
|
||||
process.env.PB_SUPERUSER_PASSWORD,
|
||||
`--dir=${PB_DATA}`,
|
||||
'--migrationsDir=./pb_migrations',
|
||||
])
|
||||
console.log('[supervisor] superuser ensured')
|
||||
|
||||
run('seed', 'node', ['scripts/seed.mjs'])
|
||||
} else {
|
||||
console.warn('[supervisor] PB_SUPERUSER_EMAIL/PASSWORD not set — skipping superuser + seed')
|
||||
}
|
||||
|
||||
console.log(`[supervisor] starting site server on :${PORT}…`)
|
||||
start('site', 'node', ['.output/server/index.mjs'], {
|
||||
PORT,
|
||||
PB_URL: process.env.PB_URL ?? 'http://127.0.0.1:8090',
|
||||
NODE_ENV: 'production',
|
||||
})
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(`[supervisor] fatal: ${error.message}`)
|
||||
shutdown(1)
|
||||
})
|
||||
Reference in New Issue
Block a user