Skip to content

powertoys: Refactor installation & uninstallation logic#17104

Open
SorYoshino wants to merge 2 commits intoScoopInstaller:masterfrom
SorYoshino:powertoys
Open

powertoys: Refactor installation & uninstallation logic#17104
SorYoshino wants to merge 2 commits intoScoopInstaller:masterfrom
SorYoshino:powertoys

Conversation

@SorYoshino
Copy link
Contributor

@SorYoshino SorYoshino commented Jan 28, 2026

Summary

Refactors the powertoys manifest and associated scripts to improve installation reliability, shell extension (context menu) management, and registry state validation.

Related issues or pull requests

Changes

  • Refine license field with explicit SPDX identifier and source URL.
  • Refactor installer logic to use [version] type checking and improved Expand-DarkArchive parameters.
  • Improve post_install and uninstaller scripts with robust registry value validation (checking for specific protocol strings/status markers) before performing actions.
  • Standardize script templates by renaming internal variables (e.g., {{scoop_dir}} to {{powertoys_dir}}) and using UTF-8 encoding for file operations.
  • Add automatic explorer restart and sleep delay in the uninstaller to ensure shell extensions are properly released.
  • Update checkver to use repository ID, filter out pre-releases, and utilize a more precise regex for asset detection.
  • Enhance autoupdate regex to handle diverse HTML structures on the release page.

Notes

  • Context Menu Re-registration: Due to logic changes in how the context menu is tracked in the registry, users are required to re-run the registration command even if it was previously enabled.
  • Reasons for changing the context menu registration status check:
    • Regardless of whether the installation is global, PowerToys may use HKCU:\Software\Classes\powertoys to create values such as AllowDataDiagnostics and DataDiagnosticsViewEnabled.
    • If the logic only checks whether $registry_path exists
      • These values may be removed, resulting in partial configuration loss.
      • In addition, if a user has installed the machine-wide version of PowerToys and then installs PowerToys again via scoop install powertoys, the context menu will always be registered, which may cause confusion for users who are only testing the manifest.

Testing

Note

Since I have already installed the machine-wide version of PowerToys via winget, the following tests do not include the context menu registration component in order to avoid unnecessary complications.

The test results are as follows:
┏[ D:\Software\Scoop\Local\apps\scoop\current\bin][ develop ≡]
└─> .\checkver.ps1 -App powertoys -Dir 'D:\Temporary\Software\Microsoft\Windows Sandbox\Repositories\Scoop\Buckets\Extras\bucket' -f
powertoys: 0.97.1 (scoop version is 0.97.1)
Forcing autoupdate!
Autoupdating powertoys
DEBUG[1769632121] $substitutions (hashtable) -> D:\Software\Scoop\Local\apps\scoop\current\lib\autoupdate.ps1:230:5
DEBUG[1769632121] $substitutions.$cleanVersion                  0971
DEBUG[1769632121] $substitutions.$majorVersion                  0
DEBUG[1769632121] $substitutions.$match1                        0.97.1
DEBUG[1769632121] $substitutions.$underscoreVersion             0_97_1
DEBUG[1769632121] $substitutions.$matchHead                     0.97.1
DEBUG[1769632121] $substitutions.$patchVersion                  1
DEBUG[1769632121] $substitutions.$basenameNoExt                 PowerToysUserSetup-0.97.1-arm64
DEBUG[1769632121] $substitutions.$matchTail
DEBUG[1769632121] $substitutions.$urlNoExt                      https://github.com/microsoft/PowerToys/releases/download/v0.97.1/PowerToysUserSetup-0.97.1-arm64
DEBUG[1769632121] $substitutions.$baseurl                       https://github.com/microsoft/PowerToys/releases/download/v0.97.1
DEBUG[1769632121] $substitutions.$dashVersion                   0-97-1
DEBUG[1769632121] $substitutions.$preReleaseVersion             0.97.1
DEBUG[1769632121] $substitutions.$basename                      PowerToysUserSetup-0.97.1-arm64.exe
DEBUG[1769632121] $substitutions.$dotVersion                    0.97.1
DEBUG[1769632121] $substitutions.$minorVersion                  97
DEBUG[1769632121] $substitutions.$matchTag                      v0.97.1
DEBUG[1769632121] $substitutions.$url                           https://github.com/microsoft/PowerToys/releases/download/v0.97.1/PowerToysUserSetup-0.97.1-arm64.exe
DEBUG[1769632121] $substitutions.$version                       0.97.1
DEBUG[1769632121] $substitutions.$buildVersion
DEBUG[1769632121] $hashfile_url = https://github.com/microsoft/PowerToys/releases/tag/v0.97.1 -> D:\Software\Scoop\Local\apps\scoop\current\lib\autoupdate.ps1:233:5
Searching hash for PowerToysUserSetup-0.97.1-arm64.exe in https://github.com/microsoft/PowerToys/releases/tag/v0.97.1
DEBUG[1769632121] $regex = (?s)PowerToysUserSetup-0\.97\.1-arm64\.exe.+?([a-fA-F0-9]{64}) -> D:\Software\Scoop\Local\apps\scoop\current\lib\autoupdate.ps1:80:9
Found: b16a1aee649c82da4062b7bd2adbe68a9928cd5ff567bfa9098c0d70ee3653a5 using Extract Mode
Writing updated powertoys manifest

┏[ D:\Software\Scoop\Local\apps\scoop\current\bin][ develop ≡]
└─> scoop install Unofficial
Installing 'powertoys' (0.97.1) [64bit] from 'Unofficial' bucket
Loading PowerToysUserSetup-0.97.1-x64.exe from cache.
Checking hash of PowerToysUserSetup-0.97.1-x64.exe... OK.
Running installer script... Done.
Linking D:\Software\Scoop\Local\apps\powertoys\current => D:\Software\Scoop\Local\apps\powertoys\0.97.1
Creating shortcut for PowerToys (PowerToys.exe)
Running post_install script... Done.
'powertoys' (0.97.1) was installed successfully!
Notes
-----
To register the context menu entry, please execute the following command:
Invoke-Expression -Command "& `"D:\Software\Scoop\Local\apps\powertoys\current\install-context.ps1`""

Please note that the related logic has been changed.
The context menu must be re-registered once, even if it was previously registered.
No further action is required if this message appears again.
-----

┏[ D:\Software\Scoop\Local\apps\scoop\current\bin][ develop ≡]
└─> scoop update powertoys -f
powertoys: 0.97.1 -> 0.97.1
Updating one outdated app:
Updating 'powertoys' (0.97.1 -> 0.97.1)
Downloading new version
Loading PowerToysUserSetup-0.97.1-x64.exe from cache.
Checking hash of PowerToysUserSetup-0.97.1-x64.exe... OK.
Uninstalling 'powertoys' (0.97.1)
Running uninstaller script... Done.
Unlinking D:\Software\Scoop\Local\apps\powertoys\current
Installing 'powertoys' (0.97.1) [64bit] from Unofficial/powertoys.json'
Loading PowerToysUserSetup-0.97.1-x64.exe from cache.
Running installer script... Done.
Linking D:\Software\Scoop\Local\apps\powertoys\current => D:\Software\Scoop\Local\apps\powertoys\0.97.1
Creating shortcut for PowerToys (PowerToys.exe)
Running post_install script... Done.
'powertoys' (0.97.1) was installed successfully!
Notes
-----
To register the context menu entry, please execute the following command:
Invoke-Expression -Command "& `"D:\Software\Scoop\Local\apps\powertoys\current\install-context.ps1`""

Please note that the related logic has been changed.
The context menu must be re-registered once, even if it was previously registered.
No further action is required if this message appears again.
-----

┏[ D:\Software\Scoop\Local\apps\scoop\current\bin][ develop ≡]
└─> scoop uninstall powertoys -p
Uninstalling 'powertoys' (0.97.1).
Running uninstaller script... Done.
Removing shortcut ~\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Scoop Apps\PowerToys.lnk
Unlinking D:\Software\Scoop\Local\apps\powertoys\current
Removing older version (_0.97.1.old).
Removing persisted data.
'powertoys' was uninstalled.
  • Use conventional PR title: <manifest-name[@version]|chore>: <general summary of the pull request>
  • I have read the Contributing Guide

Summary by CodeRabbit

  • New Features

    • Added a formal uninstaller with improved, context-aware cleanup.
  • Bug Fixes

    • Improved installation and update handling with more robust archive extraction, asset detection, and version-aware extraction behavior.
    • Consolidated post-install configuration to reliably set installation scope and registry context.
    • Refined registry and context-menu cleanup logic for uninstall scenarios.
  • Chores

    • License declaration updated to include identifier and reference URL.

✏️ Tip: You can customize this high-level summary in your review settings.

* Update notes field
* Update checkver & autoupdate
* Improve shell extension management
@coderabbitai
Copy link

coderabbitai bot commented Jan 28, 2026

Walkthrough

Updated the PowerToys manifest and scripts: license field normalized to an object, installer extraction logic revamped with engine-version branching and cleanup, unified post-install registry/script placeholder handling, added an uninstaller block with registry/Appx cleanup, and tightened Appx/checkver/autoupdate asset selection.

Changes

Cohort / File(s) Summary
Manifest / Metadata
bucket/powertoys.json
License changed from string to object (identifier + url); added uninstaller block; installer/post_install logic reworked for versioned MSI extraction, scope-based placeholders, and registry handling; checkver/autoupdate asset filters/regex updated.
Installer Context
scripts/powertoys/install-context.ps1
Extraction flow rewritten to use generic temp paths, derive MSI from extracted contents, read engine version from manifest.xml, branch extraction based on engine version (>=5.0 vs legacy), remove temp artifacts, and replace direct scoop_dir refs with powertoys_dir.
Uninstaller Context
scripts/powertoys/uninstall-context.ps1
New/expanded registry cleanup targets (CLSID, shell handlers, DefaultIcon), hardened Test-Path / Remove-Item usage with explicit flags and error suppression, reorganized Appx package detection/removal, and reordered context-menu processing.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Installer as Installer Script
    participant FSys as File System
    participant Manifest as manifest.xml
    participant Registry
    participant Appx as Appx Engine

    User->>Installer: Run installation
    Installer->>FSys: Extract archive to temp (generic path)
    Installer->>FSys: Locate MSI within extracted contents
    Installer->>Manifest: Read engine version
    alt Engine >= 5.0
        Installer->>FSys: Extract MSI to version-specific directory
    else Legacy Engine
        Installer->>FSys: Extract MSI to legacy directory
    end
    Installer->>Registry: Compute powertoys_dir and registry_scope
    Installer->>Registry: Create or set registry defaults and placeholders
    Installer->>Appx: Add/import Appx packages (if present)
    Installer->>FSys: Cleanup temporary artifacts
    Installer->>User: Installation complete
Loading
sequenceDiagram
    participant User
    participant Uninstaller as Uninstaller Script
    participant Registry
    participant Appx as Appx Engine
    participant FSys as File System

    User->>Uninstaller: Run uninstallation
    Uninstaller->>Registry: Determine registry scope and powertoys entry
    Uninstaller->>Appx: Query matching Appx packages by name pattern
    Uninstaller->>Appx: Remove matching Appx packages
    alt Uninstall Flag Set
        Uninstaller->>Registry: Remove PowerToys registry keys and CLSIDs
    else Keep Installation
        Uninstaller->>Registry: Reset registry default values
    end
    Uninstaller->>FSys: Stop explorer (with delay)
    Uninstaller->>User: Uninstallation complete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • z-Fng

Poem

🐰 A rabbit hops with glee and cheer,
New uninstall paths now tidy and clear,
Engine checks, MSI found and cleaned,
Registry trimmed, Appx packages screened,
A tiny hop — install and disappear.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'powertoys: Refactor installation & uninstallation logic' accurately summarizes the main changes in the PR, which focus on refactoring installer, post_install, and uninstaller scripts.
Description check ✅ Passed The PR description follows the template with all required sections: summary, related issues, changes, notes, testing, and both required checklist items are marked complete.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Contributor

All changes look good.

Wait for review from human collaborators.

powertoys

  • Lint
  • Description
  • License
  • Hashes
  • Checkver
  • Autoupdate
  • Autoupdate Hash Extraction

Check the full log for details.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@scripts/powertoys/uninstall-context.ps1`:
- Around line 35-37: The loop using ForEach-Object currently relies on nested $_
which causes the inner Where-Object $_ to shadow the outer iterator
(ForEach-Object) and match every package; fix by capturing the outer value into
a named variable (e.g., $feature or $pattern) before calling Get-AppxPackage,
then use that named variable inside the Where-Object scriptblock to compare
PackageFullName (so Get-AppxPackage | Where-Object { $_.PackageFullName -like
"*$pattern*" } | Remove-AppxPackage) ensuring only packages matching the
intended feature names are removed; update references to $_ accordingly in the
ForEach-Object, Where-Object, and Remove-AppxPackage pipeline.
🧹 Nitpick comments (3)
scripts/powertoys/install-context.ps1 (1)

10-21: Consider guarding component key creation.

The components subkey is always recreated with -Force, which is acceptable. However, for consistency with lines 1-8, you could add a similar Test-Path guard to avoid unnecessary recreation.

bucket/powertoys.json (2)

27-38: Consider error handling for missing manifest.xml.

If manifest.xml is missing or malformed, the XML parsing on line 31 or version extraction on line 32 will fail. The script may throw an error rather than gracefully falling back.

♻️ Proposed defensive check
             "Expand-DarkArchive -Path \"$dir\\$fname\" -DestinationPath \"$dir\\tmp\" -Removal",
             "$installer_file = Get-ChildItem -Path \"$dir\\tmp\\AttachedContainer\" -Filter '*.msi' -File",
-            "[xml]$xml = Get-Content -Path \"$dir\\tmp\\UX\\manifest.xml\" -Encoding utf8",
-            "$engine_version = $xml.BurnManifest.EngineVersion",
-            "if ($null -ne $engine_version -and [version]$engine_version -ge [version]::new(5, 0)) {",
+            "$manifest_path = \"$dir\\tmp\\UX\\manifest.xml\"",
+            "$engine_version = $null",
+            "if (Test-Path -Path $manifest_path) {",
+            "    [xml]$xml = Get-Content -Path $manifest_path -Encoding utf8",
+            "    $engine_version = $xml.BurnManifest.EngineVersion",
+            "}",
+            "if ($null -ne $engine_version -and [version]$engine_version -ge [version]::new(5, 0)) {",

77-78: Explorer restart impacts user experience.

Stopping explorer.exe closes all open File Explorer windows and temporarily removes the taskbar. While necessary to release shell extension DLLs, consider warning users in the notes or adding a confirmation. The 1500ms delay is reasonable for explorer restart.

@SorYoshino
Copy link
Contributor Author

/verify

@github-actions
Copy link
Contributor

All changes look good.

Wait for review from human collaborators.

powertoys

  • Lint
  • Description
  • License
  • Hashes
  • Checkver
  • Autoupdate
  • Autoupdate Hash Extraction

Check the full log for details.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant