Repository: lima-vm/lima

Issue:
Document usage of ~/.lima/NAME/ssh.config
### Description

Users should add this to their ~/.ssh/config:

```
Include ~/.lima/*/ssh.config
```

To be able to connect to the lima instances with the ssh command:

```sh
ssh lima-{NAME}
```

See more useful examples in https://github.com/lima-vm/lima/issues/3613#issuecomment-2949736880

We mention ssh.config here:
https://lima-vm.io/docs/usage/

Discussion:
@jandubois: Another example:

```shell
rsync -a -e ssh . lima-default:/tmp/repo
```@afbjorklund: Depending on the rest of your ssh config, you might have to include:

```
Match all
Include ~/.lima/*/ssh.config
```

i.e. if you had already some `Host` entries in the file, since before...@afbjorklund: Other scenarios, for the lima documentation:
 
* https://github.com/lima-vm/lima/discussions/1890 - VSCode
* https://github.com/lima-vm/lima/discussions/3605 - Ansible
* `sftp sftp://lima-almalinux-10` (from issue 852) - EDIT: maybe **not** filezilla: https://trac.filezilla-project.org/ticket/8450
* `sshfs lima-almalinux-10: /tmp/alma` - Note: sshfs has some issues on non-Linux platforms, like macOS or Windows

Solution patch:
diff --git a/.golangci.yml b/.golangci.yml
index 2c032917..9654f770 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -25,6 +25,7 @@ linters:
   - perfsprint
   - revive
   - staticcheck
+  - unconvert
   - unused
   - usetesting
   - whitespace
diff --git a/pkg/imgutil/nativeimgutil/nativeimgutil.go b/pkg/imgutil/nativeimgutil/nativeimgutil.go
index 910512d9..3008ff37 100644
--- a/pkg/imgutil/nativeimgutil/nativeimgutil.go
+++ b/pkg/imgutil/nativeimgutil/nativeimgutil.go
@@ -163,7 +163,7 @@ func (n *NativeImageUtil) CreateDisk(disk string, size int64) error {
 	}
 	defer f.Close()
 	roundedSize := roundUp(size)
-	return f.Truncate(int64(roundedSize))
+	return f.Truncate(roundedSize)
 }
 
 // ConvertToRaw converts a disk image to raw format.
diff --git a/pkg/store/instance_windows.go b/pkg/store/instance_windows.go
index 4f7b3171..084c4487 100644
--- a/pkg/store/instance_windows.go
+++ b/pkg/store/instance_windows.go
@@ -72,7 +72,7 @@ func GetWslStatus(instName string) (string, error) {
 		"--verbose",
 	})
 	if err != nil {
-		return "", fmt.Errorf("failed to run `wsl --list --verbose`, err: %w (out=%q)", err, string(out))
+		return "", fmt.Errorf("failed to run `wsl --list --verbose`, err: %w (out=%q)", err, out)
 	}
 
 	if out == "" {
@@ -80,9 +80,8 @@ func GetWslStatus(instName string) (string, error) {
 	}
 
 	// Check for edge cases first
-	outString := string(out)
-	if strings.Contains(outString, "Windows Subsystem for Linux has no installed distributions.") {
-		if strings.Contains(outString, "Wsl/WSL_E_DEFAULT_DISTRO_NOT_FOUND") {
+	if strings.Contains(out, "Windows Subsystem for Linux has no installed distributions.") {
+		if strings.Contains(out, "Wsl/WSL_E_DEFAULT_DISTRO_NOT_FOUND") {
 			return StatusBroken, fmt.Errorf(
 				"failed to read instance state for instance %q because no distro is installed,"+
 					"try running `wsl --install -d Ubuntu` and then re-running Lima", instName)
@@ -96,7 +95,7 @@ func GetWslStatus(instName string) (string, error) {
 	var instState string
 	wslListColsRegex := regexp.MustCompile(`\s+`)
 	// wsl --list --verbose may have different headers depending on localization, just split by line
-	for _, rows := range strings.Split(strings.ReplaceAll(string(out), "\r\n", "\n"), "\n") {
+	for _, rows := range strings.Split(strings.ReplaceAll(out, "\r\n", "\n"), "\n") {
 		cols := wslListColsRegex.Split(strings.TrimSpace(rows), -1)
 		nameIdx := 0
 		// '*' indicates default instance
diff --git a/pkg/windows/process_windows.go b/pkg/windows/process_windows.go
index 9c88bbaf..93cb760d 100644
--- a/pkg/windows/process_windows.go
+++ b/pkg/windows/process_windows.go
@@ -29,7 +29,7 @@ func GetProcessCommandLine(name string) ([]string, error) {
 	}
 
 	var outJSON CommandLineJSON
-	if err = json.Unmarshal([]byte(out), &outJSON); err != nil {
+	if err = json.Unmarshal(out, &outJSON); err != nil {
 		return nil, fmt.Errorf("failed to unmarshal %q as %T: %w", out, outJSON, err)
 	}
 
diff --git a/pkg/wsl2/vm_windows.go b/pkg/wsl2/vm_windows.go
index 531de6c0..0ee09bca 100644
--- a/pkg/wsl2/vm_windows.go
+++ b/pkg/wsl2/vm_windows.go
@@ -30,7 +30,7 @@ func startVM(ctx context.Context, distroName string) error {
 	}, executil.WithContext(ctx))
 	if err != nil {
 		return fmt.Errorf("failed to run `wsl.exe --distribution %s`: %w (out=%q)",
-			distroName, err, string(out))
+			distroName, err, out)
 	}
 	return nil
 }
@@ -48,7 +48,7 @@ func initVM(ctx context.Context, instanceDir, distroName string) error {
 	}, executil.WithContext(ctx))
 	if err != nil {
 		return fmt.Errorf("failed to run `wsl.exe --import %s %s %s`: %w (out=%q)",
-			distroName, instanceDir, baseDisk, err, string(out))
+			distroName, instanceDir, baseDisk, err, out)
 	}
 	return nil
 }
@@ -62,7 +62,7 @@ func stopVM(ctx context.Context, distroName string) error {
 	}, executil.WithContext(ctx))
 	if err != nil {
 		return fmt.Errorf("failed to run `wsl.exe --terminate %s`: %w (out=%q)",
-			distroName, err, string(out))
+			distroName, err, out)
 	}
 	return nil
 }
@@ -172,7 +172,7 @@ func unregisterVM(ctx context.Context, distroName string) error {
 	}, executil.WithContext(ctx))
 	if err != nil {
 		return fmt.Errorf("failed to run `wsl.exe --unregister %s`: %w (out=%q)",
-			distroName, err, string(out))
+			distroName, err, out)
 	}
 	return nil
 }
diff --git a/website/content/en/docs/usage/_index.md b/website/content/en/docs/usage/_index.md
index 5a8667f9..7a77bb56 100644
--- a/website/content/en/docs/usage/_index.md
+++ b/website/content/en/docs/usage/_index.md
@@ -59,6 +59,19 @@ $ limactl ls --format='{{.SSHConfigFile}}' default
 $ ssh -F /Users/example/.lima/default/ssh.config lima-default
 ```
 
+#### Using SSH without the `-F` flag
+
+To connect directly without specifying the config file, add this to your `~/.ssh/config`:
+
+```
+Include ~/.lima/*/ssh.config
+```
+
+Then you can connect directly:
+```bash
+ssh lima-default
+```
+
 ### Shell completion
 - To enable bash completion, add `source <(limactl completion bash)` to `~/.bash_profile`.
 - To enable zsh completion, see `limactl completion zsh --help`



Test patch:
diff --git a/pkg/limayaml/validate_test.go b/pkg/limayaml/validate_test.go
index 6d459417..e3cd4315 100644
--- a/pkg/limayaml/validate_test.go
+++ b/pkg/limayaml/validate_test.go
@@ -8,6 +8,8 @@ import (
 	"testing"
 
 	"gotest.tools/v3/assert"
+
+	"github.com/lima-vm/lima/pkg/version"
 )
 
 func TestValidateEmpty(t *testing.T) {
@@ -17,6 +19,60 @@ func TestValidateEmpty(t *testing.T) {
 	assert.Error(t, err, "field `images` must be set")
 }
 
+func TestValidateMinimumLimaVersion(t *testing.T) {
+	images := `images: [{"location": "/"}]`
+
+	tests := []struct {
+		name               string
+		currentVersion     string
+		minimumLimaVersion string
+		wantErr            string
+	}{
+		{
+			name:               "minimumLimaVersion less than current version",
+			currentVersion:     "1.1.1-114-g5bf5e513",
+			minimumLimaVersion: "1.1.0",
+			wantErr:            "",
+		},
+		{
+			name:               "minimumLimaVersion greater than current version",
+			currentVersion:     "1.1.1-114-g5bf5e513",
+			minimumLimaVersion: "1.1.2",
+			wantErr:            `template requires Lima version "1.1.2"; this is only "1.1.1"`,
+		},
+		{
+			name:               "invalid current version",
+			currentVersion:     "<unknown>",
+			minimumLimaVersion: "0.8.0",
+			wantErr:            `can't parse builtin Lima version "<unknown>": <unknown> is not in dotted-tri format`,
+		},
+		{
+			name:               "invalid minimumLimaVersion",
+			currentVersion:     "1.1.1-114-g5bf5e513",
+			minimumLimaVersion: "invalid",
+			wantErr:            "field `minimumLimaVersion` must be a semvar value, got \"invalid\": invalid is not in dotted-tri format\ntemplate requires Lima version \"invalid\"; this is only \"1.1.1\"",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			oldVersion := version.Version
+			version.Version = tt.currentVersion
+			t.Cleanup(func() { version.Version = oldVersion })
+
+			y, err := Load([]byte("minimumLimaVersion: "+tt.minimumLimaVersion+"\n"+images), "lima.yaml")
+			assert.NilError(t, err)
+
+			err = Validate(y, false)
+			if tt.wantErr == "" {
+				assert.NilError(t, err)
+			} else {
+				assert.Error(t, err, tt.wantErr)
+			}
+		})
+	}
+}
+
 func TestValidateProbes(t *testing.T) {
 	images := `images: [{"location": "/"}]`
 	validProbe := `probes: [{"script": "#!foo"}]`