Skip to content

Plugin refresh has no restart hook, so service-backed plugins can't be updated in one step #2250

Description

@AltanS

Herdr 0.8.0 (checked against d78e3d3). Filing from the plugin-author side.

The gap

Reinstall is v1's documented refresh path — "There is no separate plugin update in v1; reinstall from GitHub to refresh a managed plugin" — and it does the hard parts well: temp checkout, build, atomic swap, config preserved. But it stops there.

A plugin that supervises a long-running process can't finish the job from inside that flow:

  • [[build]] is too early. run_plugin_build_commands runs in the temp checkout, before the rename into the managed path — so a build step can't restart a service onto code that isn't in place yet.
  • [[startup]] doesn't fire. Per the docs it runs after a session restore and on live handoff, explicitly not "when a client attaches, config reloads, or a plugin is linked or enabled" — and not after an install either.
  • [[events]] has no plugin lifecycle. plugin_hook_event_names() is workspace/tab/pane only; there is no plugin.* event to hang this on.

So after a reinstall the plugin is shipping new code while the old process is still running, and the user has to know to invoke a second action to land it. Ours is a web bridge under systemd --user, but this applies to anything holding a socket, a daemon, or a watcher.

The knock-on effect

Without a supported "refresh me" verb, a plugin that wants one-command updates has to reach into the managed checkout with raw git. That means depending on the shape git_checkout() leaves behind — git init + git fetch --depth 1 origin HEAD + git checkout --detach FETCH_HEAD, i.e. detached, shallow, no remote-tracking refs. That shape isn't documented, and we only learned it by reading src/cli/plugin.rs after our update action had been failing with "You are not currently on a branch" for every GitHub-installed user since our first release (AltanS/collie#63). Plugins are guessing at an internal layout because there's no seam.

There's a second, sharper edge in the same area: a plugin that tries to self-heal its registry entry with herdr plugin link <managed path> silently re-registers itself as source.kind = local, after which ensure_replacement_allowed refuses herdr plugin install — removing the user's only remaining way to refresh. We now detect and skip that case, but it's an easy trap to fall into, since plugin link is the documented way to make Herdr notice a changed manifest on older versions.

Either of these would close it

  1. herdr plugin update <id> — refetch the recorded source (honouring requested_ref), rebuild, swap, update resolved_commit. Plugins never touch the managed checkout, and the registry stays truthful about what's on disk.
  2. A manifest-declared post-install action — e.g. [plugin] after_install = "restart", invoked after the swap and registration, so a service-backed plugin lands the new code by itself.

(2) is much smaller and composes with what already exists — actions are declared and invokable, this just adds one call site at the end of install. (1) is the more complete answer, and would also let plugins stop shipping their own git logic.

Happy to send a PR for either if you have a preference on shape.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions