Fix #6515 "Expand-7zipArchive won't remove top folder if $ExtractDir depth exceeds 2" - Idea 1
Description
Expand-7zipArchive have had problems with apps where:
- A directory inside
$ExtractDiris named the same as a dir above it (issue #6011, PR #6092) - The workaround for above problem did not handle
$ExtractDirthat are two levels deep or more (issue #6515). - To make expansion with
$ExtractDirmore robust I previously suggested to extract to TEMP first, then move content from there (feature request #6093).
This PR tries to fix all that.
Motivation and Context
- Closes #6515 and #6093.
- Relates to #6011
Merging this might break some manifests that have workarounds to delete leftovers after $ExtractDir. Here are some I know of:
- https://github.com/ScoopInstaller/Extras/blob/master/bucket/foxit-reader.json
- https://github.com/ScoopInstaller/Extras/blob/master/bucket/foxit-pdf-reader.json
How Has This Been Tested?
Checklist:
- [x] I have read the Contributing Guide.
- [x] I have ensured that I am targeting the
developbranch. - [x] I have updated the documentation accordingly.
- [ ] I have updated the tests accordingly.
- [x] I have added an entry in the CHANGELOG.
Summary by CodeRabbit
-
New Features
- Improved partial-extraction workflow: when extracting a subdirectory, files are unpacked to a temporary location and moved into place to ensure reliable results across archive types.
-
Bug Fixes
- More reliable handling of destination paths and removal of temporary files to prevent leftovers and placement errors.
- Clearer errors and more consistent external tool invocation for decompression.
-
Documentation
- Updated changelog documenting decompression and extraction improvements.
Walkthrough
Updated changelog and refactored decompression handlers in lib/decompress.ps1: archive type detection, consistent external-command invocation, trimmed/normalized destinations, extract-to-temp when ExtractDir is set, then move/cleanup; uniform Remove-Item -Path usage and log cleanup adjustments.
Changes
| Cohort / File(s) | Summary |
|---|---|
Changelog entryCHANGELOG.md |
Adds entry noting that Expand-7zipArchive and Expand-ZipArchive expand into a temp directory when ExtractDir is provided. |
Core decompression refactorlib/decompress.ps1 |
- Centralized changes across archive handlers: Expand-7zipArchive, Expand-ZipArchive, Expand-MsiArchive, Expand-InnoArchive, Expand-DarkArchive, Expand-ZstdArchive.- Detects tar archives ( IsTar) and adjusts listing/extraction flows.- Trim/normalize DestinationPath; when ExtractDir provided, extract to a temp path under $env:TEMP then move ExtractDir contents to original destination and remove temp.- Use Get-Command -Name '7z' for 7z lookup; construct log paths with Split-Path -Path / friendly_path messages.- Standardize external calls to Invoke-ExternalCommand -FilePath -ArgumentList and status checks (-not $Status).- Refine post-extract cleanup: remove temp dirs, remove log only if it is a leaf, and consistently use Remove-Item -Path; adjust archive-part removal via Get-ChildItem -Path.- Minor comment/casing/formatting tweaks for consistency. |
Sequence Diagram(s)
sequenceDiagram
autonumber
participant Caller
participant Decompress as lib/decompress.ps1
participant External as 7z/tar/unzip/msiexec
participant FS as FileSystem
Caller->>Decompress: Expand-*(archive, DestinationPath, ExtractDir?)
Decompress->>Decompress: Trim/normalize DestinationPath\nDetect archive type (IsTar?)
alt ExtractDir provided
Decompress->>FS: Create tempDestination under $env:TEMP
alt Non-tar
Decompress->>External: Invoke extraction (FilePath + ArgumentList, include -ir!ExtractDir\*)
External-->>Decompress: Exit status
Decompress->>FS: Move tempDestination\ExtractDir -> Original DestinationPath
else Tar
Decompress->>External: List/extract tar (FilePath + ArgumentList)
External-->>Decompress: Exit status
Decompress->>FS: Arrange/move tar contents -> DestinationPath
end
Decompress->>FS: Remove tempDestination and cleanup logs (only if leaf)
else No ExtractDir
Decompress->>External: Invoke extraction -> DestinationPath
External-->>Decompress: Exit status
end
Decompress->>FS: Optional remove original archive parts (Get-ChildItem -Path / Remove-Item -Path)
Decompress-->>Caller: Return status/result
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~60 minutes
Poem
A hop, a zip, a tarball trip—
I nibble paths where temp nests slip.
I tuck ExtractDir in a cozy place,
then hop it home with gentle grace.
Logs and crumbs? I sweep the space. 🐰📦
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Out of Scope Changes Check | ⚠️ Warning | In addition to fixing Expand-7zipArchive, the changes also apply the temporary extraction pattern to Expand-ZipArchive, Expand-MsiArchive, Expand-InnoArchive, Expand-DarkArchive, and Expand-ZstdArchive, which are not covered by the linked issue #6515 focused solely on 7z extraction behavior. | Limit the scope of this pull request to only the Expand-7zipArchive changes required by issue #6515 or include additional linked issues that authorize similar modifications for the other archive functions. |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Linked Issues Check | ✅ Passed | The pull request implements extracting 7z archives to a temporary directory and then moving the specified ExtractDir contents back to the target path, directly addressing the nested‐folder removal issue described in issue #6515. This approach ensures that the top‐level folder is correctly removed regardless of ExtractDir depth, satisfying the linked issue’s requirements. |
| Docstring Coverage | ✅ Passed | No functions found in the changes. Docstring coverage check skipped. |
| Title Check | ✅ Passed | The title clearly references the issue number and the specific Expand-7zipArchive bug it fixes, directly tying to the main change in this pull request. It identifies the problem scope and developer intent in a single sentence. Although it includes some extra wording like “Idea 1” and nested quotes, it still summarizes the central fix for deeper $ExtractDir depths. |
✨ Finishing touches
- [ ] 📝 Generate docstrings
🧪 Generate unit tests (beta)
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
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.
Comment @coderabbitai help to get the list of available commands and usage tips.
Merging this might break some manifests that have workarounds to delete leftovers after $ExtractDir. Here are some I know of:
- https://github.com/ScoopInstaller/Extras/blob/master/bucket/foxit-reader.json
- https://github.com/ScoopInstaller/Extras/blob/master/bucket/foxit-pdf-reader.json
This isn't specifically meant to remove leftovers ($dir\msi\.rsrc) -- deleting $DestinationPath ($dir\msi) is a common operation.
"Expand-7zipArchive \"$dir\\$fname\" \"$dir\\msi\" -ExtractDir '.rsrc\\1033\\PAYLOAD' -Removal",
"Remove-Item \"$dir\\msi\", \"$dir\\extracted\", \"$dir\\install.log\" -Force -Recurse"
As far as I'm aware, there aren't any related workarounds to delete leftovers after $ExtractDir.