Repository navigation
feat(linux): port transactional installer upgrade mode and in-app updater - #360
Conversation
…ater to Filee 2.0.0
KnifeLemon
left a comment
There was a problem hiding this comment.
Thanks a lot for this, @manish0467! A Linux in-app updater is something Filee is missing, and the work shows. The tar extraction is defensive, install.sh --upgrade checks the marker, stages on the same filesystem and rolls back from a trap, and there are plenty of tests. The FindFiles change that stops Windows from picking a -linux-/-osx- SHA256SUMS file also fixes a latent bug on main. Thank you.
Before we can merge, a few things need another pass:
- Quit first, then swap. The in-app path skips --quit (FILEE_IN_APP_UPDATE=1), so install.sh moves the running app’s folder aside and deletes it while the old Filee is still running. After that, the old process verifies the version and shuts down while loading any assemblies it hadn’t loaded yet from the new files, and running conversions lose their bundled engines. Please follow the Windows order instead: start a small detached helper (it can be the same /bin/sh watcher), let Filee exit, have the helper run install.sh --upgrade once the PID is gone, then start the new Filee. Start it with --background --whats-new as Setup does, so the “What’s new” slides show.
- No test hooks in the shipped installer. Please move the FILEE_TEST* fault injection (install.sh lines 43–52 and 218–278) out of install.sh into the tests, for example by putting a failing mv shim first on PATH or by changing permissions from the test itself.
- Please split out the LibreOffice/Ghostscript --version changes. They’re unrelated to the updater and not in the description. Running soffice --version inside GetStatus() is a blocking external call of up to 3 s, and on Windows soffice.exe prints nothing anyway. A separate PR is welcome.
- Keep the Windows path as it was. Using a fresh random workspace on Windows means a successful update leaves the ~120 MB Setup.exe in %TEMP% every time, because Setup closes Filee before the cleanup runs. It also breaks reuse of the download after a declined UAC prompt. Please keep %TEMP%\Filee-update on Windows, or clean up old workspaces at startup.
- Don’t stage in XDG_RUNTIME_DIR. It’s a RAM-backed tmpfs that is usually 10% of RAM, and 119 MB plus the unpacked tree can fill it on small machines. Something like $XDG_DATA_HOME/filee/.update-* (same filesystem as the install) or ~/.cache works better.
- FindLinuxRecoveryInfo gets AppContext.BaseDirectory with a trailing /, so Path.GetDirectoryName returns the application folder itself and never finds .backup-*. Please trim the separator first.
- Please capture install.sh’s stdout/stderr into Filee’s log. Right now only the exit code is logged, so rollback messages are lost.
- Smaller items:
- Drop the unused --restart argument, the undocumented FILEE_INSTALL_DIR and the .filee-version fallback.
- Mask tar modes to rwx bits so no setuid/setgid survives.
- Reuse App.Quit().
- Use Assert.Skip(...) rather than return in LinuxInstallerTests on other OSes.
- Skip DeleteSecureWorkspace_refuses_to_follow_symlink when symlinks can’t be created (it fails on Windows without Developer Mode).
- Add a short “Linux updates” note to docs/ARCHITECTURE.md next to the Windows installer section.
Note: #367 also rewrites the top of installer/linux/install.sh, in a way that conflicts with this. We’ll sort that out on our side.
CI hasn’t run on this branch yet. It’s waiting for approval, and we’ll start it once the next push lands. Thanks again; happy to help if any of this is unclear!
…ater and installer - Start detached helper to wait for old Filee PID to exit before running install.sh --upgrade - Launch new Filee instance with --background --whats-new upon successful update - Reuse App.Quit() for graceful application shutdown before file swap - Remove FILEE_TEST_* fault-injection hooks from shipped install.sh - Implement fault injection in LinuxInstallerTests via external mv shim on PATH - Remove --restart option, FILEE_INSTALL_DIR override, and .filee-version fallback - Preserve Windows update directory %TEMP%\Filee-update and download reuse - Securely stage Linux update archives in XDG_DATA_HOME/filee/.update-* (0700) - Mask extracted tar entry permissions to RwxMask (0x1FF) stripping setuid/setgid/sticky - Fix FindLinuxRecoveryInfo trailing path separator handling - Redirect and append install.sh stdout and stderr to Filee daily log - Update LinuxInstallerTests to use Assert.SkipUnless(OperatingSystem.IsLinux(), ...) - Document Linux installer and update lifecycle in docs/ARCHITECTURE.md
|
Thanks for the detailed review! I've pushed the follow-up fixes in I've addressed the requested updater lifecycle, installer test hooks, Windows workspace reuse, Linux staging and recovery handling, logging, archive permissions, and test portability changes. I've also separated the LibreOffice/Ghostscript version-probing changes into a separate local branch for a future PR. I've requested another review. The workflow is still awaiting maintainer approval, so its CI results remain to be verified. Please let me know if anything needs another pass. |
|
Thanks @manish0467! We'll start the CI run for 26640d5 and go through the changes again. |
KnifeLemon
left a comment
There was a problem hiding this comment.
Thanks @manish0467 for the quick and careful follow-up. I checked the eight points against 26640d5:
- Quit first, then swap: done. The helper waits for Filee's PID, runs
install.sh --upgrade, and starts the new Filee with--background --whats-new;App.Quitis reused. - No test hooks left in install.sh (and the test that sets the old variables is a nice touch).
- The LibreOffice/Ghostscript
--versionchanges are out. - Windows keeps
%TEMP%\Filee-update. - Staging is under
$XDG_DATA_HOME/filee/.update-*. - The trailing separator is trimmed.
- install.sh's output goes into the day's log.
- All the small items are done.
What's left:
- A failed update leaves Filee closed. When
install.sh --upgradefails and rolls back, the helper only writes "Update failed" to the log and exits, so Filee just disappears. The rollback puts the old version back at the same path, so the helper can start"$3"in both cases (--whats-newonly on success). Ideally the restarted Filee then says the update didn't go through, for example from a small marker file the helper writes and UpdateService reads at start, pointing to the log. - Dead code, and a test that fails on Windows.
VerifyInstalledVersion,GetInstalledVersion(which starts filee-cli) andFindLinuxRecoveryInfoare no longer called by the app, only by tests.VerifyInstalledVersion_detects_mismatchruns a#!/bin/shscript, so it fails on Windows (UpdateServiceTests here: 44 passed, 1 skipped, this one failed), and CI's build job runs on Windows. Please remove the three methods and their tests. - Leftovers. If the old Filee doesn't exit within 60 s, the helper exits before its
rm -rf, so the ~125 MB archive and the unpacked payload stay in~/.local/share/filee/.update-*. Please remove the workspace on that path too, and sweep stale.update-*folders at start (asJobQueue.DeleteStaleWorkFoldersdoes for work folders). Concurrent_secure_workspaces_are_isolatedcallsCreateSecureWorkspace()without a base folder, so on Linux it creates folders in the developer's real~/.local/share/filee. Please give it a temp folder.
Not blocking: the workspace is now on the same filesystem as the installation, but install.sh still copies the payload into .staging-*, so an update briefly needs the unpacked payload, the staging copy and the backup at once. Moving instead of copying when the payload comes from the updater's own workspace would halve that.
About #408: it's now limited to the Windows progress dialog, as we asked, and Linux in-app updating stays here. Both PRs change UpdateAsync and FindFiles, so whichever lands second gets rebased; we'll take care of that. The checksum rule here (exactly -SHA256SUMS.txt on Windows, skipping -linux-/-osx- files) is the one we want.
|
Thanks for the follow-up review! I've pushed The targeted updater and installer tests, non-render App test suite, formatting, and ShellCheck all pass locally. The new GitHub Actions run is currently awaiting maintainer approval, so I'll verify the CI results once it runs. Please let me know if anything else needs attention. |
KnifeLemon
left a comment
There was a problem hiding this comment.
Thanks @manish0467! I checked 213bd40 against the four points:
- A failed update now brings Filee back. After the rollback the helper starts
"$3" --backgroundand leaves.update-failedwith the log path, which the next start reads and reports. On success it still starts with--whats-new. VerifyInstalledVersion,GetInstalledVersionandFindLinuxRecoveryInfoare gone, together with their tests.- The timeout path removes the workspace too, and stale
.update-*folders are swept at start. Concurrent_secure_workspaces_are_isolateduses a temp folder.
CI has run on this commit now. The App tests pass on Windows, UpdateServiceTests included. The build job is red only because Filee.Cli.Tests timed out while xUnit was discovering the tests ("Test process did not respond within 60 seconds"), which has nothing to do with this PR. We'll re-run it.
One thing to change:
The startup sweep runs on every platform. App.Start calls UpdateService.DeleteStaleWorkspaces() on Windows and macOS too, where the default root is Path.GetTempPath(). There it deletes every folder in the user's temp folder whose name starts with .update- and that is older than a day, including folders other programs made. A name without a number, such as .update-cache, skips the PID check and goes straight to the age check. Workspaces are only created on Linux, so please run the sweep only there, and only for names Filee creates (.update-<pid>-<32 hex characters>), with a test that a foreign .update-something folder is left alone.
On our side, nothing needed from you:
- After a rolled-back update, the restarted Filee shows
update.failed_alert("Couldn't download the update…"), which isn't quite what happened, and the "Log:" line is in English for every language. We'll add a translated message for that case when we rebase. - CI runs the tests only on Windows, so
LinuxInstallerTestsand the helper-script tests are skipped there. We'll run them on Linux before merging. - #408 and #469 also change
UpdateAsyncandFindFilesand will go in first. We'll rebase this one (a few small conflicts inUpdateService.csand its tests), keep your per-platformFindFiles, and run #469's signature check only in the Windows branch.
Thanks again for sticking with this!
|
I've pushed The targeted tests and formatting checks passed locally. Please let me know if anything else needs attention. |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After a failed install the helper rolls back and starts the old Filee, which showed "Couldn't download the update" with an English "Log:" line. It now says the update couldn't be installed and the previous version was kept (update.rolled_back_alert), with the log path in a translated line (update.rolled_back_log), in all 14 languages. A failure in the current run keeps update.failed_alert without a log line from an earlier run. DeleteStaleWorkspaces does nothing on other platforms when no root is given: there the default root is the shared temp folder. The Linux update test no longer starts a real `true`, which is missing on Windows without Git's tools on PATH. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> # Conflicts: # src/Filee.App/Services/UpdateService.cs # tests/Filee.App.Tests/UpdateServiceTests.cs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
install.sh: the GNOME extension step moves into KnifeLemon#360's first-install branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What does this change?
Adds a Linux in-app updater for Filee 2.0.0, including transactional installation, rollback on failure, release checksum verification, and safe application restart handling.
The updater supports Linux x64 and ARM64 releases and aims to preserve user settings and downloaded conversion engines during upgrades.
How was it tested?
dotnet format --verify-no-changesdotnet testrun in a clean CI environmentAdditional checks passed locally, including Core, CLI, Platform, and non-render App test suites. Some engine tests failed in the local environment, and render tests were not run in the final audit.
Implementation notes
Review notes
Please review the Linux update lifecycle, archive extraction safeguards, rollback behavior, and restart handling. This branch has been tested locally; its CI status should be verified after submission.