Skip to content

Commit 47fb01d

Browse files
committed
Add edit command
Signed-off-by: ye.sijun <[email protected]>
1 parent 53b78dc commit 47fb01d

File tree

4 files changed

+96
-9
lines changed

4 files changed

+96
-9
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ Detailed usage:
151151

152152
- Run `limactl delete [--force] <INSTANCE>` to delete the instance.
153153

154+
- Run `limactl edit <INSTANCE>` to edit the instance.
155+
154156
- To enable bash completion, add `source <(limactl completion bash)` to `~/.bash_profile`.
155157

156158
- To enable zsh completion, see `limactl completion zsh --help`

cmd/limactl/edit.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"fmt"
7+
"os"
8+
"path/filepath"
9+
10+
"github.com/lima-vm/lima/pkg/limayaml"
11+
"github.com/lima-vm/lima/pkg/store"
12+
"github.com/lima-vm/lima/pkg/store/filenames"
13+
"github.com/sirupsen/logrus"
14+
"github.com/spf13/cobra"
15+
)
16+
17+
func newEditCommand() *cobra.Command {
18+
var editCommand = &cobra.Command{
19+
Use: "edit INSTANCE",
20+
Short: "Edit an instance of Lima",
21+
Args: cobra.MaximumNArgs(1),
22+
RunE: editAction,
23+
ValidArgsFunction: editBashComplete,
24+
}
25+
return editCommand
26+
}
27+
28+
func editAction(cmd *cobra.Command, args []string) error {
29+
instName := DefaultInstanceName
30+
if len(args) > 0 {
31+
instName = args[0]
32+
}
33+
34+
inst, err := store.Inspect(instName)
35+
if err != nil {
36+
if errors.Is(err, os.ErrNotExist) {
37+
logrus.Infof("Instance %q not found", instName)
38+
return nil
39+
}
40+
return err
41+
}
42+
43+
filePath := filepath.Join(inst.Dir, filenames.LimaYAML)
44+
yContent, err := os.ReadFile(filePath)
45+
if err != nil {
46+
return err
47+
}
48+
hdr := fmt.Sprintf("# Please edit the folling configuration for Lima instance %q\n", instName)
49+
hdr += "# and an empty file will abort the edit.\n"
50+
hdr += "\n"
51+
yBytes, err := openEditor(cmd, instName, yContent, hdr)
52+
if err != nil {
53+
return err
54+
}
55+
if len(yBytes) == 0 {
56+
logrus.Info("Aborting, as requested by saving the file with empty content")
57+
return nil
58+
}
59+
if bytes.Compare(yBytes, yContent) == 0 {
60+
logrus.Info("Aborting, no changes made to the instance")
61+
return nil
62+
}
63+
y, err := limayaml.Load(yBytes, filePath)
64+
if err != nil {
65+
return err
66+
}
67+
if err := limayaml.Validate(*y, true); err != nil {
68+
rejectedYAML := "lima.REJECTED.yaml"
69+
if writeErr := os.WriteFile(rejectedYAML, yBytes, 0644); writeErr != nil {
70+
return fmt.Errorf("the YAML is invalid, attempted to save the buffer as %q but failed: %v: %w", rejectedYAML, writeErr, err)
71+
}
72+
// TODO: may need to support editing the rejected YAML
73+
return fmt.Errorf("the YAML is invalid, saved the buffer as %q: %w", rejectedYAML, err)
74+
}
75+
if err := os.WriteFile(filePath, yBytes, 0644); err != nil {
76+
return err
77+
}
78+
logrus.Infof("Instance %q edited, you need restart instance to apply it", instName)
79+
return nil
80+
}
81+
82+
func editBashComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
83+
return bashCompleteInstanceNames(cmd)
84+
}

cmd/limactl/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ func newApp() *cobra.Command {
8282
newInfoCommand(),
8383
newShowSSHCommand(),
8484
newDebugCommand(),
85+
newEditCommand(),
8586
)
8687
return rootCmd
8788
}

cmd/limactl/start.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,13 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string) (*store.Instance, e
141141
answerOpenEditor = false
142142
}
143143
if answerOpenEditor {
144-
yBytes, err = openEditor(cmd, instName, yBytes)
144+
hdr := fmt.Sprintf("# Review and modify the following configuration for Lima instance %q.\n", instName)
145+
if instName == DefaultInstanceName {
146+
hdr += "# - In most cases, you do not need to modify this file.\n"
147+
}
148+
hdr += "# - To cancel starting Lima, just save this file as an empty file.\n"
149+
hdr += "\n"
150+
yBytes, err = openEditor(cmd, instName, yBytes, hdr)
145151
if err != nil {
146152
return nil, err
147153
}
@@ -208,7 +214,7 @@ func askWhetherToOpenEditor(name string) (bool, error) {
208214
// openEditor opens an editor, and returns the content (not path) of the modified yaml.
209215
//
210216
// openEditor returns nil when the file was saved as an empty file, optionally with whitespaces.
211-
func openEditor(cmd *cobra.Command, name string, initialContent []byte) ([]byte, error) {
217+
func openEditor(cmd *cobra.Command, name string, content []byte, hdr string) ([]byte, error) {
212218
editor := editorcmd.Detect()
213219
if editor == "" {
214220
return nil, errors.New("could not detect a text editor binary, try setting $EDITOR")
@@ -219,14 +225,8 @@ func openEditor(cmd *cobra.Command, name string, initialContent []byte) ([]byte,
219225
}
220226
tmpYAMLPath := tmpYAMLFile.Name()
221227
defer os.RemoveAll(tmpYAMLPath)
222-
hdr := fmt.Sprintf("# Review and modify the following configuration for Lima instance %q.\n", name)
223-
if name == DefaultInstanceName {
224-
hdr += "# - In most cases, you do not need to modify this file.\n"
225-
}
226-
hdr += "# - To cancel starting Lima, just save this file as an empty file.\n"
227-
hdr += "\n"
228228
if err := os.WriteFile(tmpYAMLPath,
229-
append([]byte(hdr), initialContent...),
229+
append([]byte(hdr), content...),
230230
0o600); err != nil {
231231
return nil, err
232232
}

0 commit comments

Comments
 (0)