pip-audit queries PyPI's advisory API and exits non-zero when it cannot
reach it, exactly as `npm audit` does — the same class of outage that
reddened #409 would have failed the Python half too.
Both now go through .github/scripts/audit-with-retry.sh, which retries
only when the output names a transport or availability failure and lets
a real finding fail on the first attempt, unretried. The npm loop added
in the previous commit is folded into it.
ha-relevant: no
`npm audit` exits 1 both for a real advisory and for a registry that
refuses to answer, so a 503 from the audit endpoint reddened PRs that
changed no dependency at all — as it did on #409.
Retry up to three times, but only when the output names a transport or
availability failure. An advisory at or above high severity still fails
on the first attempt, unretried.
ha-relevant: no
The runner hands container steps `sh -e {0}` unless told otherwise, so
every step died on `set: Illegal option -o pipefail` before the installer
ran. Pin the job's shell to bash; `[[ … ]]` in the assertions needs it too.
ha-relevant: no
ShellCheck reads the installer but never runs it, so it cannot catch a
`set -euo pipefail` abort — the failure class that broke this script on a
fresh host. This job executes it for real in debian:12, which carries no
Node and gives the job no TTY, so both the nodesource path and the prompt
fallbacks are exercised on every run.
It asserts what the script promises: SECRET_KEY generated, the bcrypt hash
and the JSON values single-quoted (the systemd EnvironmentFile trap),
.env at mode 600, SQLITE_PATH under the install dir, the venv and the Vite
build present, the service user created, the unit's ExecStart and
EnvironmentFile correct. It then boots the backend by the unit's own
ExecStart and waits on /api/v1/health, which proves the generated .env
actually parses, and re-runs the installer to check the idempotency claim
leaves .env untouched.
systemd is out of reach in a container: systemctl is stubbed, so the unit
is written but never started, and the EnvironmentFile parse itself stays
untested. The job is gated on paths, since it costs roughly six minutes.
Also adds gnupg to the installer's apt list — the nodesource setup script
needs it and a minimal Debian does not have it.
Verified by running the same steps locally in debian:12: all assertions
pass, the backend answers /api/v1/health, and the second run keeps .env.
ha-relevant: no
The push that carried the whole branch through TruffleHog failed on a
"verified" Lob credential: `test_updates_an_existing_device_in_place`, a test
function in backend/tests/test_racks.py. Lob's detector matches any
`test_`-prefixed identifier of that length and reports it verified, because
Lob test-mode keys authenticate unconditionally — so verification proves
nothing here.
Lob is a direct-mail API this project does not use, and any pytest function
named that long trips it again, so the detector is excluded rather than the
test renamed.
ha-relevant: no
The node type fix left the same stale-enum bug in place for edges:
create_edge only advertised ethernet/wifi/iot/vlan/virtual, while the
frontend EdgeType union also defines cluster, fibre and electrical.
The backend's EdgeBase.type is a plain str, so those three were always
accepted server-side — only the MCP schema rejected them.
- Extract EDGE_TYPES in mcp/app/tools.py and use it for create_edge.
- Add mcp/tests/test_edge_types_sync.py, mirroring the NodeType guard:
parses EdgeType out of frontend/src/types/index.ts and asserts set
equality with EDGE_TYPES.
- Add create_edge cases for the three previously-rejected types plus a
schema test pinning the enum contents and the ethernet default.
- Add an `mcp` job to quality.yml. Both sync guards were unreachable
from CI before: quality.yml only ran frontend and backend, so the
enums could drift again with nothing to catch it. The job runs from a
full checkout because the guards read the frontend sources, which are
not in the mcp Docker build context.
- Pin mcp[cli]>=1.26,<2. The bound is load-bearing: SDK 2.0 dropped the
low-level Server.list_resources/list_tools decorator API that
app/resources.py and app/tools.py are built on, so an unbounded spec
breaks both the tests and the Docker image at import time.
- Move pytest/pytest-anyio/trio into mcp/requirements.txt, matching how
backend/requirements.txt carries its own test deps. trio is required
because pytest-anyio parametrises every async test over both backends.
Tested: 114 passed in mcp/, both in the existing venv and in a clean
Python 3.13 venv installed the way the new CI job does.
ha-relevant: no
The dependency-audit job failed on vulnerabilities from `shadcn` and its
transitive tree (hono, fast-uri, body-parser, @modelcontextprotocol/sdk).
`shadcn` is a build-time scaffolding CLI plus a CSS `@import`, never shipped
in the browser bundle, so it belongs in devDependencies.
- move `shadcn` to devDependencies (regenerated lockfile: dev flags only,
no version changes)
- run `npm audit` with `--omit=dev`, matching the runtime-only intent of the
pip-audit step
npm audit --omit=dev --audit-level=high now reports 0 vulnerabilities; the
production build still resolves the shadcn CSS import.
ha-relevant: no
Add mcp matrix entry in docker-publish workflow so the MCP server
image is built and pushed alongside backend/frontend. Also wire the
prebuilt compose file so users can pull and run it directly.
Closes#162
Tier 1 — quality.yml: ShellCheck on lxc-install.sh, hadolint on both Dockerfiles
Tier 2 — docker-ci.yml: build images, smoke-test backend health + frontend 200
Tier 3 — test_integration.py: full stack pytest (auth, canvas save/reload, dimensions)
Also adds Docker healthcheck to backend service in docker-compose.yml
- Build and push backend/frontend images to ghcr.io on every push to main and on version tags
- Add docker-compose.prebuilt.yml for pull-based install (no clone needed)
- Update README Quick Start with inline install one-liner using prebuilt images