Add rebuild endpoint — docker compose up --build without git pull

This commit is contained in:
jtricerolph 2026-07-14 13:22:57 +00:00
parent c72ebdf79a
commit db4bb4deb8

View file

@ -193,13 +193,12 @@ async function refreshStatus(repo) {
return entry
}
async function deploy(target, repoName) {
async function runRemote(target, repoName, cmd, label) {
if (!/^\/opt\/[a-z0-9-]+$/.test(target.path)) {
app.log.error(`Rejecting suspicious path for deploy: ${target.path}`)
app.log.error(`Rejecting suspicious path for ${label}: ${target.path}`)
return
}
const remoteCmd = `cd ${target.path} && git pull && docker compose up -d --build`
const entry = { repo: repoName, host: target.host, started: new Date().toISOString(), status: 'running' }
const entry = { repo: repoName, host: target.host, started: new Date().toISOString(), status: 'running', type: label }
deployLog.unshift(entry)
if (deployLog.length > 50) deployLog.pop()
@ -209,20 +208,28 @@ async function deploy(target, repoName) {
'-o', 'StrictHostKeyChecking=no',
'-o', 'UserKnownHostsFile=/dev/null',
`root@${target.host}`,
'sh', '-c', remoteCmd,
'sh', '-c', cmd,
], { timeout: 300_000 })
entry.status = 'success'
entry.output = stdout.slice(-500)
app.log.info(`Deploy ${repoName} → success`)
app.log.info(`${label} ${repoName} → success`)
delete statusCache[repoName]
} catch (err) {
entry.status = 'failed'
entry.error = err.message.slice(-500)
app.log.error(`Deploy ${repoName} → failed`)
app.log.error(`${label} ${repoName} → failed`)
}
entry.finished = new Date().toISOString()
}
async function deploy(target, repoName) {
return runRemote(target, repoName, `cd ${target.path} && git pull && docker compose up -d --build`, 'deploy')
}
async function rebuild(target, repoName) {
return runRemote(target, repoName, `cd ${target.path} && docker compose up -d --build`, 'rebuild')
}
// ── Routes ────────────────────────────────────────────────────────────────────
app.get('/health', async () => ({ status: 'healthy' }))
@ -284,6 +291,18 @@ app.post('/deploy/:repo', async (request, reply) => {
return reply.status(202).send({ ok: true, deploying: repo })
})
app.post('/rebuild/:repo', async (request, reply) => {
const { repo } = request.params
const target = getDeployTarget(repo)
if (!target) return reply.status(404).send({ error: 'Unknown repo' })
const running = deployLog.find(d => d.repo === repo && d.status === 'running')
if (running) return reply.status(409).send({ error: 'Deploy already in progress' })
rebuild(target, repo).catch(() => {})
return reply.status(202).send({ ok: true, rebuilding: repo })
})
app.post('/exec', async (request, reply) => {
const { host, command } = request.body || {}
if (!host || !command || typeof command !== 'string' || command.length > 2000)