Skip to main content

content-plugin-finder

Unsupported prototype. This repository is an early experiment under active development. APIs, CLI flags, and behavior may change without notice. There is no support commitment, stability guarantee, or production readiness claim. Use at your own risk.

Find Ansible modules, filters, and lookups used in directories such as Molecule scenarios or ansible-test integration targets, and map git changes to molecule scenarios / ansible-test integration targets that should run.

Uses a pluggable crawler subsystem. v1 ships three crawlers; more plugin kinds can be added later without changing the orchestrator.

License: MIT (see LICENSE).

This project vendors ansible-content-capture (Apache-2.0) under src/ansible_content_capture/. See LICENSE.ansible-content-capture and NOTICE.

Vendored note: loader.get_scanner_version() was patched to use importlib.metadata instead of removed pkg_resources (setuptools ≥83).

Impact view: changed file → plugin FQCNs → molecule scenarios and integration targets

Install

pip install -e ".[dev]"

Visualizer

A zero-build dual-mode graph UI lives in viz/ (d3 v7 from CDN).

1. Export JSON

From a collection checkout:

# Plugin dependency DAG (full graph, or focus one FQCN)
content-plugin-finder --collection-graph . --format json > graph.json
content-plugin-finder --collection-graph . --plugin ansible.platform.application --format json > plugin.json

# Impact flow (needs a git base, or pipe paths)
content-plugin-finder --impact . --base origin/main --format json > impact.json
echo 'plugins/action/base_action.py' \
  | content-plugin-finder --impact . --from-stdin --format json > impact.json

2. Open the UI

# optional: from the repo root
xdg-open viz/index.html   # or open viz/index.html in your browser
  1. Open viz/index.html (double-click or file:// is fine).
  2. Click Choose File / Load JSON and select graph.json or impact.json.
  3. Mode is auto-detected from the JSON shape; you can override with the Mode control.
  4. Plugin dependencies: pick a Focus plugin FQCN (recommended). Click a node for path + owning plugins.
  5. Impact: explore changed files → plugins → molecule / integration roots. Counts appear in the toolbar.
Mode JSON source View
Plugin dependencies --collection-graph --format json (full or --plugin FQCN) Focus one FQCN (recommended) or capped “All”; nodes are Python files
Impact --impact --format json Changed files → plugin FQCNs → molecule / integration roots

No npm build. The file picker works with file:// (browsers block fetch of local paths).

Usage

content-plugin-finder path/to/scenario [path/to/target ...]
content-plugin-finder --format json --by-directory path/to/scenario
content-plugin-finder --types module,filter path/to/scenario
content-plugin-finder --list-crawlers

# Discover Molecule scenarios + integration targets under a collection/repo
content-plugin-finder --parent ../ansible.platform --depth 4 --list-roots
content-plugin-finder --parent ../ansible.platform --depth 4

--parent walks for:

  • directories containing molecule.yml
  • tests/integration/targets/<name>/

--depth is the max relative depth under --parent (default 4). For a collection root, depth 3 reaches extensions/molecule/<scenario>; depth 4 also reaches tests/integration/targets/<target>.

Crawler subsystem

Crawler Kind Source
module module ansible-content-capture task/module trees
filter filter Jinja pipes in YAML scalars
lookup lookup ACC lookup/query tasks + Jinja lookup() / query()

Modules and action plugins are reported together as modules (not distinguishable from content alone).

Collection import graph

Build a Python import tree and plugin name map for a collection:

content-plugin-finder --collection-graph ../ansible.platform
content-plugin-finder --collection-graph ../ansible.platform --plugin application
content-plugin-finder --collection-graph ../ansible.platform --plugin ansible.platform.application --format json

Output is per plugin (FQCN): entry files (module + action when both exist) plus the transitive in-collection dependency closure (depends_on_files). Direct file→import edges remain available in the JSON under plugins / imports.

Plugin identity is always the FQCN {namespace}.{name}.{plugin} from galaxy.yml plus the short plugin name (e.g. ansible.platform.application). --plugin application is accepted as a short alias and resolved to that FQCN.

Kind Short name source (then prefixed with collection FQCN)
module DOCUMENTATION module: / file stem
action ActionModule.MODULE_NAME, else matching plugins/modules/<stem>.py, else stem
filter keys from FilterModule.filters() (one file may expose many filters)
lookup DOCUMENTATION name: / file stem

Impact (git changes → tests to run)

# Local / CI with explicit base (PR target SHA or branch)
content-plugin-finder --impact . --parent . --base origin/main
content-plugin-finder --impact . --base "$PR_BASE_SHA" --head "$PR_HEAD_SHA"

# Pipe changed paths (CI escape hatch)
git diff --name-only "$BASE" "$HEAD" | content-plugin-finder --impact . --from-stdin

# Emit only names for a matrix
content-plugin-finder --impact . --base origin/main --emit molecule --names-only
content-plugin-finder --impact . --base origin/main --emit integration --names-only
content-plugin-finder --impact . --base origin/main --format json

Text output (default):

molecule	extensions/molecule/application_mock
integration	tests/integration/targets/applications_test

In CI, pass the PR base SHA (or fetch the target branch). Bare git diff without a base is not reliable on shallow/detached checkouts. Three-dot base...head is the default; use --two-dot for base head.

git changed files
        │
        ├─ under scenario/target     → that root
        │     (path layout works even if discovery depth missed the root)
        ├─ shared molecule file      → all discovered molecule scenarios
        │     (e.g. extensions/molecule/requirements.yml)
        └─ collection .py            → file_to_plugins → FQCNs
                │
                └─ content index → molecule scenarios / integration targets

Direct path matches cover scenario and integration-target edits themselves, not only plugin→content reverse mapping. Shared files under a molecule/ directory (outside any scenario) select every discovered molecule scenario. Deleted paths are included in the git diff (ACMRD).

Running Molecule or ansible-test from the list

Emit leaf names, then drive the tools your collection already uses:

content-plugin-finder --impact . --base origin/main \
  --emit molecule --names-only > molecule.txt
content-plugin-finder --impact . --base origin/main \
  --emit integration --names-only > integration.txt

Molecule (scenario name = directory under extensions/molecule/ or molecule/):

Serial (one scenario at a time):

while read -r scenario; do
  [ -n "$scenario" ] || continue
  molecule test -s "$scenario"
done < molecule.txt

With Molecule workers (experimental concurrent scenarios; multiple -s + --workers):

args=()
while read -r scenario; do
  [ -n "$scenario" ] || continue
  args+=(-s "$scenario")
done < molecule.txt

((${#args[@]})) || exit 0
molecule test "${args[@]}" --workers cpus-1
# optional: --continue-on-error

--workers accepts an integer, cpus, or cpus-1. Prefer this over a serial loop when the collection supports shared-state / ansible-native multi-scenario runs. See Molecule’s docs for --workers / --shared-state behavior (default scenario create/destroy stays on the main process).

ansible-test integration (target name = directory under tests/integration/targets/):

mapfile -t targets < integration.txt
if ((${#targets[@]})); then
  ansible-test integration --docker default "${targets[@]}"
fi

Adjust Molecule/ansible-test flags to match your collection CI. An empty file means nothing to run for that kind.

Minimal PR CI sketch (fetch full history or the base SHA first):

- uses: actions/checkout@v4
  with:
    fetch-depth: 0

- name: Select tests
  run: |
    content-plugin-finder --impact . \
      --base "${{ github.event.pull_request.base.sha }}" \
      --head "${{ github.sha }}" \
      --emit molecule --names-only | tee molecule.txt
    content-plugin-finder --impact . \
      --base "${{ github.event.pull_request.base.sha }}" \
      --head "${{ github.sha }}" \
      --emit integration --names-only | tee integration.txt

- name: Molecule
  run: |
    args=()
    while read -r s; do
      [ -n "$s" ] || continue
      args+=(-s "$s")
    done < molecule.txt
    ((${#args[@]})) || exit 0
    molecule test "${args[@]}" --workers cpus-1

- name: ansible-test
  run: |
    mapfile -t t < integration.txt
    ((${#t[@]})) || exit 0
    ansible-test integration --docker default "${t[@]}"

Library

from pathlib import Path
from content_plugin_finder.crawl.orchestrator import Orchestrator
from content_plugin_finder.collection import build_collection_graph
from content_plugin_finder.impact import compute_impact

report = Orchestrator().scan([Path("extensions/molecule/default")])
print(report.to_dict())

graph = build_collection_graph(Path("../ansible.platform"))
print(graph.resolve("application").name)  # ansible.platform.application

impact = compute_impact(
    collection_root=Path("../ansible.platform"),
    changed_files=["plugins/action/base_action.py"],
)
print(impact.molecule_scenarios)
print(impact.integration_targets)

Metadata

Release files for content-plugin-finder 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for content-plugin-finder 0.1.0
File Size Uploaded
content_plugin_finder-0.1.0.tar.gz 266.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for content-plugin-finder 0.1.0
File Interpreter ABI Platform
content_plugin_finder-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 552.7 kB

Release files / content_plugin_finder-0.1.0.tar.gz

Download URL content_plugin_finder-0.1.0.tar.gz
Size 266.9 kB
Tags Source
SHA-256 checksum
How to use checksums
d839dd92682909219fbf3511aa4789ac36fbb8d7f32ff28f78a81816dadcbb22
BLAKE2b-256 checksum
How to use checksums
ca11267894ee59f630259fc4344fbdf05c8776a691dfc1e72cff6cd12ccec510
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.15 {"installer":{"name":"uv","version":"0.12.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.

Transparency log

Release files / content_plugin_finder-0.1.0-py3-none-any.whl

Download URL content_plugin_finder-0.1.0-py3-none-any.whl
Size 285.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
091720b3363431ba6b22a2465fc401a82b7676ae1156f7d6d9831320e53d0bf4
BLAKE2b-256 checksum
How to use checksums
ae8f28d83b9009bb10fc8171aa6c45ee869d181ec621f11fd047508c2284c004
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.15 {"installer":{"name":"uv","version":"0.12.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page