Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Skip adding entries in external dirs instead of erroring #3671

Merged
merged 1 commit into from
Mar 31, 2024
Merged
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
45 changes: 30 additions & 15 deletions internal/chezmoi/sourcestate.go
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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 --
Loading