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

pantsbuild / pants / 20332790708

18 Dec 2025 09:48AM UTC coverage: 64.992% (-15.3%) from 80.295%
20332790708

Pull #22949

github

web-flow
Merge f730a56cd into 407284c67
Pull Request #22949: Add experimental uv resolver for Python lockfiles

54 of 97 new or added lines in 5 files covered. (55.67%)

8270 existing lines in 295 files now uncovered.

48990 of 75379 relevant lines covered (64.99%)

1.81 hits per line

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

54.1
/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
5✔
4

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

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

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

28

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

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

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

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

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

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

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

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

59

60
@rule(desc="Find Go binary", level=LogLevel.DEBUG)
5✔
61
async def setup_goroot(
5✔
62
    golang_subsystem: GolangSubsystem, go_bootstrap: GoBootstrap, env_target: EnvironmentTarget
63
) -> GoRoot:
64
    search_paths = go_bootstrap.go_search_paths
×
65
    all_go_binary_paths = await find_binary(
×
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:
×
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(
×
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 = []
×
105
    for binary_path, version_result in zip(all_go_binary_paths.paths, version_results):
×
106
        try:
×
107
            _raw_version = version_result.stdout.decode("utf-8").split()[
×
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]
×
111
            version = f"{_version_components[0]}.{_version_components[1]}"
×
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(
×
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
×
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())
×
135
            return GoRoot(
×
136
                path=sdk_metadata["GOROOT"], version=version, _raw_metadata=FrozenDict(sdk_metadata)
137
            )
138

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

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

147
    invalid_versions_str = bullet_list(
×
148
        f"{path}: {version}" for path, version in sorted(invalid_versions)
149
    )
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():
5✔
169
    return (
5✔
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

© 2025 Coveralls, Inc