• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pantsbuild / pants / 22285099215

22 Feb 2026 08:52PM UTC coverage: 75.854% (-17.1%) from 92.936%
22285099215

Pull #23121

github

web-flow
Merge c7299df9c into ba8359840
Pull Request #23121: fix issue with optional fields in dependency validator

28 of 29 new or added lines in 2 files covered. (96.55%)

11174 existing lines in 400 files now uncovered.

53694 of 70786 relevant lines covered (75.85%)

1.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.52
/src/python/pants/backend/go/util_rules/goroot.py
1
# Copyright 2022 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3
from __future__ import annotations
4✔
4

5
import json
4✔
6
import logging
4✔
7
from dataclasses import dataclass
4✔
8

9
from pants.backend.go.subsystems.golang import GolangSubsystem
4✔
10
from pants.backend.go.util_rules import go_bootstrap
4✔
11
from pants.backend.go.util_rules.go_bootstrap import GoBootstrap, compatible_go_version
4✔
12
from pants.core.environments.target_types import EnvironmentTarget
4✔
13
from pants.core.util_rules.system_binaries import (
4✔
14
    BinaryNotFoundError,
15
    BinaryPathRequest,
16
    BinaryPathTest,
17
    find_binary,
18
)
19
from pants.engine.internals.selectors import concurrently
4✔
20
from pants.engine.process import Process, fallible_to_exec_result_or_raise
4✔
21
from pants.engine.rules import collect_rules, implicitly, rule
4✔
22
from pants.util.frozendict import FrozenDict
4✔
23
from pants.util.logging import LogLevel
4✔
24
from pants.util.strutil import bullet_list, softwrap
4✔
25

26
logger = logging.getLogger(__name__)
4✔
27

28

29
@dataclass(frozen=True)
4✔
30
class GoRoot:
4✔
31
    """Path to the Go installation (the `GOROOT`)."""
32

33
    path: str
4✔
34
    version: str
4✔
35

36
    _raw_metadata: FrozenDict[str, str]
4✔
37

38
    def is_compatible_version(self, version: str) -> bool:
4✔
39
        """Can this Go compiler handle the target version?"""
40
        return compatible_go_version(compiler_version=self.version, target_version=version)
4✔
41

42
    def major_version(self, version: str) -> str:
4✔
43
        _version_components = version.split(".")  # e.g. [1, 17] or [1, 17, 1]
4✔
44
        major_version = ".".join(_version_components[:2])
4✔
45
        return major_version
4✔
46

47
    @property
4✔
48
    def full_version(self) -> str:
4✔
49
        return self._raw_metadata["GOVERSION"]
4✔
50

51
    @property
4✔
52
    def goos(self) -> str:
4✔
53
        return self._raw_metadata["GOOS"]
4✔
54

55
    @property
4✔
56
    def goarch(self) -> str:
4✔
57
        return self._raw_metadata["GOARCH"]
4✔
58

59

60
@rule(desc="Find Go binary", level=LogLevel.DEBUG)
4✔
61
async def setup_goroot(
4✔
62
    golang_subsystem: GolangSubsystem, go_bootstrap: GoBootstrap, env_target: EnvironmentTarget
63
) -> GoRoot:
64
    search_paths = go_bootstrap.go_search_paths
4✔
65
    all_go_binary_paths = await find_binary(
4✔
66
        BinaryPathRequest(
67
            search_path=search_paths,
68
            binary_name="go",
69
            test=BinaryPathTest(["version"]),
70
        ),
71
        **implicitly(),
72
    )
73
    if not all_go_binary_paths.paths:
4✔
UNCOV
74
        raise BinaryNotFoundError(
×
75
            softwrap(
76
                f"""
77
                Cannot find any `go` binaries using the option `[golang].go_search_paths`:
78
                {list(search_paths)}
79

80
                To fix, please install Go (https://golang.org/doc/install) with the version
81
                {golang_subsystem.minimum_expected_version} or newer (set by
82
                `[golang].minimum_expected_version`). Then ensure that it is discoverable via
83
                `[golang].go_search_paths`.
84
                """
85
            )
86
        )
87

88
    # `go env GOVERSION` does not work in earlier Go versions (like 1.15), so we must run
89
    # `go version` and `go env GOROOT` to calculate both the version and GOROOT.
90
    version_results = await concurrently(
4✔
91
        fallible_to_exec_result_or_raise(
92
            **implicitly(
93
                Process(
94
                    (binary_path.path, "version"),
95
                    description=f"Determine Go version for {binary_path.path}",
96
                    level=LogLevel.DEBUG,
97
                    cache_scope=env_target.executable_search_path_cache_scope(),
98
                )
99
            )
100
        )
101
        for binary_path in all_go_binary_paths.paths
102
    )
103

104
    invalid_versions = []
4✔
105
    for binary_path, version_result in zip(all_go_binary_paths.paths, version_results):
4✔
106
        try:
4✔
107
            _raw_version = version_result.stdout.decode("utf-8").split()[
4✔
108
                2
109
            ]  # e.g. go1.17 or go1.17.1
110
            _version_components = _raw_version[2:].split(".")  # e.g. [1, 17] or [1, 17, 1]
4✔
111
            version = f"{_version_components[0]}.{_version_components[1]}"
4✔
112
        except IndexError:
×
113
            raise AssertionError(
×
114
                f"Failed to parse `go version` output for {binary_path}. Please open a bug at "
115
                f"https://github.com/pantsbuild/pants/issues/new/choose with the below data."
116
                f"\n\n"
117
                f"{version_result}"
118
            )
119

120
        if compatible_go_version(
4✔
121
            compiler_version=version, target_version=golang_subsystem.minimum_expected_version
122
        ):
123
            env_result = await fallible_to_exec_result_or_raise(  # noqa: PNT30: requires triage
4✔
124
                **implicitly(
125
                    Process(
126
                        (binary_path.path, "env", "-json"),
127
                        description=f"Determine Go SDK metadata for {binary_path.path}",
128
                        level=LogLevel.DEBUG,
129
                        cache_scope=env_target.executable_search_path_cache_scope(),
130
                        env={"GOPATH": "/does/not/matter"},
131
                    )
132
                )
133
            )
134
            sdk_metadata = json.loads(env_result.stdout.decode())
4✔
135
            return GoRoot(
4✔
136
                path=sdk_metadata["GOROOT"], version=version, _raw_metadata=FrozenDict(sdk_metadata)
137
            )
138

UNCOV
139
        logger.debug(
×
140
            f"Go binary at {binary_path.path} has version {version}, but this "
141
            f"repository expects at least {golang_subsystem.minimum_expected_version} "
142
            "(set by `[golang].expected_minimum_version`). Ignoring."
143
        )
144

UNCOV
145
        invalid_versions.append((binary_path.path, version))
×
146

UNCOV
147
    invalid_versions_str = bullet_list(
×
148
        f"{path}: {version}" for path, version in sorted(invalid_versions)
149
    )
UNCOV
150
    raise BinaryNotFoundError(
×
151
        softwrap(
152
            f"""
153
            Cannot find a `go` binary compatible with the minimum version of
154
            {golang_subsystem.minimum_expected_version} (set by `[golang].minimum_expected_version`).
155

156
            Found these `go` binaries, but they had incompatible versions:
157

158
            {invalid_versions_str}
159

160
            To fix, please install the expected version or newer (https://golang.org/doc/install)
161
            and ensure that it is discoverable via the option `[golang].go_search_paths`, or change
162
            `[golang].expected_minimum_version`.
163
            """
164
        )
165
    )
166

167

168
def rules():
4✔
169
    return (
4✔
170
        *collect_rules(),
171
        *go_bootstrap.rules(),
172
    )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc