From 4a9fcfd2edf81e2f439eb8a3b3129290e6ad736f Mon Sep 17 00:00:00 2001 From: Antoine Toussaint <312536060+antoinetoussaint-byte@users.noreply.github.com> Date: Tue, 22 Sep 2026 01:32:58 -0400 Subject: [PATCH] fix: stage recipe inputs relative to the service build context --- build_test.go | 8 ++++---- builder.go | 9 ++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/build_test.go b/build_test.go index e9e113e..18db567 100644 --- a/build_test.go +++ b/build_test.go @@ -134,7 +134,7 @@ func TestBuildEmitsRecipeForOutputDirectory(t *testing.T) { require.Len(t, plan.GetRecipes(), 1) recipe := plan.GetRecipes()[0] - require.Equal(t, "builder/Dockerfile", recipe.GetDockerfile()) + require.Equal(t, "Dockerfile", recipe.GetDockerfile()) require.Equal(t, ".", recipe.GetContext()) require.Equal(t, []string{"linux/amd64", "linux/arm64"}, recipe.GetPlatforms()) require.NotEmpty(t, recipe.GetImage()) @@ -144,13 +144,13 @@ func TestBuildEmitsRecipeForOutputDirectory(t *testing.T) { sources := copySources(string(dockerfile)) require.NotEmpty(t, sources, "expected the Dockerfile to COPY at least one file") for _, src := range sources { - _, err := os.Stat(filepath.Join(outDir, recipe.GetContext(), src)) + _, err := os.Stat(filepath.Join(builder.Location, recipe.GetContext(), src)) require.NoErrorf(t, err, "COPY source %q missing from recipe context", src) } } // TestBuildRecipeDockerfileIsValidDocker runs `docker buildx build --check` -// over the recipe Build emits into an output_directory, from that directory as +// over the recipe Build emits into an output_directory, from the service as // the context — the frontend validation a consumer without the codefly // toolchain gets for free before building. VerifyDockerBuildPlan only checks // the tree against the digest; it does not parse the Dockerfile, so an @@ -205,7 +205,7 @@ func TestBuildRecipeDockerfileIsValidDocker(t *testing.T) { defer cancel() cmd := exec.CommandContext(checkCtx, "docker", "buildx", "build", "--check", "-f", filepath.Join(outDir, filepath.FromSlash(recipe.GetDockerfile())), - filepath.Join(outDir, filepath.FromSlash(recipe.GetContext()))) + filepath.Join(builder.Location, filepath.FromSlash(recipe.GetContext()))) out, err := cmd.CombinedOutput() require.NoErrorf(t, err, "docker buildx build --check of the emitted recipe failed:\n%s", out) } diff --git a/builder.go b/builder.go index 85eb199..51f4163 100644 --- a/builder.go +++ b/builder.go @@ -4,7 +4,6 @@ import ( "context" "embed" "fmt" - "path/filepath" "github.com/codefly-dev/core/agents/communicate" dockerhelpers "github.com/codefly-dev/core/agents/helpers/docker" @@ -366,14 +365,18 @@ func (s *Builder) Build(ctx context.Context, req *builderv0.BuildRequest) (*buil // pushes a multi-arch manifest list, so the image becomes a reproducible // artifact a consumer can rebuild without the agent toolchain. func (s *Builder) buildRecipe(ctx context.Context, out string, image *resources.DockerImage, docker DockerTemplating) (*builderv0.BuildResponse, error) { - err := s.Templates(ctx, docker, services.WithBuilder(builderFS).WithDestination("%s", filepath.Join(out, "builder"))) + // COPY paths resolve from the service, while the Dockerfile and inventory + // resolve from out. Stage the bootstrap in both locations when they differ. + err := s.Templates(ctx, docker, + services.WithBuilder(builderFS), + services.WithBuilder(builderFS).WithDestination("%s", out)) if err != nil { return s.Builder.BuildError(err) } recipe := &builderv0.DockerBuildRecipe{ Name: s.Base.Service.Name, - Dockerfile: "builder/Dockerfile", + Dockerfile: "Dockerfile", Context: ".", Image: image.FullName(), Platforms: []string{"linux/amd64", "linux/arm64"},