Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion internal/cli/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -1266,7 +1266,13 @@ func runAgent(ctx context.Context, agentName, fullsendDir, outputBase, targetRep

// 9d. Extract target repo back to host. SafeDownload removes dangerous
// symlinks (absolute or repo-escaping) and .git/hooks/ to prevent sandbox escape.
if clearErr := os.RemoveAll(hostRepositoryDir); clearErr != nil {
//
// Clear the contents of the directory rather than removing the directory
// itself. Removing the directory would invalidate the inode, which breaks
Comment thread
ralphbean marked this conversation as resolved.
// any shell whose CWD points at it (the next command in that shell would
// fail with "getwd: no such file or directory").
printer.StepInfo(fmt.Sprintf("Clearing %s before extraction", hostRepositoryDir))
if clearErr := clearDirContents(hostRepositoryDir); clearErr != nil {
Comment thread
ralphbean marked this conversation as resolved.
return fmt.Errorf("clearing local repo %s before extraction: %w", hostRepositoryDir, clearErr)
}
repoExtractStart := time.Now()
Expand Down Expand Up @@ -2972,3 +2978,19 @@ func containedLocalPath(baseDir, source string) (string, error) {
}
return real, nil
}

// clearDirContents removes all entries inside dir without removing dir itself.
// This preserves the directory inode so that shells whose CWD points at dir
// continue to work after the call.
func clearDirContents(dir string) error {
entries, err := os.ReadDir(dir)
if err != nil {
return err
}
for _, e := range entries {
if err := os.RemoveAll(filepath.Join(dir, e.Name())); err != nil {
return err
}
}
return nil
}
41 changes: 41 additions & 0 deletions internal/cli/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3494,6 +3494,47 @@ func TestRunAgent_StatusNotifierSetup(t *testing.T) {
assert.Contains(t, err.Error(), "openshell")
}

func TestClearDirContents(t *testing.T) {
Comment thread
ralphbean marked this conversation as resolved.
t.Run("populated directory", func(t *testing.T) {
dir := t.TempDir()

// Create some files and a subdirectory.
require.NoError(t, os.WriteFile(filepath.Join(dir, "a.txt"), []byte("a"), 0o644))
require.NoError(t, os.Mkdir(filepath.Join(dir, "sub"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(dir, "sub", "b.txt"), []byte("b"), 0o644))

// Grab inode before clearing.
infoBefore, err := os.Stat(dir)
require.NoError(t, err)

require.NoError(t, clearDirContents(dir))

// Directory itself still exists with the same inode.
infoAfter, err := os.Stat(dir)
require.NoError(t, err)
assert.True(t, os.SameFile(infoBefore, infoAfter), "directory inode should be preserved")

// Contents are gone.
entries, err := os.ReadDir(dir)
require.NoError(t, err)
assert.Empty(t, entries)
})

t.Run("empty directory", func(t *testing.T) {
dir := t.TempDir()
require.NoError(t, clearDirContents(dir))

entries, err := os.ReadDir(dir)
require.NoError(t, err)
assert.Empty(t, entries)
})

t.Run("non-existent directory", func(t *testing.T) {
err := clearDirContents(filepath.Join(t.TempDir(), "does-not-exist"))
require.Error(t, err)
})
}

func TestResolveBackendFromConfigData_OrgConfig(t *testing.T) {
t.Parallel()

Expand Down
Loading