CI and operations tooling: 5,800 tests run serially three times, unpinned tools, releases not gated on CI, no config checks or request ids #233
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Postulo/postulo#233
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found in the 2026-09-15 code audit.
CI speed
pytest-xdist(pyproject.toml:56-64), three times, each with--cov(.forgejo/workflows/ci.yml:42,89).uv sync --lockedalso installs thee2egroup (Playwright, about 136 MB), becausedefault-groupsincludes it.tests/security/test_secret_key.pyspawns a fresh interpreter per case, at 0.7–2.2 s each. The plugins page tests take 3–9 s each (see the query-performance issue (#231)).Proposal:
pytest-xdist -n auto, after auditing module-level caches such asregistry._cache.fail_under(none exists today).uv sync --no-group e2ein the test job, plus uv and Playwright caches.Supply chain
https://astral.sh/uv/install.sh, always the latest version, in four places (ci.yml:62,124,227,release.yml:43).uvx zizmoranduv run --with pip-audittake whatever is newest.ghcr.io/astral-sh/uv:0.12, a floating tag.actions/checkout@v4andupload-artifact@v3/v4are mutable tags.uv-pre-commit 0.12.5.Proposal:
zizmor.yml.Dockerfile:151-162argues.Releases
release.ymlpublishes the sdist and wheel on anyv*tag without checking the commit's CI, andimage.ymlpushes:X.Y,:X.Y.Zand:latestfor any tag typed into the dispatch box.scripts/release_tools.py checkvalidates only the version and the changelog.Proposal: have
release_tools.py checkquery the Forgejo commit-status API for the tagged SHA (success ontest*andbrowser), and call it fromimage.ymltoo.Dependency updates
CONTRIBUTING.mdsays "once a month…uv lock --upgrade", and nothing automates it; the weeklypip-auditreports vulnerabilities, not staleness.Proposal: a self-hosted Renovate run (it supports Forgejo and
uv.lock) grouped monthly, or a scheduled workflow that upgrades the lock and opens a PR when tests pass.Configuration checks and logs
POSTULO_EMAIL_SECURITY,POSTULO_EMAIL_AUTH,POSTULO_PDF_BACKENDand thePOSTULO_*_RATEstrings are read raw, socheck --deployin the entrypoint cannot catch a typo; it surfaces at the first send, render or throttle.simpleformat, and JSON goes only to the rotating file. No request id exists, so a gunicorn access line cannot be matched to an application log line or a scheduler run.Proposal:
postulo.core.checksmodule checking enum membership, rate syntax and thePOSTULO_PUBLIC_URLscheme atErrorlevel.POSTULO_LOG_FORMAT=json|simplefor the console.X-Request-ID, adds it to log records and echoes it in the response.From the CodeQL run of 2026-09-16 (#248, #249), one thing for this issue rather than a
separate one: nothing in CI runs CodeQL.
The first run over
mainat23c742c7fproduced 131 results and no true-positive securityfinding, so the value is not in what it caught — it is that the guards it could not see
(
http.public_only_clientfor #215,%rin the plugin logger, the username pattern) havenothing watching them for regressions. A future commit that routes a fetch around
public_only_client, or swaps a%rfor a%s, would light up a rule that is currentlygreen and unobserved.
Cost, measured on this machine: database build about 4 minutes for 354 files, the
python-security-and-qualitysuite a few minutes more on--threads=0. That is too slowfor every push and about right for a nightly or a pre-release job.
If it is wired up, the triage in #249 has to go with it or the job is 131 results of noise
on day one — the
...-in-Protocoland deferred-import rules alone are 97 of them. Thesuite takes a filter file, so
py/ineffectual-statementandpy/cyclic-importcan beexcluded there while #248 is open.
A template formatter belongs in this list
The tooling audit above covers Python, CI and the supply chain. The templates have none of
it: 179 files and 11,375 lines, checked for correctness by
tests/test_template_lint.py(multiline
{# #}, physical sides) and formatted by hand.djade— 1.9.0, written in Rust, formats Django template syntax to a style derivedfrom Django's own contribution guidelines. It reads the Django version from
pyproject.toml, runs as a pre-commit hook beside the ones already configured, and is fastenough not to be noticed (377 templates in about 20 ms). It also carries fixers for
deprecated template tags and filters, which is the part that earns its place at the next
Django bump rather than this one.
Worth pairing with
django-upgrade, the same author's codemod for Django idioms inPython. Low value while pinned to
django>=6.1,<6.2; genuinely useful at the version afterthat, so it may be better added when the pin moves rather than now.
Both are development-only and neither touches what ships. Filed here rather than as their
own issue because this is where the tooling discussion already lives.
One note if
djadeis adopted: it reformats, so the first run will touch many templates atonce, and a reformat moves the
#:source references in the catalogues without changing asingle string. Run it as its own commit, before any template work, and confirm with
git diff -U0 -- src/postulo/locale | grep '^[-+]msg'that nothing moved.