fix(plugins): treat a same-version upload as a reinstall, not a first install - #498
Open
mostafasadeghidev wants to merge 1 commit into
Open
Conversation
… install
Re-uploading a plugin at an unchanged version number fell through both semver
guards — `semverGt` false, `semverLt` false — and landed in
`installFreshFromPackage`, the path written for a plugin that is not there.
On a live install that meant: the running version's `deactivate` never ran;
`install` ran a second time on a plugin that was already installed; `migrate`
was skipped; there was no rollback if a hook threw, so a failure returned 201
with the plugin parked in `error` and its assets already overwritten; and the
audit event said `plugin.install`, recording a replacement as a first-time
install.
Rebuilding a package without bumping the version is the normal inner loop of
plugin development, so this is the path a developer hits most.
Since a lower version already returned above, anything reaching the branch is
installed at the same version or higher — so `existing` alone now selects the
upgrade path.
That surfaced two places where "old version" and "new version" are the same
directory, and both would have deleted a working plugin:
- Step 6 drops the old version's assets after a successful upgrade. With
equal versions that names the directory step 2 has just written.
- `rollbackUpgrade` drops the new version's assets on failure. With equal
versions that is the only copy, and the restored row still points at it.
Both are now guarded on the versions actually differing.
The review dialog told the operator none of this: it only set
`upgradeFromVersion` when the versions differed, so a reinstall rendered the
first-install screen — no mention of the plugin already being there, every
permission badged "new" though all were already approved. It now carries the
installed version whenever there is one and distinguishes three cases:
reinstall, update, and downgrade. Downgrade previously promised "settings and
stored data are preserved; the plugin runs its migrate hook" and was then
refused by the server; it now says so before the operator commits, and the
confirm button is disabled rather than buying them a round trip.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mostafasadeghidev
added a commit
to mostafasadeghidev/Instatic
that referenced
this pull request
Sep 6, 2026
…at had no gate The fork gate stopped at CoreBunch#359. Everything merged onto the stack since — CoreBunch#497 through CoreBunch#501, and now CoreBunch#505 — was carried with nothing to catch it being dropped by a merge resolution, which is the exact failure the gate exists for. Pinning them turned one up immediately: CoreBunch#498's guard around `removePluginVersionAssets(…, fromVersion)` is gone, because CoreBunch#359 removed that call outright — an upgrade now leaves the old version's assets on disk so published pages keep resolving. What survives of CoreBunch#498 is the rollback guard, so that is what the row pins. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Re-uploading a plugin at an unchanged version number falls through both semver guards —
semverGtfalse,semverLtfalse — and lands ininstallFreshFromPackage, the path written for a plugin that is not there.On a live install that means:
deactivatenever runsinstallruns a second time on an already-installed pluginmigrateis skippederrorand its assets already overwrittenplugin.install, recording a replacement as a first-time installRebuilding a package without bumping the version is the normal inner loop of plugin development, so this is the path a developer hits most often.
The fix
A lower version already returns above, so anything reaching the branch is installed at the same version or higher —
existingalone now selects the upgrade path.That surfaced two places where "old version" and "new version" name the same directory, and both would have deleted a working plugin:
rollbackUpgrade— drop the new version's assets on failureBoth are now guarded on the versions actually differing. The new test fails without either guard.
The dialog was silent too
It only set
upgradeFromVersionwhen the versions differed, so a reinstall rendered the first-install screen: no mention of the plugin already being there, and every permission badged "new" though all were already approved.It now carries the installed version whenever there is one, and distinguishes three cases:
Co-Authored-By: Claude Opus 5 noreply@anthropic.com