Skip to content

Commit 2c48318

Browse files
authored
Merge pull request #3482 from jandubois/user-templates
Finish $LIMA_TEMPLATES_PATH, add $LIMA_HOME/_templates
2 parents 5e4a6b0 + 592ca32 commit 2c48318

File tree

4 files changed

+67
-36
lines changed

4 files changed

+67
-36
lines changed

cmd/limactl/start.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -371,16 +371,18 @@ func createStartActionCommon(cmd *cobra.Command, _ []string) (exit bool, err err
371371
if listTemplates, err := cmd.Flags().GetBool("list-templates"); err != nil {
372372
return true, err
373373
} else if listTemplates {
374-
if templates, err := templatestore.Templates(); err == nil {
375-
w := cmd.OutOrStdout()
376-
for _, f := range templates {
377-
// Don't show internal base templates like `_default/*` and `_images/*`.
378-
if !strings.HasPrefix(f.Name, "_") {
379-
_, _ = fmt.Fprintln(w, f.Name)
380-
}
374+
templates, err := templatestore.Templates()
375+
if err != nil {
376+
return true, err
377+
}
378+
w := cmd.OutOrStdout()
379+
for _, f := range templates {
380+
// Don't show internal base templates like `_default/*` and `_images/*`.
381+
if !strings.HasPrefix(f.Name, "_") {
382+
_, _ = fmt.Fprintln(w, f.Name)
381383
}
382-
return true, nil
383384
}
385+
return true, nil
384386
}
385387
return false, nil
386388
}

pkg/templatestore/templatestore.go

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@
44
package templatestore
55

66
import (
7+
"cmp"
78
"errors"
89
"fmt"
910
"io/fs"
1011
"os"
1112
"path/filepath"
13+
"slices"
1214
"strings"
1315
"unicode"
1416

1517
securejoin "github.com/cyphar/filepath-securejoin"
18+
"github.com/lima-vm/lima/pkg/store/dirnames"
1619
"github.com/lima-vm/lima/pkg/usrlocalsharelima"
1720
)
1821

@@ -21,25 +24,37 @@ type Template struct {
2124
Location string `json:"location"`
2225
}
2326

24-
func Read(name string) ([]byte, error) {
25-
var pathList []string
27+
func TemplatesPaths() ([]string, error) {
2628
if tmplPath := os.Getenv("LIMA_TEMPLATES_PATH"); tmplPath != "" {
27-
pathList = strings.Split(tmplPath, string(filepath.ListSeparator))
28-
} else {
29-
dir, err := usrlocalsharelima.Dir()
30-
if err != nil {
31-
return nil, err
32-
}
33-
pathList = []string{filepath.Join(dir, "templates")}
29+
return strings.Split(tmplPath, string(filepath.ListSeparator)), nil
30+
}
31+
limaDir, err := dirnames.LimaDir()
32+
if err != nil {
33+
return nil, err
34+
}
35+
shareDir, err := usrlocalsharelima.Dir()
36+
if err != nil {
37+
return nil, err
38+
}
39+
return []string{
40+
filepath.Join(limaDir, "_templates"),
41+
filepath.Join(shareDir, "templates"),
42+
}, nil
43+
}
44+
45+
func Read(name string) ([]byte, error) {
46+
paths, err := TemplatesPaths()
47+
if err != nil {
48+
return nil, err
3449
}
3550
ext := filepath.Ext(name)
3651
// Append .yaml extension if name doesn't have an extension, or if it starts with a digit.
3752
// So "docker.sh" would remain unchanged but "ubuntu-24.04" becomes "ubuntu-24.04.yaml".
3853
if len(ext) < 2 || unicode.IsDigit(rune(ext[1])) {
3954
name += ".yaml"
4055
}
41-
for _, path := range pathList {
42-
filePath, err := securejoin.SecureJoin(path, name)
56+
for _, templatesDir := range paths {
57+
filePath, err := securejoin.SecureJoin(templatesDir, name)
4358
if err != nil {
4459
return nil, err
4560
}
@@ -53,31 +68,39 @@ func Read(name string) ([]byte, error) {
5368
const Default = "default"
5469

5570
func Templates() ([]Template, error) {
56-
usrlocalsharelimaDir, err := usrlocalsharelima.Dir()
71+
paths, err := TemplatesPaths()
5772
if err != nil {
5873
return nil, err
5974
}
60-
templatesDir := filepath.Join(usrlocalsharelimaDir, "templates")
6175

62-
var res []Template
63-
walkDirFn := func(p string, _ fs.DirEntry, err error) error {
64-
if err != nil {
65-
return err
76+
templates := make(map[string]string)
77+
for _, templatesDir := range paths {
78+
if _, err := os.Stat(templatesDir); os.IsNotExist(err) {
79+
continue
6680
}
67-
base := filepath.Base(p)
68-
if strings.HasPrefix(base, ".") || !strings.HasSuffix(base, ".yaml") {
81+
walkDirFn := func(p string, _ fs.DirEntry, err error) error {
82+
if err != nil {
83+
return err
84+
}
85+
base := filepath.Base(p)
86+
if strings.HasPrefix(base, ".") || !strings.HasSuffix(base, ".yaml") {
87+
return nil
88+
}
89+
// Name is like "default", "debian", "deprecated/centos-7", ...
90+
name := strings.TrimSuffix(strings.TrimPrefix(p, templatesDir+"/"), ".yaml")
91+
if _, ok := templates[name]; !ok {
92+
templates[name] = p
93+
}
6994
return nil
7095
}
71-
x := Template{
72-
// Name is like "default", "debian", "deprecated/centos-7", ...
73-
Name: strings.TrimSuffix(strings.TrimPrefix(p, templatesDir+"/"), ".yaml"),
74-
Location: p,
96+
if err = filepath.WalkDir(templatesDir, walkDirFn); err != nil {
97+
return nil, err
7598
}
76-
res = append(res, x)
77-
return nil
7899
}
79-
if err = filepath.WalkDir(templatesDir, walkDirFn); err != nil {
80-
return nil, err
100+
var res []Template
101+
for name, loc := range templates {
102+
res = append(res, Template{Name: name, Location: loc})
81103
}
104+
slices.SortFunc(res, func(i, j Template) int { return cmp.Compare(i.Name, j.Name) })
82105
return res, nil
83106
}

website/content/en/docs/config/environment-variables.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ This page documents the environment variables used in Lima.
3030
### `LIMA_TEMPLATES_PATH`
3131

3232
- **Description**: Specifies the directories used to resolve `template://` URLs.
33-
- **Default**: `/usr/local/share/lima/templates`
33+
- **Default**: `$LIMA_HOME/_templates:/usr/local/share/lima/templates`
3434
- **Usage**:
3535
```sh
3636
export LIMA_TEMPLATES_PATH="$HOME/.config/lima/templates:/usr/local/share/lima/templates"

website/content/en/docs/dev/internals.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ When using `vmType: vz` (Virtualization.framework), on boot, any qcow2 (default)
107107

108108
`ls` will also only show the full/virtual size of the disks. To see the allocated space, `du -h disk_path` or `qemu-img info disk_path` can be used instead. See [#1405](https://github.com/lima-vm/lima/pull/1405) for more details.
109109

110+
## Templates directory (`${LIMA_HOME}/_templates`)
111+
112+
The templates directory can store additional template files that can be referenced with the `template://` schema.
113+
114+
If the template directory exists (and `$LIMA_TEMPLATES_PATH` is not set), then this directory will be searched before the `/usr/local/share/lima/templates` default directory that contains all the templates bundled with Lima itself.
115+
110116
## Lima cache directory (`~/Library/Caches/lima`)
111117

112118
Currently hard-coded to `~/Library/Caches/lima` on macOS.

0 commit comments

Comments
 (0)