fix(sandbox): use tarball upload to preserve symlinks - #1079
Conversation
openshell sandbox upload dereferences symlinks into real directories when building the tar archive (confirmed on v0.0.36 and v0.0.42, filed upstream as NVIDIA/OpenShell#1425). This causes git status inside the sandbox to report all git-tracked symlinks as deleted:, producing spurious noise and misleading agents. After uploading the repo and .git directory, exec a git command in the sandbox that reads the index for mode-120000 entries and recreates each symlink in place. Uses git cat-file blob to recover the target without needing to know the symlinks in advance. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Not sure about this approach yet, I need to test it locally. |
Site previewPreview: https://74b08680-site.fullsend-ai.workers.dev Commit: |
ReviewFindingsLow
Info
Previous runReviewFindingsInfo
Previous run (2)ReviewFindingsLow
Info
|
| // RestoreSymlinks recreates git-tracked symlinks after a sandbox upload. | ||
| // openshell sandbox upload dereferences symlinks into real directories; | ||
| // this reads the git index for mode-120000 entries and recreates them. | ||
| func RestoreSymlinks(sandboxName, repoDir string) error { |
There was a problem hiding this comment.
[info] test-coverage
RestoreSymlinks has no unit test. A table-driven test with a mock Exec could validate the shell command construction without requiring a live sandbox.
Review follow-upsCreated follow-up issues for actionable non-blocking review findings:
Previous runReview follow-upsCreated follow-up issues for actionable non-blocking review findings:
Previous run (2)Review follow-upsCreated follow-up issues for actionable non-blocking review findings:
|
Addresses review findings from #1080 and #1081: - Fix AWK field parsing to handle symlink paths with spaces by using tab field separator (-F'\t') instead of default whitespace splitting - Add shell escaping for repoDir parameter to prevent injection via single-quote escaping: strings.ReplaceAll(repoDir, "'", "'\\''") - Add error handling safeguards: set -euo pipefail, empty path guard - Move RestoreSymlinks call inside .git upload success block to avoid spurious warnings when .git is not uploaded - Add unit tests for RestoreSymlinks covering openshell-not-found, paths with spaces, and paths with single quotes - Increase timeout from 30s to 60s for repos with many symlinks - Add upstream issue reference to doc comment The AWK parsing fix prevents silent data corruption when symlink paths contain spaces - previously $4 captured only the first word, causing rm -rf and ln -s to operate on wrong paths. Signed-off-by: Wayne Sun <gsun@redhat.com>
Replace openshell sandbox upload + RestoreSymlinks with UploadDir, which builds a local tar archive (symlinks preserved by default), uploads it, and extracts it in the sandbox. This also eliminates the separate .git upload step since the tarball includes everything in one shot. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| assert.NoError(t, err) | ||
| } | ||
|
|
||
| func TestUploadDir_OpenshellNotInPath(t *testing.T) { |
There was a problem hiding this comment.
[low] test-adequacy
TestUploadDir_OpenshellNotInPath only exercises the error path. The happy path (tarball creation with symlink preservation) is not unit-tested. Manual live-sandbox verification was performed per the PR description.
Suggested fix: Add a unit test that creates a directory with a symlink, runs the tar step in isolation, and inspects the archive to confirm symlinks are preserved.
| } | ||
|
|
||
| extractCmd := fmt.Sprintf("mkdir -p %s && tar -xzf %s -C %s && rm %s", remotePath, remoteTar, remotePath, remoteTar) | ||
| _, stderr, exitCode, err := Exec(sandboxName, extractCmd, transferTimeout) |
There was a problem hiding this comment.
[info] style
extractCmd is built with fmt.Sprintf without shell-escaping remotePath or remoteTar. Safe in practice since values are internally constructed, consistent with all other Exec() call sites.
Closes #1133
Summary
openshell sandbox uploaddereferences symlinks into real directories when building the tar archive — confirmed on v0.0.36 and v0.0.42, filed upstream as NVIDIA/OpenShell#1425git statusinside the sandbox to report all git-tracked symlinks asdeleted:, producing spurious noise and misleading agents into attempting to restore files that were never deletedRestoreSymlinkspost-processing workaround withUploadDir: build a local tar archive (GNU tar preserves symlinks by default), upload it, and extract in the sandbox — one step, no git dependency, covers untracked symlinks too.gitupload step since the tarball includes everythingHow it works
UploadDircreates a temp tarball withtar -czf(symlinks are preserved by default — only-h/--dereferencewould follow them), uploads it to/tmp/fs-upload-<sandbox>.tar.gz, then execsmkdir -p <dest> && tar -xzf ... && rmin the sandbox.Test plan
find -type lreturns empty,link-dirshows asdrwxr-xr-xinstead of symlinkfind -type lreturns/sandbox/repo/link-dir,ls -lashowslrwxrwxrwx -> real-dir,git statusclean🤖 Generated with Claude Code