Preview: Runner-Verfolger verdraengt sauber, R15-Grenzen
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4258b5b9f1
commit
d788dba937
2 files changed
+126
-15
No files matched your search
@@ -6,7 +6,7 @@ import { config } from '../config.js'
|
||||
import { listDocuments, updateDocument, Query } from './appwriteAdmin.js'
|
||||
import { withLocks } from './keyedLock.js'
|
||||
import { giteaFetch, splitRepo } from './giteaClient.js'
|
||||
import { envKey, decryptEnv } from './previewEnv.js'
|
||||
import { envKey, decryptEnv, maskLog } from './previewEnv.js'
|
||||
import { saveRunLog } from './previewLogs.js'
|
||||
import * as runnerClient from './previewRunnerClient.js'
|
||||
|
||||
@@ -430,7 +430,7 @@ export async function syncPreviewTlsOnStartup({
|
||||
// ---- Preview-Runner (PREVIEW_RUNNER=on) ----
|
||||
|
||||
// Zeiten des Hintergrund-Verfolgers (fuer Tests ueberschreibbar)
|
||||
export const runnerTiming = { pollMs: 2000, maxMs: 16 * 60 * 1000 }
|
||||
export const runnerTiming = { pollMs: 2000, maxMs: 60 * 60 * 1000, unreachableMs: 5 * 60 * 1000 }
|
||||
// Neuester Lauf je Repo: aeltere Verfolger schreiben nichts mehr (R14)
|
||||
const latestRun = new Map()
|
||||
const followers = new Set()
|
||||
@@ -484,24 +484,31 @@ async function resolveSha(repoFullName, branch, sha) {
|
||||
return head
|
||||
}
|
||||
|
||||
async function followRun({ repoFullName, project, runId, subdomain, job, env, warning }) {
|
||||
async function followRun({ repoFullName, project, token, runId, subdomain, job, env, warning }) {
|
||||
const deadline = Date.now() + runnerTiming.maxMs
|
||||
const current = () => latestRun.get(repoFullName) === runId
|
||||
const current = () => latestRun.get(repoFullName) === token
|
||||
let run = null
|
||||
let failure = null
|
||||
let unreachableSince = 0
|
||||
try {
|
||||
while (current()) {
|
||||
await new Promise((r) => setTimeout(r, runnerTiming.pollMs))
|
||||
if (!current()) return
|
||||
try {
|
||||
run = await runnerClient.getRun(runId)
|
||||
unreachableSince = 0
|
||||
} catch (err) {
|
||||
run = null
|
||||
if (err.status === 404) { failure = 'Der Preview-Lauf ist beim Runner nicht mehr bekannt'; break }
|
||||
console.error('[previewDeploy] Runner-Abfrage fehlgeschlagen:', err.message)
|
||||
run = null
|
||||
unreachableSince ||= Date.now()
|
||||
if (Date.now() - unreachableSince > runnerTiming.unreachableMs) {
|
||||
failure = 'Preview-Runner nicht erreichbar'
|
||||
break
|
||||
}
|
||||
}
|
||||
if (run?.state === 'done' || run?.state === 'failed') break
|
||||
if (Date.now() > deadline) { failure = 'Zeitüberschreitung beim Preview-Build'; break }
|
||||
if (Date.now() > deadline) { failure = 'Status unbekannt – bitte neu bauen'; break }
|
||||
}
|
||||
if (!current()) return
|
||||
|
||||
@@ -529,7 +536,7 @@ async function followRun({ repoFullName, project, runId, subdomain, job, env, wa
|
||||
} else {
|
||||
await patchProject(repoFullName, project, {
|
||||
previewState: 'failed',
|
||||
previewError: failure || run?.error || 'Preview-Build fehlgeschlagen',
|
||||
previewError: maskLog(failure || run?.error || 'Preview-Build fehlgeschlagen', env),
|
||||
})
|
||||
}
|
||||
if (!current()) return
|
||||
@@ -539,19 +546,25 @@ async function followRun({ repoFullName, project, runId, subdomain, job, env, wa
|
||||
} catch (err) {
|
||||
console.error(`[previewDeploy] Verfolger ${runId} abgebrochen:`, err.message)
|
||||
} finally {
|
||||
if (latestRun.get(repoFullName) === runId && run && (run.state === 'done' || run.state === 'failed')) {
|
||||
latestRun.delete(repoFullName)
|
||||
}
|
||||
if (latestRun.get(repoFullName) === token) latestRun.delete(repoFullName)
|
||||
}
|
||||
}
|
||||
|
||||
async function runDeployViaRunner(repoFullName, branch, previewConfig, { project, sha } = {}) {
|
||||
const subdomain = previewConfig.subdomain || repoFullName.split('/').pop()
|
||||
// Platz fuer diesen Aufruf sofort belegen (vor jedem await): aeltere Verfolger scheiden aus
|
||||
const token = Symbol('run')
|
||||
latestRun.set(repoFullName, token)
|
||||
const current = () => latestRun.get(repoFullName) === token
|
||||
const release = () => { if (current()) latestRun.delete(repoFullName) }
|
||||
|
||||
const doc = project || (await findProject(repoFullName))
|
||||
const mode = doc?.previewMode || 'auto'
|
||||
|
||||
if (mode === 'off') {
|
||||
await patchProject(repoFullName, doc, { previewState: 'off' })
|
||||
try {
|
||||
if (current()) await patchProject(repoFullName, doc, { previewState: 'off' })
|
||||
} finally { release() }
|
||||
return { deployed: false, skipped: true, reason: 'Preview ist ausgeschaltet', subdomain }
|
||||
}
|
||||
|
||||
@@ -576,15 +589,16 @@ async function runDeployViaRunner(repoFullName, branch, previewConfig, { project
|
||||
env,
|
||||
}
|
||||
const { runId } = await runnerClient.submitBuild(job)
|
||||
latestRun.set(repoFullName, runId)
|
||||
await patchProject(repoFullName, doc, { previewState: 'building', previewError: warning })
|
||||
const follower = followRun({ repoFullName, project: doc, runId, subdomain, job, env, warning })
|
||||
if (current()) await patchProject(repoFullName, doc, { previewState: 'building', previewError: warning })
|
||||
const follower = followRun({ repoFullName, project: doc, token, runId, subdomain, job, env, warning })
|
||||
followers.add(follower)
|
||||
follower.finally(() => followers.delete(follower))
|
||||
return { deployed: false, queued: true, runId, subdomain }
|
||||
} catch (err) {
|
||||
console.error(`[previewDeploy] Runner-Auftrag fuer ${repoFullName} fehlgeschlagen:`, err.message)
|
||||
await patchProject(repoFullName, doc, { previewState: 'failed', previewError: err.message })
|
||||
try {
|
||||
if (current()) await patchProject(repoFullName, doc, { previewState: 'failed', previewError: maskLog(err.message, env) })
|
||||
} finally { release() }
|
||||
return { deployed: false, error: err.message, subdomain }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user