Skip to content

Commit

Permalink
Merge pull request twpayne#87 from twpayne/exact-dirs
Browse files Browse the repository at this point in the history
Add support for exact directories
  • Loading branch information
twpayne committed Jan 5, 2019
2 parents 67bb438 + b28a722 commit b4d34d8
Show file tree
Hide file tree
Showing 10 changed files with 318 additions and 135 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -395,19 +395,20 @@ with a `.`. The following prefixes and suffixes are special.
| -------------------- | ----------------------------------------------------------------------------------|
| `private_` prefix | Remove all group and world permissions from the target file or directory. |
| `empty_` prefix | Ensure the file exists, even if is empty. By default, empty files are removed. |
| `exact_` prefix | Remove anything not managed by `chezmoi`. |
| `executable_` prefix | Add executable permissions to the target file. |
| `symlink_` prefix | Create a symlink instead of a regular file. |
| `dot_` prefix | Rename to use a leading dot, e.g. `dot_foo` becomes `.foo`. |
| `.tmpl` suffix | Treat the contents of the source file as a template. |

Order is important, the order is `private_`, `empty_`, `executable_`,
Order is important, the order is `exact_`, `private_`, `empty_`, `executable_`,
`symlink_`, `dot_`, `.tmpl`.

Different target types allow different prefixes and suffixes.

| Target type | Allowed prefixes and suffixes |
| ------------- | ---------------------------------------------------- |
| Directory | `private_`, `dot_` |
| Directory | `exact_, `private_`, `dot_` |
| Regular file | `private_`, `empty_`, `executable_`, `dot_`, `.tmpl` |
| Symbolic link | `symlink_`, `dot_`, `.tmpl` |

Expand Down
14 changes: 7 additions & 7 deletions cmd/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"path/filepath"

"github.com/spf13/cobra"
"github.com/twpayne/chezmoi/lib/chezmoi"
vfs "github.com/twpayne/go-vfs"
)

Expand All @@ -16,20 +17,19 @@ var addCommand = &cobra.Command{
RunE: makeRunE(config.runAddCommand),
}

// An AddCommandConfig is a configuration for the add command.
type addCommandConfig struct {
empty bool
recursive bool
template bool
options chezmoi.AddOptions
}

func init() {
rootCommand.AddCommand(addCommand)

persistentFlags := addCommand.PersistentFlags()
persistentFlags.BoolVarP(&config.add.empty, "empty", "e", false, "add empty files")
persistentFlags.BoolVarP(&config.add.options.Empty, "empty", "e", false, "add empty files")
persistentFlags.BoolVarP(&config.add.options.Exact, "exact", "x", false, "add directories exactly")
persistentFlags.BoolVarP(&config.add.recursive, "recursive", "r", false, "recurse in to subdirectories")
persistentFlags.BoolVarP(&config.add.template, "template", "T", false, "add files as templates")
persistentFlags.BoolVarP(&config.add.options.Template, "template", "T", false, "add files as templates")
}

func (c *Config) runAddCommand(fs vfs.FS, command *cobra.Command, args []string) error {
Expand Down Expand Up @@ -65,12 +65,12 @@ func (c *Config) runAddCommand(fs vfs.FS, command *cobra.Command, args []string)
if err != nil {
return err
}
return ts.Add(fs, path, info, c.add.empty, c.add.template, mutator)
return ts.Add(fs, c.add.options, path, info, mutator)
}); err != nil {
return err
}
} else {
if err := ts.Add(fs, path, nil, c.add.empty, c.add.template, mutator); err != nil {
if err := ts.Add(fs, c.add.options, path, nil, mutator); err != nil {
return err
}
}
Expand Down
160 changes: 105 additions & 55 deletions cmd/add_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmd
import (
"testing"

"github.com/twpayne/chezmoi/lib/chezmoi"
"github.com/twpayne/go-vfs/vfst"
)

Expand All @@ -16,142 +17,191 @@ func TestAddCommand(t *testing.T) {
}{
{
name: "add_first_file",
args: []string{"/home/jenkins/.bashrc"},
args: []string{"/home/user/.bashrc"},
root: map[string]interface{}{
"/home/jenkins/.bashrc": "foo",
"/home/user/.bashrc": "foo",
},
tests: []vfst.Test{
vfst.TestPath("/home/jenkins/.chezmoi",
vfst.TestPath("/home/user/.chezmoi",
vfst.TestIsDir,
vfst.TestModePerm(0700),
),
vfst.TestPath("/home/jenkins/.chezmoi/dot_bashrc",
vfst.TestPath("/home/user/.chezmoi/dot_bashrc",
vfst.TestModeIsRegular,
vfst.TestContentsString("foo"),
),
},
},
{
name: "add_template",
args: []string{"/home/jenkins/.gitconfig"},
args: []string{"/home/user/.gitconfig"},
add: addCommandConfig{
template: true,
options: chezmoi.AddOptions{
Template: true,
},
},
root: map[string]interface{}{
"/home/jenkins": &vfst.Dir{Perm: 0755},
"/home/jenkins/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/jenkins/.gitconfig": "[user]\n\tname = John Smith\n\temail = john.smith@company.com\n",
"/home/user": &vfst.Dir{Perm: 0755},
"/home/user/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/user/.gitconfig": "[user]\n\tname = John Smith\n\temail = john.smith@company.com\n",
},
tests: []vfst.Test{
vfst.TestPath("/home/jenkins/.chezmoi/dot_gitconfig.tmpl",
vfst.TestPath("/home/user/.chezmoi/dot_gitconfig.tmpl",
vfst.TestModeIsRegular,
vfst.TestContentsString("[user]\n\tname = {{ .name }}\n\temail = {{ .email }}\n"),
),
},
},
{
name: "add_recursive",
args: []string{"/home/jenkins/.config"},
args: []string{"/home/user/.config"},
add: addCommandConfig{
recursive: true,
},
root: map[string]interface{}{
"/home/jenkins": &vfst.Dir{Perm: 0755},
"/home/jenkins/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/jenkins/.config/micro/settings.json": "{}",
"/home/user": &vfst.Dir{Perm: 0755},
"/home/user/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/user/.config/micro/settings.json": "{}",
},
tests: []vfst.Test{
vfst.TestPath("/home/jenkins/.chezmoi/dot_config/micro/settings.json",
vfst.TestPath("/home/user/.chezmoi/dot_config/micro/settings.json",
vfst.TestModeIsRegular,
vfst.TestContentsString("{}"),
),
},
},
{
name: "add_nested_directory",
args: []string{"/home/jenkins/.config/micro/settings.json"},
args: []string{"/home/user/.config/micro/settings.json"},
root: map[string]interface{}{
"/home/jenkins": &vfst.Dir{Perm: 0755},
"/home/jenkins/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/jenkins/.config/micro/settings.json": "{}",
"/home/user": &vfst.Dir{Perm: 0755},
"/home/user/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/user/.config/micro/settings.json": "{}",
},
tests: []vfst.Test{
vfst.TestPath("/home/jenkins/.chezmoi/dot_config/micro/settings.json",
vfst.TestPath("/home/user/.chezmoi/dot_config/micro/settings.json",
vfst.TestModeIsRegular,
vfst.TestContentsString("{}"),
),
},
},
{
name: "add_exact_dir",
args: []string{"/home/user/dir"},
add: addCommandConfig{
options: chezmoi.AddOptions{
Exact: true,
},
},
root: map[string]interface{}{
"/home/user": &vfst.Dir{Perm: 0755},
"/home/user/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/user/dir": &vfst.Dir{Perm: 0755},
},
tests: []vfst.Test{
vfst.TestPath("/home/user/.chezmoi/exact_dir",
vfst.TestIsDir,
),
},
},
{
name: "add_exact_dir_recursive",
args: []string{"/home/user/dir"},
add: addCommandConfig{
recursive: true,
options: chezmoi.AddOptions{
Exact: true,
},
},
root: map[string]interface{}{
"/home/user": &vfst.Dir{Perm: 0755},
"/home/user/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/user/dir": map[string]interface{}{
"foo": "bar",
},
},
tests: []vfst.Test{
vfst.TestPath("/home/user/.chezmoi/exact_dir",
vfst.TestIsDir,
),
vfst.TestPath("/home/user/.chezmoi/exact_dir/foo",
vfst.TestModeIsRegular,
vfst.TestContentsString("bar"),
),
},
},
{
name: "add_empty_file",
args: []string{"/home/jenkins/empty"},
args: []string{"/home/user/empty"},
add: addCommandConfig{
empty: true,
options: chezmoi.AddOptions{
Empty: true,
},
},
root: map[string]interface{}{
"/home/jenkins": &vfst.Dir{Perm: 0755},
"/home/jenkins/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/jenkins/empty": "",
"/home/user": &vfst.Dir{Perm: 0755},
"/home/user/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/user/empty": "",
},
tests: []vfst.Test{
vfst.TestPath("/home/jenkins/.chezmoi/empty_empty",
vfst.TestPath("/home/user/.chezmoi/empty_empty",
vfst.TestModeIsRegular,
vfst.TestContents(nil),
),
},
},
{
name: "add_symlink",
args: []string{"/home/jenkins/foo"},
args: []string{"/home/user/foo"},
root: map[string]interface{}{
"/home/jenkins": &vfst.Dir{Perm: 0755},
"/home/jenkins/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/jenkins/foo": &vfst.Symlink{Target: "bar"},
"/home/user": &vfst.Dir{Perm: 0755},
"/home/user/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/user/foo": &vfst.Symlink{Target: "bar"},
},
tests: []vfst.Test{
vfst.TestPath("/home/jenkins/.chezmoi/symlink_foo",
vfst.TestPath("/home/user/.chezmoi/symlink_foo",
vfst.TestModeIsRegular,
vfst.TestContentsString("bar"),
),
},
},
{
name: "add_symlink_in_dir_recursive",
args: []string{"/home/jenkins/foo"},
args: []string{"/home/user/foo"},
add: addCommandConfig{
recursive: true,
},
root: map[string]interface{}{
"/home/jenkins": &vfst.Dir{Perm: 0755},
"/home/jenkins/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/jenkins/foo/bar": &vfst.Symlink{Target: "baz"},
"/home/user": &vfst.Dir{Perm: 0755},
"/home/user/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/user/foo/bar": &vfst.Symlink{Target: "baz"},
},
tests: []vfst.Test{
vfst.TestPath("/home/jenkins/.chezmoi/foo",
vfst.TestPath("/home/user/.chezmoi/foo",
vfst.TestIsDir,
),
vfst.TestPath("/home/jenkins/.chezmoi/foo/symlink_bar",
vfst.TestPath("/home/user/.chezmoi/foo/symlink_bar",
vfst.TestModeIsRegular,
vfst.TestContentsString("baz"),
),
},
},
{
name: "add_symlink_with_parent_dir",
args: []string{"/home/jenkins/foo/bar/baz"},
args: []string{"/home/user/foo/bar/baz"},
root: map[string]interface{}{
"/home/jenkins": &vfst.Dir{Perm: 0755},
"/home/jenkins/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/jenkins/foo/bar/baz": &vfst.Symlink{Target: "qux"},
"/home/user": &vfst.Dir{Perm: 0755},
"/home/user/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/user/foo/bar/baz": &vfst.Symlink{Target: "qux"},
},
tests: []vfst.Test{
vfst.TestPath("/home/jenkins/.chezmoi/foo",
vfst.TestPath("/home/user/.chezmoi/foo",
vfst.TestIsDir,
),
vfst.TestPath("/home/jenkins/.chezmoi/foo/bar",
vfst.TestPath("/home/user/.chezmoi/foo/bar",
vfst.TestIsDir,
),
vfst.TestPath("/home/jenkins/.chezmoi/foo/bar/symlink_baz",
vfst.TestPath("/home/user/.chezmoi/foo/bar/symlink_baz",
vfst.TestModeIsRegular,
vfst.TestContentsString("qux"),
),
Expand All @@ -160,8 +210,8 @@ func TestAddCommand(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
c := &Config{
SourceDir: "/home/jenkins/.chezmoi",
TargetDir: "/home/jenkins",
SourceDir: "/home/user/.chezmoi",
TargetDir: "/home/user",
Umask: 022,
DryRun: false,
Verbose: true,
Expand All @@ -187,39 +237,39 @@ func TestAddCommand(t *testing.T) {

func TestAddAfterModification(t *testing.T) {
c := &Config{
SourceDir: "/home/jenkins/.chezmoi",
TargetDir: "/home/jenkins",
SourceDir: "/home/user/.chezmoi",
TargetDir: "/home/user",
Umask: 022,
DryRun: false,
Verbose: true,
}
fs, cleanup, err := vfst.NewTestFS(map[string]interface{}{
"/home/jenkins": &vfst.Dir{Perm: 0755},
"/home/jenkins/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/jenkins/.bashrc": "# contents of .bashrc\n",
"/home/user": &vfst.Dir{Perm: 0755},
"/home/user/.chezmoi": &vfst.Dir{Perm: 0700},
"/home/user/.bashrc": "# contents of .bashrc\n",
})
defer cleanup()
if err != nil {
t.Fatalf("vfst.NewTestFS(_) == _, _, %v, want _, _, <nil>", err)
}
args := []string{"/home/jenkins/.bashrc"}
args := []string{"/home/user/.bashrc"}
if err := c.runAddCommand(fs, nil, args); err != nil {
t.Errorf("c.runAddCommand(fs, nil, %+v) == %v, want <nil>", args, err)
}
vfst.RunTests(t, fs, "",
vfst.TestPath("/home/jenkins/.chezmoi/dot_bashrc",
vfst.TestPath("/home/user/.chezmoi/dot_bashrc",
vfst.TestModeIsRegular,
vfst.TestContentsString("# contents of .bashrc\n"),
),
)
if err := fs.WriteFile("/home/jenkins/.bashrc", []byte("# new contents of .bashrc\n"), 0644); err != nil {
if err := fs.WriteFile("/home/user/.bashrc", []byte("# new contents of .bashrc\n"), 0644); err != nil {
t.Errorf("fs.WriteFile(...) == %v, want <nil>", err)
}
if err := c.runAddCommand(fs, nil, args); err != nil {
t.Errorf("c.runAddCommand(fs, nil, %+v) == %v, want <nil>", args, err)
}
vfst.RunTests(t, fs, "",
vfst.TestPath("/home/jenkins/.chezmoi/dot_bashrc",
vfst.TestPath("/home/user/.chezmoi/dot_bashrc",
vfst.TestModeIsRegular,
vfst.TestContentsString("# new contents of .bashrc\n"),
),
Expand Down
Loading

0 comments on commit b4d34d8

Please sign in to comment.