previewDeploy: node_build-Autodetect fuer App-Repos + Build-Output-Erkennung
- runDeploy: Repos mit package.json-build-Script ohne index.html werden automatisch als node_build gebaut (Expo/Vite/React zeigen sonst nur den Platzhalter); Build-Fehler fallen auf static+Platzhalter zurueck - deployNodeBuild: npm-ci-Fallback auf npm install, Output-Erkennung dist/build/out/web-build, maxBuffer+Timeout - updateWebsiteProject: bestehenden type/index aus .webklar-preview.json erhalten statt bei jedem Edit auf static zurueckzusetzen Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -432,9 +432,20 @@ export async function updateWebsiteProject(projectId, { projectName, subdomain,
|
||||
/* Default main */
|
||||
}
|
||||
|
||||
// Deploy-Typ (z.B. node_build) und index-Unterordner aus der vorhandenen
|
||||
// Repo-Config uebernehmen - sonst wuerde jedes Bearbeiten im Portal
|
||||
// App-Projekte (Expo/Vite/React) wieder auf "static" zuruecksetzen.
|
||||
let existingCfg = null
|
||||
try {
|
||||
existingCfg = await fetchPreviewConfig(repoFullName, branch)
|
||||
} catch {
|
||||
/* keine bestehende Config -> Defaults */
|
||||
}
|
||||
|
||||
const previewConfig = {
|
||||
enabled: hasWebsite,
|
||||
type: 'static',
|
||||
type: existingCfg?.type || 'static',
|
||||
...(existingCfg?.index ? { index: existingCfg.index } : {}),
|
||||
branch,
|
||||
displayName: nextName,
|
||||
templateName: doc.templateName || config.gitea.templateRepo,
|
||||
|
||||
@@ -215,11 +215,60 @@ async function ensureIndexHtml(targetDir, displayName) {
|
||||
return { placeholder: true }
|
||||
}
|
||||
|
||||
/**
|
||||
* Erkennt App-Repos, die einen Build-Schritt brauchen (Expo, Vite, React ...):
|
||||
* package.json mit "build"-Script vorhanden, aber keine index.html im Webroot.
|
||||
* Solche Repos wuerden als "static" nur den Platzhalter zeigen - deshalb wird
|
||||
* der Deploy-Typ automatisch auf node_build angehoben (Langzeit-Fix, damit
|
||||
* App-Projekte ohne manuelle Config-Pflege korrekt angezeigt werden).
|
||||
*/
|
||||
async function shouldAutoBuild(sourceDir, indexSubdir = '') {
|
||||
try {
|
||||
const pkg = JSON.parse(await fs.readFile(path.join(sourceDir, 'package.json'), 'utf8'))
|
||||
if (!pkg?.scripts?.build) return false
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
const webroot = indexSubdir ? path.join(sourceDir, indexSubdir) : sourceDir
|
||||
try {
|
||||
await fs.access(path.join(webroot, 'index.html'))
|
||||
return false // echte statische Startseite vorhanden -> kein Build noetig
|
||||
} catch {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* npm ci + npm run build, dann das Build-Ausgabeverzeichnis deployen.
|
||||
* Ausgabeverzeichnis wird erkannt (dist > build > out > web-build), weil
|
||||
* Toolchains unterschiedliche Defaults haben (Expo/Vite: dist, CRA: build).
|
||||
*/
|
||||
async function deployNodeBuild(sourceDir, targetDir) {
|
||||
await execFileAsync('npm', ['ci'], { cwd: sourceDir })
|
||||
await execFileAsync('npm', ['run', 'build'], { cwd: sourceDir })
|
||||
const distDir = path.join(sourceDir, 'dist')
|
||||
await deployStatic(distDir, targetDir)
|
||||
const execOpts = {
|
||||
cwd: sourceDir,
|
||||
maxBuffer: 64 * 1024 * 1024,
|
||||
timeout: 15 * 60 * 1000,
|
||||
env: { ...process.env, CI: '1' },
|
||||
}
|
||||
try {
|
||||
await execFileAsync('npm', ['ci'], execOpts)
|
||||
} catch {
|
||||
// Ohne (passendes) package-lock.json schlaegt npm ci fehl -> npm install.
|
||||
await execFileAsync('npm', ['install', '--no-audit', '--no-fund'], execOpts)
|
||||
}
|
||||
await execFileAsync('npm', ['run', 'build'], execOpts)
|
||||
|
||||
for (const dirName of ['dist', 'build', 'out', 'web-build']) {
|
||||
const candidate = path.join(sourceDir, dirName)
|
||||
try {
|
||||
await fs.access(path.join(candidate, 'index.html'))
|
||||
await deployStatic(candidate, targetDir)
|
||||
return
|
||||
} catch {
|
||||
/* naechsten Kandidaten pruefen */
|
||||
}
|
||||
}
|
||||
throw new Error('Build erzeugte kein Ausgabeverzeichnis mit index.html (dist/build/out/web-build)')
|
||||
}
|
||||
|
||||
export function buildPreviewUrl(subdomain) {
|
||||
@@ -310,8 +359,21 @@ export async function runDeploy(repoFullName, branch, previewConfig) {
|
||||
|
||||
await cloneRepo(cloneUrl, workDir, branch, config.gitea.apiToken)
|
||||
|
||||
if (previewConfig.type === 'node_build') {
|
||||
await deployNodeBuild(workDir, targetDir)
|
||||
let deployType = previewConfig.type
|
||||
if (deployType !== 'node_build' && (await shouldAutoBuild(workDir, previewConfig.index || ''))) {
|
||||
console.log(`[previewDeploy] ${repoFullName}: package.json mit build-Script, keine index.html -> node_build (auto)`)
|
||||
deployType = 'node_build'
|
||||
}
|
||||
|
||||
if (deployType === 'node_build') {
|
||||
try {
|
||||
await deployNodeBuild(workDir, targetDir)
|
||||
} catch (err) {
|
||||
// Build-Fehler duerfen die Subdomain nicht toeten: statisch deployen,
|
||||
// ensureIndexHtml haelt sie mit Kandidat/Platzhalter erreichbar.
|
||||
console.error(`[previewDeploy] node_build fuer ${repoFullName} fehlgeschlagen, Fallback auf static:`, err.message)
|
||||
await deployStatic(workDir, targetDir, previewConfig.index || '')
|
||||
}
|
||||
} else {
|
||||
await deployStatic(workDir, targetDir, previewConfig.index || '')
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user