Catalogue tool: every plugin's catalogues are stamped "same license as Postulo (AGPL-3.0-or-later)", whatever the plugin's licence #417
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#417
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 postulo-helloworld at 19f0bcc29, reading the catalogue headers of the reference plugin and then running the tool's own check against a copy of it.Checked again on 2026-10-01 against
mainat0a5cad587: still present.postulo-messages extractwrites the same second line into every catalogue it produces, in whichever project it runs: "This file is distributed under the same license as Postulo (AGPL-3.0-or-later)." The line is true for the core and, by coincidence, for the four AGPL plugins. It is false forpostulo-helloworld, which is MIT precisely so that it can be copied, and for any third-party plugin whose author chose another licence. The author cannot correct it: the tool's check treats a corrected line as a stale catalogue, and the next extract writes the AGPL line back.core/messages_tool.py:416, indump(): the licence line is a string literal. The line above it already takes the project's name fromproject().describe(subject)(core/messages_tool.py:71), so the header names the plugin and then Postulo's licence.core/messages_tool.py:95, inuse():pyproject.tomlis read forproject.nameonly;project.licenseis not looked at.core/messages_tool.py:617and:634, incmd_extract()and_without_dates(): the check compares the whole file apart from the two date headers, so the comment block is part of what must match.core/messages_tool.py:545-546and:556, inheader_for(): the same kind of literal. Every plugin's catalogue carriesProject-Id-Version: Postulo,Report-Msgid-Bugs-To: https://source.tiagoagueda.com/postulo/postulo/issuesandX-Generator: postulo scripts/messages.py, although a plugin's issues belong in its own repository and no plugin has ascripts/messages.py.postulo-helloworld:pyproject.toml:7(license = "MIT"),LICENSE,README.md:90-93("MIT, so that copying it into your own plugin is a decision you never have to think about"), and line 2 of all 68 files matchingsrc/postulo_helloworld/locale/*/LC_MESSAGES/django.po.README.md:49is the step that tells an author to run the extract.postulo-helloworld, keeps MIT or picks another licence, and runsuv run postulo-messages extractas the README says. All 68 catalogues state AGPL-3.0-or-later.tool.cmd_extract(check=True)now returns 1 and lists that file as out of date, sotest_the_catalogues_are_currentin the copiedtests/test_catalogues.py:43-44fails. Confirmed on a copy of the snapshot with only line 2 offr_FRchanged.LICENSEand the metadata say one thing, 68 shipped files say another. Anyone reusing the translations, or a packager checking licences per file, has to guess which statement holds, and the plugin's author has no way to ship a correct one short of dropping the check. The reference plugin exists to be copied, so the mistake is copied with it. The bug-report address has the same cost on a smaller scale: a translator who finds a wrong source string in a plugin is sent to the core's tracker.Projectalicenceread inuse()fromproject.license(the SPDX string, or thetextof the older table form), and havedump()write "the same license as the<name>package", followed by the licence in brackets when the project declares one. Keeping today's wording whenproject().is_postuloavoids rewriting the core's own catalogues.header_for(), takeProject-Id-Versionfrom the project's name andReport-Msgid-Bugs-Tofrom the project's own URLs inpyproject.toml(falling back to today's value for the core), and name the generatorpostulo-messages.postulo-helloworld, so its catalogues stop naming AGPL. The other plugin repositories (postulo-apprise,postulo-dav,postulo-imap,postulo-paperless) will fail their owntest_the_catalogues_are_currenton the next repin until they are re-extracted as well; that is the header changing, not a regression.Done when
tests/test_messages_tool.pypointsuse()at a temporary project whosepyproject.tomlsayslicense = "MIT"and asserts that the comment block written bydump()names that project and MIT and does not contain "AGPL"; it fails today.header_for()no longer reports bugs to the core's tracker when the project declares its own URL.postulo-helloworld,tests/test_catalogues.pyasserts that no catalogue undersrc/postulo_helloworld/locale/names a licence other than the one inpyproject.toml, and the 68 catalogues are re-extracted so that it passes.