Plugin CI: one shared definition for the seven plugin workflows, with uv's cache and no second run on a tag #1170
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#1170
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?
This issue holds 3 findings of the audit that share one fix or the same files.
PC7: Plugin CI: seven copies of one workflow drift apart, and a uv or core bump is fifteen hand edits across eight repositories
What exists today
Read from the server's
mainof each repository (fetched 2026-10-30; the local checkouts are two to three weeks behind and gave the scout wrong details). The seven Python pluginci.ymlfiles (101-137 lines each) are one template copied by hand, and they have drifted:--no-project.tags: ["v*"]is in six of seven (PC6); templates installs an extra library (libharfbuzz-subset0); apprise has anevery-python.ymlthe others lack.release.ymlfiles are the same template again, with differently worded steps.Corrections to the candidate: the core pin is no longer five SHAs; all seven pin
a8467ab21(2026-10-23, 39 commits behind coremain). The text of #1090 still lists the old five pins and should be refreshed before that round starts. The uv pin is the same everywhere (PC2 refuted).The bump procedure:
CONTRIBUTING.mdlines 626-634 say bumping uv takes four edits "in the five workflows" of the core, andtests/test_supply_chain_pins.pyrefuses a half-done one there; the plugin comments say "bump both together, with the core", but nothing lists the plugin files or checks them, so a uv bump is about fifteen more edits (eightci.yml, sixrelease.yml, apprise'severy-python.yml) in eight repositories, each through its own pull request. Moving the core pin (#1090) is likewise sevenpyproject.tomland sevenuv.lockchanges with no written procedure.What would change
postulo/plugin-ci), referenced by full address,uses: https://source.tiagoagueda.com/postulo/plugin-ci@v1, which Forgejo Actions accepts. A bare name is not safe: the core'sci.ymlcomment (lines 8-16) says bare names resolve against "Forgejo's own copy", but postulo-firefox'sci.yml(lines 34-38) says this instance serves actions from data.forgejo.org, so a barepostulo/plugin-cimight be looked up there. Each plugin'sci.ymlshrinks to checkout plus one step, and the uv version and hash live in one place.jobs.test.uses: postulo/plugin-ci/.forgejo/workflows/plugin.yml@v1), if this Forgejo version supports cross-repository reusable workflows; to be tried on one repository first.scripts/plugin_ci.pyin the core that renders the seven files from one template and a check that they match, run when the core bumps uv.CONTRIBUTING.md(or the wiki's Writing a plugin), with the uv and pin bumps listed for every repository.ci.yml(PC6), the uv cache (PC1).What it saves or protects
medium (maintenance and reliability): one place to change the plugin CI instead of seven, and drift caught instead of discovered
Accessibility
none
Decided by the maintainer (2026-10-31)
shared plugin CI as a composite action in its own repo, referenced by full address, tried on helloworld first; one core issue carrying PC1 and PC6
Decisions
PC1: Plugin CI: no plugin keeps uv's cache, so every run fetches the core's git repository again, about 90 s of a 3-minute job
What exists today
Read from each repository's
mainon the server (the local checkouts are two weeks behind; files fetched through the API on 2026-10-30): postulo-imap, -dav, -paperless, -templates, -helloworld, -apprise and tiagoagueda/postulo-francetravail all pin the core in their dev group aspostulo @ git+https://source.tiagoagueda.com/postulo/postulo.git@a8467ab21…(the same commit in all seven) and install withuv sync --locked --python 3.14(ci.ymlline 65 in imap). None usesactions/cache(grep over every plugin workflow); the core does, for~/.cache/uvand the browser (postulo/.forgejo/workflows/ci.ymllines 120-128 and 247-252),continue-on-error: true, with restore keys withheld from pull requests.Measured from the log of postulo-imap run 81 (push to main, 2026-10-25), timestamps in the log:
Updating https://source.tiagoagueda.com/postulo/postulo.git (a8467ab21…)at 13:28:57.9,Updatedat 13:30:25.5: 88 s cloning the core.Prepared 63 packages in 1m 30s,Installed 63 packages in 18.41s, Python 3.14 downloaded (34 MiB) as well.The other repositories' test jobs take 2.5-4 minutes on the same pattern (task list through the API). The release workflows (
release.yml) repeat the same install on a tag. The candidate's "96 MB tree" is the core's checkout; uv clones it into its cache, which is what a cache would keep.Not covered by #1033 (core CI only) or #1090 (pins and tags).
What would change
In each plugin
ci.yml(andrelease.yml), beforeuv sync, the core's own cache step:uv's git cache holds the fetched core, so a run whose lock did not change skips the clone and the downloads. A further step, outside this issue: once the core publishes wheels to the Forgejo package registry with its releases (#1090 moves the pins to tags), the dev group can name the wheel instead of git, and no clone happens even without a cache.
What it saves or protects
large per run: about 90 s of fetch and 20 s of downloads in each of seven repositories' test jobs (postulo-imap run 81: 1 min 30 s of 3 min 12 s preparing packages)
Accessibility
none
Decisions
PC6: Plugin CI: a tag runs the whole CI again beside the release gate, on a commit
mainalready testedWhat exists today
On the server's
main,ci.ymlline 17 haspush: tags: ["v*"]in postulo-apprise, -dav, -francetravail, -imap, -paperless and -templates (postulo-helloworld dropped it). Each of these also hasrelease.yml, triggered by the same tag, which checks out, installs the WeasyPrint libraries, installs uv, runsuv sync --lockedandpytest -m release. A tag push therefore starts two workflows that each clone the core and install everything (about 1 min 50 s of install alone, PC1), and the CI one re-runs lint, the catalogue step and the full suite on the commit thatmain's own push already tested, since every change reachesmainthrough a green pull request (#940).No plugin is tagged on its
mainCI history yet, so the cost has not been paid; #1090 is about to tag every plugin.What would change
Remove
tags: ["v*"]fromon.pushin the sixci.ymlfiles, leavingrelease.ymlas the only tag workflow, as postulo-helloworld already does. If a tag should still prove the suite, adduv run pytestbeforepytest -m releaseinrelease.ymlinstead, so the install happens once.What it saves or protects
small: one full install and test run (about 3 minutes of the shared runner) saved per plugin tag; the round of tags #1090 plans would be the first to pay it
Accessibility
none
Decisions
Found by the efficiency and reliability audit of 2026-10-30 (finding PC7, PC1, PC6; index postulo/postulo#1108). Checked against
mainatdaee30b15(2026-10-30).Go-ahead from the maintainer (2026-11-12)
Make the shared plugin CI: a composite action in its own repository on this Forgejo (
Postulo/plugin-ci), referenced by full address and tagged, tried on postulo-helloworld first, then the other plugin repositories, carrying PC1 (uv's cache) and PC6 (no CI run on a tag).🤖 Generated with Claude Code
The shared plugin CI is in place: the action
Postulo/plugin-ci@v1(repository created and tagged 2026-11-12) runs CI in all seven Python plugins: helloworld first (its first run green: uv checksum, a full cache key, catalogue checks, tests), then imap, dav, paperless, templates, apprise and francetravail. uv is pinned in one place, uv's cache is kept (PC1), no CI runs on a tag (PC6), and the audit is its own job and runs weekly. Status-check names are unchanged.Left for the core: CONTRIBUTING's uv-bump section should name
plugin-ci/action.ymlas the one place the plugins' uv pin lives.🤖 Generated with Claude Code