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

pantsbuild / pants / 23173035367

17 Mar 2026 12:47AM UTC coverage: 91.371% (-1.6%) from 92.933%
23173035367

push

github

web-flow
update helm (and friends) to a recent 3.x seies (#23143)

v4 is a major breaking change, so holding off on that. (There was also
just a 3.20, but 3.19 has this reassuring series of bug fixes and was
also quite recent.)

2 of 2 new or added lines in 2 files covered. (100.0%)

1263 existing lines in 73 files now uncovered.

86196 of 94336 relevant lines covered (91.37%)

3.87 hits per line

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

96.91
/src/python/pants/backend/javascript/nodejs_project_environment.py
1
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3
from __future__ import annotations
10✔
4

5
import os.path
10✔
6
from collections.abc import Iterable
10✔
7
from dataclasses import dataclass, field
10✔
8

9
from pants.backend.javascript import package_json, resolve
10✔
10
from pants.backend.javascript.nodejs_project import NodeJSProject
10✔
11
from pants.backend.javascript.package_json import (
10✔
12
    NodePackageExtraEnvVarsField,
13
    NodePackageNameField,
14
    OwningNodePackage,
15
    OwningNodePackageRequest,
16
    find_owning_package,
17
)
18
from pants.backend.javascript.resolve import (
10✔
19
    ChosenNodeResolve,
20
    RequestNodeResolve,
21
    resolve_for_package,
22
)
23
from pants.backend.javascript.subsystems import nodejs
10✔
24
from pants.backend.javascript.subsystems.nodejs import NodeJSToolProcess, setup_node_tool_process
10✔
25
from pants.build_graph.address import Address
10✔
26
from pants.core.util_rules.env_vars import environment_vars_subset
10✔
27
from pants.engine.env_vars import EnvironmentVarsRequest
10✔
28
from pants.engine.internals.native_engine import EMPTY_DIGEST, Digest, MergeDigests
10✔
29
from pants.engine.internals.selectors import concurrently
10✔
30
from pants.engine.intrinsics import merge_digests, path_globs_to_digest
10✔
31
from pants.engine.process import Process
10✔
32
from pants.engine.rules import Rule, collect_rules, implicitly, rule
10✔
33
from pants.engine.target import Target
10✔
34
from pants.engine.unions import UnionRule
10✔
35
from pants.util.dirutil import fast_relpath
10✔
36
from pants.util.frozendict import FrozenDict
10✔
37
from pants.util.logging import LogLevel
10✔
38

39

40
@dataclass(frozen=True)
10✔
41
class NodeJSProjectEnvironmentRequest:
10✔
42
    address: Address
10✔
43

44

45
@dataclass(frozen=True)
10✔
46
class NodeJsProjectEnvironment:
10✔
47
    resolve: ChosenNodeResolve
10✔
48
    package: OwningNodePackage | None = None
10✔
49

50
    @classmethod
10✔
51
    def from_root(cls, project: NodeJSProject) -> NodeJsProjectEnvironment:
10✔
UNCOV
52
        return cls(resolve=ChosenNodeResolve(project), package=None)
×
53

54
    @property
10✔
55
    def project(self) -> NodeJSProject:
10✔
56
        return self.resolve.project
7✔
57

58
    @property
10✔
59
    def root_dir(self) -> str:
10✔
60
        return self.project.root_dir
7✔
61

62
    @property
10✔
63
    def node_modules_directories(self) -> Iterable[str]:
10✔
64
        yield "node_modules"
7✔
65
        if self.package and not self.project.single_workspace:
7✔
66
            yield os.path.join(self.relative_workspace_directory(), "node_modules")
2✔
67

68
    @property
10✔
69
    def target(self) -> Target | None:
10✔
70
        if self.package:
7✔
71
            return self.package.target
7✔
72
        else:
UNCOV
73
            return None
×
74

75
    def package_dir(self) -> str:
10✔
76
        if self.package and not self.project.single_workspace:
3✔
77
            return self.ensure_target().residence_dir
2✔
78
        else:
79
            return self.root_dir
1✔
80

81
    def relative_workspace_directory(self) -> str:
10✔
82
        target = self.ensure_target()
3✔
83
        from_root_to_workspace = fast_relpath(target.residence_dir, self.root_dir)
3✔
84
        return from_root_to_workspace
3✔
85

86
    def ensure_target(self) -> Target:
10✔
87
        if self.target:
7✔
88
            return self.target
7✔
89
        raise ValueError("")
×
90

91

92
@dataclass(frozen=True)
10✔
93
class NodeJsProjectEnvironmentProcess:
10✔
94
    env: NodeJsProjectEnvironment
10✔
95
    args: Iterable[str]
10✔
96
    description: str
10✔
97
    level: LogLevel = LogLevel.INFO
10✔
98
    input_digest: Digest = EMPTY_DIGEST
10✔
99
    output_files: tuple[str, ...] = ()
10✔
100
    output_directories: tuple[str, ...] = ()
10✔
101
    per_package_caches: FrozenDict[str, str] = field(default_factory=FrozenDict)
10✔
102
    project_caches: FrozenDict[str, str] = field(default_factory=FrozenDict)
10✔
103
    timeout_seconds: int | None = None
10✔
104
    extra_env: FrozenDict[str, str] = field(default_factory=FrozenDict)
10✔
105

106
    def targeted_args(self) -> tuple[str, ...]:
10✔
107
        if (
7✔
108
            not self.env.project.single_workspace
109
            and self.env.target
110
            and self.env.root_dir != self.env.package_dir()
111
        ):
112
            target = self.env.ensure_target()
2✔
113
            return (
2✔
114
                self.env.project.workspace_specifier_arg,
115
                target[NodePackageNameField].value,
116
                *self.args,
117
            )
118
        else:
119
            return tuple(self.args)
7✔
120

121

122
@rule(desc="Assembling nodejs project environment")
10✔
123
async def get_nodejs_environment(req: NodeJSProjectEnvironmentRequest) -> NodeJsProjectEnvironment:
10✔
124
    node_resolve, owning_tgt = await concurrently(
7✔
125
        resolve_for_package(RequestNodeResolve(req.address), **implicitly()),
126
        find_owning_package(OwningNodePackageRequest(req.address)),
127
    )
128
    assert owning_tgt.target, f"Already ensured to exist by {ChosenNodeResolve.__name__}."
7✔
129

130
    return NodeJsProjectEnvironment(node_resolve, owning_tgt)
7✔
131

132

133
@rule
10✔
134
async def setup_nodejs_project_environment_process(
10✔
135
    req: NodeJsProjectEnvironmentProcess,
136
    nodejs: nodejs.NodeJS,
137
) -> Process:
138
    target_env_vars = (
7✔
139
        req.env.target.get(NodePackageExtraEnvVarsField).value or () if req.env.target else ()
140
    )
141

142
    lockfile_digest, project_digest, subsystem_env_vars, env_vars = await concurrently(
7✔
143
        path_globs_to_digest(req.env.resolve.get_lockfile_glob()),
144
        merge_digests(req.env.project.get_project_digest()),
145
        environment_vars_subset(EnvironmentVarsRequest(nodejs.extra_env_vars), **implicitly()),
146
        environment_vars_subset(EnvironmentVarsRequest(target_env_vars), **implicitly()),
147
    )
148
    merged = await merge_digests(MergeDigests((req.input_digest, lockfile_digest, project_digest)))
7✔
149

150
    args = req.targeted_args()
7✔
151
    output_files = req.output_files
7✔
152
    output_directories = req.output_directories
7✔
153
    per_package_caches = FrozenDict(
7✔
154
        {
155
            key: os.path.join(req.env.package_dir(), value)
156
            for key, value in req.per_package_caches.items()
157
        }
158
    )
159
    append_only_caches: FrozenDict[str, str] = FrozenDict(
7✔
160
        **per_package_caches, **req.project_caches, **req.env.project.extra_caches()
161
    )
162

163
    return await setup_node_tool_process(
7✔
164
        **implicitly(
165
            NodeJSToolProcess(
166
                tool=req.env.project.package_manager.name,
167
                tool_version=req.env.project.package_manager.version,
168
                args=args,
169
                description=req.description,
170
                level=req.level,
171
                input_digest=merged,
172
                working_directory=req.env.root_dir,
173
                output_files=output_files,
174
                output_directories=output_directories,
175
                append_only_caches=append_only_caches,
176
                timeout_seconds=req.timeout_seconds,
177
                project_digest=project_digest,
178
                extra_env=FrozenDict(
179
                    {
180
                        **subsystem_env_vars,
181
                        **env_vars,
182
                        **req.extra_env,
183
                        **req.env.project.extra_env(),
184
                    }
185
                ),
186
            )
187
        )
188
    )
189

190

191
def rules() -> Iterable[Rule | UnionRule]:
10✔
192
    return [*collect_rules(), *nodejs.rules(), *resolve.rules(), *package_json.rules()]
10✔
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