How the go-modern-guidelines CLI Determines the Target Go Version from go.mod

The go-modern-guidelines CLI determines the target Go version by checking for an explicit --go-version flag, parsing the go directive from go.mod or go.work files (searching upward through the directory tree if necessary), and falling back to the locally installed Go toolchain when no module file exists.

The JetBrains/go-modern-guidelines repository provides a CLI tool that filters Go coding guidelines based on the target language version. When executing the list command, the tool must resolve which Go version to target so it can return the appropriate modern guidelines set. This resolution logic is centralized in the internal/goversion package, which implements a cascading priority system for version detection.

Resolution Logic Overview

The entry point for version detection is the goversion.Resolve function, invoked by runList in internal/cli/cli.go (lines 79-84). This function accepts three parameters: a file path, an explicit version string from the --go-version flag, and the latest known version from the guidelines package. The function implements a priority-based decision tree where explicit flags take precedence, followed by module file parsing, with a final fallback to the local Go installation.

Explicit Version Flag Override

The --go-version Flag

When the user supplies the --go-version flag, goversion.Resolve immediately normalizes the provided value and returns it. This override supports various formats including 1.24, go1.24.3, or devel tags (internal/goversion/goversion.go, lines 18-27). This ensures developers can force specific guideline sets regardless of the project's module configuration.


# Force guidelines for Go 1.24 regardless of go.mod

$ go-modern-guidelines list --go-version=1.24

Automatic Detection from Module Files

When no explicit version is provided, the CLI attempts to locate and parse module files automatically via resolveGoVersionFromPath (internal/goversion/goversion.go, lines 31-33).

Direct File Parsing with parseGoDirective

If the supplied path points directly to a go.mod or go.work file, the code calls parseGoDirective (internal/goversion/goversion.go, lines 69-75). This function reads the file and uses golang.org/x/mod/modfile to extract the go directive value.

Directory Traversal with findUp

When the provided path is a directory or a non-module file, the CLI walks upward through the filesystem hierarchy. The findUp utility (internal/goversion/goversion.go, lines 77-84 and 9-22) searches parent directories for the nearest go.mod or go.work file. Once found, the file is parsed using the same parseGoDirective logic as direct file handling.

// Example: Resolve from a specific directory
version, err := goversion.Resolve("path/to/project", "", guidelines.LatestKnownVersion())
if err != nil {
    log.Fatal(err)
}
fmt.Println("Detected Go version:", version) // Output: "1.23"

Fallback to Local Toolchain

If no module file is found during the directory traversal, the CLI executes resolveGoToolVersion (internal/goversion/goversion.go, lines 62-71). This function runs go env GOVERSION to retrieve the version of the locally installed Go toolchain, ensuring the tool remains functional even outside module-aware directories.


# CLI automatically detects version from nearest go.mod

$ go-modern-guidelines list

# If the current directory contains a go.mod with "go 1.23", 

# the output will be based on 1.23

Version Normalization

All version strings—whether from flags, module files, or the local toolchain—pass through normalizeGoVersion (internal/goversion/goversion.go, lines 74-88). This function uses a regular expression to extract the major.minor pair (e.g., converting go1.24.3 to 1.24) and discards patch versions and suffixes. This normalization ensures consistent version comparison across different input formats.

Summary

  • The goversion.Resolve function in internal/goversion/goversion.go orchestrates version detection with explicit flags taking highest priority.
  • Explicit version override: The --go-version flag bypasses automatic detection and normalizes inputs like 1.24 or go1.24.3.
  • Module file parsing: The tool parses go.mod and go.work files using golang.org/x/mod/modfile to extract the go directive.
  • Directory walking: If given a directory, the CLI searches upward via findUp to locate the nearest module file.
  • Toolchain fallback: When no module file exists, the CLI falls back to the local Go version via go env GOVERSION.
  • Normalization: All versions are standardized to major.minor format using normalizeGoVersion before guideline selection.

Frequently Asked Questions

How does go-modern-guidelines handle pre-release or development Go versions?

The CLI accepts development versions through the --go-version flag, including formats like devel or specific pre-release tags. The normalizeGoVersion function processes these strings and extracts the major.minor pair when possible, or preserves the development identifier for guideline matching according to the implementation in internal/goversion/goversion.go.

Can the tool detect versions from go.work files as well as go.mod?

Yes. The parseGoDirective function and findUp utility specifically search for both go.mod and go.work files. The golang.org/x/mod/modfile parser handles both file types, extracting the go directive that specifies the language version for the workspace or module.

What happens if my project has no go.mod file?

If the directory traversal via findUp fails to locate a go.mod or go.work file, the CLI calls resolveGoToolVersion to execute go env GOVERSION. This retrieves the version of the currently installed Go toolchain and uses that as the target version for filtering guidelines.

Why does the CLI normalize versions to major.minor format?

The normalizeGoVersion function strips patch versions and suffixes to ensure consistent comparison logic. Since Go language features and guidelines are typically gated at the minor version level (e.g., 1.21, 1.22), normalizing to major.minor allows the guideline database to match against stable version identifiers regardless of the specific patch level installed.

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 →