Add MIT license, full README and CLAUDE.md
- LICENSE: MIT, referenced from pyproject.toml metadata.
- README: explains what the project is, how to install conandeps with uv,
every option, exit codes, a worked example with a conflict/override
walkthrough, how it works internally, and the container dev workflow.
- CLAUDE.md: hard constraints (Conan 1.66 only, Python <3.12, all work in
the container, private remote "knor" never available in tests) and the
design decisions behind conandeps so future sessions do not revisit them.
- Ignore uv build output (dist/).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYNdaGDrpaqDyT8QtrTQbM
2026-08-25 14:56:00 +02:00
|
|
|
|
# CLAUDE.md
|
|
|
|
|
|
|
|
|
|
|
|
Guidance for working on this repository. It records decisions made when the
|
|
|
|
|
|
project was started (August 2026) that are not obvious from the code.
|
|
|
|
|
|
|
|
|
|
|
|
## What this is
|
|
|
|
|
|
|
|
|
|
|
|
Small single-file Python utilities for **Conan 1.x, targeting 1.66**. Not
|
|
|
|
|
|
Conan 2. The first (and so far only) tool is `conandeps.py`; see `README.md`
|
|
|
|
|
|
for what it does and how it is used.
|
|
|
|
|
|
|
2026-08-25 15:35:21 +02:00
|
|
|
|
## Commands (all inside the container)
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
./dev.sh build # first time, and after editing Containerfile
|
|
|
|
|
|
./dev.sh python -m pytest # tests (starts a throw-away conan_server)
|
|
|
|
|
|
./dev.sh ruff check . && ./dev.sh ruff format .
|
|
|
|
|
|
shellcheck dev.sh # after editing the wrapper (runs on host)
|
|
|
|
|
|
./dev.sh uv lock # after changing pyproject.toml
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Users install/upgrade on their own machine with
|
|
|
|
|
|
`uv tool install git+https://kode.naiv.no/olemd/conan-1.6-utilities.git` /
|
|
|
|
|
|
`uv tool upgrade conan-utils`; a pushed commit is immediately installable.
|
|
|
|
|
|
|
Add MIT license, full README and CLAUDE.md
- LICENSE: MIT, referenced from pyproject.toml metadata.
- README: explains what the project is, how to install conandeps with uv,
every option, exit codes, a worked example with a conflict/override
walkthrough, how it works internally, and the container dev workflow.
- CLAUDE.md: hard constraints (Conan 1.66 only, Python <3.12, all work in
the container, private remote "knor" never available in tests) and the
design decisions behind conandeps so future sessions do not revisit them.
- Ignore uv build output (dist/).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYNdaGDrpaqDyT8QtrTQbM
2026-08-25 14:56:00 +02:00
|
|
|
|
## Hard constraints
|
|
|
|
|
|
|
|
|
|
|
|
- **Conan 1.66 only.** Use the `conans.client.conan_api.ConanAPIV1` API and
|
|
|
|
|
|
Conan 1 graph internals (`Node.binary`, `Node.dependencies`,
|
|
|
|
|
|
`conanfile.requires`). Do not introduce Conan 2 APIs or a Conan 2 code path.
|
2026-08-25 15:07:07 +02:00
|
|
|
|
- **Python 3.8+.** Conan 1.66 runs on new Pythons too (it falls back to
|
|
|
|
|
|
`importlib` where `imp` is gone) – verified on 3.14. It does emit
|
|
|
|
|
|
SyntaxWarnings from its own modules there, which `conandeps.py` silences
|
|
|
|
|
|
before importing `conans`; keep `conans` imports deferred so that filter
|
|
|
|
|
|
is in place first. `requires-python` in `pyproject.toml` and the PEP 723
|
|
|
|
|
|
header must stay in sync.
|
Add MIT license, full README and CLAUDE.md
- LICENSE: MIT, referenced from pyproject.toml metadata.
- README: explains what the project is, how to install conandeps with uv,
every option, exit codes, a worked example with a conflict/override
walkthrough, how it works internally, and the container dev workflow.
- CLAUDE.md: hard constraints (Conan 1.66 only, Python <3.12, all work in
the container, private remote "knor" never available in tests) and the
design decisions behind conandeps so future sessions do not revisit them.
- Ignore uv build output (dist/).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYNdaGDrpaqDyT8QtrTQbM
2026-08-25 14:56:00 +02:00
|
|
|
|
- **All development runs inside the container.** Use `./dev.sh <cmd>` for
|
|
|
|
|
|
every python/pytest/ruff/uv invocation. Never run `conan` against the
|
|
|
|
|
|
host's `~/.conan`; never `pip install` on the host. Rebuild the image with
|
|
|
|
|
|
`./dev.sh build` after touching `Containerfile`.
|
2026-08-25 15:39:43 +02:00
|
|
|
|
- **Never depend on the private remote.** The user's real remote only
|
|
|
|
|
|
exists on the machine where the tool is used and must not be named in the
|
|
|
|
|
|
repository – use a placeholder such as `myremote` in docs and help text.
|
|
|
|
|
|
Tests must use the local `conan_server` fixture in
|
|
|
|
|
|
`tests/test_conandeps.py`.
|
Add MIT license, full README and CLAUDE.md
- LICENSE: MIT, referenced from pyproject.toml metadata.
- README: explains what the project is, how to install conandeps with uv,
every option, exit codes, a worked example with a conflict/override
walkthrough, how it works internally, and the container dev workflow.
- CLAUDE.md: hard constraints (Conan 1.66 only, Python <3.12, all work in
the container, private remote "knor" never available in tests) and the
design decisions behind conandeps so future sessions do not revisit them.
- Ignore uv build output (dist/).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYNdaGDrpaqDyT8QtrTQbM
2026-08-25 14:56:00 +02:00
|
|
|
|
|
|
|
|
|
|
## Design decisions (and why)
|
|
|
|
|
|
|
|
|
|
|
|
- **Binary availability is computed via Conan's graph, not `conan search`.**
|
|
|
|
|
|
The graph is built once per build type through `ConanAPIV1.info()` and each
|
|
|
|
|
|
node's `binary` status is read. This honours `package_id()` overrides,
|
|
|
|
|
|
options and `default_package_id_mode` exactly like `conan install`. Do not
|
|
|
|
|
|
replace this with settings-dict matching against `conan search` output.
|
|
|
|
|
|
- **"Missing" means missing for the exact profile.** Not "no binary with
|
|
|
|
|
|
that build_type at all".
|
2026-08-25 15:39:03 +02:00
|
|
|
|
- **Dependency table shows the shortest path, not a level number.**
|
|
|
|
|
|
`_paths()` does a breadth-first walk from the root; `Dep.path` is the list
|
|
|
|
|
|
of package names on the *shortest* path (empty = direct, so a package that
|
2026-08-25 15:42:22 +02:00
|
|
|
|
is both direct and transitive is direct). `Dep.paths` holds one path per
|
|
|
|
|
|
direct parent (shortest path to that parent + parent) – bounded, unlike
|
|
|
|
|
|
"all paths". The `via` column renders them one per line as `A -> B`
|
|
|
|
|
|
(`→`/`<br>` in HTML); `_table()` supports multi-line cells. Rows sort on
|
|
|
|
|
|
(depth, path, host before build, name). The tree section keeps graph order.
|
2026-08-25 15:49:59 +02:00
|
|
|
|
- **Tree tags are typed.** `_tree_rows()` yields `(head, [(kind, text)], tail)`
|
|
|
|
|
|
with kinds path/build-require/override/range/missing; `_tree_lines()` (text,
|
|
|
|
|
|
ANSI via a `fmt_tag` callback) and the HTML renderer (`<span class=…>`)
|
|
|
|
|
|
colour by kind from that one source. Add new tree annotations there, never
|
|
|
|
|
|
by string-replacing rendered lines.
|
2026-08-25 15:14:54 +02:00
|
|
|
|
- **Only the remote counts, and the source is always shown.** Conan says
|
|
|
|
|
|
`Cache` for a locally cached binary without asking the remote, so
|
|
|
|
|
|
`_RemoteIndex` verifies cache hits with `search_packages` on the remote.
|
|
|
|
|
|
Cache-only binaries are reported as `not available` and count as a
|
|
|
|
|
|
problem (exit 1). Every available cell names the remote. The user rejected
|
|
|
|
|
|
an opt-in flag for this – it is the tool's core question.
|
Add MIT license, full README and CLAUDE.md
- LICENSE: MIT, referenced from pyproject.toml metadata.
- README: explains what the project is, how to install conandeps with uv,
every option, exit codes, a worked example with a conflict/override
walkthrough, how it works internally, and the container dev workflow.
- CLAUDE.md: hard constraints (Conan 1.66 only, Python <3.12, all work in
the container, private remote "knor" never available in tests) and the
design decisions behind conandeps so future sessions do not revisit them.
- Ignore uv build output (dist/).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYNdaGDrpaqDyT8QtrTQbM
2026-08-25 14:56:00 +02:00
|
|
|
|
- **Overrides are parsed from Conan's WARN output.** Conan 1 does not record
|
|
|
|
|
|
overrides on the graph; `Requirements.update()` mutates `req.ref` in place
|
|
|
|
|
|
and only emits `"<pkg>: requirement <old> overridden by <who> to <new>"`.
|
|
|
|
|
|
`conandeps` captures the output stream with a non-coloured `ConanOutput`
|
|
|
|
|
|
and matches `_OVERRIDE_RE`. `test_overrides` pins this format – if a Conan
|
|
|
|
|
|
patch release changes the wording, that test is the alarm.
|
|
|
|
|
|
- **Both explicit and implicit overrides are reported.** `override=True`
|
|
|
|
|
|
requirements are collected from `conanfile.requires` to label an override
|
|
|
|
|
|
as explicit; everything else parsed from the WARN lines is implicit.
|
|
|
|
|
|
- **`range_ref` is not a version range after an override.** Conan reuses
|
|
|
|
|
|
`Requirement.range_ref` to hold the pre-override reference, so only treat
|
|
|
|
|
|
it as a range when `req.version_range` is truthy.
|
|
|
|
|
|
- **Single file, stdlib + Conan only.** No third-party deps beyond Conan.
|
|
|
|
|
|
Packaging is hatchling with `only-include = ["conandeps.py"]`; the PEP 723
|
|
|
|
|
|
header makes the file usable via `uv run conandeps.py` without a checkout.
|
|
|
|
|
|
- **Exit codes:** `0` fine, `1` missing binaries, `2` Conan error. CI relies
|
|
|
|
|
|
on this.
|
2026-08-25 15:19:22 +02:00
|
|
|
|
- **Tables and colour, consistently.** Dependencies, missing binaries and
|
|
|
|
|
|
overrides are all rendered through the same `_table()` helper (text) and
|
|
|
|
|
|
`_html_table()` (HTML). Colour is an aid only – every state is also spelled
|
|
|
|
|
|
out in words – and `_table()` aligns on *visible* width so ANSI codes never
|
|
|
|
|
|
break columns. `--color auto` is the default (terminal + no `NO_COLOR`).
|
2026-08-25 15:07:07 +02:00
|
|
|
|
- **stdout is the report, stderr is progress.** Graph resolution against a
|
|
|
|
|
|
remote takes a while; progress lines (`_progress`) and `--verbose` live
|
|
|
|
|
|
Conan output go to stderr only, so stdout can be redirected to a file.
|
Add MIT license, full README and CLAUDE.md
- LICENSE: MIT, referenced from pyproject.toml metadata.
- README: explains what the project is, how to install conandeps with uv,
every option, exit codes, a worked example with a conflict/override
walkthrough, how it works internally, and the container dev workflow.
- CLAUDE.md: hard constraints (Conan 1.66 only, Python <3.12, all work in
the container, private remote "knor" never available in tests) and the
design decisions behind conandeps so future sessions do not revisit them.
- Ignore uv build output (dist/).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYNdaGDrpaqDyT8QtrTQbM
2026-08-25 14:56:00 +02:00
|
|
|
|
|
|
|
|
|
|
## Workflow
|
|
|
|
|
|
|
|
|
|
|
|
- Lint/format: `./dev.sh ruff check .` and `./dev.sh ruff format .`
|
|
|
|
|
|
(config in `pyproject.toml`, target py38, line length 100). Run
|
|
|
|
|
|
`shellcheck dev.sh` after editing the wrapper.
|
|
|
|
|
|
- Tests: `./dev.sh python -m pytest`. The fixture spins up `conan_server`
|
|
|
|
|
|
on a free port with a pre-written `server.conf` (the port cannot be given
|
|
|
|
|
|
on the command line), creates fake packages whose `build()` does nothing,
|
|
|
|
|
|
uploads them, then wipes the cache so binaries can only come from the
|
|
|
|
|
|
remote. Fake recipes need `--build=missing` at create time because
|
|
|
|
|
|
dependencies' binaries are deliberately incomplete.
|
2026-08-25 15:35:21 +02:00
|
|
|
|
- **Assert on table output with regexes, not fixed-width strings.** Column
|
|
|
|
|
|
widths depend on the longest ref in the fixture and package_ids change
|
|
|
|
|
|
whenever a fixture recipe's requirements change (`semver_direct_mode`).
|
2026-08-25 16:23:24 +02:00
|
|
|
|
Use `re.search(r"libbar/1\.1\s+libfoo\s+host", text)` and `[0-9a-f]{40}`
|
2026-08-25 15:35:21 +02:00
|
|
|
|
for ids. Also: `_conan()` in the tests raises with Conan's full output on
|
|
|
|
|
|
failure – read it before guessing.
|
2026-08-25 16:23:24 +02:00
|
|
|
|
- **Use the `conan_env` fixture only for graph/remote behaviour.** Anything
|
|
|
|
|
|
about rendering (`_via`, `_table`, `_tree_rows`, HTML) should be a
|
|
|
|
|
|
fixture-free unit test on hand-built `Dep` objects, like
|
|
|
|
|
|
`test_via_lists_every_parent_path` – instant, and no `conan_server`.
|
Add MIT license, full README and CLAUDE.md
- LICENSE: MIT, referenced from pyproject.toml metadata.
- README: explains what the project is, how to install conandeps with uv,
every option, exit codes, a worked example with a conflict/override
walkthrough, how it works internally, and the container dev workflow.
- CLAUDE.md: hard constraints (Conan 1.66 only, Python <3.12, all work in
the container, private remote "knor" never available in tests) and the
design decisions behind conandeps so future sessions do not revisit them.
- Ignore uv build output (dist/).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYNdaGDrpaqDyT8QtrTQbM
2026-08-25 14:56:00 +02:00
|
|
|
|
- Dependencies: `uv.lock` is committed; regenerate with `./dev.sh uv lock`
|
|
|
|
|
|
after changing `pyproject.toml`.
|
|
|
|
|
|
- Commits: atomic, no `--amend`; run ruff + pytest + shellcheck before
|
|
|
|
|
|
committing. Add a memory of anything a future session could not derive
|
|
|
|
|
|
from the repository.
|
2026-08-25 16:23:24 +02:00
|
|
|
|
- Releases: bump `version` in `pyproject.toml` (must match the tag), commit,
|
|
|
|
|
|
then `git tag -a vX.Y.Z && git push origin vX.Y.Z`, build with
|
|
|
|
|
|
`./dev.sh uv build` (writes `dist/`, gitignored), write `SHA256SUMS`, and
|
|
|
|
|
|
`fj release create --tag vX.Y.Z --attach dist/<wheel> --attach dist/<sdist>
|
|
|
|
|
|
--attach dist/SHA256SUMS --body "..." "vX.Y.Z"` from inside the repo –
|
|
|
|
|
|
`fj release` does **not** accept `--repo`. Then update the "Latest:" line
|
|
|
|
|
|
in the README's Releases section and delete `dist/`.
|
Add MIT license, full README and CLAUDE.md
- LICENSE: MIT, referenced from pyproject.toml metadata.
- README: explains what the project is, how to install conandeps with uv,
every option, exit codes, a worked example with a conflict/override
walkthrough, how it works internally, and the container dev workflow.
- CLAUDE.md: hard constraints (Conan 1.66 only, Python <3.12, all work in
the container, private remote "knor" never available in tests) and the
design decisions behind conandeps so future sessions do not revisit them.
- Ignore uv build output (dist/).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYNdaGDrpaqDyT8QtrTQbM
2026-08-25 14:56:00 +02:00
|
|
|
|
|
|
|
|
|
|
## Adding another tool
|
|
|
|
|
|
|
|
|
|
|
|
Follow the `conandeps.py` pattern: one executable file with a module
|
|
|
|
|
|
docstring explaining *how* and *why*, a PEP 723 header, a `main(argv)` that
|
|
|
|
|
|
returns an exit code, a `[project.scripts]` entry, an `only-include` entry in
|
|
|
|
|
|
`pyproject.toml`, tests under `tests/` using the shared `conan_env` fixture,
|
|
|
|
|
|
and a section in `README.md`.
|