coder / envbuilder

Build development environments from a Dockerfile on Docker, Kubernetes, and OpenShift. Enable developers to modify their development environment quickly.
Apache License 2.0
111 stars 23 forks source link

devcontainer: support multi-stage build with dangling build stage #231

Open johnstcn opened 1 month ago

johnstcn commented 1 month ago

Currently, building a devcontainer that specifies a multi-stage build with a "dangling" FROM image AS alias fails.

Example integration test that reproduces the issue:

func TestBuildFromDevcontainerWithMultistage(t *testing.T) {
    t.Parallel()

    // Ensures that a Git repository with a devcontainer.json is cloned and built.
    srv := createGitServer(t, gitServerOptions{
        files: map[string]string{
            "devcontainer.json": `{
                "name": "Test",
                "build": {
                    "dockerfile": "Dockerfile"
                },
            }`,
            "Dockerfile": fmt.Sprintf(`FROM %s as a
RUN date > /root/date.txt
FROM %s as b
COPY --from=a /root/date.txt /date.txt`, testImageUbuntu, testImageUbuntu),
        },
    })
    ctr, err := runEnvbuilder(t, options{env: []string{
        envbuilderEnv("GIT_URL", srv.URL),
    }})
    require.NoError(t, err)

    output := execContainer(t, ctr, "cat /date.txt")
    require.NotEmpty(t, strings.TrimSpace(output))
}

The above test fails with the following error:

--- FAIL: TestBuildFromDevcontainerWithMultistage (0.39s)
    /home/coder/src/coder/envbuilder/integration/integration_test.go:1207: "\x1b[1menvbuilder\x1b[22m - Build development environments from repositories in a container"
    /home/coder/src/coder/envbuilder/integration/integration_test.go:1207: "#1: šŸ“¦ Cloning \x1b[36mhttp://127.0.0.1:39405\x1b[0m to \x1b[36m/workspaces/empty\x1b[0m..."
    /home/coder/src/coder/envbuilder/integration/integration_test.go:1207: "#1: šŸ‘¤ Using no authentication!"
    /home/coder/src/coder/envbuilder/integration/integration_test.go:1207: "#1: šŸ“¦ Cloned repository! [9.601971ms]"
    /home/coder/src/coder/envbuilder/integration/integration_test.go:1207: "error: compile devcontainer.json: parse image from dockerfile: parse image ref \"localhost:5000/envbuilder-test-ubuntu:latest as b\": could not parse reference: localhost:5000/envbuilder-test-ubuntu:latest as b"
    /home/coder/src/coder/envbuilder/integration/integration_test.go:526: 
            Error Trace:    /home/coder/src/coder/envbuilder/integration/integration_test.go:526
            Error:          Received unexpected error:
                            error: compile devcontainer.json: parse image from dockerfile: parse image ref "localhost:5000/envbuilder-test-ubuntu:latest as b": could not parse reference: localhost:5000/envbuilder-test-ubuntu:latest as b
            Test:           TestBuildFromDevcontainerWithMultistage

It looks like we need to handle this in devcontainer.ImageFromDockerfile.

Workaround: remove the dangling directive:

FROM image AS a
RUN thing
FROM anotherimage AS b
COPY --from=a /something /somewhere

becomes

FROM image AS a
RUN thing
FROM anotherimage
COPY --from=a /something /somewhere