From a10a8bb396934c46bd6e73190bed36be85ab7e50 Mon Sep 17 00:00:00 2001 From: Norio Nomura Date: Thu, 5 Sep 2024 22:54:52 +0900 Subject: [PATCH] yqutil: use yamlfmt to fix indentation of sequence Signed-off-by: Norio Nomura `limactl edit`: support editing lima.yaml file This allows `limactl edit --set "..." ` to be used instead of `yq -i eval "..." ` in scripts like `hack/inject-cmdline-to-template.sh`. Signed-off-by: Norio Nomura yqutil: Use `LF` for line endings regardless of the platform. Signed-off-by: Norio Nomura `limactl edit`: update command description Signed-off-by: Norio Nomura yqutil_test: fix typo Signed-off-by: Norio Nomura --- cmd/limactl/edit.go | 61 +++++++++++++++++++++++++++++---------- go.mod | 5 ++++ go.sum | 10 +++++++ pkg/yqutil/yqutil.go | 18 +++++++++++- pkg/yqutil/yqutil_test.go | 13 +++++---- 5 files changed, 85 insertions(+), 22 deletions(-) diff --git a/cmd/limactl/edit.go b/cmd/limactl/edit.go index 32305f8b54a..6cafe0c873a 100644 --- a/cmd/limactl/edit.go +++ b/cmd/limactl/edit.go @@ -8,6 +8,7 @@ import ( "path/filepath" "github.com/lima-vm/lima/cmd/limactl/editflags" + "github.com/lima-vm/lima/cmd/limactl/guessarg" "github.com/lima-vm/lima/pkg/editutil" "github.com/lima-vm/lima/pkg/instance" "github.com/lima-vm/lima/pkg/limayaml" @@ -22,8 +23,8 @@ import ( func newEditCommand() *cobra.Command { editCommand := &cobra.Command{ - Use: "edit INSTANCE", - Short: "Edit an instance of Lima", + Use: "edit INSTANCE|FILE.yaml", + Short: "Edit an instance of Lima or a template", Args: WrapArgsError(cobra.MaximumNArgs(1)), RunE: editAction, ValidArgsFunction: editBashComplete, @@ -34,24 +35,43 @@ func newEditCommand() *cobra.Command { } func editAction(cmd *cobra.Command, args []string) error { - instName := DefaultInstanceName + var arg string if len(args) > 0 { - instName = args[0] + arg = args[0] } - inst, err := store.Inspect(instName) - if err != nil { - if errors.Is(err, os.ErrNotExist) { - return fmt.Errorf("instance %q not found", instName) + var filePath string + var err error + var inst *store.Instance + switch { + case guessarg.SeemsYAMLPath(arg): + // absolute path is required for `limayaml.Validate` + filePath, err = filepath.Abs(arg) + if err != nil { + return err + } + default: + var instName string + if arg != "" { + instName = arg + } else { + instName = DefaultInstanceName } - return err - } - if inst.Status == store.StatusRunning { - return errors.New("cannot edit a running instance") + inst, err = store.Inspect(instName) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return fmt.Errorf("instance %q not found", instName) + } + return err + } + + if inst.Status == store.StatusRunning { + return errors.New("cannot edit a running instance") + } + filePath = filepath.Join(inst.Dir, filenames.LimaYAML) } - filePath := filepath.Join(inst.Dir, filenames.LimaYAML) yContent, err := os.ReadFile(filePath) if err != nil { return err @@ -73,7 +93,12 @@ func editAction(cmd *cobra.Command, args []string) error { return err } } else if tty { - hdr := fmt.Sprintf("# Please edit the following configuration for Lima instance %q\n", instName) + var hdr string + if inst != nil { + hdr = fmt.Sprintf("# Please edit the following configuration for Lima instance %q\n", inst.Name) + } else { + hdr = fmt.Sprintf("# Please edit the following configuration %q\n", filePath) + } hdr += "# and an empty file will abort the edit.\n" hdr += "\n" hdr += editutil.GenerateEditorWarningHeader() @@ -105,12 +130,18 @@ func editAction(cmd *cobra.Command, args []string) error { if err := os.WriteFile(filePath, yBytes, 0o644); err != nil { return err } - logrus.Infof("Instance %q configuration edited", instName) + if inst != nil { + logrus.Infof("Instance %q configuration edited", inst.Name) + } if !tty { // use "start" to start it return nil } + if inst == nil { + // edited a limayaml file directly + return nil + } startNow, err := askWhetherToStart() if err != nil { return err diff --git a/go.mod b/go.mod index 8f84ea132dd..6096dc5ab78 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/foxcpp/go-mockdns v1.1.0 github.com/goccy/go-yaml v1.12.0 github.com/google/go-cmp v0.6.0 + github.com/google/yamlfmt v0.13.0 github.com/lima-vm/go-qcow2reader v0.1.2 github.com/lima-vm/sshocker v0.3.4 github.com/mattn/go-isatty v0.0.20 @@ -58,6 +59,8 @@ require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/a8m/envsubst v1.4.2 // indirect github.com/alecthomas/participle/v2 v2.1.1 // indirect + github.com/bmatcuk/doublestar/v4 v4.6.0 // indirect + github.com/braydonk/yaml v0.7.0 // indirect github.com/containerd/errdefs v0.1.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/digitalocean/go-libvirt v0.0.0-20220804181439-8648fbde413e // indirect @@ -95,6 +98,7 @@ require ( github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mdlayher/socket v0.4.1 // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect @@ -104,6 +108,7 @@ require ( github.com/pkg/sftp v1.13.6 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/yuin/gopher-lua v1.1.1 // indirect diff --git a/go.sum b/go.sum index f8481ab3009..02472d8bf46 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,10 @@ github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/Y github.com/armon/go-proxyproto v0.0.0-20210323213023-7e956b284f0a/go.mod h1:QmP9hvJ91BbJmGVGSbutW19IC0Q9phDCLGaomwTJbgU= github.com/balajiv113/fd v0.0.0-20230330094840-143eec500f3e h1:IdMhFPEfTZQU971tIHx3UhY4l+yCeynprnINrDTSrOc= github.com/balajiv113/fd v0.0.0-20230330094840-143eec500f3e/go.mod h1:aXGMJsd3XrnUFTuyf/pTGg5jG6CY8JMZ5juywvShjgQ= +github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc= +github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/braydonk/yaml v0.7.0 h1:ySkqO7r0MGoCNhiRJqE0Xe9yhINMyvOAB3nFjgyJn2k= +github.com/braydonk/yaml v0.7.0/go.mod h1:hcm3h581tudlirk8XEUPDBAimBPbmnL0Y45hCRl47N4= github.com/cheggaaa/pb/v3 v3.1.5 h1:QuuUzeM2WsAqG2gMqtzaWithDJv0i+i6UlnwSCI4QLk= github.com/cheggaaa/pb/v3 v3.1.5/go.mod h1:CrxkeghYTXi1lQBEI7jSn+3svI3cuc19haAj6jM60XI= github.com/containerd/containerd v1.7.22 h1:nZuNnNRA6T6jB975rx2RRNqqH2k6ELYKDZfqTHqwyy0= @@ -123,6 +127,8 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/yamlfmt v0.13.0 h1:OS8cjQNhRhEP437e1axMgZiwrYu60mWLtvb4/jtQCtE= +github.com/google/yamlfmt v0.13.0/go.mod h1:y8JNH/2TqTaCSUjk/zhn0lYlibMvS0R+pbVUDo8oz9o= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= @@ -192,6 +198,8 @@ github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= github.com/mikefarah/yq/v4 v4.44.3 h1:3zxHntH67maSHr6ynCjM44htw7LZNINmTzYn3tM2t+I= github.com/mikefarah/yq/v4 v4.44.3/go.mod h1:1pm9sJoyZLDql3OqgklvRCkD0XIIHMZV38jKZgAuxwY= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -234,6 +242,8 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI= +github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs= github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU= github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs= github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af h1:Sp5TG9f7K39yfB+If0vjp97vuT74F72r8hfRpP8jLU0= diff --git a/pkg/yqutil/yqutil.go b/pkg/yqutil/yqutil.go index e8628adc019..139d9fcbce3 100644 --- a/pkg/yqutil/yqutil.go +++ b/pkg/yqutil/yqutil.go @@ -6,6 +6,7 @@ import ( "os" "strings" + "github.com/google/yamlfmt/formatters/basic" "github.com/mikefarah/yq/v4/pkg/yqlib" "github.com/sirupsen/logrus" logging "gopkg.in/op/go-logging.v1" @@ -69,7 +70,7 @@ func EvaluateExpression(expression string, content []byte) ([]byte, error) { return nil, err } - return out.Bytes(), nil + return yamlfmt(out.Bytes()) } func Join(yqExprs []string) string { @@ -78,3 +79,18 @@ func Join(yqExprs []string) string { } return strings.Join(yqExprs, " | ") } + +func yamlfmt(content []byte) ([]byte, error) { + factory := basic.BasicFormatterFactory{} + config := map[string]interface{}{ + "indentless_arrays": true, + "line_ending": "lf", // prefer LF even on Windows + "pad_line_comments": 2, + "retain_line_breaks": true, // does not affect to the output because yq removes empty lines before formatting + } + formatter, err := factory.NewFormatter(config) + if err != nil { + return nil, err + } + return formatter.Format(content) +} diff --git a/pkg/yqutil/yqutil_test.go b/pkg/yqutil/yqutil_test.go index 297a2e2c6b6..fcf1260ec24 100644 --- a/pkg/yqutil/yqutil_test.go +++ b/pkg/yqutil/yqutil_test.go @@ -41,17 +41,18 @@ mounts: ` // Note: yq will use canonical yaml, with indented sequences // Note: yq will not explicitly quote strings, when not needed + // Note: yamlfmt will fix indentation of sequences expected := ` # Expose host directories to the guest, the mount point might be accessible from all UIDs in the guest # 🟢 Builtin default: null (Mount nothing) # 🔵 This file: Mount the home as read-only, /tmp/lima as writable mounts: - - location: "~" - # Configure the mountPoint inside the guest. - # 🟢 Builtin default: value of location - mountPoint: null - - location: foo - mountPoint: bar +- location: "~" + # Configure the mountPoint inside the guest. + # 🟢 Builtin default: value of location + mountPoint: null +- location: foo + mountPoint: bar ` out, err := EvaluateExpression(expression, []byte(content)) assert.NilError(t, err)