Ghost CLI version downgrade leads to broken install
`ghost update` keeps 2 most current, not 2 last active versions, breaking downgrading
Summary
The Ghost CLI removes old versions while updating. In the easiest-to-find search results, it is documented or mentioned, that the last 5 versions are kept, e.g.:
That includes the most current version, that was likely just installed through the update. So only 4 "old" versions are kept.
This has been changed in version 1.15.0 with no mention in the short changelog through the commit badb662, so I just missed it.
Since then, only the 2 most current versions are kept, because of disk space concerns: #201 (comment)
As this includes the most current version, only 1 "old" version is kept.
Checking the code, it seems like the version removal feature only takes Semver sorting into account:
Lines 149 to 164 of update.js
:
async removeOldVersions({instance}, task) {
const semver = require('semver');
const versionDirs = await fs.readdir(path.join(instance.dir, 'versions'));
const versions = versionDirs.filter(semver.valid).sort(semver.compare);
if (versions.length <= 2) {
task.skip();
return;
}
const promises = versions.slice(0, -2)
.map(version => fs.remove(path.join(instance.dir, 'versions', version)));
await Promise.all(promises);
}
It does not check for the currently active or just installed version or sort the version by last use.
This leads to the unfortunate scenario, of just having downgraded to an older version and Ghost not being able to start, as it was promptly removed again and the current
symlink points to a version directory that does not exist.
Steps to Reproduce
Have a Ghost install with the 2 most current versions installed
Run
ghost update
with an older version, likeghost update 5.2.0 --force
Observe, that old Ghost versions are being removed
Try to start Ghost and see errors or check the
current
symlink pointing to a version directory that does not exist.
ghost update 5.2.0 --force
CLI command log:
$ ghost update 5.2.0 --force
Love open source? We’re hiring JavaScript Engineers to work on Ghost full-time.
https://careers.ghost.org
Running in development mode
✔ Checking system Node.js version - found v16.18.1
✔ Ensuring user is not logged in as ghost user
✔ Checking if logged in user is directory owner
✔ Checking current folder permissions
✔ Checking memory availability
✔ Checking free space
✔ Checking for available migrations
✔ Checking for latest Ghost version
✔ Release notes were not found
✔ Downloading and updating Ghost to v5.2.0
✔ Linking latest Ghost and recording versions
✔ Removing old Ghost versions
ghost update 5.2.0 --force 15.27s user 9.01s system 137% cpu 17.708 total
Possible solutions, not mutually exclusive
Always keep the current active version
Add a check, to never delete the currently active version or exclude it from the removal feature outright.
The latter would increase the number of kept versions to 3 if the feature is not adjusted for that.
Add a config option and/or CLI flag for the number of versions to keep
The change from keeping 5 versions to just 2 went by unnoticed, and it would have been great if users/admins could define the number themselves. Everybody has different infrastructure, storage requirements and rollback needs.
This would be great as a config option as well as a CLI command flag.
Keep the last active versions
Instead of just keeping the latest versions according to Semver, it would be cleaner to keep track of the active and used versions. People might not just upgrade to newer versions, but jump around.
Keeping previous versions according to the history of used versions would simplify things a lot.
GitHub issue:
Workarounds, tips and hints
Make sure to have a "free slot" for keeping versions available. With only 2 versions being kept, this means removing all (typically 1) but the most current one. That way, the feature won't be triggered, and the downgrade version is not deleted.