fix(install): make Windows zip extraction resilient (issue #603)#713
Conversation
The Windows extraction step relied on `powershell -Command Expand-Archive`,
which fails when:
- Microsoft.PowerShell.Archive (a script module) cannot be loaded due to
PSModulePath shadowing (Store-installed pwsh injecting WindowsApps
paths) or ExecutionPolicy Restricted (issue #603), or
- the temp directory contains characters that corrupt PowerShell string
parsing (e.g. a single quote in TEMP).
Switch to a two-tier extraction:
1. Primary: Add-Type System.IO.Compression.FileSystem +
[ZipFile]::ExtractToDirectory. Bypasses the PowerShell module system
entirely. .NET 4.5+, available on Win 8 / Server 2012 by default and
widely on Win 7 SP1.
2. Fallback: Expand-Archive -LiteralPath, kept for the rare host without
.NET 4.5 but with PS 5.0+ (e.g. Win 7 SP1 with WMF 5).
Both paths pass file paths through env vars ($env:LARK_CLI_ARCHIVE /
$env:LARK_CLI_DEST) so quoting / wildcard chars in the path can no longer
break command parsing. -LiteralPath ensures Expand-Archive treats the value
literally rather than as a wildcard pattern. $ErrorActionPreference='Stop'
makes non-terminating cmdlet errors propagate as non-zero exit codes.
Also drop `stdio: "ignore"` so the actual PowerShell error surfaces in the
postinstall log when both paths fail, instead of leaving users with
"Command failed: powershell ..." with no detail.
Verified on Windows 10 + PS 5.1:
- Reproduced #603 with shadow Microsoft.PowerShell.Archive +
Restricted ExecutionPolicy: original install.js fails, patched
install.js succeeds.
- Reproduced single-quote-in-TEMP path corruption: original fails,
patched succeeds.
- Fallback path verified end-to-end with primary forced to fail.
- Normal-environment install: no regression.
📝 WalkthroughWalkthroughWindows ZIP extraction logic in Changes
Sequence Diagram(s)sequenceDiagram
participant Node as Node.js<br/>(install.js)
participant NetIO as System.IO<br/>Compression
participant PS as PowerShell
participant Disk as File System
Node->>NetIO: Attempt ExtractToDirectory()
alt Extraction succeeds
NetIO->>Disk: Extract ZIP
Disk-->>Node: ✓ Success
else Extraction fails
NetIO-->>Node: ✗ Error
Node->>PS: Configure PSExecutionPolicy
Node->>PS: Expand-Archive -Force
alt PowerShell succeeds
PS->>Disk: Extract ZIP
Disk-->>Node: ✓ Success
else PowerShell fails
PS-->>Node: ✗ Error
Node-->>Node: Combine error messages
Node->>Node: Throw combined error
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scripts/install.js`:
- Around line 151-153: The combined error message template that builds the
string for the extraction failure contains an extra period/space sequence
causing "... . .NET ZipFile attempt..."; locate the template that references
archivePath, primaryErr.message and fallbackErr.message and adjust the
punctuation so sentences flow cleanly (e.g., remove the extra period/space so it
becomes "Failed to extract ${archivePath}. .NET ZipFile attempt:
${primaryErr.message}. Expand-Archive fallback: ${fallbackErr.message}" or join
with a single period and space between clauses).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
🚀 PR Preview Install Guide🧰 CLI updatenpm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@7f2149b9da87172ee379f8cab56785442e132959🧩 Skill updatenpx skills add larksuite/cli#fix/install-windows-zipfile -y -g |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #713 +/- ##
=======================================
Coverage 64.14% 64.14%
=======================================
Files 504 504
Lines 44285 44285
=======================================
Hits 28406 28406
Misses 13411 13411
Partials 2468 2468 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
) (larksuite#713) The Windows extraction step relied on `powershell -Command Expand-Archive`, which fails when: - Microsoft.PowerShell.Archive (a script module) cannot be loaded due to PSModulePath shadowing (Store-installed pwsh injecting WindowsApps paths) or ExecutionPolicy Restricted (issue larksuite#603), or - the temp directory contains characters that corrupt PowerShell string parsing (e.g. a single quote in TEMP). Switch to a two-tier extraction: 1. Primary: Add-Type System.IO.Compression.FileSystem + [ZipFile]::ExtractToDirectory. Bypasses the PowerShell module system entirely. .NET 4.5+, available on Win 8 / Server 2012 by default and widely on Win 7 SP1. 2. Fallback: Expand-Archive -LiteralPath, kept for the rare host without .NET 4.5 but with PS 5.0+ (e.g. Win 7 SP1 with WMF 5). Both paths pass file paths through env vars ($env:LARK_CLI_ARCHIVE / $env:LARK_CLI_DEST) so quoting / wildcard chars in the path can no longer break command parsing. -LiteralPath ensures Expand-Archive treats the value literally rather than as a wildcard pattern. $ErrorActionPreference='Stop' makes non-terminating cmdlet errors propagate as non-zero exit codes. Also drop `stdio: "ignore"` so the actual PowerShell error surfaces in the postinstall log when both paths fail, instead of leaving users with "Command failed: powershell ..." with no detail. Verified on Windows 10 + PS 5.1: - Reproduced larksuite#603 with shadow Microsoft.PowerShell.Archive + Restricted ExecutionPolicy: original install.js fails, patched install.js succeeds. - Reproduced single-quote-in-TEMP path corruption: original fails, patched succeeds. - Fallback path verified end-to-end with primary forced to fail. - Normal-environment install: no regression.
) (larksuite#713) The Windows extraction step relied on `powershell -Command Expand-Archive`, which fails when: - Microsoft.PowerShell.Archive (a script module) cannot be loaded due to PSModulePath shadowing (Store-installed pwsh injecting WindowsApps paths) or ExecutionPolicy Restricted (issue larksuite#603), or - the temp directory contains characters that corrupt PowerShell string parsing (e.g. a single quote in TEMP). Switch to a two-tier extraction: 1. Primary: Add-Type System.IO.Compression.FileSystem + [ZipFile]::ExtractToDirectory. Bypasses the PowerShell module system entirely. .NET 4.5+, available on Win 8 / Server 2012 by default and widely on Win 7 SP1. 2. Fallback: Expand-Archive -LiteralPath, kept for the rare host without .NET 4.5 but with PS 5.0+ (e.g. Win 7 SP1 with WMF 5). Both paths pass file paths through env vars ($env:LARK_CLI_ARCHIVE / $env:LARK_CLI_DEST) so quoting / wildcard chars in the path can no longer break command parsing. -LiteralPath ensures Expand-Archive treats the value literally rather than as a wildcard pattern. $ErrorActionPreference='Stop' makes non-terminating cmdlet errors propagate as non-zero exit codes. Also drop `stdio: "ignore"` so the actual PowerShell error surfaces in the postinstall log when both paths fail, instead of leaving users with "Command failed: powershell ..." with no detail. Verified on Windows 10 + PS 5.1: - Reproduced larksuite#603 with shadow Microsoft.PowerShell.Archive + Restricted ExecutionPolicy: original install.js fails, patched install.js succeeds. - Reproduced single-quote-in-TEMP path corruption: original fails, patched succeeds. - Fallback path verified end-to-end with primary forced to fail. - Normal-environment install: no regression.
Summary
Windows postinstall used
powershell -Command Expand-Archive, which fails on real-world environments outside the maintainer's machine:postinstallhardcodespowershell+ `Expand-Archive #603:Microsoft.PowerShell.Archiveis a script module (.psm1). Any environment where its loading is blocked — Microsoft Store pwsh 7 injecting aWindowsAppspath intoPSModulePath, or ExecutionPolicyRestrictedfrom a GPO — causesExpand-Archiveto fail withmodule could not be loaded, breaking install.TEMP(legal Windows path char) breaks parsing entirely.stdio: "ignore"swallowed the underlying PowerShell error, leaving users withCommand failed: powershell ...and no clue why.Changes
scripts/install.js— replace inlineExpand-Archivewith a newextractZipWindows():Add-Type -AssemblyName System.IO.Compression.FileSystem+[ZipFile]::ExtractToDirectory(...). Calls .NET BCL directly, doesn't traversePSModulePath, doesn't load script modules. Available on .NET 4.5+ — Win 8 / Server 2012 by default, broadly on Win 7 SP1.Expand-Archive -LiteralPathfor hosts without .NET 4.5 but with PowerShell 5.0+.$env:LARK_CLI_ARCHIVEand$env:LARK_CLI_DESTinstead of string interpolation. Env-var expansion is value-only, so quotes / wildcards / spaces in the path can't corrupt parsing.-LiteralPathadds belt-and-suspenders forExpand-Archive.$ErrorActionPreference='Stop'propagates non-terminating cmdlet errors as non-zero exit codes;stdio: ["ignore", "inherit", "inherit"]lets stderr/stdout flow to the npm postinstall log.-NoProfile -ExecutionPolicy Bypassto defend against user profile and system policy interference.If both paths fail, the thrown
Errorincludes both the .NET attempt's message and theExpand-Archivefallback's message, so postinstall logs are diagnosable.Test Plan
Verified on Windows 10 (PowerShell 5.1.19041, Node v22.11.0) via real
npm install -g <tarball>:postinstallhardcodespowershell+ `Expand-Archive #603 trigger (shadowMicrosoft.PowerShell.ArchiveinPSModulePath+PSExecutionPolicyPreference=Restricted):install.jsfrommain: fails with the exactFailed to install lark-cli: Command failed: powershell -Command Expand-Archive ...error from the issue report.install.js: succeeds (added 7 packages in 16s,lark-cli version 1.0.21).TEMP(C:\Users\Administrator\AppData\Local\Temp\lark'quote-h1test):MissingEndParenthesisInMethodCall).mkdtempSyncdestination): succeeds, files extracted.node --test scripts/install.test.js).Related Issues
postinstallhardcodespowershell+ `Expand-Archive #603Summary by CodeRabbit