Plugin install docs: the image-on-top example has no lock constraint, and one installs an unverified PyPI name #542
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
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#542
Loading…
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 on 2026-09-29 in a read-only review of postulo at
95348908dand the wiki at de1f52467, comparing the plugin install instructions withdocker/Dockerfile.Related to #61.
Checked again on 2026-10-01 against
mainat0a5cad587: still present.Both pages that explain baking a plugin into the image give two routes — the
POSTULO_EXTRA_PACKAGESbuild argument and a DockerfileFROMPostulo's published image — and then say that either way the plugin is installed with Postulo's lock as a constraint. Only the build argument does that. TheFROMexample runs a bareuv pip installas root in the runtime image, which carries no lock, so a plugin's requirements are free to move Django, allauth, httpx or anything else Postulo pins. Writing a plugin also installs the bare namepostulo-apprise, which resolves on PyPI, where Postulo's plugins are not published.postulo.wiki/Installing-Postulo.md:137-148;postulo.wiki/Writing-a-plugin.md:1528-1537;docker/Dockerfile:119-128(the constraint, build stage only) and:264-269in the runtime stage ("No lock, no uv cache": only.venv,srcandmanage.pyare copied).FROM source.tiagoagueda.com/postulo/postulo:0.2example.uv pip install --no-cache <plugin>resolves the plugin's requirements with nothing holding the environment's pins, so a loosedjango>=…orhttpx>=…in a plugin upgrades a core dependency to a version Postulo was never tested with. The page promised the opposite.postulo-apprise. The official plugin lives atgit+https://source.tiagoagueda.com/postulo/postulo-apprise.git(its own README, and Installing Postulo on the same step); Writing a plugin line 382 says Postulo itself is not on PyPI. Whoever registers that name there decides what runs, as root at build time and inside Postulo's process afterwards.git(docker/Dockerfile:244-260, the runtimeapt-get installlist), and uv needs thegitexecutable for agit+httpsrequirement, so as written that example is likely to fail at build.postulo-apprise(or any other official plugin name) on PyPI, against an operator who follows Writing a plugin; and any plugin with loose requirements, against an operator who follows eitherFROMexample.COPY --from=build /tmp/constraints.txt /app/constraints.txt, exporting it unconditionally), or generate one at install time from the running environment asplugins/installing.py'sconstraints()already does for uploads. Showuv pip install --no-cache --constraint /app/constraints.txt git+https://…in bothFROMexamples, withapt-get install gitbefore it (or a wheel URL instead ofgit+https). Replace the barepostulo-apprisewith the git URL. If none of that is done, drop the constraint sentence from theFROMroute.Done when
tests/test_image_build.pyfails while the runtime stage ships no constraints file, and passes once/app/constraints.txt(or its equivalent) is in the image.