From b193b327ab7c1e5a44b9d685b2ca8e6086afd0f0 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 11:10:54 +0000 Subject: [PATCH] Preview: Verfolger-Wettlauf bei neuen Projekten behoben Co-Authored-By: Claude Sonnet 5.5 --- server/services/previewDeploy.js | 21 +++++++++++++++++++-- test/previewFinalFixes.test.js | 14 ++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/server/services/previewDeploy.js b/server/services/previewDeploy.js index a4d910c..68c0114 100644 --- a/server/services/previewDeploy.js +++ b/server/services/previewDeploy.js @@ -542,7 +542,7 @@ export async function syncPreviewTlsOnStartup({ // Zeiten des Hintergrund-Verfolgers (fuer Tests ueberschreibbar) const NO_DEV_SCRIPT_ERROR = 'Kein dev-Skript in package.json' -export const runnerTiming = { pollMs: 2000, maxMs: 60 * 60 * 1000, unreachableMs: 5 * 60 * 1000 } +export const runnerTiming = { pollMs: 2000, maxMs: 60 * 60 * 1000, unreachableMs: 5 * 60 * 1000, docWaitMs: 15000, docPollMs: 50 } // Neuester Lauf je Repo: aeltere Verfolger schreiben nichts mehr (R14) const latestRun = new Map() const followers = new Set() @@ -600,6 +600,20 @@ function clampAttrs(data) { return out } +/** + * Neues Projekt: der Webhook legt das Dokument erst NACH runDeploy an. Ein schneller Verfolger + * findet es dann noch nicht - kurz auf die Anlage warten, sonst geht sein Ergebnis verloren. + */ +async function waitForProject(repoFullName, project) { + if (project?.$id) return project + const until = Date.now() + runnerTiming.docWaitMs + for (;;) { + const doc = await findProject(repoFullName) + if (doc || Date.now() >= until) return doc || project + await new Promise((r) => setTimeout(r, runnerTiming.docPollMs)) + } +} + async function patchProject(repoFullName, project, data) { data = clampAttrs(data) const doc = project?.$id ? project : await findProject(repoFullName) @@ -649,8 +663,11 @@ async function followRun({ repoFullName, project, token, runId, subdomain, job, } if (!current()) return + project = await waitForProject(repoFullName, project) + if (!current()) return + if (run?.log?.length) { - try { await saveRunLog((project?.$id || (await findProject(repoFullName))?.$id), run.log, env) } catch (err) { + try { await saveRunLog(project?.$id, run.log, env) } catch (err) { console.error('[previewDeploy] Protokoll speichern fehlgeschlagen:', err.message) } } diff --git a/test/previewFinalFixes.test.js b/test/previewFinalFixes.test.js index 8595299..d976836 100644 --- a/test/previewFinalFixes.test.js +++ b/test/previewFinalFixes.test.js @@ -132,6 +132,20 @@ test('C1: neues Projekt per Push wird nach Verfolger-Erfolg bereit', async () => assert.equal(d.provisioningStatus, 'ready') }) +test('C1: Verfolger ist vor der Anlage des Projektdokuments fertig -> Ergebnis geht nicht verloren', async () => { + const { createDocument } = await import('../server/services/appwriteAdmin.js') + rs.script = done() + await pd.runDeploy('WEBklar/spaet', 'main', { subdomain: 'spaet', type: 'static', branch: 'main' }, { sha: SHA }) + // Webhook legt das Dokument erst nach dem Lauf an (simulierte Verzoegerung, Lauf ist nach ~10 ms fertig) + await new Promise((r) => setTimeout(r, 80)) + await createDocument('websiteProjects', { repoFullName: 'WEBklar/spaet', projectName: 'S', subdomain: 'spaet', status: 'pending', provisioningStatus: 'pending' }, 'spaet') + await pd.waitForRunFollowers() + const d = aw.get('websiteProjects', 'spaet') + assert.equal(d.provisioningStatus, 'ready') + assert.equal(d.status, 'deployed') + assert.equal(d.previewState, 'online') +}) + test('C1: deployKeepsStatus nur mit Runner an', () => { assert.equal(pd.deployKeepsStatus({ deployed: false, queued: true }), true) assert.equal(pd.deployKeepsStatus({ deployed: false, skipped: true }), true)