This file provides guidance to AI coding agents when working with code in this repository.
dfetch is a Python vendoring tool that fetches external dependencies (from Git, SVN, or archive files) and copies them directly into a project as plain files — no submodules, no externals. Dependencies are declared in a YAML manifest file.
# Unit tests (pytest)
pytest tests/
# Single unit test
pytest tests/test_manifest.py::test_name
# BDD feature tests (behave)
behave features/
# Single feature file
behave features/git-fetch.feature
# Feature tests for a specific command (e.g. update, check, diff, report)
behave features/ --tags=update
# Full test run with coverage (as CI does it)
pytest --cov=dfetch tests/
coverage run --source=dfetch --append -m behave features
coverage xml -o coverage.xmlblack --check dfetch # formatting
isort --diff dfetch # import ordering
pylint dfetch # PEP8 / style
ruff check dfetch # fast linter
mypy dfetch # type checking
pyright . # additional type checking
pydocstyle dfetch # docstring style (Google convention)
bandit -r dfetch # security checks
lint-imports # architecture layer enforcement (see below)pip install -e .[development,test]The architecture follows a strict layered C4 model enforced by lint-imports. Dependencies between layers are unidirectional — lower layers cannot import from higher ones. Violating this will fail CI.
dfetch.commands ← top layer (CLI commands, user-facing)
dfetch.reporting ← report generation
dfetch.project ← project/dependency abstraction
dfetch.manifest ← YAML manifest parsing (independent)
dfetch.vcs ← VCS backends: Git, SVN, Archive (independent)
dfetch.util ← shared utilities (independent)
dfetch.log ← logging (lowest layer)
dfetch/__main__.py— CLI entry point; builds argparse subcommands and dispatchesdfetch/commands/— One file per CLI command (e.g.,update.py,check.py); all inherit fromcommand.py's abstractCommandbasedfetch/manifest/— YAML manifest loading/writing withstrictyamlschema validation;manifest.pyis the main handlerdfetch/project/— AbstractSubproject/Superprojectclasses with concrete Git, SVN, and Archive implementations; factory functionscreate_sub_project()andcreate_super_project()are the main entry pointsdfetch/vcs/— Low-level VCS operations:git.py,svn.py,archive.py(with hash verification inintegrity_hash.py), andpatch.pydfetch/reporting/— Output formatters; check results can be emitted as stdout, Jenkins JSON, SARIF, or Code Climate format; SBOM output uses CycloneDX formatdfetch/terminal/— Terminal UI components (interactive prompts, tree browser, ANSI colors)
- Create
dfetch/commands/<name>.pyimplementing theCommandABC - Register it in
dfetch/__main__.py - Respect layer boundaries: commands may import from
reporting,project,manifest,vcs,util,log— but not vice versa
Implement the abstract interfaces in dfetch/project/subproject.py and dfetch/vcs/ and register via the factory in dfetch/project/.
- Unit tests live in
tests/and usepytest. Test files mirror module names (e.g.,tests/test_manifest.py). - BDD feature tests live in
features/and usebehave. Step definitions are infeatures/steps/. Feature files describe end-to-end workflows. - Feature files are tagged with the command they exercise (e.g.
@update,@check). If your change affects a command, run its feature tests using the command tag:behave features/ --tags=<command>. - Docstrings in test functions follow Google style as a convention (CI runs
pydocstyle dfetchand does not checktests/, so this is not enforced automatically).
- Cyclomatic complexity must stay below 8 per function. If a function grows beyond this, refactor it — extract helpers, simplify conditionals, or split responsibilities.
- No lint suppressions without fixing the root cause. Avoid
# noqa,# type: ignore,# pylint: disable,# pyright: ignore, and similar inline suppressions. If a tool flags something, fix it properly rather than silencing it. The one accepted exception is module-level tool headers at the top of test files (e.g.# mypy: ignore-errorsor# flake8: noqaon line 1–5 of a test module); these are permitted where the test file structure genuinely prevents a clean fix.
Every change must be reflected in the documentation. Depending on the nature of the change:
- User-visible behaviour (new feature, changed CLI, new option) → update the relevant
doc/how-to/ordoc/reference/page - Notable fix or change → add an entry to the changelog (
doc/changelog/) - Architecture change → update
doc/explanation/architecture.rst
Documentation lives in doc/ and is built with Sphinx.
Write documentation that is helpful, supportive, and positive. Assume the reader is capable — guide them forward rather than warning them away. Prefer "you can" over "you must", favour concrete examples over abstract rules, and end explanations on what the user can now do rather than what might go wrong.