diff --git a/src/spec-node/dockerfileUtils.ts b/src/spec-node/dockerfileUtils.ts index 0f34476c7..2a07023aa 100644 --- a/src/spec-node/dockerfileUtils.ts +++ b/src/spec-node/dockerfileUtils.ts @@ -227,6 +227,10 @@ export function ensureDockerfileHasFinalStageName(dockerfile: string, defaultLas // Find the last line that starts with "FROM" (possibly preceeded by white-space) const fromLines = [...dockerfile.matchAll(findFromLines)]; + if (fromLines.length === 0) { + throw new Error('Error parsing Dockerfile: Dockerfile contains no FROM instructions'); + } + const lastFromLineMatch = fromLines[fromLines.length - 1]; const lastFromLine = lastFromLineMatch.groups?.line as string; diff --git a/src/test/dockerfileUtils.test.ts b/src/test/dockerfileUtils.test.ts index bd9489ddc..979375b91 100644 --- a/src/test/dockerfileUtils.test.ts +++ b/src/test/dockerfileUtils.test.ts @@ -1,4 +1,4 @@ -import { assert } from 'chai'; +import { assert, expect } from 'chai'; import { imageMetadataLabel, internalGetImageBuildInfoFromDockerfile } from '../spec-node/imageMetadata'; import { ensureDockerfileHasFinalStageName, extractDockerfile, findBaseImage, findUserStatement, supportsBuildContexts } from '../spec-node/dockerfileUtils'; import { ImageDetails } from '../spec-shutdown/dockerUtils'; @@ -143,6 +143,16 @@ RUN another command }); }); }); + + describe('without any from stage (invalid Dockerfile)', () => { + it('should throw a descriptive error', () => { + const dockerfile = ` +RUN some command +`; + expect(() => ensureDockerfileHasFinalStageName(dockerfile, 'placeholder')).to.throw('Error parsing Dockerfile: Dockerfile contains no FROM instructions'); + }); + }); + }); describe('getImageBuildInfo', () => {