From db4bb4deb8c753b039f960b8aaad9da921880f7e Mon Sep 17 00:00:00 2001 From: jtricerolph Date: Tue, 14 Jul 2026 13:22:57 +0000 Subject: [PATCH] =?UTF-8?q?Add=20rebuild=20endpoint=20=E2=80=94=20docker?= =?UTF-8?q?=20compose=20up=20--build=20without=20git=20pull?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- updater/src/index.js | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/updater/src/index.js b/updater/src/index.js index 345a31d..43b0af1 100644 --- a/updater/src/index.js +++ b/updater/src/index.js @@ -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)