fix: keep project build context as container path so local builder can stat it (#2314)#2346
Merged
kmendell merged 2 commits intogetarcaneapp:mainfrom Apr 11, 2026
Conversation
Member
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
kmendell
approved these changes
Apr 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #2314 — clicking Build on a project whose compose file contains a
build:section fails withbuild context not found: stat /<host-projects-dir>/<service>: no such file or directorywhenever the projects bind mount uses a different host path than the container path (e.g./storage/volumes/arcane:/app/data). The reporter's case is the canonical setup:with
Dockerfilenext tocompose.yamlunderPROJECTS_DIRECTORY=/app/data/projects.Root cause
prepareServiceBuildRequestinbackend/internal/services/project_service.gowas passing the build context (and any absolute Dockerfile path) throughtranslateBuildPathInternal→pathMapper.ContainerToHost, converting/app/data/projects/caddyinto/storage/volumes/arcane/projects/caddybefore stuffing it into theBuildRequest.This translation is correct for bind mount sources — those are sent to the daemon and have to make sense from the host's perspective. It is wrong for the build context, which is read by Arcane's own process:
archive.TarWithOptions(contextDir, …)and then streams it through the daemon's/buildAPI (backend/pkg/libarcane/libbuild/builder_docker.go:208).SolveOpt.LocalDirs{"context": contextDir, "dockerfile": dockerfileDir}(backend/pkg/libarcane/libbuild/builder_buildkit.go:149), which is a client-side local directory.validateBuildRequestInternalwhich callsos.Stat(contextDir)on the Arcane process's filesystem (backend/pkg/libarcane/libbuild/builder_utils.go:31).Once the contextDir has been rewritten to the host path, none of those steps can see the directory and the build aborts. Inline Dockerfile builds were just as broken (they go through the same code path), which is why the reporter found that the workaround from #1959 also failed.
The translation was added in cea8140 (PR #1875, "feat(autoupdate/compose): implement docker compose aware update logic"). The accompanying test,
TestProjectService_PrepareServiceBuildRequest_UsesExecutorVisiblePaths, locked in the regression by asserting host paths.Fix
translateBuildPathInternalcalls inprepareServiceBuildRequest— the build context and Dockerfile path now flow through unchanged.translateBuildPathInternalhelper.TestProjectService_PrepareServiceBuildRequest_BuildDotKeepsContainerPath) that mirrors 🐞 Bug: Project builds not working as expected #2314 exactly:build: ., projects mounted at/app/data/projectsfrom/storage/volumes/arcane/projects.pathMapperis still received byprepareServiceBuildRequest(callers up the chain pass it through), but is no longer consumed inside this function. I left the parameter in place to keep the diff scoped to the bug — it can be cleaned up alongside any future refactor of the build orchestration entry points.Tests
go test ./backend/internal/services/... -run PrepareServiceBuildRequest -v✅ (5/5)go test ./backend/internal/services/...✅gofmt -lclean,go vet ./backend/internal/services/...cleanTest plan
/storage/volumes/arcane:/app/data), create a project withbuild: .andDockerfilenext tocompose.yaml, hit Build — image builds successfullydockerfile_inline:) variant of the same project also builds (covers the regression from 🐞 Bug: Project builds not working as expected #1959 that 🐞 Bug: Project builds not working as expected #2314 also re-tripped)/app/data/projects:/app/data/projects) still buildDisclaimer Greptiles Reviews use AI, make sure to check over its work.
To better help train Greptile on our codebase, if the comment is useful and valid Like the comment, if its not helpful or invalid Dislike
To have Greptile Re-Review the changes, mention
greptileai.Greptile Summary
This PR fixes a bug (#2314) where clicking Build on a project with a
build:section in its compose file would fail withstat /<host-path>/...: no such file or directoryon installations where the projects bind mount uses a different host path than the container path. The root cause wasprepareServiceBuildRequesttranslating the build context and Dockerfile paths from container paths to host paths viapathMapper.ContainerToHost, even though both the docker provider (archive.TarWithOptions) and the buildkit provider (SolveOpt.LocalDirs) read those paths from the Arcane process's own (container) filesystem — not the host's.The fix removes both
translateBuildPathInternalcalls and deletes the now-unused helper, adds a clear explanatory comment at the call site, and includes two regression tests mirroring the exact failure scenario.Confidence Score: 5/5
Safe to merge — the fix correctly removes erroneous container-to-host path translation for build contexts, is well-tested, and introduces no new risk.
The logic change is correct and well-understood: os.Stat, archive.TarWithOptions, and SolveOpt.LocalDirs all operate on the Arcane container's filesystem, so keeping the container path is the only valid option. The fix removes the helper entirely rather than just disabling it, and two targeted regression tests guard against re-introduction. Only a P2 style note remains about the _ = pathMapper blank assignment.
No files require special attention.
Prompt To Fix All With AI
Reviews (1): Last reviewed commit: "fix: keep project build context as conta..." | Re-trigger Greptile