Skip to content

Commit fce1b2f

Browse files
authored
Merge pull request #572 from Junnplus/edit-cmd
Add edit command
2 parents ec7b50d + 4af98d1 commit fce1b2f

File tree

4 files changed

+128
-9
lines changed

4 files changed

+128
-9
lines changed

README.md

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

159159
- Run `limactl delete [--force] <INSTANCE>` to delete the instance.
160160

161+
- Run `limactl edit <INSTANCE>` to edit the instance.
162+
161163
- To enable bash completion, add `source <(limactl completion bash)` to `~/.bash_profile`.
162164

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

cmd/limactl/edit.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"errors"
6+
"fmt"
7+
"os"
8+
"path/filepath"
9+
10+
"github.com/AlecAivazis/survey/v2"
11+
"github.com/lima-vm/lima/pkg/limayaml"
12+
networks "github.com/lima-vm/lima/pkg/networks/reconcile"
13+
"github.com/lima-vm/lima/pkg/start"
14+
"github.com/lima-vm/lima/pkg/store"
15+
"github.com/lima-vm/lima/pkg/store/filenames"
16+
"github.com/sirupsen/logrus"
17+
"github.com/spf13/cobra"
18+
)
19+
20+
func newEditCommand() *cobra.Command {
21+
var editCommand = &cobra.Command{
22+
Use: "edit INSTANCE",
23+
Short: "Edit an instance of Lima",
24+
Args: cobra.MaximumNArgs(1),
25+
RunE: editAction,
26+
ValidArgsFunction: editBashComplete,
27+
}
28+
return editCommand
29+
}
30+
31+
func editAction(cmd *cobra.Command, args []string) error {
32+
instName := DefaultInstanceName
33+
if len(args) > 0 {
34+
instName = args[0]
35+
}
36+
37+
inst, err := store.Inspect(instName)
38+
if err != nil {
39+
if errors.Is(err, os.ErrNotExist) {
40+
logrus.Infof("Instance %q not found", instName)
41+
return nil
42+
}
43+
return err
44+
}
45+
46+
if inst.Status == store.StatusRunning {
47+
return errors.New("Cannot edit a running instance")
48+
}
49+
50+
filePath := filepath.Join(inst.Dir, filenames.LimaYAML)
51+
yContent, err := os.ReadFile(filePath)
52+
if err != nil {
53+
return err
54+
}
55+
hdr := fmt.Sprintf("# Please edit the following configuration for Lima instance %q\n", instName)
56+
hdr += "# and an empty file will abort the edit.\n"
57+
hdr += "\n"
58+
yBytes, err := openEditor(cmd, instName, yContent, hdr)
59+
if err != nil {
60+
return err
61+
}
62+
if len(yBytes) == 0 {
63+
logrus.Info("Aborting, as requested by saving the file with empty content")
64+
return nil
65+
}
66+
if bytes.Compare(yBytes, yContent) == 0 {
67+
logrus.Info("Aborting, no changes made to the instance")
68+
return nil
69+
}
70+
y, err := limayaml.Load(yBytes, filePath)
71+
if err != nil {
72+
return err
73+
}
74+
if err := limayaml.Validate(*y, true); err != nil {
75+
rejectedYAML := "lima.REJECTED.yaml"
76+
if writeErr := os.WriteFile(rejectedYAML, yBytes, 0644); writeErr != nil {
77+
return fmt.Errorf("the YAML is invalid, attempted to save the buffer as %q but failed: %v: %w", rejectedYAML, writeErr, err)
78+
}
79+
// TODO: may need to support editing the rejected YAML
80+
return fmt.Errorf("the YAML is invalid, saved the buffer as %q: %w", rejectedYAML, err)
81+
}
82+
if err := os.WriteFile(filePath, yBytes, 0644); err != nil {
83+
return err
84+
}
85+
logrus.Infof("Instance %q configuration edited", instName)
86+
87+
startNow, err := askWhetherToStart()
88+
if err != nil {
89+
return err
90+
}
91+
if !startNow {
92+
return nil
93+
}
94+
ctx := cmd.Context()
95+
err = networks.Reconcile(ctx, inst.Name)
96+
if err != nil {
97+
return err
98+
}
99+
return start.Start(ctx, inst)
100+
}
101+
102+
func askWhetherToStart() (bool, error) {
103+
ans := true
104+
prompt := &survey.Confirm{
105+
Message: "Do you want to start the instance now? ",
106+
Default: true,
107+
}
108+
if err := survey.AskOne(prompt, &ans); err != nil {
109+
return false, err
110+
}
111+
return ans, nil
112+
}
113+
114+
func editBashComplete(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
115+
return bashCompleteInstanceNames(cmd)
116+
}

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)