How go-modern-guidelines Detects the Go Version Without Flags or File Paths

When invoked without explicit inputs, the go-modern-guidelines CLI automatically queries the local Go toolchain via go env GOVERSION, normalizes the output to a major.minor format, and uses that value to filter applicable coding guidelines.

The JetBrains/go-modern-guidelines tool analyzes Go source code against modern best practices, but it requires a target Go version to determine which rules apply. When you run the list command without specifying --go-version or --file-path, the CLI implements a sophisticated fallback chain that interrogates your local Go installation directly.

The Auto-Detection Pipeline

The detection mechanism spans three logical layers: the command parser, the resolution engine, and the toolchain interface. This ensures that even in bare invocations, the linter evaluates code against the correct Go version constraints.

Entry Point in the CLI Layer

The process begins in internal/cli/cli.go within the runList function. This handler parses your command-line arguments and immediately delegates version resolution to the goversion package:

targetVersion, err := goversion.Resolve(filePath, goVersion, guidelines.LatestKnownVersion())

If both filePath and goVersion are empty strings—meaning you provided no --file-path or --go-version flags—the Resolve function triggers the automatic detection sequence rather than returning an error.

Resolution Logic in the goversion Package

Located in internal/goversion/goversion.go, the Resolve function implements a priority-based fallback strategy:

  1. Check for explicit version via --go-version flag
  2. Check for module file via --file-path argument
  3. Fallback to local Go toolchain execution

When both inputs are empty, the function calls resolveGoToolVersion with a descriptive label and the latest known guideline version as a safety bound:

return resolveGoToolVersion("local Go toolchain", develVersion)

Executing the Toolchain Query

The resolveGoToolVersion function executes an external process call to extract the Go version from your environment:

output, err := exec.Command("go", "env", "GOVERSION").Output()
version, err := normalizeGoVersion(string(output), develVersion)

This spawns go env GOVERSION, which returns the exact version string of the Go binary currently in your PATH (e.g., go1.22.3). The function captures stdout and passes it to the normalization routine to ensure consistent formatting.

Normalizing the Version String

Raw toolchain output varies by installation (some distributions append build metadata or release candidates). The normalizeGoVersion function in internal/goversion/goversion.go sanitizes this using regex matching:

match := goVersionInText.FindStringSubmatch(trimmed)
return fmt.Sprintf("%d.%d", majorMinor.major, majorMinor.minor), nil

This reduces strings like go1.22.3 or 1.22rc1 to the canonical major.minor format (e.g., 1.22) required by the guideline matrix. The CLI then compares this normalized version against its internal database of modern Go recommendations to determine which rules to display or enforce.

Why Runtime Detection Over Build-Time

The go-modern-guidelines binary also defines a detectedVersion() function that reads debug.ReadBuildInfo to determine which Go version compiled the linter itself. However, the list command deliberately ignores this build-time constant.

Runtime detection via go env GOVERSION ensures the linter evaluates your source code against the Go version you use to build that code, not the version used to build the linter. This distinction matters in CI/CD environments where the linter binary might be compiled with Go 1.21 but is analyzing code intended for Go 1.22 deployment.

Practical Usage Examples

Automatic Detection (No Flags)

go-modern-guidelines list

The CLI invokes go env GOVERSION, extracts the local toolchain version (e.g., 1.22), and displays guidelines applicable to that release.

Explicit Version Override

go-modern-guidelines list --go-version 1.20

Bypasses auto-detection entirely; the specified version is normalized and used directly.

Module File Override

go-modern-guidelines list --file-path ./go.mod

The CLI parses the go directive within the specified go.mod file, taking precedence over the local toolchain query.

Summary

  • The runList function in internal/cli/cli.go calls goversion.Resolve to determine the target Go version.
  • When no flags are provided, Resolve falls back to resolveGoToolVersion, which executes go env GOVERSION.
  • The normalizeGoVersion function converts the raw toolchain output (e.g., go1.22.3) into a standardized major.minor string.
  • Auto-detection uses the runtime Go toolchain rather than the linter's build-time version to ensure accurate guideline matching for the code being analyzed.

Frequently Asked Questions

What happens if the go command is not in my PATH when I run go-modern-guidelines?

If the CLI cannot locate the go binary to execute go env GOVERSION, the resolveGoToolVersion function returns an error and the command fails. You must either add Go to your PATH or use the --go-version flag to specify the version manually.

Can I force the CLI to use a different Go version than the one in my PATH?

Yes. Providing either --go-version or --file-path overrides the automatic detection entirely. The CLI does not execute go env GOVERSION when an explicit version source is provided.

Why does the CLI report a different version than go version shows?

The CLI normalizes full version strings (e.g., go1.22.3) to their major.minor components (e.g., 1.22) using the normalizeGoVersion function. This truncation allows the tool to match against guideline categories that apply to entire minor releases, rather than specific patch levels.

Is there a way to see which version the CLI detected automatically?

While the CLI does not have a dedicated "verbose version detection" flag, running go-modern-guidelines list without arguments and observing which guidelines appear will indicate the detected version, as the output is filtered specifically for that Go release. You can verify the underlying value by running go env GOVERSION directly in your shell.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →