--- id: RAIL-BS-WP-0001 type: workplan title: "Dependency Management — Add lockfile for Ansible control-node deps" domain: railiance repo: railiance-cluster status: completed owner: railiance topic_slug: railiance state_hub_workstream_id: 59155efb-b461-4caa-ad7b-b3fce348db84 state_hub_task_id: 5f8cade5-119c-42e8-ba93-e9d0478650e4 created: "2026-03-01" updated: "2026-03-01" completed: "2026-03-01" --- # Dependency Management — Add Ansible control-node lockfile ## Problem This repo drives all Ansible automation but carries no pinned, machine-readable inventory of its own runtime dependencies. The Ansible version (and all pip packages it depends on) are whatever is installed on the control node at any given time. This means: - Behaviour is not reproducible across machines or over time - The Custodian State Hub SBOM scanner finds nothing to ingest (`last_sbom_at = null`) - Licence and vulnerability auditing of the actual dependencies in use is impossible - The `railiance-bootstrap` repo appears as a gap in the SBOM coverage map ## Root cause No `pyproject.toml` (or `requirements.txt`) declares the control-node pip dependencies. No `ansible/requirements.yml` exists for Galaxy collections (correct if none are used; but it should be explicit). ## Expected state after this task - `pyproject.toml` at repo root declares `ansible` as a dependency (and any other pip packages used by playbooks or the `bin/` commands) - `uv.lock` is generated and committed — pins Ansible + full transitive pip tree - If Galaxy collections are used: `ansible/requirements.yml` lists them - SBOM is ingested: `last_sbom_at` is not null in the State Hub - The SBOM dashboard shows `railiance-bootstrap` in the railiance domain row with a package count ## Tasks ### T1 — Audit control-node pip dependencies ```task id: RAIL-BS-WP-0001-T01 state_hub_task_id: 5f8cade5-119c-42e8-ba93-e9d0478650e4 status: done priority: medium completed: "2026-03-01" ``` Review `bin/` commands, Ansible playbooks, and any Python scripts in the repo. List all pip packages that must be present on the control node: - `ansible` (minimum version) - Any collections-related tools (ansible-core, ansible-lint, etc.) - Any other pip deps called from scripts (e.g. `paramiko`, `netaddr`, `jinja2`) ### T2 — Create pyproject.toml and generate uv.lock ```task id: RAIL-BS-WP-0001-T02 status: done priority: medium completed: "2026-03-01" state_hub_task_id: "8aa8a9d3-6560-4176-b933-72a21e6d43d4" ``` 1. Create `pyproject.toml`: ```toml [project] name = "railiance-bootstrap" version = "0.1.0" requires-python = ">=3.11" dependencies = [ "ansible>=10", # adjust version as appropriate # add other deps found in T1 ] ``` 2. Run `uv lock` to generate `uv.lock` 3. Commit both files ### T3 — Ingest SBOM into State Hub ```task id: RAIL-BS-WP-0001-T03 status: done priority: medium completed: "2026-03-01" state_hub_task_id: "4fb477e9-dbac-4e43-84d0-5202c68f4705" ``` From `~/the-custodian/state-hub/`: ```bash make ingest-sbom REPO=railiance-bootstrap SCAN=1 REPO_PATH=/home/worsch/railiance-bootstrap ``` Verify in the SBOM dashboard: railiance domain should show `railiance-bootstrap` with a package count and no gap warning. ### T4 — Create ansible/requirements.yml (even if empty) ```task id: RAIL-BS-WP-0001-T04 status: done priority: low completed: "2026-03-01" state_hub_task_id: "d0eb1c96-e7c2-4f6b-b934-a3f295e4db72" ``` Create `ansible/requirements.yml`. If no Galaxy roles or collections are used, create it empty with a comment. This makes the absence of collections explicit: ```yaml --- # No external Ansible Galaxy roles or collections required. # Add roles/collections here as needed: # roles: [] # collections: [] ``` ## References - Custodian SBOM Convention: `canon/standards/sbom-convention_v0.1.md` - SBOM dashboard: http://127.0.0.1:3000/sbom - Repos coverage page: http://127.0.0.1:3000/repos - State Hub task: `5f8cade5-119c-42e8-ba93-e9d0478650e4`