Analyse: Runner-Auftrag
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
813c56e6a4
commit
62f196f705
2 files changed
+110
-3
No files matched your search
@@ -3,7 +3,8 @@ import { promisify } from 'node:util'
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import { config } from '../config.js'
|
||||
import { listDocuments, updateDocument, Query } from './appwriteAdmin.js'
|
||||
import { listDocuments, updateDocument, getDocument, Query } from './appwriteAdmin.js'
|
||||
import { getFeatureDoc } from './siteFeatures.js'
|
||||
import { withLocks } from './keyedLock.js'
|
||||
import { giteaFetch, splitRepo } from './giteaClient.js'
|
||||
import { envKey, decryptEnv, maskLog } from './previewEnv.js'
|
||||
@@ -669,7 +670,7 @@ async function followRun({ repoFullName, project, token, runId, subdomain, job,
|
||||
}
|
||||
if (run.result === 'dev') {
|
||||
await patchProject(repoFullName, project, { ...base, previewState: 'sleeping' })
|
||||
try { await runnerClient.startDev(subdomain, { ...job, mode: 'dev', displayName: undefined }) } catch (err) {
|
||||
try { await runnerClient.startDev(subdomain, { ...job, mode: 'dev', displayName: undefined, analytics: undefined }) } catch (err) {
|
||||
console.error('[previewDeploy] Dev-Registrierung fehlgeschlagen:', err.message)
|
||||
}
|
||||
} else {
|
||||
@@ -696,6 +697,36 @@ async function followRun({ repoFullName, project, token, runId, subdomain, job,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyse-Feld fuer den Build-Auftrag: nur bei aktiver Funktion 'analyse', oeffentlichem Projekt mit Subdomain
|
||||
* und vollstaendigen Datenschutz-Angaben (Firma + E-Mail Pflicht, Adresse optional). Sonst null.
|
||||
* Wirft nie: der Build darf an der Analyse nicht scheitern.
|
||||
*/
|
||||
async function buildAnalytics(project) {
|
||||
try {
|
||||
if (!project?.$id || project.isPublic !== true || !project.subdomain || !project.customerId) return null
|
||||
const feature = await getFeatureDoc(project.customerId, project.$id, 'analyse')
|
||||
if (!feature || feature.state !== 'active' || !feature.siteKey) return null
|
||||
let customer = null
|
||||
try {
|
||||
customer = await getDocument(config.collections.customers, project.customerId)
|
||||
} catch (err) {
|
||||
if (err.status !== 404) throw err
|
||||
}
|
||||
const company = String(customer?.companyName || customer?.name || '').trim()
|
||||
const email = String(customer?.email || '').trim()
|
||||
if (!company || !email) {
|
||||
console.warn(`[previewDeploy] Analyse fuer Projekt ${project.$id} uebersprungen: Firma oder E-Mail fehlt`)
|
||||
return null
|
||||
}
|
||||
const address = [String(customer.street || '').trim(), `${customer.postalCode || ''} ${customer.city || ''}`.trim()].filter(Boolean).join(', ')
|
||||
return { siteKey: feature.siteKey, color: feature.accentColor || '#111111', privacy: { company, address, email } }
|
||||
} catch (err) {
|
||||
console.warn(`[previewDeploy] Analyse-Abfrage fuer Projekt ${project?.$id} fehlgeschlagen: ${err.message}`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -736,6 +767,7 @@ async function runDeployViaRunner(repoFullName, branch, previewConfig, { project
|
||||
...(previewConfig.index ? { index: previewConfig.index } : {}),
|
||||
...(displayName ? { displayName } : {}),
|
||||
env,
|
||||
analytics: await buildAnalytics(doc),
|
||||
}
|
||||
const { runId } = await runnerClient.submitBuild(job)
|
||||
if (current()) await patchProject(repoFullName, doc, { previewState: 'building', previewError: warning })
|
||||
|
||||
@@ -112,7 +112,7 @@ test('an: Auftrag mit entschlüsselten Variablen, sofortige Rückgabe, Ergebnis
|
||||
assert.equal(aw.get('websiteProjects', id).previewState, 'building')
|
||||
const [sub] = submits()
|
||||
assert.equal(sub.auth, 'Bearer sekret')
|
||||
assert.deepEqual(sub.body, { sub: repo.split('/')[1], repo, sha: SHA, branch: 'main', mode: 'auto', displayName: 'Mein Projekt', env: { API_URL: 'https://x.test' } })
|
||||
assert.deepEqual(sub.body, { sub: repo.split('/')[1], repo, sha: SHA, branch: 'main', mode: 'auto', displayName: 'Mein Projekt', env: { API_URL: 'https://x.test' }, analytics: null })
|
||||
await pd.waitForRunFollowers()
|
||||
const d = aw.get('websiteProjects', id)
|
||||
assert.equal(d.previewState, 'online')
|
||||
@@ -366,3 +366,78 @@ test('dev-Ergebnis: PUT /dev/<sub> mit vollständigem Auftrag inkl. Variablen',
|
||||
const put = rs.calls.find((c) => c.method === 'PUT')
|
||||
assert.deepEqual(put.body, { sub: repo.split('/')[1], repo, sha: SHA, branch: 'main', mode: 'dev', env: { A: 'wert1234' } })
|
||||
})
|
||||
|
||||
// ---- Analyse-Feld im Build-Auftrag (Task 10) ----
|
||||
async function analyseProject({ feature = {}, customer = {}, project: pextra = {}, noFeature = false, noCustomer = false } = {}) {
|
||||
const { createDocument } = await import('../server/services/appwriteAdmin.js')
|
||||
const cid = `cust-an-${n + 1}`
|
||||
const p = await project({ customerId: cid, subdomain: `an-${n + 1}`, isPublic: true, ...pextra })
|
||||
if (!noCustomer) {
|
||||
await createDocument('customers', { name: 'Max Muster', companyName: 'Muster GmbH', email: 'info@muster.test', street: 'Hauptstr. 1', postalCode: '12345', city: 'Berlin', ...customer }, cid)
|
||||
}
|
||||
if (!noFeature) {
|
||||
await createDocument('portalFeatures', { customerId: cid, projectId: p.id, featureKey: 'analyse', state: 'active', enabled: true, siteKey: 'abcdef0123456789abcd', accentColor: '#ff0066', ...feature }, `f_${p.id}_analyse`)
|
||||
}
|
||||
return p
|
||||
}
|
||||
async function submitFor(p) {
|
||||
await pd.runDeploy(p.repo, 'main', cfg(p.repo), { project: p.doc, sha: SHA })
|
||||
await pd.waitForRunFollowers()
|
||||
return submits().at(-1).body
|
||||
}
|
||||
|
||||
test('Analyse: aktiv + öffentlich ⇒ analytics mit siteKey, Farbe, Datenschutzangaben', async () => {
|
||||
const body = await submitFor(await analyseProject())
|
||||
assert.deepEqual(body.analytics, {
|
||||
siteKey: 'abcdef0123456789abcd',
|
||||
color: '#ff0066',
|
||||
privacy: { company: 'Muster GmbH', address: 'Hauptstr. 1, 12345 Berlin', email: 'info@muster.test' },
|
||||
})
|
||||
})
|
||||
|
||||
test('Analyse: Standardfarbe, Firma fällt auf Name zurück, Adresse optional', async () => {
|
||||
const p = await analyseProject({ feature: { accentColor: '' }, customer: { companyName: '', street: '', postalCode: '', city: '' } })
|
||||
const body = await submitFor(p)
|
||||
assert.equal(body.analytics.color, '#111111')
|
||||
assert.equal(body.analytics.privacy.company, 'Max Muster')
|
||||
assert.equal(body.analytics.privacy.address, '')
|
||||
})
|
||||
|
||||
test('Analyse: aus ⇒ null', async () => {
|
||||
assert.equal((await submitFor(await analyseProject({ feature: { state: 'off', enabled: false } }))).analytics, null)
|
||||
assert.equal((await submitFor(await analyseProject({ noFeature: true }))).analytics, null)
|
||||
})
|
||||
|
||||
test('Analyse: nicht öffentlich oder ohne Subdomain ⇒ null', async () => {
|
||||
assert.equal((await submitFor(await analyseProject({ project: { isPublic: false } }))).analytics, null)
|
||||
assert.equal((await submitFor(await analyseProject({ project: { subdomain: '' } }))).analytics, null)
|
||||
})
|
||||
|
||||
test('Analyse: fehlende E-Mail oder Firma ⇒ null mit Warnung, Build läuft trotzdem', async (t) => {
|
||||
const warn = t.mock.method(console, 'warn', () => {})
|
||||
const a = await analyseProject({ customer: { email: '' } })
|
||||
assert.equal((await submitFor(a)).analytics, null)
|
||||
assert.ok(warn.mock.calls.some((c) => String(c.arguments[0]).includes(a.id)))
|
||||
assert.equal((await submitFor(await analyseProject({ customer: { companyName: '', name: '' } }))).analytics, null)
|
||||
assert.equal((await submitFor(await analyseProject({ noCustomer: true }))).analytics, null)
|
||||
})
|
||||
|
||||
test('Analyse: Fehler bei der Funktions-Abfrage ⇒ null, Auftrag wird trotzdem gesendet', async (t) => {
|
||||
t.mock.method(console, 'warn', () => {})
|
||||
const p = await analyseProject()
|
||||
aw.failNext({ method: 'GET', pathIncludes: 'portalFeatures', status: 500 })
|
||||
const before = submits().length
|
||||
const body = await submitFor(p)
|
||||
assert.equal(submits().length, before + 1)
|
||||
assert.equal(body.analytics, null)
|
||||
})
|
||||
|
||||
test('Analyse: Dev-PUT nach erfolgreichem Build (Modus dev) enthält kein analytics', async () => {
|
||||
rs.script = done({ result: 'dev' })
|
||||
const p = await analyseProject({ project: { previewMode: 'dev' } })
|
||||
const body = await submitFor(p)
|
||||
assert.ok(body.analytics)
|
||||
const puts = rs.calls.filter((c) => c.method === 'PUT' && c.url.startsWith('/dev/'))
|
||||
assert.ok(puts.length >= 1)
|
||||
for (const c of puts) assert.equal('analytics' in c.body, false)
|
||||
})
|
||||
Reference in new issue
Block a user