Skip to content

Commit 8131e8a

Browse files
GiulioSavinigiulio.savini
andauthored
fix: keep project build context as container path so local builder can stat it (#2314) (#2346)
Co-authored-by: giulio.savini <giulio.savini@bvtech.com>
1 parent a1d5232 commit 8131e8a

File tree

2 files changed

+51
-19
lines changed

2 files changed

+51
-19
lines changed

backend/internal/services/project_service.go

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1759,14 +1759,6 @@ func resolveDockerfilePathInternal(svc composetypes.ServiceConfig) (string, erro
17591759
return dockerfilePath, nil
17601760
}
17611761

1762-
func translateBuildPathInternal(path string, pathMapper *projects.PathMapper) (string, error) {
1763-
if pathMapper == nil || strings.TrimSpace(path) == "" || !filepath.IsAbs(path) {
1764-
return path, nil
1765-
}
1766-
1767-
return pathMapper.ContainerToHost(path)
1768-
}
1769-
17701762
func buildArgsFromCompose(args map[string]*string) map[string]string {
17711763
buildArgs := map[string]string{}
17721764
for key, value := range args {
@@ -1895,14 +1887,18 @@ func (s *ProjectService) prepareServiceBuildRequest(
18951887
return imagetypes.BuildRequest{}, updatedSvc, updated, fmt.Errorf("service %s must define an image when push is enabled", serviceName)
18961888
}
18971889

1890+
// The build context (and any absolute Dockerfile path) is read locally by
1891+
// Arcane — both the docker provider (`archive.TarWithOptions`) and the
1892+
// buildkit provider (`SolveOpt.LocalDirs`) stream the directory contents
1893+
// to the daemon from the Arcane process's own filesystem. It must
1894+
// therefore stay as a container path; translating it to the host path
1895+
// (which is what bind mount sources need) makes `os.Stat` fail because
1896+
// the host path doesn't exist inside the Arcane container. See #2314.
1897+
// pathMapper is intentionally not consumed here for that reason.
18981898
contextDir, err := resolveBuildContextInternal(project.WorkingDir, updatedSvc, serviceName)
18991899
if err != nil {
19001900
return imagetypes.BuildRequest{}, updatedSvc, updated, err
19011901
}
1902-
contextDir, err = translateBuildPathInternal(contextDir, pathMapper)
1903-
if err != nil {
1904-
return imagetypes.BuildRequest{}, updatedSvc, updated, fmt.Errorf("translate build context for service %s: %w", serviceName, err)
1905-
}
19061902

19071903
dockerfileInline := updatedSvc.Build.DockerfileInline
19081904
if strings.TrimSpace(updatedSvc.Build.Dockerfile) != "" && strings.TrimSpace(dockerfileInline) != "" {
@@ -1915,10 +1911,6 @@ func (s *ProjectService) prepareServiceBuildRequest(
19151911
if err != nil {
19161912
return imagetypes.BuildRequest{}, updatedSvc, updated, err
19171913
}
1918-
dockerfilePath, err = translateBuildPathInternal(dockerfilePath, pathMapper)
1919-
if err != nil {
1920-
return imagetypes.BuildRequest{}, updatedSvc, updated, fmt.Errorf("translate Dockerfile path for service %s: %w", serviceName, err)
1921-
}
19221914
}
19231915

19241916
buildReq := imagetypes.BuildRequest{

backend/internal/services/project_service_test.go

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1436,7 +1436,13 @@ func TestProjectService_PrepareServiceBuildRequest_MapsComposeFields(t *testing.
14361436
assert.Contains(t, req.ExtraHosts[0], "10.0.0.5")
14371437
}
14381438

1439-
func TestProjectService_PrepareServiceBuildRequest_UsesExecutorVisiblePaths(t *testing.T) {
1439+
// TestProjectService_PrepareServiceBuildRequest_KeepsContainerPaths is a
1440+
// regression test for #2314: Arcane's local build pipeline (the docker and
1441+
// buildkit providers both read the build context via the Arcane process's own
1442+
// filesystem) cannot use host paths, so prepareServiceBuildRequest must leave
1443+
// the build context and any absolute Dockerfile path as container paths even
1444+
// when the projects mount has a non-matching host prefix.
1445+
func TestProjectService_PrepareServiceBuildRequest_KeepsContainerPaths(t *testing.T) {
14401446
svc := &ProjectService{}
14411447
proj := &composetypes.Project{WorkingDir: "/app/data/projects/demo", Name: "demo"}
14421448
pm := projects.NewPathMapper("/app/data/projects", "/docker-data/arcane/projects")
@@ -1461,8 +1467,42 @@ func TestProjectService_PrepareServiceBuildRequest_UsesExecutorVisiblePaths(t *t
14611467
)
14621468
require.NoError(t, err)
14631469

1464-
assert.Equal(t, "/docker-data/arcane/projects/demo", req.ContextDir)
1465-
assert.Equal(t, "/docker-data/arcane/projects/demo/Dockerfile.custom", req.Dockerfile)
1470+
assert.Equal(t, "/app/data/projects/demo", req.ContextDir)
1471+
assert.Equal(t, "/app/data/projects/demo/Dockerfile.custom", req.Dockerfile)
1472+
}
1473+
1474+
// TestProjectService_PrepareServiceBuildRequest_BuildDotKeepsContainerPath
1475+
// reproduces the exact configuration from #2314: a compose file with
1476+
// `build: .` next to its Dockerfile, on an installation where the projects
1477+
// directory is bind-mounted from a different host path than the container
1478+
// path. The resulting BuildRequest must point at the container path so the
1479+
// local builder can stat / tar the directory.
1480+
func TestProjectService_PrepareServiceBuildRequest_BuildDotKeepsContainerPath(t *testing.T) {
1481+
svc := &ProjectService{}
1482+
proj := &composetypes.Project{WorkingDir: "/app/data/projects/caddy", Name: "caddy"}
1483+
pm := projects.NewPathMapper("/app/data/projects", "/storage/volumes/arcane/projects")
1484+
1485+
serviceCfg := composetypes.ServiceConfig{
1486+
Name: "caddy",
1487+
Image: "caddy",
1488+
Build: &composetypes.BuildConfig{
1489+
Context: ".",
1490+
},
1491+
}
1492+
1493+
req, _, _, err := svc.prepareServiceBuildRequest(
1494+
context.Background(),
1495+
"project-id",
1496+
proj,
1497+
"caddy",
1498+
serviceCfg,
1499+
ProjectBuildOptions{},
1500+
pm,
1501+
)
1502+
require.NoError(t, err)
1503+
1504+
assert.Equal(t, "/app/data/projects/caddy", req.ContextDir)
1505+
assert.Equal(t, "Dockerfile", req.Dockerfile)
14661506
}
14671507

14681508
func TestProjectService_PrepareServiceBuildRequest_UsesInlineDockerfile(t *testing.T) {

0 commit comments

Comments
 (0)