Skip to content

Reject symlinked directories in templates - #6758

Open
Vivek1106-04 wants to merge 2 commits into
databricks:mainfrom
Vivek1106-04:fix/template-symlinked-directory
Open

Vivek1106-04 wants to merge 2 commits into
databricks:mainfrom
Vivek1106-04:fix/template-symlinked-directory

Conversation

@Vivek1106-04

Copy link
Copy Markdown

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 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:

$ databricks bundle init ./tpl --config-file config.json --output-dir out
Error: read ./template/linked: is a directory
$ ls out
linked          # empty, left behind

After:

$ databricks bundle init ./tpl --config-file config.json --output-dir out
Error: linked: symbolic links to directories are not supported in templates
$ ls out
                # nothing written

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-driven TestRendererWalkSymlinks covering 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, via find.py, that no output is left behind. Reverting renderer.go alone makes it fail with the old message plus a stray linked directory.
  • The symlink is created in script rather than committed because the acceptance harness copies inputs with filepath.Walk + read, which cannot copy a symlinked directory. GOOS.windows = false since 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-formatting fails on my machine both with and without this change; it looks environment-related and unrelated.

## 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`.
@github-actions

github-actions Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

Approval status: pending

/acceptance/bundle/ - needs approval

6 files changed
Suggested: @denik
Also eligible: @janniklasrose, @pietern, @andrewnester, @lennartkats-db, @anton-107, @shreyas-goenka

/libs/template/ - needs approval

Files: libs/template/renderer.go, libs/template/renderer_test.go
Suggested: @denik
Also eligible: @janniklasrose, @pietern, @andrewnester, @lennartkats-db, @anton-107, @shreyas-goenka

General files (require maintainer)

Files: .nextchanges/bundles/template-symlinked-directory.md
Based on git history:

  • @denik -- recent work in .nextchanges/bundles/, libs/template/

Any maintainer (@andrewnester, @anton-107, @denik, @pietern, @shreyas-goenka, @simonfaltum, @renaudhartert-db, @janniklasrose, @lennartkats-db, @rugpanov, @rclarey) can approve all areas.
See OWNERS for ownership rules.

@github-actions

Copy link
Copy Markdown
Contributor

An authorized user can trigger integration tests manually by following the instructions below:

Trigger:
go/deco-tests-run/cli

Inputs:

  • PR number: 6758
  • Commit SHA: 3e98fc3e270ff1bc96eed1018f7df466b68d461d

Checks will be approved automatically on success.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bundle init fails on a symlinked directory inside a template's template/

1 participant