Reject symlinked directories in templates - #6758
Vivek1106-04 wants to merge 2 commits into
Conversation
## Changes `bundle init` failed with `read <path>: is a directory` when a template's `template/` tree contained a symlink to a directory, after already having created part of the output. `fs.ReadDir` reports entries via `Lstat`, so `IsDir()` is false for a symlink pointing at a directory, and the walker treated it as a file. The failure only surfaced during materialization, when the copied file was read. The walker now resolves symlinked entries and rejects links to directories with an actionable error, before anything is written to disk. Symlinks to files keep working as before. ## Why The previous message described an internal read failure rather than the unsupported construct, and the partially materialized output directory had to be cleaned up by hand. Rejecting is preferred over following the link: a followed symlink can form a cycle (`ln -s . loop`), which would make the breadth-first walk loop forever. Fixes databricks#6391 ## Tests - New unit test in `libs/template/renderer_test.go` covering both symlink kinds (to a file: unchanged, rendered; to a directory: rejected). - New acceptance test `bundle/templates-machinery/symlinked-directory` asserting the error and that no output is left behind. Before this change it produced `Error: read ./template/linked: is a directory` plus a stray `linked` directory. - `go test ./libs/template/...`, `go test ./acceptance -run 'TestAccept/bundle/templates'`, `./task fmt`, `./task ws`, `./task lint`.
Approval status: pending
|
|
An authorized user can trigger integration tests manually by following the instructions below: Trigger: Inputs:
Checks will be approved automatically on success. |
Changes
bundle initfailed withread <path>: is a directorywhen a template'stemplate/tree contained a symlink to a directory, after already having created part of the output.fs.ReadDirreports entries viaLstat, soIsDir()is false for a symlink pointing at a directory, and the walker treated it as a file. The failure only surfaced later, during materialization, when the copied file was read.The walker now resolves symlinked entries and rejects links to directories with an actionable error, before anything is written to disk. Symlinks to files keep working as before.
Before:
After:
Why
Fixes #6391.
The previous message described an internal read failure rather than the unsupported construct, so it was hard to act on, and the partially materialized output directory had to be cleaned up by hand.
The issue offers two acceptable behaviours: follow the link, or report it as unsupported. This PR reports it. Following a directory symlink admits a cycle (
ln -s . loop) that would make the breadth-first walk loop forever, so supporting it means also carrying a visited-set keyed on resolved paths. Happy to switch to following if you would rather have the feature.Tests
libs/template/renderer_test.go: table-drivenTestRendererWalkSymlinkscovering both symlink kinds — to a file (unchanged: rendered) and to a directory (rejected). The file case fails if the new check is ever widened to all symlinks.acceptance/bundle/templates-machinery/symlinked-directory: asserts the error and, viafind.py, that no output is left behind. Revertingrenderer.goalone makes it fail with the old message plus a straylinkeddirectory.scriptrather than committed because the acceptance harness copies inputs withfilepath.Walk+ read, which cannot copy a symlinked directory.GOOS.windows = falsesince symlink creation needs privilege there.go test ./libs/template/...,go test ./acceptance -run 'TestAccept/bundle/templates',./task fmt,./task ws,./task lint(0 issues).bundle/templates/pydabs/check-formattingfails on my machine both with and without this change; it looks environment-related and unrelated.