From 43ca78e83e8bd14327e7da7477d34293285ac93b Mon Sep 17 00:00:00 2001 From: Mukul Sharma Date: Sat, 5 Sep 2026 22:08:13 +0530 Subject: [PATCH] buildDocker: fail fast with a specific message when the repo doesn't match the fallback's assumed layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every fallback template (go/node/python/java) assumes one fixed repo layout — a root go.mod, a root package.json, requirements.txt, a root pom.xml. Without this, a repo shaped any other way failed inside `docker build`'s COPY step with a raw "file not found" error that never said which file the fallback expected or why, several minutes into a build someone was told needs no Dockerfile at all (testing1 hit exactly this: no go.mod, "COPY failed: file not found in build context: go.mod"). validateRepoStructure checks for the one file each language's template actually requires before rendering it, and errors with the file name, what it's for, and the escape hatch (bring your own Dockerfile, which always wins over this fallback regardless of layout). This does not make the fallback dynamic — it is still one fixed layout per language. It only turns a silent wrong assumption into a message that says what the assumption was. Real detection across arbitrary repo layouts is Cloud Native Buildpacks' job, tracked as its own step in toolshed's docs/PRODUCT-ARCHITECTURE.md. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Wajog7nELA3i8JWTjxYGHF --- src/com/homelab/stages/buildDocker.groovy | 34 +++++++++++++++++++++++ 1 file changed, 34 insertions(+) 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.") + } +}