Skip to content

Commit

Permalink
Only evaluate target states when needed
Browse files Browse the repository at this point in the history
  • Loading branch information
twpayne committed Mar 29, 2021
1 parent b196247 commit 382988d
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 94 deletions.
87 changes: 87 additions & 0 deletions cmd/managedcmd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package cmd

import (
"bytes"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/twpayne/go-vfs/v2"

"github.com/twpayne/chezmoi/internal/chezmoitest"
)

func TestManagedCmd(t *testing.T) {
for _, tc := range []struct {
name string
root interface{}
args []string
expectedOutput string
}{
{
name: "simple",
root: map[string]interface{}{
"/home/user/.local/share/chezmoi/dot_file": "# contents of .file\n",
},
expectedOutput: chezmoitest.JoinLines(
".file",
),
},
{
name: "template",
root: map[string]interface{}{
"/home/user/.local/share/chezmoi/dot_template.tmpl": "{{ fail \"Template should not be executed\" }}\n",
},
expectedOutput: chezmoitest.JoinLines(
".template",
),
},
{
name: "create_template",
root: map[string]interface{}{
"/home/user/.local/share/chezmoi/create_dot_file.tmpl": "{{ fail \"Template should not be executed\" }}\n",
},
expectedOutput: chezmoitest.JoinLines(
".file",
),
},
{
name: "modify_template",
root: map[string]interface{}{
"/home/user/.local/share/chezmoi/modify_dot_file.tmpl": "{{ fail \"Template should not be executed\" }}\n",
},
expectedOutput: chezmoitest.JoinLines(
".file",
),
},
{
name: "script_template",
root: map[string]interface{}{
"/home/user/.local/share/chezmoi/run_script.tmpl": "{{ fail \"Template should not be executed\" }}\n",
},
args: []string{
"--include", "scripts",
},
expectedOutput: chezmoitest.JoinLines(
"script",
),
},
{
name: "symlink_template",
root: map[string]interface{}{
"/home/user/.local/share/chezmoi/symlink_dot_symlink.tmpl": "{{ fail \"Template should not be executed\" }}\n",
},
expectedOutput: chezmoitest.JoinLines(
".symlink",
),
},
} {
t.Run(tc.name, func(t *testing.T) {
chezmoitest.WithTestFS(t, tc.root, func(fs vfs.FS) {
stdout := &bytes.Buffer{}
require.NoError(t, newTestConfig(t, fs, withStdout(stdout)).execute(append([]string{"managed"}, tc.args...)))
assert.Equal(t, tc.expectedOutput, stdout.String())
})
})
}
}
20 changes: 6 additions & 14 deletions internal/chezmoi/actualstateentry.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,9 @@ func NewActualStateEntry(system System, absPath AbsPath, info os.FileInfo, err e
return &ActualStateFile{
absPath: absPath,
perm: info.Mode() & os.ModePerm,
lazyContents: &lazyContents{
contentsFunc: func() ([]byte, error) {
return system.ReadFile(absPath)
},
},
lazyContents: newLazyContentsFunc(func() ([]byte, error) {
return system.ReadFile(absPath)
}),
}, nil
case os.ModeDir:
return &ActualStateDir{
Expand All @@ -70,15 +68,9 @@ func NewActualStateEntry(system System, absPath AbsPath, info os.FileInfo, err e
case os.ModeSymlink:
return &ActualStateSymlink{
absPath: absPath,
lazyLinkname: &lazyLinkname{
linknameFunc: func() (string, error) {
linkname, err := system.Readlink(absPath)
if err != nil {
return "", err
}
return linkname, nil
},
},
lazyLinkname: newLazyLinknameFunc(func() (string, error) {
return system.Readlink(absPath)
}),
}, nil
default:
return nil, &errUnsupportedFileType{
Expand Down
14 changes: 14 additions & 0 deletions internal/chezmoi/lazy.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ func newLazyContents(contents []byte) *lazyContents {
}
}

// newLazyContentsFunc returns a new lazyContents with contentsFunc.
func newLazyContentsFunc(contentsFunc func() ([]byte, error)) *lazyContents {
return &lazyContents{
contentsFunc: contentsFunc,
}
}

// Contents returns lc's contents.
func (lc *lazyContents) Contents() ([]byte, error) {
if lc == nil {
Expand Down Expand Up @@ -68,6 +75,13 @@ func newLazyLinkname(linkname string) *lazyLinkname {
}
}

// newLazyLinknameFunc returns a new lazyLinkname with linknameFunc.
func newLazyLinknameFunc(linknameFunc func() (string, error)) *lazyLinkname {
return &lazyLinkname{
linknameFunc: linknameFunc,
}
}

// Linkname returns s's linkname.
func (ll *lazyLinkname) Linkname() (string, error) {
if ll == nil {
Expand Down
76 changes: 38 additions & 38 deletions internal/chezmoi/sourcestate.go
Original file line number Diff line number Diff line change
Expand Up @@ -839,26 +839,28 @@ func (s *SourceState) newSourceStateFile(sourceRelPath SourceRelPath, fileAttr F
}
return &TargetStateFile{
lazyContents: newLazyContents(contents),
empty: true,
perm: fileAttr.perm(),
}, nil
}
case SourceFileTypeFile:
targetStateEntryFunc = func(destSystem System, destAbsPath AbsPath) (TargetStateEntry, error) {
contents, err := sourceLazyContents.Contents()
if err != nil {
return nil, err
}
if fileAttr.Template {
contents, err = s.ExecuteTemplateData(sourceRelPath.String(), contents)
contentsFunc := func() ([]byte, error) {
contents, err := sourceLazyContents.Contents()
if err != nil {
return nil, err
}
}
if !fileAttr.Empty && isEmpty(contents) {
return &TargetStateRemove{}, nil
if fileAttr.Template {
contents, err = s.ExecuteTemplateData(sourceRelPath.String(), contents)
if err != nil {
return nil, err
}
}
return contents, nil
}
return &TargetStateFile{
lazyContents: newLazyContents(contents),
lazyContents: newLazyContentsFunc(contentsFunc),
empty: fileAttr.Empty,
perm: fileAttr.perm(),
}, nil
}
Expand Down Expand Up @@ -918,47 +920,48 @@ func (s *SourceState) newSourceStateFile(sourceRelPath SourceRelPath, fileAttr F
return destSystem.IdempotentCmdOutput(cmd)
}
return &TargetStateFile{
lazyContents: &lazyContents{
contentsFunc: contentsFunc,
},
perm: fileAttr.perm(),
lazyContents: newLazyContentsFunc(contentsFunc),
perm: fileAttr.perm(),
}, nil
}
case SourceFileTypeScript:
targetStateEntryFunc = func(destSystem System, destAbsPath AbsPath) (TargetStateEntry, error) {
contents, err := sourceLazyContents.Contents()
if err != nil {
return nil, err
}
if fileAttr.Template {
contents, err = s.ExecuteTemplateData(sourceRelPath.String(), contents)
contentsFunc := func() ([]byte, error) {
contents, err := sourceLazyContents.Contents()
if err != nil {
return nil, err
}
if fileAttr.Template {
contents, err = s.ExecuteTemplateData(sourceRelPath.String(), contents)
if err != nil {
return nil, err
}
}
return contents, nil
}
return &TargetStateScript{
lazyContents: newLazyContents(contents),
lazyContents: newLazyContentsFunc(contentsFunc),
name: targetRelPath,
once: fileAttr.Once,
}, nil
}
case SourceFileTypeSymlink:
targetStateEntryFunc = func(destSystem System, destAbsPath AbsPath) (TargetStateEntry, error) {
linknameBytes, err := sourceLazyContents.Contents()
if err != nil {
return nil, err
}
if fileAttr.Template {
linknameBytes, err = s.ExecuteTemplateData(sourceRelPath.String(), linknameBytes)
linknameFunc := func() (string, error) {
linknameBytes, err := sourceLazyContents.Contents()
if err != nil {
return nil, err
return "", err
}
}
if isEmpty(linknameBytes) {
return &TargetStateRemove{}, nil
if fileAttr.Template {
linknameBytes, err = s.ExecuteTemplateData(sourceRelPath.String(), linknameBytes)
if err != nil {
return "", err
}
}
return string(bytes.TrimSpace(linknameBytes)), nil
}
return &TargetStateSymlink{
lazyLinkname: newLazyLinkname(string(bytes.TrimSpace(linknameBytes))),
lazyLinkname: newLazyLinknameFunc(linknameFunc),
}, nil
}
default:
Expand Down Expand Up @@ -1022,16 +1025,15 @@ func (s *SourceState) sourceStateEntry(actualStateEntry ActualStateEntry, destAb
return nil, err
}
}
lazyContents := &lazyContents{
contents: contents,
}
lazyContents := newLazyContents(contents)
sourceRelPath := parentSourceRelPath.Join(NewSourceRelPath(RelPath(fileAttr.SourceName(s.encryption.EncryptedSuffix()))))
return &SourceStateFile{
Attr: fileAttr,
sourceRelPath: sourceRelPath,
lazyContents: lazyContents,
targetStateEntry: &TargetStateFile{
lazyContents: lazyContents,
empty: options.Empty,
perm: 0o666,
},
}, nil
Expand All @@ -1050,9 +1052,7 @@ func (s *SourceState) sourceStateEntry(actualStateEntry ActualStateEntry, destAb
contents = autoTemplate(contents, s.TemplateData())
}
contents = append(contents, '\n')
lazyContents := &lazyContents{
contents: contents,
}
lazyContents := newLazyContents(contents)
sourceRelPath := parentSourceRelPath.Join(NewSourceRelPath(RelPath(fileAttr.SourceName(s.encryption.EncryptedSuffix()))))
return &SourceStateFile{
Attr: fileAttr,
Expand Down
20 changes: 6 additions & 14 deletions internal/chezmoi/sourcestate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -931,14 +931,10 @@ func TestSourceStateRead(t *testing.T) {
TargetName: "file",
Type: SourceFileTypeFile,
},
lazyContents: &lazyContents{
contents: []byte("# contents of .dir/file\n"),
},
lazyContents: newLazyContents([]byte("# contents of .dir/file\n")),
targetStateEntry: &TargetStateFile{
perm: 0o666,
lazyContents: &lazyContents{
contents: []byte("# contents of .dir/file\n"),
},
perm: 0o666,
lazyContents: newLazyContents([]byte("# contents of .dir/file\n")),
},
},
}),
Expand Down Expand Up @@ -1008,14 +1004,10 @@ func TestSourceStateRead(t *testing.T) {
TargetName: "file1",
Type: SourceFileTypeFile,
},
lazyContents: &lazyContents{
contents: []byte("# contents of dir/file1\n"),
},
lazyContents: newLazyContents([]byte("# contents of dir/file1\n")),
targetStateEntry: &TargetStateFile{
perm: 0o666,
lazyContents: &lazyContents{
contents: []byte("# contents of dir/file1\n"),
},
perm: 0o666,
lazyContents: newLazyContents([]byte("# contents of dir/file1\n")),
},
},
"dir/file2": &SourceStateRemove{
Expand Down
Loading

0 comments on commit 382988d

Please sign in to comment.