Repository: lima-vm/lima

Issue:
Improvement to developer's guide
### Description

As part of the DD for Lima's incubation application, we identified that the developer's guide could be improved.

Currently this page contains relevant information but focuses on internal structures and configuration options and running the test suite:
https://lima-vm.io/docs/dev/

The project could consider a more friendly developer guide to ease newcomers wanting to contribute to the project.

Hints from issue comments:
@afbjorklund: We could use a more detailed User's Guide as well, I started on a framework for it but didn't fill it in much with info.

https://github.com/afbjorklund/lima-man@jandubois: > I’m interested in contributing to this and was wondering if the issue is still open. If so, could I please be assigned to it?

Thank you for your offer! I've assigned the issue to you.

If you want to make larger structural changes to the docs, then please have a discussion about it first. But if you are just going to improve the content while staying with the basic structure, then go right ahead!

One thing that is currently a bit hidden is the link to https://deepwiki.com/lima-vm/lima (it is just a badge on the `README.md`). It contains AI generated documentation of the whole repo and is updated on a weekly basis.

This resource should be pointed at in the developer docs because it is a good way to learn about the project and architecture. The deepwiki page even has an agent, so you can ask questions about parts you don't understand (or want to learn more about).@ijayhub: Thanks for the clarification!
I’ll stick to improving the content within the current structure and avoid large structural changes for now.@AkihiroSuda: > Currently this page contains relevant information but focuses on internal structures and configuration options and running the test suite:
> https://lima-vm.io/docs/dev/
> 
> The project could consider a more friendly developer guide to ease newcomers wanting to contribute to the project.


Can we just link add a link to https://lima-vm.io/docs/community/contributing/ in https://lima-vm.io/docs/dev/ ?

@rochaporto: > > Currently this page contains relevant information but focuses on internal structures and configuration options and running the test suite:
> > https://lima-vm.io/docs/dev/
> > The project could consider a more friendly developer guide to ease newcomers wanting to contribute to the project.
> 
> Can we just link add a link to https://lima-vm.io/docs/community/contributing/ in https://lima-vm.io/docs/dev/ ?

That sounds reasonable.@AkihiroSuda: PR
- https://github.com/lima-vm/lima/pull/3855

Solution patch:
diff --git a/pkg/guestagent/procnettcp/procnettcp.go b/pkg/guestagent/procnettcp/procnettcp.go
index 0d429f8f..b86237a2 100644
--- a/pkg/guestagent/procnettcp/procnettcp.go
+++ b/pkg/guestagent/procnettcp/procnettcp.go
@@ -41,11 +41,11 @@ type Entry struct {
 	State State  `json:"state"`
 }
 
-func parse(r io.Reader, kind Kind) ([]Entry, error) {
-	return parseWithEndian(r, kind, cpu.IsBigEndian)
+func Parse(r io.Reader, kind Kind) ([]Entry, error) {
+	return ParseWithEndian(r, kind, cpu.IsBigEndian)
 }
 
-func parseWithEndian(r io.Reader, kind Kind, isBE bool) ([]Entry, error) {
+func ParseWithEndian(r io.Reader, kind Kind, isBE bool) ([]Entry, error) {
 	switch kind {
 	case TCP, TCP6, UDP, UDP6:
 	default:
@@ -78,7 +78,7 @@ func parseWithEndian(r io.Reader, kind Kind, isBE bool) ([]Entry, error) {
 		default:
 			// localAddress is like "0100007F:053A"
 			localAddress := fields[fieldNames["local_address"]]
-			ip, port, err := parseAddressWithEndian(localAddress, isBE)
+			ip, port, err := ParseAddressWithEndian(localAddress, isBE)
 			if err != nil {
 				return entries, err
 			}
@@ -105,7 +105,7 @@ func parseWithEndian(r io.Reader, kind Kind, isBE bool) ([]Entry, error) {
 	return entries, nil
 }
 
-// parseAddressWithEndian parses a string.
+// ParseAddress parses a string.
 //
 // Little endian hosts:
 // "0100007F:0050"                         (127.0.0.1:80)
@@ -118,7 +118,11 @@ func parseWithEndian(r io.Reader, kind Kind, isBE bool) ([]Entry, error) {
 // "00000000000000000000000000000000:0050" (0.0.0.0:80)
 //
 // See https://serverfault.com/questions/592574/why-does-proc-net-tcp6-represents-1-as-1000
-func parseAddressWithEndian(s string, isBE bool) (net.IP, uint16, error) {
+func ParseAddress(s string) (net.IP, uint16, error) {
+	return ParseAddressWithEndian(s, cpu.IsBigEndian)
+}
+
+func ParseAddressWithEndian(s string, isBE bool) (net.IP, uint16, error) {
 	split := strings.SplitN(s, ":", 2)
 	if len(split) != 2 {
 		return nil, 0, fmt.Errorf("unparsable address %q", s)
diff --git a/pkg/guestagent/procnettcp/procnettcp_linux.go b/pkg/guestagent/procnettcp/procnettcp_linux.go
index 8810b692..ae39224f 100644
--- a/pkg/guestagent/procnettcp/procnettcp_linux.go
+++ b/pkg/guestagent/procnettcp/procnettcp_linux.go
@@ -25,7 +25,7 @@ func ParseFiles() ([]Entry, error) {
 			}
 			return res, err
 		}
-		parsed, err := parse(r, kind)
+		parsed, err := Parse(r, kind)
 		if err != nil {
 			_ = r.Close()
 			return res, err
diff --git a/website/content/en/docs/community/contributing.md b/website/content/en/docs/community/contributing.md
index c7b9aa32..cfd74073 100644
--- a/website/content/en/docs/community/contributing.md
+++ b/website/content/en/docs/community/contributing.md
@@ -13,6 +13,26 @@ For reporting vulnerabilities, see <https://github.com/lima-vm/.github/blob/main
 
 ## Contributing code
 
+### Getting Involved
+
+We welcome new contributors! Here are some ways to get started and engage with the Lima community:
+
+#### Introduce Yourself
+
+- Join our [community communication channels](https://lima-vm.io/docs/community/#communication-channels) (Slack, GitHub Discussions, Zoom meetings) and say hello! Let us know your interests and how you’d like to help. Also share how your [organization](https://github.com/lima-vm/lima/discussions/2390) is involved with Lima.
+
+#### Learn Where Work Is Needed
+
+- Check the [Lima Roadmap](https://lima-vm.io/docs/community/roadmap/), related [issues](https://github.com/lima-vm/lima/issues), and [discussions](https://github.com/lima-vm/lima/discussions) to see ongoing and planned work.
+- Read through the [documentation](https://lima-vm.io/docs/) to understand the project’s goals and architecture.
+
+#### Find Open Issues
+
+- Browse [GitHub Issues](https://github.com/lima-vm/lima/issues) labeled as [`good first issue`](https://github.com/lima-vm/lima/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) for tasks that are great for new contributors.
+- If you’re unsure where to start, ask in the community channels or open a new discussion.
+
+We’re glad to have you here, your contributions make Lima better!
+
 ### Developer Certificate of Origin
 
 Every commit must be signed off with the `Signed-off-by: REAL NAME <email@example.com>` line.
@@ -35,6 +55,8 @@ It is highly suggested to add [tests](../../dev/testing/) for every non-trivial
 A test can be implemented as a unit test rather than an integration test when it is possible,
 to avoid slowing the integration test CI.
 
+For tips on squashing commits and rebasing before submitting your pull request, see [Git Tips](../dev/git.md).
+
 ### Merging pull requests
 
 [Committers](../governance) can merge pull requests.
diff --git a/website/content/en/docs/dev/_index.md b/website/content/en/docs/dev/_index.md
index bbe1aa09..0af89886 100644
--- a/website/content/en/docs/dev/_index.md
+++ b/website/content/en/docs/dev/_index.md
@@ -2,3 +2,8 @@
 title: Developers' guide
 weight: 500
 ---
+
+This section provides technological resources for developers of Lima.
+
+See also [`Community`](../community) » [`Contributing`](../community/contributing)
+for how to contribute to the project.
diff --git a/website/content/en/docs/dev/git.md b/website/content/en/docs/dev/git.md
new file mode 100644
index 00000000..f3443ee2
--- /dev/null
+++ b/website/content/en/docs/dev/git.md
@@ -0,0 +1,56 @@
+---
+title: Git tips
+weight: 30
+---
+
+## Squashing Commits
+
+To combine multiple commits into one (recommended unless your PR covers multiple topics):
+
+```bash
+# Adjust the number based on how many commits you want to squash
+git rebase -i HEAD~3
+```
+
+In the interactive editor that appears:
+1. Keep the first commit as `pick`
+2. Change subsequent commits from `pick` to `fixup` (short form`f`). You may also choose `squash` (`s`), however, `fixup` is recommended to keep the commit message clean.
+3. Save and close the editor to proceed
+
+Example:
+```
+pick aaaaaaa First commit message
+pick bbbbbbb Second commit message
+pick ccccccc Fix typo
+```
+
+To:
+```
+pick aaaaaaa First commit message
+f bbbbbbb Second commit message
+f ccccccc Fix typo
+```
+
+## Rebasing onto Upstream Master
+
+To update your branch with the latest changes from upstream:
+
+```bash
+git remote add upstream https://github.com/lima-vm/lima.git  # Only needed once
+git fetch upstream
+git rebase upstream/master
+```
+
+## Troubleshooting
+
+If you encounter issues during rebase:
+
+```bash
+git rebase --abort  # Cancel the rebase and return to original state
+git status          # Check current state
+```
+
+For merge conflicts during rebase:
+1. Resolve the conflicts in the files
+2. `git add` the resolved files
+3. `git rebase --continue`



Test patch:
diff --git a/pkg/guestagent/procnettcp/fuzz_test.go b/pkg/guestagent/procnettcp/fuzz_test.go
index 79e94b51..9d3fa7b3 100644
--- a/pkg/guestagent/procnettcp/fuzz_test.go
+++ b/pkg/guestagent/procnettcp/fuzz_test.go
@@ -14,6 +14,6 @@ func FuzzParse(f *testing.F) {
 		if tcp6 {
 			kind = TCP6
 		}
-		_, _ = parse(bytes.NewReader(data), kind)
+		_, _ = Parse(bytes.NewReader(data), kind)
 	})
 }
diff --git a/pkg/guestagent/procnettcp/procnettcp_test.go b/pkg/guestagent/procnettcp/procnettcp_test.go
index d53d9f97..cd9c2b9d 100644
--- a/pkg/guestagent/procnettcp/procnettcp_test.go
+++ b/pkg/guestagent/procnettcp/procnettcp_test.go
@@ -21,7 +21,7 @@ func TestParseTCP(t *testing.T) {
    4: 0100007F:053A 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 31430 1 0000000000000000 100 0 0 10 0                     
    5: 0B3CA8C0:0016 690AA8C0:F705 01 00000000:00000000 02:00028D8B 00000000     0        0 32989 4 0000000000000000 20 4 31 10 19
 `
-	entries, err := parseWithEndian(strings.NewReader(procNetTCP), TCP, isBE)
+	entries, err := ParseWithEndian(strings.NewReader(procNetTCP), TCP, isBE)
 	assert.NilError(t, err)
 	t.Log(entries)
 
@@ -38,7 +38,7 @@ func TestParseTCP6(t *testing.T) {
 	const isBE = false
 	procNetTCP := `  sl  local_address                         remote_address                        st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode
 	   0: 000080FE00000000FF57A6705DC771FE:0050 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 850222 1 0000000000000000 100 0 0 10 0`
-	entries, err := parseWithEndian(strings.NewReader(procNetTCP), TCP6, isBE)
+	entries, err := ParseWithEndian(strings.NewReader(procNetTCP), TCP6, isBE)
 	assert.NilError(t, err)
 	t.Log(entries)
 
@@ -54,7 +54,7 @@ func TestParseTCP6Zero(t *testing.T) {
    1: 00000000000000000000000000000000:006F 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 26772 1 0000000000000000 100 0 0 10 0
    2: 00000000000000000000000000000000:0050 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 1210901 1 0000000000000000 100 0 0 10 0
 `
-	entries, err := parseWithEndian(strings.NewReader(procNetTCP), TCP6, isBE)
+	entries, err := ParseWithEndian(strings.NewReader(procNetTCP), TCP6, isBE)
 	assert.NilError(t, err)
 	t.Log(entries)
 
@@ -71,7 +71,7 @@ func TestParseUDP(t *testing.T) {
   731: 0369A8C0:0044 00000000:0000 07 00000000:00000000 00:00000000 00000000   998        0 29132 2 0000000000000000 0         
   731: 0F05A8C0:0044 00000000:0000 07 00000000:00000000 00:00000000 00000000   998        0 4049 2 0000000000000000 0          
  1768: 00000000:1451 00000000:0000 07 00000000:00000000 00:00000000 00000000   502        0 28364 2 0000000000000000 0  `
-	entries, err := parseWithEndian(strings.NewReader(procNetTCP), UDP, isBE)
+	entries, err := ParseWithEndian(strings.NewReader(procNetTCP), UDP, isBE)
 	assert.NilError(t, err)
 	t.Log(entries)
 
@@ -149,7 +149,7 @@ func TestParseAddress(t *testing.T) {
 
 	for _, test := range tests {
 		t.Run(test.input, func(t *testing.T) {
-			ip, port, err := parseAddressWithEndian(test.input, test.bigEndian)
+			ip, port, err := ParseAddressWithEndian(test.input, test.bigEndian)
 			if test.expectedErrSubstr != "" {
 				assert.ErrorContains(t, err, test.expectedErrSubstr)
 			} else {