diff --git a/go.mod b/go.mod index 8e0ad30ca43..9851a0b0262 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/coreos/go-semver v0.3.1 github.com/fsnotify/fsnotify v1.8.0 github.com/go-git/go-git/v5 v5.13.2 + github.com/goccy/go-yaml v1.15.15 github.com/google/go-github/v68 v68.0.0 github.com/google/renameio/v2 v2.0.0 github.com/gopasspw/gopass v1.15.15 @@ -56,7 +57,6 @@ require ( golang.org/x/sys v0.29.0 golang.org/x/term v0.28.0 gopkg.in/ini.v1 v1.67.0 - gopkg.in/yaml.v3 v3.0.1 howett.net/plist v1.0.1 mvdan.cc/sh/v3 v3.10.0 ) @@ -180,4 +180,5 @@ require ( golang.org/x/tools v0.29.0 // indirect google.golang.org/protobuf v1.36.4 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 72222e0814e..0381aee4be1 100644 --- a/go.sum +++ b/go.sum @@ -242,6 +242,8 @@ github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7 github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/goccy/go-yaml v1.15.15 h1:5turdzAlutS2Q7/QR/9R99Z1K0J00qDb4T0pHJcZ5ew= +github.com/goccy/go-yaml v1.15.15/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= diff --git a/internal/chezmoi/entrytypeset.go b/internal/chezmoi/entrytypeset.go index 9923df4d3fc..9838bef7f1e 100644 --- a/internal/chezmoi/entrytypeset.go +++ b/internal/chezmoi/entrytypeset.go @@ -268,7 +268,7 @@ func (s *EntryTypeSet) MarshalJSON() ([]byte, error) { } } -// MarshalYAML implements gopkg.in/yaml.v3.Marshaler. +// MarshalYAML implements github.com/goccy/go-yaml.Marshaler. func (s *EntryTypeSet) MarshalYAML() (any, error) { if s.bits == EntryTypesAll { return []string{"all"}, nil diff --git a/internal/chezmoi/format.go b/internal/chezmoi/format.go index 0baa4768826..d713e0630ff 100644 --- a/internal/chezmoi/format.go +++ b/internal/chezmoi/format.go @@ -10,9 +10,9 @@ import ( "slices" "strings" + "github.com/goccy/go-yaml" "github.com/pelletier/go-toml/v2" "github.com/tailscale/hujson" - "gopkg.in/yaml.v3" ) // Formats. diff --git a/internal/chezmoi/hexbytes.go b/internal/chezmoi/hexbytes.go index 67dd115bdea..83ad8d27606 100644 --- a/internal/chezmoi/hexbytes.go +++ b/internal/chezmoi/hexbytes.go @@ -2,6 +2,7 @@ package chezmoi import ( "encoding/hex" + "strconv" ) // A HexBytes is a []byte which is marshaled as a hex string. @@ -22,6 +23,15 @@ func (h HexBytes) MarshalText() ([]byte, error) { return result, nil } +// MarshalYAML implements github.com/goccy/go-yaml.BytesMarshaler.MarshalYAML. +func (h HexBytes) MarshalYAML() ([]byte, error) { + data := make([]byte, 2+2*len(h)) + data[0] = '"' + hex.Encode(data[1:len(data)-1], []byte(h)) + data[len(data)-1] = '"' + return data, nil +} + // UnmarshalText implements encoding.TextUnmarshaler.UnmarshalText. func (h *HexBytes) UnmarshalText(text []byte) error { if len(text) == 0 { @@ -36,6 +46,24 @@ func (h *HexBytes) UnmarshalText(text []byte) error { return nil } +// UnmarshalYAML implements github.com/goccy/go-yaml.BytesUnmarshaler.UnmarshalYAML. +func (h *HexBytes) UnmarshalYAML(data []byte) error { + s, err := strconv.Unquote(string(data)) + if err != nil { + return err + } + if s == "" { + *h = nil + return nil + } + hexBytes, err := hex.DecodeString(s) + if err != nil { + return err + } + *h = hexBytes + return nil +} + func (h HexBytes) String() string { return hex.EncodeToString(h) } diff --git a/internal/chezmoi/template.go b/internal/chezmoi/template.go index a08a5373303..f5f633d11e0 100644 --- a/internal/chezmoi/template.go +++ b/internal/chezmoi/template.go @@ -8,10 +8,10 @@ import ( "strings" "text/template" + "github.com/goccy/go-yaml" "github.com/mattn/go-runewidth" "github.com/mitchellh/copystructure" "github.com/pelletier/go-toml/v2" - "gopkg.in/yaml.v3" ) // A Template extends text/template.Template with support for directives. @@ -61,8 +61,9 @@ func ParseTemplate(name string, data []byte, options TemplateOptions) (*Template } funcs["toYaml"] = func(data any) string { var builder strings.Builder - encoder := yaml.NewEncoder(&builder) - encoder.SetIndent(runewidth.StringWidth(options.FormatIndent)) + encoder := yaml.NewEncoder(&builder, + yaml.Indent(runewidth.StringWidth(options.FormatIndent)), + ) if err := encoder.Encode(data); err != nil { panic(err) } diff --git a/internal/cmd/autobool.go b/internal/cmd/autobool.go index 66ea9dfa9f0..5271c9bac10 100644 --- a/internal/cmd/autobool.go +++ b/internal/cmd/autobool.go @@ -1,14 +1,13 @@ package cmd import ( - "errors" + "bytes" "fmt" "reflect" "strconv" "strings" "github.com/mitchellh/mapstructure" - "gopkg.in/yaml.v3" "github.com/twpayne/chezmoi/v2/internal/chezmoi" ) @@ -38,7 +37,7 @@ func (b autoBool) MarshalJSON() ([]byte, error) { } } -// MarshalYAML implements gopkg.in/yaml.v3.Marshaler. +// MarshalYAML implements github.com/goccy/go-yaml.Marshaler. func (b autoBool) MarshalYAML() (any, error) { if b.auto { return "auto", nil @@ -89,15 +88,12 @@ func (b *autoBool) UnmarshalJSON(data []byte) error { } // UnmarshalYAML implements gopkg.in/yaml.Unmarshaler.UnmarshalYAML. -func (b *autoBool) UnmarshalYAML(value *yaml.Node) error { - if value.Kind != yaml.ScalarNode { - return errors.New("expected scalar node") - } - if value.Value == "auto" { +func (b *autoBool) UnmarshalYAML(data []byte) error { + if bytes.Equal(data, []byte("auto")) { b.auto = true return nil } - boolValue, err := chezmoi.ParseBool(value.Value) + boolValue, err := chezmoi.ParseBool(string(data)) if err != nil { return err } diff --git a/internal/cmd/testdata/scripts/configstate.txtar b/internal/cmd/testdata/scripts/configstate.txtar index 77b43adf708..c927c0727ea 100644 --- a/internal/cmd/testdata/scripts/configstate.txtar +++ b/internal/cmd/testdata/scripts/configstate.txtar @@ -64,8 +64,8 @@ exec chezmoi diff email = "me@home.org" -- golden/state-dump.yaml -- configState: - configState: - configTemplateContentsSHA256: af43121a524340707b84e390f510c949731177e6f2a25b3b6b11b2fc656cf8f2 + configState: + configTemplateContentsSHA256: af43121a524340707b84e390f510c949731177e6f2a25b3b6b11b2fc656cf8f2 entryState: {} gitHubKeysState: {} gitHubLatestReleaseState: {} diff --git a/internal/cmd/testdata/scripts/dumpyaml.txtar b/internal/cmd/testdata/scripts/dumpyaml.txtar index 8c1e5b33414..1fde31aad50 100644 --- a/internal/cmd/testdata/scripts/dumpyaml.txtar +++ b/internal/cmd/testdata/scripts/dumpyaml.txtar @@ -10,125 +10,111 @@ cmp stdout golden/dump-except-dirs.yaml -- golden/dump-except-dirs.yaml -- .create: - type: file - name: .create - contents: | - # contents of .create - perm: 420 + type: file + name: .create + contents: "# contents of .create\n" + perm: 420 .dir/file: - type: file - name: .dir/file - contents: | - # contents of .dir/file - perm: 420 + type: file + name: .dir/file + contents: "# contents of .dir/file\n" + perm: 420 .dir/subdir/file: - type: file - name: .dir/subdir/file - contents: | - # contents of .dir/subdir/file - perm: 420 + type: file + name: .dir/subdir/file + contents: "# contents of .dir/subdir/file\n" + perm: 420 .empty: - type: file - name: .empty - contents: "" - perm: 420 + type: file + name: .empty + contents: "" + perm: 420 .executable: - type: file - name: .executable - contents: | - # contents of .executable - perm: 493 + type: file + name: .executable + contents: "# contents of .executable\n" + perm: 493 .file: - type: file - name: .file - contents: | - # contents of .file - perm: 420 + type: file + name: .file + contents: "# contents of .file\n" + perm: 420 .private: - type: file - name: .private - contents: | - # contents of .private - perm: 384 + type: file + name: .private + contents: "# contents of .private\n" + perm: 384 .readonly: - type: file - name: .readonly - contents: | - # contents of .readonly - perm: 292 + type: file + name: .readonly + contents: "# contents of .readonly\n" + perm: 292 .symlink: - type: symlink - name: .symlink - linkname: .dir/subdir/file + type: symlink + name: .symlink + linkname: .dir/subdir/file .template: - type: file - name: .template - contents: | - key = value - perm: 420 + type: file + name: .template + contents: | + key = value + perm: 420 -- golden/dump.yaml -- .create: - type: file - name: .create - contents: | - # contents of .create - perm: 420 + type: file + name: .create + contents: "# contents of .create\n" + perm: 420 .dir: - type: dir - name: .dir - perm: 493 + type: dir + name: .dir + perm: 493 .dir/file: - type: file - name: .dir/file - contents: | - # contents of .dir/file - perm: 420 + type: file + name: .dir/file + contents: "# contents of .dir/file\n" + perm: 420 .dir/subdir: - type: dir - name: .dir/subdir - perm: 493 + type: dir + name: .dir/subdir + perm: 493 .dir/subdir/file: - type: file - name: .dir/subdir/file - contents: | - # contents of .dir/subdir/file - perm: 420 + type: file + name: .dir/subdir/file + contents: "# contents of .dir/subdir/file\n" + perm: 420 .empty: - type: file - name: .empty - contents: "" - perm: 420 + type: file + name: .empty + contents: "" + perm: 420 .executable: - type: file - name: .executable - contents: | - # contents of .executable - perm: 493 + type: file + name: .executable + contents: "# contents of .executable\n" + perm: 493 .file: - type: file - name: .file - contents: | - # contents of .file - perm: 420 + type: file + name: .file + contents: "# contents of .file\n" + perm: 420 .private: - type: file - name: .private - contents: | - # contents of .private - perm: 384 + type: file + name: .private + contents: "# contents of .private\n" + perm: 384 .readonly: - type: file - name: .readonly - contents: | - # contents of .readonly - perm: 292 + type: file + name: .readonly + contents: "# contents of .readonly\n" + perm: 292 .symlink: - type: symlink - name: .symlink - linkname: .dir/subdir/file + type: symlink + name: .symlink + linkname: .dir/subdir/file .template: - type: file - name: .template - contents: | - key = value - perm: 420 + type: file + name: .template + contents: | + key = value + perm: 420 diff --git a/internal/cmd/testdata/scripts/external.txtar b/internal/cmd/testdata/scripts/external.txtar index a6b8bdd8000..c39b292f394 100644 --- a/internal/cmd/testdata/scripts/external.txtar +++ b/internal/cmd/testdata/scripts/external.txtar @@ -163,12 +163,12 @@ chhome home16/user url: {{ env "HTTPD_URL" }}/.file checksum: size: 20 - md5: 49fe9018f97349cdd0a0ac7b7f668b05 - ripemd160: 2320636f6e74656e7473206f66202e66696c650a9c1185a5c5e9fc54612808977ee8f548b2258d31 - sha1: cb91d72dc73f6d984b33ac5745f1cf6f76745bd2 - sha256: 634a4dd193c7b3b926d2e08026aa81a416fd41cec52854863b974af422495663 - sha384: f8545bb66433eb514727bbc61c4e4939c436d38079767f39f12b8803d6472ca1dfcd101675b20cd525f7e3d02c368b61 - sha512: a68814ec3d16e8bd28c9291bbc596f0282687c5ba5d1f4c26c4e427166666a03c11df1dab3577b4483142764c37d4887def77244c4a52cb9852a234fa8cb15ba + md5: "49fe9018f97349cdd0a0ac7b7f668b05" + ripemd160: "2320636f6e74656e7473206f66202e66696c650a9c1185a5c5e9fc54612808977ee8f548b2258d31" + sha1: "cb91d72dc73f6d984b33ac5745f1cf6f76745bd2" + sha256: "634a4dd193c7b3b926d2e08026aa81a416fd41cec52854863b974af422495663" + sha384: "f8545bb66433eb514727bbc61c4e4939c436d38079767f39f12b8803d6472ca1dfcd101675b20cd525f7e3d02c368b61" + sha512: "a68814ec3d16e8bd28c9291bbc596f0282687c5ba5d1f4c26c4e427166666a03c11df1dab3577b4483142764c37d4887def77244c4a52cb9852a234fa8cb15ba" -- home11/user/.local/share/chezmoi/.chezmoiexternal.toml -- [".file"] type = "file" diff --git a/internal/cmd/testdata/scripts/initconfig.txtar b/internal/cmd/testdata/scripts/initconfig.txtar index bf8319c210a..64ddafa2ccc 100644 --- a/internal/cmd/testdata/scripts/initconfig.txtar +++ b/internal/cmd/testdata/scripts/initconfig.txtar @@ -110,16 +110,24 @@ exists $HOME/.chezmoi/chezmoistate.boltdb -- golden/chezmoi1.yaml -- data: - email: "mail1@example.com" + email: "mail1@example.com" -- golden/chezmoi2.yaml -- data: - email: "mail2@example.com" + email: "mail2@example.com" -- golden/chezmoi3.toml -- [data] - email = "mail3@example.com" + email = "mail3@example.com" -- golden/error1.log -- chezmoi: multiple config files: $CHEZMOICONFIGDIR/chezmoi.toml and $CHEZMOICONFIGDIR/chezmoi.yaml -- golden/error2.log -- -chezmoi: invalid config: $HOME/.chezmoi/athome.yaml: yaml: unmarshal errors: line 1: cannot unmarshal !!seq into map[string]interface {} +chezmoi: invalid config: $WORK/home2/user/.chezmoi/athome.yaml: [2:3] value is not allowed in this context + 1 | [data] +> 2 | email = "mail3@example.com" + ^ + -- golden/error3.log -- -chezmoi: invalid config: $HOME/.chezmoi/athome.txt: yaml: unmarshal errors: line 1: cannot unmarshal !!seq into map[string]interface {} +chezmoi: invalid config: $WORK/home3/user/.chezmoi/athome.txt: [2:3] value is not allowed in this context + 1 | [data] +> 2 | email = "mail3@example.com" + ^ + diff --git a/internal/cmd/testdata/scripts/issue2695.txtar b/internal/cmd/testdata/scripts/issue2695.txtar index 2c5f18692dd..0d5302bf0d3 100644 --- a/internal/cmd/testdata/scripts/issue2695.txtar +++ b/internal/cmd/testdata/scripts/issue2695.txtar @@ -27,7 +27,7 @@ stderr 'invalid config' # test that chezmoi doctor warns about invalid YAML config files ! exec chezmoi doctor -stdout 'error\s+config-file\s+.*unmarshal errors' +stdout 'error\s+config-file\s+.*string was used where mapping is expected' -- home/user/.config/chezmoi/chezmoi.json -- { diff --git a/internal/cmd/testdata/scripts/managed.txtar b/internal/cmd/testdata/scripts/managed.txtar index d6c2790bde1..14e9e785986 100644 --- a/internal/cmd/testdata/scripts/managed.txtar +++ b/internal/cmd/testdata/scripts/managed.txtar @@ -146,25 +146,25 @@ $WORK/home/user/.template } -- golden/managed-all.yaml -- .create: - absolute: $WORK/home2/user/.create - sourceAbsolute: $WORK/home2/user/.local/share/chezmoi/create_dot_create.tmpl - sourceRelative: create_dot_create.tmpl + absolute: $WORK/home2/user/.create + sourceAbsolute: $WORK/home2/user/.local/share/chezmoi/create_dot_create.tmpl + sourceRelative: create_dot_create.tmpl .file: - absolute: $WORK/home2/user/.file - sourceAbsolute: $WORK/home2/user/.local/share/chezmoi/modify_dot_file.tmpl - sourceRelative: modify_dot_file.tmpl + absolute: $WORK/home2/user/.file + sourceAbsolute: $WORK/home2/user/.local/share/chezmoi/modify_dot_file.tmpl + sourceRelative: modify_dot_file.tmpl .symlink: - absolute: $WORK/home2/user/.symlink - sourceAbsolute: $WORK/home2/user/.local/share/chezmoi/symlink_dot_symlink.tmpl - sourceRelative: symlink_dot_symlink.tmpl + absolute: $WORK/home2/user/.symlink + sourceAbsolute: $WORK/home2/user/.local/share/chezmoi/symlink_dot_symlink.tmpl + sourceRelative: symlink_dot_symlink.tmpl .template: - absolute: $WORK/home2/user/.template - sourceAbsolute: $WORK/home2/user/.local/share/chezmoi/dot_template.tmpl - sourceRelative: dot_template.tmpl + absolute: $WORK/home2/user/.template + sourceAbsolute: $WORK/home2/user/.local/share/chezmoi/dot_template.tmpl + sourceRelative: dot_template.tmpl script: - absolute: $WORK/home2/user/script - sourceAbsolute: $WORK/home2/user/.local/share/chezmoi/run_script.tmpl - sourceRelative: run_script.tmpl + absolute: $WORK/home2/user/script + sourceAbsolute: $WORK/home2/user/.local/share/chezmoi/run_script.tmpl + sourceRelative: run_script.tmpl -- golden/managed-exclude-encrypted -- .create .dir diff --git a/internal/cmd/testdata/scripts/state.txtar b/internal/cmd/testdata/scripts/state.txtar index d757f9ee3c9..35fd30849aa 100644 --- a/internal/cmd/testdata/scripts/state.txtar +++ b/internal/cmd/testdata/scripts/state.txtar @@ -21,4 +21,4 @@ cmp stdout golden/data-after-delete.yaml bucket: {} -- golden/data.yaml -- bucket: - key: value + key: value diff --git a/internal/cmds/execute-template/main.go b/internal/cmds/execute-template/main.go index c38b332a648..4375c22fb0b 100644 --- a/internal/cmds/execute-template/main.go +++ b/internal/cmds/execute-template/main.go @@ -16,9 +16,9 @@ import ( "text/template" "github.com/Masterminds/sprig/v3" + "github.com/goccy/go-yaml" "github.com/google/go-github/v68/github" "github.com/google/renameio/v2/maybe" - "gopkg.in/yaml.v3" "github.com/twpayne/chezmoi/v2/internal/chezmoi" ) diff --git a/internal/cmds/generate-install.sh/main.go b/internal/cmds/generate-install.sh/main.go index 4411f616abf..a7b77eba8e3 100644 --- a/internal/cmds/generate-install.sh/main.go +++ b/internal/cmds/generate-install.sh/main.go @@ -10,7 +10,7 @@ import ( "slices" "text/template" - "gopkg.in/yaml.v3" + "github.com/goccy/go-yaml" "github.com/twpayne/chezmoi/v2/internal/chezmoiset" )