Skip to content

feat(gvm): add auto-detect Go version from .go-version / go.mod - #16

Merged
brianrobt merged 3 commits into
masterfrom
cursor/p3-gomod-autodetect-10
Jul 23, 2026
Merged

brianrobt merged 3 commits into
masterfrom
cursor/p3-gomod-autodetect-10

Conversation

@brianrobt

@brianrobt brianrobt commented Jul 23, 2026

Copy link
Copy Markdown
Owner

Implement gvm use and gvm install without an explicit version argument by resolving the required Go version from the nearest project files, closing #10 / moovweb/gvm#523.

Key changes:

  • Add scripts/function/resolve_project_version with helpers:
  • __gvm_find_file_upwards: cwd-safe upward directory walker (no longer mutates caller's working directory)
  • __gvm_parse_go_mod_version: reads toolchain / go directive from go.mod
  • __gvm_read_dot_go_version_file: reads .go-version pin
  • __gvm_map_version_hint_to_tag: maps language versions like 1.22 to the latest installed or available patch tag
  • __gvm_resolve_project_go_version: orchestrates precedence (.go-versiongo.mod)
  • Update scripts/env/use to call __gvm_resolve_project_go_version when no version argument is given
  • Refactor scripts/env/applymod to reuse the same shared helpers
  • Fix scripts/function/find_path_upwards to avoid cd side-effects and external dirname calls
  • Document empty-string contract for _bash_pseudo_hash (no distinct nil values)
  • Add CI smoke test for go.mod auto-detection from a subdirectory
  • Bump version to 1.3.0 and update ChangeLog / README.md

feat(install): add auto-detection of Go version from project files

When no version is specified, gvm install now resolves the required Go version from the nearest .go-version or go.mod file using __gvm_resolve_project_go_version. Language-level versions (e.g. go 1.22) are mapped to the latest available patch release for that minor version.

This removes the hard failure on missing version argument and replaces it with a smarter fallback, making it easier to install the correct Go version when working inside a project directory without having to look up or type the version manually.

Summary by CodeRabbit

  • New Features
    • gvm use and gvm install can automatically detect Go versions from .go-version or go.mod.
    • Version hints such as 1.22 are mapped to the latest matching patch release.
    • gvm applymod now follows the same project-version detection behavior.
  • Bug Fixes
    • Project configuration files are reliably discovered in parent directories without changing the working directory.
  • Documentation
    • Updated release documentation, usage guidance, and version information for 1.3.0.
  • Tests
    • Added coverage for automatic detection from nested Go modules.

Implement `gvm use` and `gvm install` without an explicit version
argument by resolving the required Go version from the nearest
project files, closing #10 / moovweb/gvm#523.

Key changes:
- Add `scripts/function/resolve_project_version` with helpers:
 - `__gvm_find_file_upwards`: cwd-safe upward directory walker
 (no longer mutates caller's working directory)
 - `__gvm_parse_go_mod_version`: reads `toolchain` / `go`
 directive from `go.mod`
 - `__gvm_read_dot_go_version_file`: reads `.go-version` pin
 - `__gvm_map_version_hint_to_tag`: maps language versions like
 `1.22` to the latest installed or available patch tag
 - `__gvm_resolve_project_go_version`: orchestrates precedence
 (`.go-version` → `go.mod`)
- Update `scripts/env/use` to call `__gvm_resolve_project_go_version`
 when no version argument is given
- Refactor `scripts/env/applymod` to reuse the same shared helpers
- Fix `scripts/function/find_path_upwards` to avoid `cd` side-effects
 and external `dirname` calls
- Document empty-string contract for `_bash_pseudo_hash` (no
 distinct nil values)
- Add CI smoke test for `go.mod` auto-detection from a subdirectory
- Bump version to 1.3.0 and update `ChangeLog` / `README.md`

feat(install): add auto-detection of Go version from project files

When no version is specified, `gvm install` now resolves the required
Go version from the nearest `.go-version` or `go.mod` file using
`__gvm_resolve_project_go_version`. Language-level versions (e.g.
`go 1.22`) are mapped to the latest available patch release for that
minor version.

This removes the hard failure on missing version argument and replaces
it with a smarter fallback, making it easier to install the correct
Go version when working inside a project directory without having to
look up or type the version manually.
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@brianrobt, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 41 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5d693235-b2cf-4e9e-a187-fab6909960d8

📥 Commits

Reviewing files that changed from the base of the PR and between f380d4d and 0c91bd6.

📒 Files selected for processing (5)
  • ChangeLog
  • scripts/function/find_path_upwards
  • scripts/function/resolve_project_version
  • tests/gvm_alias_comment_test.sh
  • tests/gvm_use_gomod_comment_test.sh
📝 Walkthrough

Walkthrough

Changes

Project version resolution

Layer / File(s) Summary
Project file resolution and tag mapping
scripts/function/resolve_project_version, scripts/function/find_path_upwards
Adds upward project-file discovery, .go-version and go.mod parsing, version normalization, stable-tag matching, and installed/available tag mapping without mutating the working directory.
CLI and applymod integration
scripts/env/use, scripts/install, scripts/env/applymod
Enables omitted-version auto-detection for gvm use and gvm install, and routes gvm applymod through the shared resolution helpers.
Smoke coverage and release documentation
scripts/ci-smoke.sh, tests/gvm_use_gomod_comment_test.sh, README.md, ChangeLog, VERSION, scripts/function/_bash_pseudo_hash
Adds nested go.mod detection tests and updates version, usage, feature, changelog, and empty-string contract documentation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ProjectDirectory
  participant GvmUse
  participant Resolver
  participant GoMod
  participant GoInstallation
  ProjectDirectory->>GvmUse: Invoke without an explicit version
  GvmUse->>Resolver: Resolve installed project version
  Resolver->>GoMod: Search and parse go.mod
  GoMod-->>Resolver: Return go directive or toolchain hint
  Resolver-->>GvmUse: Return latest matching stable tag
  GvmUse->>GoInstallation: Activate selected Go version
Loading

Possibly related issues

Suggested reviewers: cursoragent

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: automatic Go version detection from project files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/p3-gomod-autodetect-10

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
scripts/function/resolve_project_version (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate upward-file-search implementation across two files. __gvm_find_file_upwards (resolve_project_version) and __gvmp_find_path_for_target (find_path_upwards) implement the same cwd-safe traversal algorithm almost line-for-line; the latter is now only a fallback path when the former isn't loaded, so it's dead weight that can silently drift from the primary implementation (e.g. its -f || -d check vs. the primary's -e check).

  • scripts/function/resolve_project_version#L15-55: keep as the single canonical implementation of the upward walker.
  • scripts/function/find_path_upwards#L44-88: remove __gvmp_find_path_for_target's duplicated traversal body and have it delegate unconditionally to __gvm_find_file_upwards (sourcing resolve_project_version if not already loaded), rather than keeping a parallel copy as a fallback.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/function/resolve_project_version` at line 1, Remove the duplicated
traversal implementation from __gvmp_find_path_for_target in find_path_upwards,
and make it unconditionally source or reuse resolve_project_version so it
delegates to the canonical __gvm_find_file_upwards implementation. Preserve the
existing target lookup behavior while eliminating the parallel fallback
algorithm and its differing file-existence checks.
tests/gvm_use_gomod_comment_test.sh (1)

1-13: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Harden the test script per static-analysis findings.

Three related issues flagged by static analysis: (1) unquoted $GVM_ROOT/scripts/gvm at Line 1 (SC2086); (2) cd at Line 9 with no failure handling (SC2164) — if it fails, subsequent commands including rm -rf at Line 13 run against the wrong directory; (3) predictable PID-based temp dir names at Lines 4/7 (CWE-377) instead of mktemp -d.

🛡️ Proposed fix
-source $GVM_ROOT/scripts/gvm
+source "$GVM_ROOT/scripts/gvm"

-## Requires go1.22.12 from 00gvm_install_comment_test.sh
-mkdir -p /tmp/gvm2-mod-detect-$$/subdir
-echo 'module example.com/t
-
-go 1.22
-' > /tmp/gvm2-mod-detect-$$/go.mod
-cd /tmp/gvm2-mod-detect-$$/subdir
+## Requires go1.22.12 from 00gvm_install_comment_test.sh
+tmpdir="$(mktemp -d /tmp/gvm2-mod-detect.XXXXXX)"
+mkdir -p "$tmpdir/subdir"
+echo 'module example.com/t
+
+go 1.22
+' > "$tmpdir/go.mod"
+cd "$tmpdir/subdir" || exit 1
 gvm use # status=0; match=/Now using version go1\.22\./
 go version # status=0; match=/go1\.22\./
-cd /
-rm -rf /tmp/gvm2-mod-detect-$$
+cd / || exit 1
+rm -rf "$tmpdir"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/gvm_use_gomod_comment_test.sh` around lines 1 - 13, Harden
tests/gvm_use_gomod_comment_test.sh by quoting the GVM_ROOT-based source path,
creating the temporary module directory with mktemp -d instead of a predictable
PID-based path, and making the subdirectory change fail safely before running
subsequent commands. Reuse the resulting temporary-directory variable
consistently for file creation, cd, and cleanup, ensuring cleanup cannot target
an unintended directory.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/function/resolve_project_version`:
- Around line 229-244: Add a bounded timeout to the git ls-remote invocation
inside __gvm_list_available_go_tags, using the repository’s established timeout
mechanism or a suitable platform-compatible option. Preserve the existing tag
parsing, empty-output handling, and nonzero return behavior while ensuring
unreachable remotes fail promptly.
- Around line 317-352: Update __gvm_resolve_project_go_version so that once
.go-version yields a non-empty hint, it remains authoritative even when
__gvm_map_version_hint_to_tag returns no mapped version. For scope=available,
preserve returning the exact hint; for other scopes, return failure immediately
instead of checking go.mod. Only consult go.mod when no usable .go-version hint
was found.
- Around line 116-146: The __gvm_read_dot_go_version_file function only accepts
version lines prefixed with “go”, so ecosystem-style plain versions are ignored.
Update its matching logic to accept both go-prefixed values and plain Go version
strings such as 1.22.12, normalizing plain matches to the canonical goX.Y.Z form
before assigning version and returning it; preserve comment, whitespace, and
empty-line handling.

---

Nitpick comments:
In `@scripts/function/resolve_project_version`:
- Line 1: Remove the duplicated traversal implementation from
__gvmp_find_path_for_target in find_path_upwards, and make it unconditionally
source or reuse resolve_project_version so it delegates to the canonical
__gvm_find_file_upwards implementation. Preserve the existing target lookup
behavior while eliminating the parallel fallback algorithm and its differing
file-existence checks.

In `@tests/gvm_use_gomod_comment_test.sh`:
- Around line 1-13: Harden tests/gvm_use_gomod_comment_test.sh by quoting the
GVM_ROOT-based source path, creating the temporary module directory with mktemp
-d instead of a predictable PID-based path, and making the subdirectory change
fail safely before running subsequent commands. Reuse the resulting
temporary-directory variable consistently for file creation, cd, and cleanup,
ensuring cleanup cannot target an unintended directory.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 45c7786b-d51d-4e9f-917c-d90a3f7e1d50

📥 Commits

Reviewing files that changed from the base of the PR and between db47da5 and f380d4d.

⛔ Files ignored due to path filters (1)
  • .DS_Store is excluded by !**/.DS_Store
📒 Files selected for processing (11)
  • ChangeLog
  • README.md
  • VERSION
  • scripts/ci-smoke.sh
  • scripts/env/applymod
  • scripts/env/use
  • scripts/function/_bash_pseudo_hash
  • scripts/function/find_path_upwards
  • scripts/function/resolve_project_version
  • scripts/install
  • tests/gvm_use_gomod_comment_test.sh

Comment thread scripts/function/resolve_project_version
Comment thread scripts/function/resolve_project_version
Comment thread scripts/function/resolve_project_version
tf executes each line as its own command, so a multi-line echo left an
unclosed quote and wedged the suite after the alias cleanup noise.
Bound remote tag listing with git HTTP low-speed abort, keep .go-version
authoritative, accept plain 1.x.y pins, dedupe path walking, and harden
the go.mod comment test tempdir handling.
@brianrobt
brianrobt merged commit 73239b9 into master Jul 23, 2026
7 checks passed
@brianrobt
brianrobt deleted the cursor/p3-gomod-autodetect-10 branch July 23, 2026 20:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant