diff --git a/src/com/homelab/stages/buildDocker.groovy b/src/com/homelab/stages/buildDocker.groovy index d524176..57cf964 100644 --- a/src/com/homelab/stages/buildDocker.groovy +++ b/src/com/homelab/stages/buildDocker.groovy @@ -98,8 +98,42 @@ def renderDockerfile(Map config) { def (templateFile, defaultVersion) = entry def resolvedVersion = version ?: defaultVersion + validateRepoStructure(lang) + log.info("buildDocker: no Dockerfile in repo — rendering ${templateFile} for ${lang} ${resolvedVersion}") def constructObj = new constructTemplate() constructObj.renderTemplate([version: resolvedVersion], templateFile, 'Dockerfile') sh 'cat Dockerfile' } + +// Each fallback template assumes one specific, canonical repo layout — it is +// a fixed Dockerfile per language, not a detector across the many layouts a +// real repo might actually use (a subdirectory build, Gradle instead of +// Maven, a different entrypoint name). That is a real limitation, not just +// this check's — see docs/PRODUCT-ARCHITECTURE.md's buildpacks item for the +// actual fix. Until then, this at least turns a missing file into a specific +// message naming the file and the fix, in place of a raw `docker build` +// COPY failure that never says what the fallback expected in the first +// place, several minutes into a build someone was told needs no Dockerfile. +// +// Deliberately only checks for a file's existence, never its contents (e.g. +// not whether requirements.txt's app matches gunicorn's `app:app` target) — +// that would need language-aware parsing this stage has no business doing, +// and a wrong guess would be a worse failure than no check at all. +def validateRepoStructure(String lang) { + def required = [ + go : ['go.mod', 'a Go module — run `go mod init ` at the repo root'], + node : ['package.json', 'an npm project — run `npm init` at the repo root, with a "start" script'], + python: ['requirements.txt', 'your dependencies, and expects the app itself as `app:app` (Flask/FastAPI-style) for gunicorn'], + java : ['pom.xml', 'a Maven project — Gradle repos need their own Dockerfile for now'], + maven : ['pom.xml', 'a Maven project — Gradle repos need their own Dockerfile for now'], + ] + def check = required[lang] + if (!check) { + return // php has no required file — composer.json is used only if present + } + def (file, help) = check + if (!fileExists(file)) { + error("buildDocker: no ${file} found at the repo root. The '${lang}' fallback build expects ${help}. If your repository has a different layout, add your own Dockerfile instead — that always takes priority over this fallback and can build however you like.") + } +}