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

pantsbuild / pants / 18863956295

28 Oct 2025 04:17AM UTC coverage: 80.276% (-0.009%) from 80.285%
18863956295

push

github

web-flow
CI: update Go SDK versions and use Go 1.24 for tests of Go coverage support (#22810)

Update the Go SDK versions available in CI to v1.25.3 and v1.24.9 from
v1.19.x.

Since Pants does not support the Go "coverage redesign" which became the
only coverage implementation in Go 1.25, the coverage tests in
`src/python/pants/backend/go/util_rules/coverage_test.py` are now
conditioned on Go 1.24 being available on the system. (Those tests are
skipped otherwise.)

There are some other test fixes as well due to the upgrade from Go 1.19
to Go 1.25.

56 of 69 new or added lines in 3 files covered. (81.16%)

10 existing lines in 2 files now uncovered.

77944 of 97095 relevant lines covered (80.28%)

3.35 hits per line

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

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

4
import os
12✔
5
import subprocess
12✔
6
from functools import lru_cache
12✔
7

8
import pytest
12✔
9

10

11
@lru_cache(None)
12✔
12
def _go_present() -> bool:
12✔
13
    try:
12✔
14
        subprocess.run(
12✔
15
            ["go", "version"], check=False, env={"PATH": os.getenv("PATH") or ""}
16
        ).returncode
17
    except (subprocess.CalledProcessError, FileNotFoundError):
×
18
        return False
×
19
    return True
12✔
20

21

22
def _parse_go_version(version_string: str) -> tuple[int, int] | None:
12✔
23
    """Parse 'go1.24.3' output to (1, 24)."""
24
    if not version_string.startswith("go"):
1✔
NEW
25
        return None
×
26

27
    # Strip "go" prefix and split on "."
28
    version_parts = version_string[2:].split(".")
1✔
29

30
    if len(version_parts) < 2:
1✔
NEW
31
        return None
×
32

33
    try:
1✔
34
        major = int(version_parts[0])
1✔
35
        minor = int(version_parts[1])
1✔
36
        return (major, minor)
1✔
NEW
37
    except (ValueError, IndexError):
×
NEW
38
        return None
×
39

40

41
@lru_cache(None)
12✔
42
def _discover_go_binaries() -> list[tuple[str, tuple[int, int]]]:
12✔
43
    """Discover all Go binaries in PATH and return sorted by version (newest first)."""
44
    path_env = os.getenv("PATH", "")
1✔
45
    if not path_env:
1✔
NEW
46
        return []
×
47

48
    go_binaries = []
1✔
49
    seen_paths = set()
1✔
50

51
    for path_dir in path_env.split(os.pathsep):
1✔
52
        if not path_dir or not os.path.isdir(path_dir):
1✔
53
            continue
1✔
54

55
        go_binary = os.path.join(path_dir, "go")
1✔
56
        if os.path.isfile(go_binary) and os.access(go_binary, os.X_OK):
1✔
57
            # Resolve symlinks to avoid duplicates.
58
            real_path = os.path.realpath(go_binary)
1✔
59
            if real_path in seen_paths:
1✔
60
                continue
1✔
61
            seen_paths.add(real_path)
1✔
62

63
            try:
1✔
64
                result = subprocess.run(
1✔
65
                    [go_binary, "env", "GOVERSION"],
66
                    capture_output=True,
67
                    text=True,
68
                    timeout=5,
69
                    check=False,
70
                )
71
                if result.returncode == 0:
1✔
72
                    version = _parse_go_version(result.stdout.strip())
1✔
73
                    if version:
1✔
74
                        go_binaries.append((go_binary, version))
1✔
NEW
75
            except (subprocess.TimeoutExpired, FileNotFoundError, PermissionError):
×
NEW
76
                continue
×
77

78
    # Sort by version (newest first).
79
    go_binaries.sort(key=lambda x: x[1], reverse=True)
1✔
80
    return go_binaries
1✔
81

82

83
def pytest_generate_tests(metafunc):
12✔
84
    """Parametrize tests that require specific Go versions."""
85
    # Check if the test has the require_go_version_max marker.
86
    marker = metafunc.definition.get_closest_marker("require_go_version_max")
12✔
87
    if not marker:
12✔
88
        return
12✔
89

90
    # Extract the maximum version from the marker.
91
    if len(marker.args) < 2:
1✔
NEW
92
        pytest.fail(
×
93
            f"require_go_version_max marker requires 2 args (major, minor), got {marker.args}"
94
        )
NEW
95
        return
×
96

97
    max_major, max_minor = marker.args[0], marker.args[1]
1✔
98
    max_version = (max_major, max_minor)
1✔
99

100
    # Discover available Go binaries
101
    available_go = _discover_go_binaries()
1✔
102

103
    # Filter for compatible versions (<= max_version)
104
    compatible_go = [(path, version) for path, version in available_go if version <= max_version]
1✔
105

106
    # Parametrize the test if it accepts a `go_binary_path` fixture.
107
    if "go_binary_path" in metafunc.fixturenames:
1✔
108
        if compatible_go:
1✔
109
            # Use the newest compatible version.
110
            best_go_path, best_version = compatible_go[0]
1✔
111
            metafunc.parametrize(
1✔
112
                "go_binary_path",
113
                [best_go_path],
114
                ids=[f"go{best_version[0]}.{best_version[1]}"],
115
            )
116
        else:
117
            # Parametrize with None and mark to skip, so the test shows as skipped rather than uncollected.
NEW
118
            available_versions = [f"{v[0]}.{v[1]}" for _, v in available_go]
×
NEW
119
            skip_msg = (
×
120
                f"Test requires Go <= {max_major}.{max_minor}, but only found: "
121
                f"{', '.join(available_versions) if available_versions else 'none'}"
122
            )
NEW
123
            metafunc.parametrize(
×
124
                "go_binary_path",
125
                [pytest.param(None, marks=pytest.mark.skip(reason=skip_msg))],
126
                ids=["no-compatible-go"],
127
            )
128

129

130
def pytest_configure(config):
12✔
131
    """Register custom markers."""
132
    config.addinivalue_line(
12✔
133
        "markers",
134
        "require_go_version_max(major, minor): mark test to require Go version <= specified",
135
    )
136

137

138
def pytest_runtest_setup(item: pytest.Item) -> None:
12✔
139
    if not _go_present():
12✔
140
        pytest.skip(reason="`go` not present on PATH")
×
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