• 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

60.24
/src/python/pants/backend/go/util_rules/sdk.py
1
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
5✔
5

6
import textwrap
5✔
7
from collections.abc import Iterable, Mapping
5✔
8
from dataclasses import dataclass
5✔
9

10
from pants.backend.go.subsystems.golang import GolangSubsystem
5✔
11
from pants.backend.go.util_rules import goroot
5✔
12
from pants.backend.go.util_rules.go_bootstrap import GoBootstrap
5✔
13
from pants.backend.go.util_rules.goroot import GoRoot
5✔
14
from pants.core.util_rules.env_vars import environment_vars_subset
5✔
15
from pants.core.util_rules.system_binaries import (
5✔
16
    BashBinary,
17
    BinaryShimsRequest,
18
    create_binary_shims,
19
)
20
from pants.engine.env_vars import EnvironmentVarsRequest
5✔
21
from pants.engine.fs import EMPTY_DIGEST, CreateDigest, Digest, FileContent, MergeDigests
5✔
22
from pants.engine.internals.selectors import concurrently
5✔
23
from pants.engine.intrinsics import create_digest, merge_digests
5✔
24
from pants.engine.process import Process, fallible_to_exec_result_or_raise
5✔
25
from pants.engine.rules import collect_rules, implicitly, rule
5✔
26
from pants.util.frozendict import FrozenDict
5✔
27
from pants.util.logging import LogLevel
5✔
28

29

30
@dataclass(frozen=True)
5✔
31
class GoSdkProcess:
5✔
32
    command: tuple[str, ...]
5✔
33
    description: str
5✔
34
    env: FrozenDict[str, str]
5✔
35
    input_digest: Digest
5✔
36
    working_dir: str | None
5✔
37
    output_files: tuple[str, ...]
5✔
38
    output_directories: tuple[str, ...]
5✔
39
    replace_sandbox_root_in_args: bool
5✔
40

41
    def __init__(
5✔
42
        self,
43
        command: Iterable[str],
44
        *,
45
        description: str,
46
        env: Mapping[str, str] | None = None,
47
        input_digest: Digest = EMPTY_DIGEST,
48
        working_dir: str | None = None,
49
        output_files: Iterable[str] = (),
50
        output_directories: Iterable[str] = (),
51
        allow_downloads: bool = False,
52
        replace_sandbox_root_in_args: bool = False,
53
    ) -> None:
UNCOV
54
        object.__setattr__(self, "command", tuple(command))
×
UNCOV
55
        object.__setattr__(self, "description", description)
×
UNCOV
56
        object.__setattr__(
×
57
            self,
58
            "env",
59
            (
60
                FrozenDict(env or {})
61
                if allow_downloads
62
                else FrozenDict({**(env or {}), "GOPROXY": "off"})
63
            ),
64
        )
UNCOV
65
        object.__setattr__(self, "input_digest", input_digest)
×
UNCOV
66
        object.__setattr__(self, "working_dir", working_dir)
×
UNCOV
67
        object.__setattr__(self, "output_files", tuple(output_files))
×
UNCOV
68
        object.__setattr__(self, "output_directories", tuple(output_directories))
×
UNCOV
69
        object.__setattr__(self, "replace_sandbox_root_in_args", replace_sandbox_root_in_args)
×
70

71

72
@dataclass(frozen=True)
5✔
73
class GoSdkRunSetup:
5✔
74
    digest: Digest
5✔
75
    script: FileContent
5✔
76

77
    CHDIR_ENV = "__PANTS_CHDIR_TO"
5✔
78
    SANDBOX_ROOT_ENV = "__PANTS_REPLACE_SANDBOX_ROOT"
5✔
79

80

81
@rule
5✔
82
async def go_sdk_invoke_setup(goroot: GoRoot) -> GoSdkRunSetup:
5✔
83
    # Note: The `go` tool requires GOPATH to be an absolute path which can only be resolved
84
    # from within the execution sandbox. Thus, this code uses a bash script to be able to resolve
85
    # absolute paths inside the sandbox.
86
    go_run_script = FileContent(
×
87
        "__run_go.sh",
88
        textwrap.dedent(
89
            f"""\
90
            export GOROOT={goroot.path}
91
            sandbox_root="$(/bin/pwd)"
92
            export GOPATH="${{sandbox_root}}/gopath"
93
            export GOCACHE="${{sandbox_root}}/cache"
94
            /bin/mkdir -p "$GOPATH" "$GOCACHE"
95
            if [ -n "${GoSdkRunSetup.CHDIR_ENV}" ]; then
96
              cd "${GoSdkRunSetup.CHDIR_ENV}"
97
            fi
98
            if [ -n "${GoSdkRunSetup.SANDBOX_ROOT_ENV}" ]; then
99
              export __PANTS_SANDBOX_ROOT__="$sandbox_root"
100
              args=("${{@//__PANTS_SANDBOX_ROOT__/$sandbox_root}}")
101
              set -- "${{args[@]}}"
102
            fi
103
            exec "{goroot.path}/bin/go" "$@"
104
            """
105
        ).encode("utf-8"),
106
    )
107

108
    digest = await create_digest(CreateDigest([go_run_script]))
×
109
    return GoSdkRunSetup(digest, go_run_script)
×
110

111

112
@rule
5✔
113
async def setup_go_sdk_process(
5✔
114
    request: GoSdkProcess,
115
    go_bootstrap: GoBootstrap,
116
    go_sdk_run: GoSdkRunSetup,
117
    bash: BashBinary,
118
    golang_env_aware: GolangSubsystem.EnvironmentAware,
119
    goroot: GoRoot,
120
) -> Process:
121
    # Use go search path to find extra tools
122
    search_path = go_bootstrap.go_search_paths
×
123

124
    input_digest, env_vars = await concurrently(
×
125
        merge_digests(MergeDigests([go_sdk_run.digest, request.input_digest])),
126
        environment_vars_subset(
127
            EnvironmentVarsRequest(golang_env_aware.env_vars_to_pass_to_subprocesses),
128
            **implicitly(),
129
        ),
130
    )
131

132
    env = {
×
133
        **env_vars,
134
        **request.env,
135
        GoSdkRunSetup.CHDIR_ENV: request.working_dir or "",
136
        "__PANTS_GO_SDK_CACHE_KEY": f"{goroot.full_version}/{goroot.goos}/{goroot.goarch}",
137
    }
138

139
    immutable_input_digests: dict[str, Digest] = {}
×
140

141
    # Add path to additional tools, such as git, that may be needed by the go tool
142
    if golang_env_aware.extra_tools:
×
143
        extra_tools = await create_binary_shims(
×
144
            BinaryShimsRequest.for_binaries(
145
                *golang_env_aware.extra_tools,
146
                rationale="allow additional tools for go tools",
147
                search_path=search_path,
148
            ),
149
            bash,
150
        )
151
        # Prepend path to additional tools
152
        if "PATH" in env:
×
153
            env["PATH"] = f"{extra_tools.path_component}:{env['PATH']}"
×
154
        else:
155
            env["PATH"] = extra_tools.path_component
×
156
        immutable_input_digests.update(extra_tools.immutable_input_digests)
×
157

158
    if request.replace_sandbox_root_in_args:
×
159
        env[GoSdkRunSetup.SANDBOX_ROOT_ENV] = "1"
×
160

161
    # Disable the "coverage redesign" experiment on Go v1.20+ for now since Pants does not yet support it.
162
    if goroot.is_compatible_version("1.20") and not goroot.is_compatible_version("1.25"):
×
163
        exp_str = env.get("GOEXPERIMENT", "")
×
164
        exp_fields = exp_str.split(",") if exp_str != "" else []
×
165
        exp_fields = [exp for exp in exp_fields if exp != "coverageredesign"]
×
166
        if "nocoverageredesign" not in exp_fields:
×
167
            exp_fields.append("nocoverageredesign")
×
168
        env["GOEXPERIMENT"] = ",".join(exp_fields)
×
169

170
    return Process(
×
171
        argv=[bash.path, go_sdk_run.script.path, *request.command],
172
        env=env,
173
        immutable_input_digests=immutable_input_digests,
174
        input_digest=input_digest,
175
        description=request.description,
176
        output_files=request.output_files,
177
        output_directories=request.output_directories,
178
        level=LogLevel.DEBUG,
179
    )
180

181

182
@dataclass(frozen=True)
5✔
183
class GoSdkToolIDRequest:
5✔
184
    tool_name: str
5✔
185

186

187
@dataclass(frozen=True)
5✔
188
class GoSdkToolIDResult:
5✔
189
    tool_name: str
5✔
190
    tool_id: str
5✔
191

192

193
@rule
5✔
194
async def compute_go_tool_id(request: GoSdkToolIDRequest) -> GoSdkToolIDResult:
5✔
195
    result = await fallible_to_exec_result_or_raise(
×
196
        **implicitly(
197
            GoSdkProcess(
198
                ["tool", request.tool_name, "-V=full"],
199
                description=f"Obtain tool ID for Go tool `{request.tool_name}`.",
200
            )
201
        )
202
    )
203
    return GoSdkToolIDResult(tool_name=request.tool_name, tool_id=result.stdout.decode().strip())
×
204

205

206
def rules():
5✔
207
    return (*collect_rules(), *goroot.rules())
5✔
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