Skip to content

Commit

Permalink
fix: Don't attempt to add entries in external_ directories
Browse files Browse the repository at this point in the history
  • Loading branch information
twpayne committed Feb 19, 2024
1 parent c9ddc84 commit c0b7e92
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 14 deletions.
24 changes: 22 additions & 2 deletions internal/chezmoi/sourcestate.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,8 +393,28 @@ DEST_ABS_PATH:
parentSourceRelPath = SourceRelPath{}
} else if parentEntry, ok := newSourceStateEntriesByTargetRelPath[targetParentRelPath]; ok {
parentSourceRelPath = parentEntry.SourceRelPath()
} else if parentEntry := s.root.Get(targetParentRelPath); parentEntry != nil {
parentSourceRelPath = parentEntry.SourceRelPath()
} else if nodes := s.root.GetNodes(targetParentRelPath); nodes != nil {
for i, node := range nodes {
if i == 0 {
// nodes[0].sourceStateEntry should always be nil because it
// refers to the destination directory, which is not manged.
// chezmoi manages the destination directory's contents, not
// the destination directory itself. For example, chezmoi
// does not set the name or permissions of the user's home
// directory.
if node.sourceStateEntry != nil {
panic(fmt.Errorf("nodes[0]: expected nil, got %+v", node.sourceStateEntry))
}
continue
}
switch sourceStateDir, ok := node.sourceStateEntry.(*SourceStateDir); {
case i != len(nodes)-1 && !ok:
panic(fmt.Errorf("nodes[%d]: unexpected non-terminal source state entry, got %T", i, node.sourceStateEntry))
case sourceStateDir.Attr.External:
return fmt.Errorf("%s: cannot add entry in external_ directory", destAbsPath)
}
}
parentSourceRelPath = nodes[len(nodes)-1].sourceStateEntry.SourceRelPath()
} else {
return fmt.Errorf("%s: parent directory not in source state", destAbsPath)
}
Expand Down
24 changes: 13 additions & 11 deletions internal/chezmoi/sourcestatetreenode.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,30 @@ func newSourceStateTreeNode() *sourceStateEntryTreeNode {

// Get returns the SourceStateEntry at relPath.
func (n *sourceStateEntryTreeNode) Get(relPath RelPath) SourceStateEntry {
node := n.GetNode(relPath)
if node == nil {
nodes := n.GetNodes(relPath)
if nodes == nil {
return nil
}
return node.sourceStateEntry
return nodes[len(nodes)-1].sourceStateEntry
}

// GetNode returns the SourceStateTreeNode at relPath.
func (n *sourceStateEntryTreeNode) GetNode(targetRelPath RelPath) *sourceStateEntryTreeNode {
// GetNodes returns the sourceStateEntryTreeNodes to reach targetRelPath.
func (n *sourceStateEntryTreeNode) GetNodes(targetRelPath RelPath) []*sourceStateEntryTreeNode {
if targetRelPath.Empty() {
return n
return []*sourceStateEntryTreeNode{n}
}

node := n
for _, childRelPath := range targetRelPath.SplitAll() {
if childNode, ok := node.children[childRelPath]; ok {
node = childNode
targetRelPathComponents := targetRelPath.SplitAll()
nodes := make([]*sourceStateEntryTreeNode, 0, len(targetRelPathComponents))
nodes = append(nodes, n)
for _, childRelPath := range targetRelPathComponents {
if childNode, ok := nodes[len(nodes)-1].children[childRelPath]; ok {
nodes = append(nodes, childNode)
} else {
return nil
}
}
return node
return nodes
}

// ForEach calls f for each SourceStateEntry in the tree.
Expand Down
2 changes: 1 addition & 1 deletion internal/chezmoi/sourcestatetreenode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
func TestSourceStateEntryTreeNodeEmpty(t *testing.T) {
n := newSourceStateTreeNode()
assert.Equal(t, nil, n.Get(EmptyRelPath))
assert.Equal(t, n, n.GetNode(EmptyRelPath))
assert.Equal(t, []*sourceStateEntryTreeNode{n}, n.GetNodes(EmptyRelPath))
assert.NoError(t, n.ForEach(EmptyRelPath, func(RelPath, SourceStateEntry) error {
return errors.New("should not be called")
}))
Expand Down
13 changes: 13 additions & 0 deletions internal/cmd/testdata/scripts/issue3525.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# test that chezmoi add does not add files in external_ directories
! exec chezmoi add $HOME${/}.external/file
stderr 'cannot add entry in external_ directory'

# test that chezmoi add does not add files in subdirectories of external_ directories
! exec chezmoi add $HOME${/}.external/dir/file
stderr 'cannot add entry in external_ directory'

-- home/user/.external/dir/file --
# contents of .external/dir/file
-- home/user/.external/file --
# contents of .external/file
-- home/user/.local/share/chezmoi/external_dot_external/.keep --

0 comments on commit c0b7e92

Please sign in to comment.