Skip to content

Commit

Permalink
fix: Skip adding entries in external dirs instead of erroring
Browse files Browse the repository at this point in the history
  • Loading branch information
twpayne committed Mar 31, 2024
1 parent 5749974 commit 8a83dcc
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 19 deletions.
45 changes: 30 additions & 15 deletions internal/chezmoi/sourcestate.go
Expand Up @@ -313,20 +313,21 @@ type ReplaceFunc func(targetRelPath RelPath, newSourceStateEntry, oldSourceState

// AddOptions are options to SourceState.Add.
type AddOptions struct {
AutoTemplate bool // Automatically create templates, if possible.
Create bool // Add create_ entries instead of normal entries.
Encrypt bool // Encrypt files.
EncryptedSuffix string // Suffix for encrypted files.
Exact bool // Add the exact_ attribute to added directories.
Filter *EntryTypeFilter // Entry type filter.
OnIgnoreFunc func(RelPath) // Function to call when a target is ignored.
PreAddFunc PreAddFunc // Function to be called before a source entry is added.
ConfigFileAbsPath AbsPath // Path to config file.
ProtectedAbsPaths []AbsPath // Paths that must not be added.
RemoveDir RelPath // Directory to remove before adding.
ReplaceFunc ReplaceFunc // Function to be called before a source entry is replaced.
Template bool // Add the .tmpl attribute to added files.
TemplateSymlinks bool // Add symlinks with targets in the source or home directories as templates.
AutoTemplate bool // Automatically create templates, if possible.
Create bool // Add create_ entries instead of normal entries.
Encrypt bool // Encrypt files.
EncryptedSuffix string // Suffix for encrypted files.
Errorf func(string, ...any) // Function to print errors.
Exact bool // Add the exact_ attribute to added directories.
Filter *EntryTypeFilter // Entry type filter.
OnIgnoreFunc func(RelPath) // Function to call when a target is ignored.
PreAddFunc PreAddFunc // Function to be called before a source entry is added.
ConfigFileAbsPath AbsPath // Path to config file.
ProtectedAbsPaths []AbsPath // Paths that must not be added.
RemoveDir RelPath // Directory to remove before adding.
ReplaceFunc ReplaceFunc // Function to be called before a source entry is replaced.
Template bool // Add the .tmpl attribute to added files.
TemplateSymlinks bool // Add symlinks with targets in the source or home directories as templates.
}

// Add adds destAbsPathInfos to s.
Expand Down Expand Up @@ -389,11 +390,19 @@ func (s *SourceState) Add(
newSourceStateEntries := make(map[SourceRelPath]SourceStateEntry)
newSourceStateEntriesByTargetRelPath := make(map[RelPath]SourceStateEntry)
nonEmptyDirs := make(map[SourceRelPath]struct{})
externalDirRelPaths := make(map[RelPath]struct{})
dirRenames := make(map[AbsPath]AbsPath)
DEST_ABS_PATH:
for _, destAbsPath := range destAbsPaths {
targetRelPath := destAbsPath.MustTrimDirPrefix(s.destDirAbsPath)

// Skip any entries in known external dirs.
for externalDir := range externalDirRelPaths {
if targetRelPath.HasDirPrefix(externalDir) {
continue DEST_ABS_PATH
}
}

// Find the target's parent directory in the source state.
var parentSourceRelPath SourceRelPath
if targetParentRelPath := targetRelPath.Dir(); targetParentRelPath == DotRelPath {
Expand All @@ -418,7 +427,13 @@ DEST_ABS_PATH:
case i != len(nodes)-1 && !ok:
panic(fmt.Errorf("nodes[%d]: unexpected non-terminal source state entry, got %T", i, node.sourceStateEntry))
case ok && sourceStateDir.Attr.External:
return fmt.Errorf("%s: cannot add entry in external_ directory", destAbsPath)
targetRelPathComponents := targetRelPath.SplitAll()
externalDirRelPath := EmptyRelPath.Join(targetRelPathComponents[:i]...)
externalDirRelPaths[externalDirRelPath] = struct{}{}
if options.Errorf != nil {
options.Errorf("%s: skipping entries in external_ directory\n", externalDirRelPath)
}
continue DEST_ABS_PATH
}
}
parentSourceRelPath = nodes[len(nodes)-1].sourceStateEntry.SourceRelPath()
Expand Down
1 change: 1 addition & 0 deletions internal/cmd/addcmd.go
Expand Up @@ -187,6 +187,7 @@ func (c *Config) runAddCmd(cmd *cobra.Command, args []string, sourceState *chezm
Encrypt: c.Add.Encrypt,
EncryptedSuffix: c.encryption.EncryptedSuffix(),
Exact: c.Add.exact,
Errorf: c.errorf,
Filter: c.Add.filter,
OnIgnoreFunc: c.defaultOnIgnoreFunc,
PreAddFunc: c.defaultPreAddFunc,
Expand Down
1 change: 1 addition & 0 deletions internal/cmd/importcmd.go
Expand Up @@ -88,6 +88,7 @@ func (c *Config) runImportCmd(cmd *cobra.Command, args []string, sourceState *ch
archiveReaderSystem,
archiveReaderSystem.FileInfos(),
&chezmoi.AddOptions{
Errorf: c.errorf,
Exact: c._import.exact,
Filter: c._import.filter,
RemoveDir: removeDir,
Expand Down
1 change: 1 addition & 0 deletions internal/cmd/mackupcmd_darwin.go
Expand Up @@ -109,6 +109,7 @@ func (c *Config) runMackupAddCmd(cmd *cobra.Command, args []string, sourceState
c.destSystem,
destAbsPathInfos,
&chezmoi.AddOptions{
Errorf: c.errorf,
Filter: chezmoi.NewEntryTypeFilter(chezmoi.EntryTypesAll, chezmoi.EntryTypesNone),
OnIgnoreFunc: c.defaultOnIgnoreFunc,
PreAddFunc: c.defaultPreAddFunc,
Expand Down
1 change: 1 addition & 0 deletions internal/cmd/readdcmd.go
Expand Up @@ -146,6 +146,7 @@ TARGET_REL_PATH:
if err := sourceState.Add(c.sourceSystem, c.persistentState, c.destSystem, destAbsPathInfos, &chezmoi.AddOptions{
Encrypt: sourceStateFile.Attr.Encrypted,
EncryptedSuffix: c.encryption.EncryptedSuffix(),
Errorf: c.errorf,
Filter: c.reAdd.filter,
}); err != nil {
return err
Expand Down
8 changes: 4 additions & 4 deletions internal/cmd/testdata/scripts/issue3525.txtar
@@ -1,10 +1,10 @@
# test that chezmoi add does not add files in external_ directories
! exec chezmoi add $HOME${/}.external/file
stderr 'cannot add entry in external_ directory'
exec chezmoi add $HOME${/}.external/file
stderr '.external: skipping entries 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'
exec chezmoi add $HOME${/}.external/dir/file
stderr '.external: skipping entries in external_ directory'

-- home/user/.external/dir/file --
# contents of .external/dir/file
Expand Down
12 changes: 12 additions & 0 deletions internal/cmd/testdata/scripts/issue3652.txtar
@@ -0,0 +1,12 @@
# test that chezmoi add skips files in external_ directories
exec chezmoi apply
exists $HOME/.dir/submodule/.git/.keep
exec chezmoi add $HOME${/}.dir
stderr '.dir/submodule: skipping entries in external_ directory'
cmp $CHEZMOISOURCEDIR/dot_dir/file golden/file

-- golden/file --
# contents of file
-- home/user/.dir/file --
# contents of file
-- home/user/.local/share/chezmoi/dot_dir/external_submodule/.git/.keep --

0 comments on commit 8a83dcc

Please sign in to comment.