Skip to content

Commit be90be8

Browse files
committed
feat: add command aliasing via PATH lookup
Signed-off-by: olalekan odukoya <[email protected]>
1 parent ba54094 commit be90be8

File tree

2 files changed

+123
-9
lines changed

2 files changed

+123
-9
lines changed

cmd/limactl/main.go

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"errors"
88
"fmt"
99
"os"
10+
"os/exec"
1011
"path/filepath"
1112
"runtime"
1213
"strings"
@@ -40,7 +41,8 @@ func main() {
4041
}
4142
}
4243
}
43-
if err := newApp().Execute(); err != nil {
44+
rootCmd := newApp()
45+
if err := executeWithPluginSupport(rootCmd, os.Args[1:]); err != nil {
4446
handleExitCoder(err)
4547
logrus.Fatal(err)
4648
}
@@ -61,18 +63,18 @@ func newApp() *cobra.Command {
6163
Short: "Lima: Linux virtual machines",
6264
Version: strings.TrimPrefix(version.Version, "v"),
6365
Example: fmt.Sprintf(` Start the default instance:
64-
$ limactl start
66+
$ limactl start
6567
66-
Open a shell:
67-
$ lima
68+
Open a shell:
69+
$ lima
6870
69-
Run a container:
70-
$ lima nerdctl run -d --name nginx -p 8080:80 nginx:alpine
71+
Run a container:
72+
$ lima nerdctl run -d --name nginx -p 8080:80 nginx:alpine
7173
72-
Stop the default instance:
73-
$ limactl stop
74+
Stop the default instance:
75+
$ limactl stop
7476
75-
See also template YAMLs: %s`, templatesDir),
77+
See also template YAMLs: %s`, templatesDir),
7678
SilenceUsage: true,
7779
SilenceErrors: true,
7880
DisableAutoGenTag: true,
@@ -215,6 +217,63 @@ func handleExitCoder(err error) {
215217
}
216218
}
217219

220+
// executeWithPluginSupport handles command execution with plugin support.
221+
func executeWithPluginSupport(rootCmd *cobra.Command, args []string) error {
222+
if len(args) > 0 {
223+
cmd, _, err := rootCmd.Find(args)
224+
if err != nil || cmd == rootCmd {
225+
if err := runExternalPlugin(args[0], args[1:]); err == nil {
226+
return nil
227+
}
228+
}
229+
}
230+
231+
rootCmd.SetArgs(args)
232+
return rootCmd.Execute()
233+
}
234+
235+
func runExternalPlugin(name string, args []string) error {
236+
externalCmd := "limactl-" + name
237+
execPath, err := exec.LookPath(externalCmd)
238+
if err != nil {
239+
return err
240+
}
241+
242+
if err := updatePathEnv(); err != nil {
243+
logrus.Warn("failed to update PATH environment: %v", err)
244+
// PATH update failure shouldn't prevent plugin execution
245+
}
246+
247+
logrus.Debugf("found external command: %s", execPath)
248+
249+
cmd := exec.Command(execPath, args...)
250+
cmd.Stdin = os.Stdin
251+
cmd.Stdout = os.Stdout
252+
cmd.Stderr = os.Stderr
253+
cmd.Env = os.Environ()
254+
255+
return cmd.Run()
256+
}
257+
258+
func updatePathEnv() error {
259+
exe, err := os.Executable()
260+
if err != nil {
261+
return fmt.Errorf("failed to get executable path: %w", err)
262+
}
263+
264+
binDir := filepath.Dir(exe)
265+
currentPath := os.Getenv("PATH")
266+
newPath := binDir + string(filepath.ListSeparator) + currentPath
267+
268+
if err := os.Setenv("PATH", newPath); err != nil {
269+
return fmt.Errorf("failed to set PATH environment: %w", err)
270+
}
271+
272+
logrus.Debugf("updated PATH to prioritize %s", binDir)
273+
274+
return nil
275+
}
276+
218277
// WrapArgsError annotates cobra args error with some context, so the error message is more user-friendly.
219278
func WrapArgsError(argFn cobra.PositionalArgs) cobra.PositionalArgs {
220279
return func(cmd *cobra.Command, args []string) error {

website/content/en/docs/usage/_index.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,61 @@ Then you can connect directly:
7272
ssh lima-default
7373
```
7474

75+
### Command Aliasing (Plugin System)
76+
77+
Lima supports a plugin-like command aliasing system similar to `git`, `kubectl`, and `docker`. When you run a `limactl` command that doesn't exist, Lima will automatically look for an external program named `limactl-<command>` in your system's PATH.
78+
79+
#### Creating Custom Aliases
80+
81+
To create a custom alias, create an executable script with the name `limactl-<alias>` and place it somewhere in your PATH.
82+
83+
**Example: Creating a `ps` alias for listing instances**
84+
85+
1. Create a script called `limactl-ps`:
86+
```bash
87+
#!/bin/sh
88+
# Show instances in a compact format
89+
limactl list --format table "$@"
90+
```
91+
92+
2. Make it executable and place it in your PATH:
93+
```bash
94+
chmod +x limactl-ps
95+
sudo mv limactl-ps /usr/local/bin/
96+
```
97+
98+
3. Now you can use it:
99+
```bash
100+
limactl ps # Shows instances in table format
101+
limactl ps --quiet # Shows only instance names
102+
```
103+
104+
**Example: Creating an `sh` alias**
105+
106+
```bash
107+
#!/bin/sh
108+
# limactl-sh - Connect to an instance shell
109+
limactl shell "$@"
110+
```
111+
112+
After creating this alias:
113+
```bash
114+
limactl sh default # Equivalent to: limactl shell default
115+
limactl sh myinstance bash # Equivalent to: limactl shell myinstance bash
116+
```
117+
118+
#### How It Works
119+
120+
1. When you run `limactl <unknown-command>`, Lima first tries to find a built-in command
121+
2. If no built-in command is found, Lima searches for `limactl-<unknown-command>` in your PATH
122+
3. If found, Lima executes the external program and passes all remaining arguments to it
123+
4. If not found, Lima shows the standard "unknown command" error
124+
125+
This system allows you to:
126+
- Create personal shortcuts and aliases
127+
- Extend Lima's functionality without modifying the core application
128+
- Share custom commands with your team by distributing scripts
129+
75130
### Shell completion
76131
- To enable bash completion, add `source <(limactl completion bash)` to `~/.bash_profile`.
77132
- To enable zsh completion, see `limactl completion zsh --help`

0 commit comments

Comments
 (0)