Skip to content

Clean up old update executables reliably#47057

Open
LegendaryBlair wants to merge 1 commit intomainfrom
legendaryblair/clean_updates
Open

Clean up old update executables reliably#47057
LegendaryBlair wants to merge 1 commit intomainfrom
legendaryblair/clean_updates

Conversation

@LegendaryBlair
Copy link
Copy Markdown
Contributor

Summary of the Pull Request

This pull request improves the update cleanup process in PowerToys by ensuring that old update files are reliably cleaned up after updates, and by making the cleanup code more robust against errors. The main changes include always invoking the cleanup routine, improving error handling, and simplifying the logic around when cleanup occurs.

Update cleanup improvements:

  • The updating::cleanup_updates() function is now always called after an update is installed in InstallNewVersionStage2, ensuring old update files are consistently removed.
  • The call to updating::cleanup_updates() in main.cpp has been simplified to always run in a background thread, regardless of the update state, ensuring cleanup is not skipped.

Error handling enhancements:

  • The cleanup_updates() function now wraps its logic in a try-catch block and logs errors if exceptions occur, making the cleanup process more robust and easier to debug. [1] [2]- Make startup cleanup unconditional (remove upToDate state gate)
  • Add cleanup after successful install in InstallNewVersionStage2
  • Add exception safety inside cleanup_updates() to protect all callers
    This pull request improves the update cleanup process by ensuring that old update files are always cleaned up, regardless of the update state, and adds error handling and logging to the cleanup logic. The most important changes are:

Fixes #46816

PR Checklist

  • Closes: #xxx
  • Communication: I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected
  • Tests: Added/updated and all pass
  • Localization: All end-user-facing strings can be localized
  • Dev docs: Added/updated
  • New binaries: Added on the required places
  • Documentation updated: If checked, please file a pull request on our docs repo and link it here: #xxx

Detailed Description of the Pull Request / Additional comments

Validation Steps Performed

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request aims to make PowerToys’ update cleanup more reliable by ensuring stale update installers (and related files) get deleted consistently, including after an update install completes and during normal app startup.

Changes:

  • Run updating::cleanup_updates() unconditionally on Runner startup (in a detached background thread).
  • Call updating::cleanup_updates() after a successful install in InstallNewVersionStage2.
  • Add exception handling and error logging inside updating::cleanup_updates().

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
src/runner/main.cpp Makes startup cleanup unconditional by always spawning a cleanup background thread.
src/common/updating/updating.cpp Wraps cleanup logic in try/catch and logs failures instead of allowing exceptions to escape.
src/Update/PowerToys.Update.cpp Invokes cleanup after a successful stage-2 install to remove leftover installer artifacts.

Comment thread src/runner/main.cpp
Comment on lines 553 to 555
std::thread{ [] {
auto state = UpdateState::read();
if (state.state == UpdateState::upToDate)
{
updating::cleanup_updates();
}
updating::cleanup_updates();
} }.detach();
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling updating::cleanup_updates() unconditionally at startup can delete the already-downloaded installer when UpdateState is readyToInstall. That leaves UpdateState pointing at a missing file, so the update flow can’t proceed (e.g., PowerToys.Update’s ObtainInstaller checks for the file and fails if it’s gone). Consider restoring a state gate (skip cleanup when an installer is pending), or enhance cleanup_updates to preserve the currently-referenced downloadedInstallerFilename when state==readyToInstall.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/runner/main.cpp:559

  • PR description says startup cleanup is now unconditional, but this code still skips cleanup when the state is readyToInstall and downloadedInstallerFilename is set. Either update the PR description to reflect this (to avoid deleting a pending installer), or adjust the implementation to do a safe cleanup that preserves the pending installer instead of skipping cleanup entirely.
            if (state.state != UpdateState::readyToInstall || state.downloadedInstallerFilename.empty())
            {
                updating::cleanup_updates();
            }
        } }.detach();

Comment thread src/common/updating/updating.cpp Outdated
if (err.value())
auto entryPath = entry.path().wstring();
std::transform(entryPath.begin(), entryPath.end(), entryPath.begin(), ::towlower);
if (entry.is_regular_file() && entryPath.ends_with(L".log") && entryPath.find(currentVersion) == std::string::npos)
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entryPath and currentVersion are std::wstring, but the code compares find() against std::string::npos. This works due to the shared size_t type, but it’s confusing and easy to misread—use std::wstring::npos for consistency and clarity.

Suggested change
if (entry.is_regular_file() && entryPath.ends_with(L".log") && entryPath.find(currentVersion) == std::string::npos)
if (entry.is_regular_file() && entryPath.ends_with(L".log") && entryPath.find(currentVersion) == std::wstring::npos)

Copilot uses AI. Check for mistakes.
- Make startup cleanup unconditional (remove upToDate state gate)
- Add cleanup after successful install in InstallNewVersionStage2
- Add exception safety inside cleanup_updates() to protect all callers

Fixes #46816
@LegendaryBlair LegendaryBlair force-pushed the legendaryblair/clean_updates branch from 626d9c5 to 3e6fe88 Compare April 17, 2026 16:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Clean up old update executables on disk after install

2 participants