Plugins: tell the administrator an update is available, and update one or all from the interface with a single button #1187

Open
opened 2026-10-31 21:33:40 +00:00 by tiagoagueda · 3 comments
Owner

The maintainer's request (2026-10-31): an administrator is told when a plugin has an update available, and can fetch and deploy the upgrade of one plugin, or of all of them, from the interface alone, with a single button.

What exists today (checked against main at 8eedc02de)

  • Installing from a catalogue works in the interface. Server → Plugins lists what the configured catalogues offer, with Install or Reinstall per listing (templates/server/plugins.html, core/server_views.py near :1990).
    • Each catalogue's index is signed, and each wheel is checked against the checksum it carries (plugins/catalogue.py).
    • Plugins live on the data volume, with a record of what is installed (plugins/installing.py).
    • A release that needs another plugin offers that one's install first (#884).
  • The groundwork for a safe upgrade is there. installing.take_snapshot() and roll_back() keep the previous plugins directory, and verify_imports() checks a plugin loads after install. installing.compatible(requires_postulo) refuses a release meant for another core.
  • Nothing tells anyone that an update exists.
    • The catalogues are fetched only when an administrator presses Check for updates. The page says "Postulo does not fetch them on its own", which was deliberate (#228).
    • The answer lives in the session only (request.session["plugin_listings"]): nothing is stored, nothing is compared, nobody is told.
  • An update looks like a reinstall. A listing knows only whether the plugin is installed ("installed": installing.installed(name) is not None), not which version. The button says Reinstall whether the catalogue offers the same version or a newer one.
  • There is no "update all", and no notice to administrators anywhere: the Server overview, the inbox and mail all say nothing.
  • A restart is the open technical point. A newly installed plugin "appears as soon as it is installed … without a restart" (the page's own note), because it adds entry points. Replacing a plugin whose modules the worker processes have already imported does not take effect until those processes reload. Today the page says nothing about that.

Proposal

  1. Know the installed version, and compare it. Each listing carries the installed version beside the offered one, compared by PEP 440 version order.

    • A newer compatible release is an update.
    • A newer release that needs another core is shown as "needs Postulo X.Y" and offers no button.
  2. Keep what the catalogues said. The last answer of each catalogue is stored (the serial is already kept in catalogue.remember): when it was checked, and which updates it offers. The Plugins page and the notices read from that store, not from the session.

  3. Check on a schedule, not only by hand.

    • By default a check runs once a day: one signed-index fetch per configured catalogue, with nothing downloaded and nothing installed.
    • It runs as an errand (core/errands.py), on the worker where there is one. On an instance without a scheduler, it runs at most once a day when an administrator opens a Server page.
    • POSTULO_PLUGIN_UPDATE_CHECK=daily|off turns it off (see #1186), and the page says when it last ran.
  4. Tell the administrators.

    • The Server overview and the Plugins page show a notice: "2 plugin updates: postulo-dav 0.5.1, postulo-imap 0.5.2".
    • Every administrator gets one inbox notice per new update, not every day.
    • Optionally, the instance's mail or notifiers carry the same notice.
    • Each update shows its version change, its catalogue, and a link to its changelog or release notes when the listing has one.
  5. One button for one plugin, one for all.

    • Update on a listing.
    • Update all above the list, which updates every plugin with a compatible update, in dependency order.

    Each run:

    1. takes a snapshot (take_snapshot);
    2. downloads and checks each wheel against the signed checksum;
    3. installs it with the core as a constraint, as an install does;
    4. runs verify_imports.

    Any failure restores the snapshot for the whole run (roll_back), and the page names the plugin and the reason. A plugin the administrator switched off stays switched off, and its settings and data are kept.

  6. Deploy without a shell. After an update, the running processes must load the new code.

    • With the bundled gunicorn: a graceful reload (HUP to the arbiter), which finishes the requests in flight. Where the process cannot do that, it says plainly: "Updated. Restart Postulo to finish," with the reason.
    • The page shows which version is loaded against which is installed, until they agree.
    • A plugin with database migrations has them run as part of the update, inside the same rollback.
  7. A record of it. Each update, success or rollback, is written to the instance's log and to the Server activity: who, when, which versions, which catalogue.

  • Checking automatically against #228's "Postulo does not fetch them on its own":
    • (A, recommended) a daily check that only fetches the signed index, on by default and switchable off. #228's rule was about installing, and still holds: nothing is ever installed without an administrator's click.
    • (B) check only when an administrator opens the Plugins page.
    • (C) keep it manual, which defeats the request.
  • Deploying:
    • (A, recommended) a graceful reload of gunicorn from the instance itself, with "restart to finish" as the fallback the page explains.
    • (B) always ask for a restart.
    • (C) exit and let the container's restart policy bring it back. This is not recommended: it drops the requests in flight and depends on a policy Postulo cannot see.
  • "Update all" when one plugin fails:
    • (A, recommended) all or nothing: roll back the whole run.
    • (B) keep the ones that succeeded. This leaves a mix nobody tested together.
  • Major or breaking releases:
    • (A, recommended) offered like any update, but labelled from the plugin's changelog or its requires_postulo floor, and never part of Update all without a separate confirmation.
  • Who is told:
    • (A, recommended) every staff account with server access, in the inbox, plus mail if the instance can send it.
    • (B) only the administrator who installed the plugin.

Done when

  • The Plugins page shows the installed and offered version of each plugin and labels an update as one.
  • A daily check finds updates without anyone pressing anything, and administrators are told once.
  • Update and Update all upgrade from the interface, verify, roll back as a whole on any failure, and leave the new code running, or say exactly why a restart is needed.
  • Tests cover:
    • an update found and announced;
    • an update applied;
    • a failing update rolled back with the previous version still running;
    • an incompatible release offered no button;
    • an off switch that stops the check.
  • The wiki's plugin administration page describes all of it.
The maintainer's request (2026-10-31): an administrator is told when a plugin has an update available, and can fetch and deploy the upgrade of one plugin, or of all of them, from the interface alone, with a single button. ## What exists today (checked against `main` at `8eedc02de`) - **Installing from a catalogue works in the interface.** Server → Plugins lists what the configured catalogues offer, with *Install* or *Reinstall* per listing (`templates/server/plugins.html`, `core/server_views.py` near :1990). - Each catalogue's index is signed, and each wheel is checked against the checksum it carries (`plugins/catalogue.py`). - Plugins live on the data volume, with a record of what is installed (`plugins/installing.py`). - A release that needs another plugin offers that one's install first (#884). - **The groundwork for a safe upgrade is there.** `installing.take_snapshot()` and `roll_back()` keep the previous plugins directory, and `verify_imports()` checks a plugin loads after install. `installing.compatible(requires_postulo)` refuses a release meant for another core. - **Nothing tells anyone that an update exists.** - The catalogues are fetched only when an administrator presses *Check for updates*. The page says "Postulo does not fetch them on its own", which was deliberate (#228). - The answer lives in the session only (`request.session["plugin_listings"]`): nothing is stored, nothing is compared, nobody is told. - **An update looks like a reinstall.** A listing knows only whether the plugin is installed (`"installed": installing.installed(name) is not None`), not which version. The button says *Reinstall* whether the catalogue offers the same version or a newer one. - **There is no "update all"**, and no notice to administrators anywhere: the Server overview, the inbox and mail all say nothing. - **A restart is the open technical point.** A newly installed plugin "appears as soon as it is installed … without a restart" (the page's own note), because it adds entry points. Replacing a plugin whose modules the worker processes have already imported does not take effect until those processes reload. Today the page says nothing about that. ## Proposal 1. **Know the installed version, and compare it.** Each listing carries the installed version beside the offered one, compared by PEP 440 version order. - A newer *compatible* release is an **update**. - A newer release that needs another core is shown as "needs Postulo X.Y" and offers no button. 2. **Keep what the catalogues said.** The last answer of each catalogue is stored (the serial is already kept in `catalogue.remember`): when it was checked, and which updates it offers. The Plugins page and the notices read from that store, not from the session. 3. **Check on a schedule, not only by hand.** - By default a check runs once a day: one signed-index fetch per configured catalogue, with nothing downloaded and nothing installed. - It runs as an errand (`core/errands.py`), on the worker where there is one. On an instance without a scheduler, it runs at most once a day when an administrator opens a Server page. - `POSTULO_PLUGIN_UPDATE_CHECK=daily|off` turns it off (see #1186), and the page says when it last ran. 4. **Tell the administrators.** - The Server overview and the Plugins page show a notice: "2 plugin updates: postulo-dav 0.5.1, postulo-imap 0.5.2". - Every administrator gets one inbox notice per new update, not every day. - Optionally, the instance's mail or notifiers carry the same notice. - Each update shows its version change, its catalogue, and a link to its changelog or release notes when the listing has one. 5. **One button for one plugin, one for all.** - *Update* on a listing. - *Update all* above the list, which updates every plugin with a compatible update, in dependency order. Each run: 1. takes a snapshot (`take_snapshot`); 2. downloads and checks each wheel against the signed checksum; 3. installs it with the core as a constraint, as an install does; 4. runs `verify_imports`. Any failure restores the snapshot for the whole run (`roll_back`), and the page names the plugin and the reason. A plugin the administrator switched off stays switched off, and its settings and data are kept. 6. **Deploy without a shell.** After an update, the running processes must load the new code. - With the bundled gunicorn: a graceful reload (`HUP` to the arbiter), which finishes the requests in flight. Where the process cannot do that, it says plainly: "Updated. Restart Postulo to finish," with the reason. - The page shows which version is loaded against which is installed, until they agree. - A plugin with database migrations has them run as part of the update, inside the same rollback. 7. **A record of it.** Each update, success or rollback, is written to the instance's log and to the Server activity: who, when, which versions, which catalogue. ## Decisions (recommended option first) - **Checking automatically against #228's "Postulo does not fetch them on its own":** - **(A, recommended)** a daily check that only fetches the signed index, on by default and switchable off. #228's rule was about *installing*, and still holds: nothing is ever installed without an administrator's click. - (B) check only when an administrator opens the Plugins page. - (C) keep it manual, which defeats the request. - **Deploying:** - **(A, recommended)** a graceful reload of gunicorn from the instance itself, with "restart to finish" as the fallback the page explains. - (B) always ask for a restart. - (C) exit and let the container's restart policy bring it back. This is not recommended: it drops the requests in flight and depends on a policy Postulo cannot see. - **"Update all" when one plugin fails:** - **(A, recommended)** all or nothing: roll back the whole run. - (B) keep the ones that succeeded. This leaves a mix nobody tested together. - **Major or breaking releases:** - **(A, recommended)** offered like any update, but labelled from the plugin's changelog or its `requires_postulo` floor, and never part of *Update all* without a separate confirmation. - **Who is told:** - **(A, recommended)** every staff account with server access, in the inbox, plus mail if the instance can send it. - (B) only the administrator who installed the plugin. ## Done when - The Plugins page shows the installed and offered version of each plugin and labels an update as one. - A daily check finds updates without anyone pressing anything, and administrators are told once. - *Update* and *Update all* upgrade from the interface, verify, roll back as a whole on any failure, and leave the new code running, or say exactly why a restart is needed. - Tests cover: - an update found and announced; - an update applied; - a failing update rolled back with the previous version still running; - an incompatible release offered no button; - an off switch that stops the check. - The wiki's plugin administration page describes all of it.
tiagoagueda added this to the 0.6.0 milestone 2026-10-31 21:33:41 +00:00
Author
Owner

Decided by the maintainer (2026-11-12)

The daily check is offered with an enable/disable setting on the plugins' settings page. Read as: on by default, as this issue recommends, and an administrator can switch it off. It fetches only the signed index.

🤖 Generated with Claude Code

## Decided by the maintainer (2026-11-12) The daily check is offered with an **enable/disable setting** on the plugins' settings page. Read as: on by default, as this issue recommends, and an administrator can switch it off. It fetches only the signed index. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Owner

Related: #1263 supplies the manifest this reads (builtin, official and third-party entries); detecting an update and the one-click upgrade stay here.

Related: #1263 supplies the manifest this reads (builtin, official and third-party entries); detecting an update and the one-click upgrade stay here.
Author
Owner

Related: #1264 (an administrator can hold a plugin or pin it to a version).

Related: #1264 (an administrator can hold a plugin or pin it to a version).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#1187
No description provided.