Skip to content

Commit 9685dcc

Browse files
authored
feat: allow custom build context to be configured when using DOCKERFILE_PATH (#139)
Signed-off-by: Danny Kopping <[email protected]>
1 parent f59bd37 commit 9685dcc

File tree

3 files changed

+102
-1
lines changed

3 files changed

+102
-1
lines changed

Makefile

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ build: scripts/envbuilder-$(GOARCH)
1111
test: test-registry test-images
1212
go test -count=1 ./...
1313

14+
test-race:
15+
go test -race -count=3 ./...
16+
1417
# Starts a local Docker registry on port 5000 with a local disk cache.
1518
.PHONY: test-registry
1619
test-registry: .registry-cache

envbuilder.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ type Options struct {
139139
// to using a devcontainer that some might find simpler.
140140
DockerfilePath string `env:"DOCKERFILE_PATH"`
141141

142+
// BuildContextPath can be specified when a DockerfilePath is specified outside the base WorkspaceFolder.
143+
// This path MUST be relative to the WorkspaceFolder path into which the repo is cloned.
144+
BuildContextPath string `env:"BUILD_CONTEXT_PATH"`
145+
142146
// CacheTTLDays is the number of days to use cached layers before
143147
// expiring them. Defaults to 7 days.
144148
CacheTTLDays int `env:"CACHE_TTL_DAYS"`
@@ -467,6 +471,15 @@ func Run(ctx context.Context, options Options) error {
467471
} else {
468472
// If a Dockerfile was specified, we use that.
469473
dockerfilePath := filepath.Join(options.WorkspaceFolder, options.DockerfilePath)
474+
475+
// If the dockerfilePath is specified and deeper than the base of WorkspaceFolder AND the BuildContextPath is
476+
// not defined, show a warning
477+
dockerfileDir := filepath.Dir(dockerfilePath)
478+
if dockerfileDir != filepath.Clean(options.WorkspaceFolder) && options.BuildContextPath == "" {
479+
logf(codersdk.LogLevelWarn, "given dockerfile %q is below %q and no custom build context has been defined", dockerfilePath, options.WorkspaceFolder)
480+
logf(codersdk.LogLevelWarn, "\t-> set BUILD_CONTEXT_PATH to %q to fix", dockerfileDir)
481+
}
482+
470483
dockerfile, err := options.Filesystem.Open(dockerfilePath)
471484
if err == nil {
472485
content, err := io.ReadAll(dockerfile)
@@ -476,7 +489,7 @@ func Run(ctx context.Context, options Options) error {
476489
buildParams = &devcontainer.Compiled{
477490
DockerfilePath: dockerfilePath,
478491
DockerfileContent: string(content),
479-
BuildContext: options.WorkspaceFolder,
492+
BuildContext: filepath.Join(options.WorkspaceFolder, options.BuildContextPath),
480493
}
481494
}
482495
}

integration/integration_test.go

+85
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,91 @@ func TestNoMethodFails(t *testing.T) {
719719
require.ErrorContains(t, err, envbuilder.ErrNoFallbackImage.Error())
720720
}
721721

722+
func TestDockerfileBuildContext(t *testing.T) {
723+
t.Parallel()
724+
725+
inclFile := "myfile"
726+
dockerfile := fmt.Sprintf(`FROM %s
727+
COPY %s .`, testImageAlpine, inclFile)
728+
729+
tests := []struct {
730+
name string
731+
files map[string]string
732+
dockerfilePath string
733+
buildContextPath string
734+
expectedErr string
735+
}{
736+
{
737+
// Dockerfile & build context are in the same dir, copying inclFile should work.
738+
name: "same build context (default)",
739+
files: map[string]string{
740+
"Dockerfile": dockerfile,
741+
inclFile: "...",
742+
},
743+
dockerfilePath: "Dockerfile",
744+
buildContextPath: "", // use default
745+
expectedErr: "", // expect no errors
746+
},
747+
{
748+
// Dockerfile & build context are not in the same dir, build context is still the default; this should fail
749+
// to copy inclFile since it is not in the same dir as the Dockerfile.
750+
name: "different build context (default)",
751+
files: map[string]string{
752+
"a/Dockerfile": dockerfile,
753+
"a/" + inclFile: "...",
754+
},
755+
dockerfilePath: "a/Dockerfile",
756+
buildContextPath: "", // use default
757+
expectedErr: inclFile + ": no such file or directory",
758+
},
759+
{
760+
// Dockerfile & build context are not in the same dir, but inclFile is in the default build context dir;
761+
// this should allow inclFile to be copied. This is probably not desirable though?
762+
name: "different build context (default, different content roots)",
763+
files: map[string]string{
764+
"a/Dockerfile": dockerfile,
765+
inclFile: "...",
766+
},
767+
dockerfilePath: "a/Dockerfile",
768+
buildContextPath: "", // use default
769+
expectedErr: "",
770+
},
771+
{
772+
// Dockerfile is not in the default build context dir, but the build context has been overridden; this should
773+
// allow inclFile to be copied.
774+
name: "different build context (custom)",
775+
files: map[string]string{
776+
"a/Dockerfile": dockerfile,
777+
"a/" + inclFile: "...",
778+
},
779+
dockerfilePath: "a/Dockerfile",
780+
buildContextPath: "a/",
781+
expectedErr: "",
782+
},
783+
}
784+
785+
for _, tc := range tests {
786+
tc := tc
787+
788+
t.Run(tc.name, func(t *testing.T) {
789+
url := createGitServer(t, gitServerOptions{
790+
files: tc.files,
791+
})
792+
_, err := runEnvbuilder(t, options{env: []string{
793+
"GIT_URL=" + url,
794+
"DOCKERFILE_PATH=" + tc.dockerfilePath,
795+
"BUILD_CONTEXT_PATH=" + tc.buildContextPath,
796+
}})
797+
798+
if tc.expectedErr == "" {
799+
require.NoError(t, err)
800+
} else {
801+
require.ErrorContains(t, err, tc.expectedErr)
802+
}
803+
})
804+
}
805+
}
806+
722807
// TestMain runs before all tests to build the envbuilder image.
723808
func TestMain(m *testing.M) {
724809
cleanOldEnvbuilders()

0 commit comments

Comments
 (0)