@@ -26,6 +26,7 @@ import (
2626 "github.com/lima-vm/lima/v2/pkg/limatype/dirnames"
2727 "github.com/lima-vm/lima/v2/pkg/limatype/filenames"
2828 "github.com/lima-vm/lima/v2/pkg/limayaml"
29+ "github.com/lima-vm/lima/v2/pkg/localpathutil"
2930 "github.com/lima-vm/lima/v2/pkg/networks/reconcile"
3031 "github.com/lima-vm/lima/v2/pkg/registry"
3132 "github.com/lima-vm/lima/v2/pkg/store"
@@ -44,7 +45,7 @@ func registerCreateFlags(cmd *cobra.Command, commentPrefix string) {
4445
4546func newCreateCommand () * cobra.Command {
4647 createCommand := & cobra.Command {
47- Use : "create FILE.yaml|URL" ,
48+ Use : "create [ FILE.yaml|URL...] " ,
4849 Example : `
4950 To create an instance "default" from the default Ubuntu template:
5051 $ limactl create
@@ -69,9 +70,15 @@ func newCreateCommand() *cobra.Command {
6970
7071 To create an instance "local" from a template passed to stdin (--name parameter is required):
7172 $ cat template.yaml | limactl create --name=local -
73+
74+ To create an instance from a template with local overrides:
75+ $ limactl create template:docker my-overrides.yaml
76+
77+ To create an instance from multiple templates (merged in order):
78+ $ limactl create https://example.com/base.yaml secrets.yaml
7279` ,
7380 Short : "Create an instance of Lima" ,
74- Args : WrapArgsError (cobra .MaximumNArgs ( 1 ) ),
81+ Args : WrapArgsError (cobra .ArbitraryArgs ),
7582 ValidArgsFunction : createBashComplete ,
7683 RunE : createAction ,
7784 GroupID : basicCommand ,
@@ -82,16 +89,19 @@ func newCreateCommand() *cobra.Command {
8289
8390func newStartCommand () * cobra.Command {
8491 startCommand := & cobra.Command {
85- Use : "start NAME|FILE.yaml|URL" ,
92+ Use : "start [ NAME|FILE.yaml|URL...] " ,
8693 Example : `
8794 To create an instance "default" (if not created yet) from the default Ubuntu template, and start it:
8895 $ limactl start
8996
9097 To create an instance "default" from a template "docker", and start it:
9198 $ limactl start --name=default template:docker
99+
100+ To create an instance from a template with local overrides, and start it:
101+ $ limactl start template:docker my-overrides.yaml
92102` ,
93103 Short : "Start an instance of Lima" ,
94- Args : WrapArgsError (cobra .MaximumNArgs ( 1 ) ),
104+ Args : WrapArgsError (cobra .ArbitraryArgs ),
95105 ValidArgsFunction : startBashComplete ,
96106 RunE : startAction ,
97107 GroupID : basicCommand ,
@@ -232,6 +242,7 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (*
232242 return nil , err
233243 }
234244 }
245+
235246 if isTemplateURL , templateName := limatmpl .SeemsTemplateURL (arg ); isTemplateURL {
236247 switch templateName {
237248 case "experimental/vz" :
@@ -281,6 +292,9 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (*
281292 if createOnly {
282293 return nil , fmt .Errorf ("instance %q already exists" , tmpl .Name )
283294 }
295+ if len (args ) > 1 {
296+ return nil , fmt .Errorf ("cannot specify additional templates when starting an existing instance %q" , tmpl .Name )
297+ }
284298 logrus .Infof ("Using the existing instance %q" , tmpl .Name )
285299 yqExprs , err := editflags .YQExpressions (flags , false )
286300 if err != nil {
@@ -308,7 +322,7 @@ func loadOrCreateInstance(cmd *cobra.Command, args []string, createOnly bool) (*
308322 return nil , err
309323 }
310324 } else {
311- tmpl , err = limatmpl . Read ( cmd . Context () , name , arg )
325+ tmpl , err = loadMultipleTemplates ( ctx , name , args )
312326 if err != nil {
313327 return nil , err
314328 }
@@ -615,6 +629,60 @@ func startAction(cmd *cobra.Command, args []string) error {
615629 return instance .Start (ctx , inst , launchHostAgentForeground , progress )
616630}
617631
632+ // loadMultipleTemplates creates a template from multiple CLI arguments.
633+ // All arguments are treated as base templates and merged in order.
634+ // Relative and tilde paths are expanded to absolute paths.
635+ func loadMultipleTemplates (_ context.Context , name string , args []string ) (* limatmpl.Template , error ) {
636+ bases := make (limatype.BaseTemplates , 0 , len (args ))
637+ for _ , a := range args {
638+ absLocator := a
639+ // Expand relative and tilde paths to absolute
640+ // "-" (stdin), URLs, and template: locators are kept as-is
641+ if a != "-" && ! limatmpl .SeemsHTTPURL (a ) && ! limatmpl .SeemsFileURL (a ) {
642+ if isTemplate , _ := limatmpl .SeemsTemplateURL (a ); ! isTemplate {
643+ var err error
644+ absLocator , err = localpathutil .Expand (a )
645+ if err != nil {
646+ return nil , fmt .Errorf ("failed to expand path %q: %w" , a , err )
647+ }
648+ }
649+ }
650+ bases = append (bases , limatype.LocatorWithDigest {URL : absLocator })
651+ }
652+
653+ // Create a minimal config with just the base templates
654+ config := & limatype.LimaYAML {Base : bases }
655+ bytes , err := limayaml .Marshal (config , false )
656+ if err != nil {
657+ return nil , fmt .Errorf ("failed to marshal template: %w" , err )
658+ }
659+
660+ cwd , err := os .Getwd ()
661+ if err != nil {
662+ return nil , fmt .Errorf ("failed to get working directory: %w" , err )
663+ }
664+
665+ tmpl := & limatmpl.Template {
666+ Bytes : bytes ,
667+ Name : name ,
668+ Locator : cwd ,
669+ }
670+
671+ // Derive instance name from first template if not specified
672+ if tmpl .Name == "" {
673+ tmpl .Name , err = limatmpl .InstNameFromURL (args [0 ])
674+ if err != nil {
675+ // fallback to InstNameFromYAMLPath if URL parsing fails
676+ tmpl .Name , err = limatmpl .InstNameFromYAMLPath (args [0 ])
677+ if err != nil {
678+ return nil , fmt .Errorf ("cannot derive instance name from %q: %w" , args [0 ], err )
679+ }
680+ }
681+ }
682+
683+ return tmpl , nil
684+ }
685+
618686func createBashComplete (cmd * cobra.Command , _ []string , toComplete string ) ([]string , cobra.ShellCompDirective ) {
619687 return bashCompleteTemplateNames (cmd , toComplete )
620688}
0 commit comments