How putnami observe reads a Pants repository
A repository whose root holds pants.toml is a Pants repository. Pants infers
most dependencies from imports, so no static read of the repository sees them:
the Pants detector reads the export the CI wrote, and never runs pants. The
change analyses use what it finds to name the changed and dependent
components, attribute tests and coverage, and diff the resolve lockfiles.
The export
Write the export in the CI, before putnami observe:
pants peek :: > pants-graph.jsonputnami observe reads pants-graph.json at the repository root, or the file
--pants-graph names, resolved against the directory you run in. peek lists
every target, the targets Pants generates for each file included, and each
target's dependencies: the dependencies Pants expanded from the BUILD files
and inferred from imports, with no flag needed from Pants 2.20 on. Keep the
export out of the commit, as a file the job writes.
Components
A component is a directory, of ecosystem pants. Each target belongs to the
directory of the BUILD file that declares it or its generator, so
src/python/pricing:pricing, src/python/pricing:tests and the file target
src/python/pricing/pricing.py are one component src/python/pricing. A
target generated for a file in a subdirectory, such as
src/python/pricing/rules/vat.py:../pricing, belongs to the directory of its
BUILD file, src/python/pricing. // is the root. With the export, the
component's source is orchestrator.
A third-party target is no component: python_requirement,
python_requirements, uv_requirements, poetry_requirements,
pipenv_requirements, pants_requirements, go_mod,
go_third_party_package, jvm_artifact, jvm_artifacts and
node_third_party_package. Neither is an internal target, whose type starts
with _, such as a resolve's _lockfiles. A directory such as
3rdparty/python, which holds only requirements, is therefore no component,
and a file in it, such as a lockfile, belongs to none.
Edges
Each address in a target's dependencies is an edge from the target's
component to the dependency's component, when the two differ. A dependency on
a third-party or internal target is no edge, nor one on the component itself.
For example, when src/python/api/main.py imports pricing.pricing, the
export lists src/python/pricing/pricing.py among its dependencies, and the
component src/python/api depends on src/python/pricing:
components 1 changed, 1 dependent, 1 unaffected
changed src/python/pricing [pants, orchestrator]
dependent src/python/api [pants, orchestrator], depends on src/python/pricingWithout the export
Without an export, each directory whose BUILD or BUILD.* file declares a
target other than a third-party or internal one is a component of source
workspace, with no edge. The walk skips dist, node_modules,
__pycache__ and the directories whose name starts with .. A BUILD file
that only sets __defaults__ declares no target. The info diagnostic
observe.pants_graph_missing names the export command; it is a warning when
--pants-graph named the missing file. --pants-graph in a repository
without pants.toml is the warning observe.pants_graph_unused. An export
that is not a JSON array of targets, each with an address, is the warning
observe.pants_graph_unreadable, and the BUILD files answer instead.
Reports
Run the tests with the reports on:
pants test --test-report --use-coverage --coverage-py-report=xml ::putnami observe finds the JUnit files under dist/test/reports and the
coverage.xml under dist/coverage/python by their content, as any report.
Pants runs pytest from the root of its sandbox, so a test case's class name is
its file's path, such as src.python.pricing.pricing_test, and lands on the
component that holds src/python/pricing/pricing_test.py. The coverage report
names each file from the repository root, so the sandbox path in its
<source> does not matter.
Lockfiles
Each lockfile the [python.resolves] table of pants.toml names is a
lockfile of ecosystem pypi:
[python.resolves]
python-default = "3rdparty/python/default.lock"Pants writes it in the PEX JSON lock format, after a header of // comment
lines. putnami observe reads the project name, normalized as PyPI does, and
the version of each locked requirement of each locked resolve. A path that
starts with %(buildroot)s/ is read from the repository root; a path outside
the repository is the warning observe.pants_config_invalid, and so is a
pants.toml that does not parse. Without a [python.resolves] table, no
Pants lockfile is read, even with enable_resolves = true and the default
python-default resolve: write the table to have its lockfile read. JVM and Go resolves are not read.