Skip to content

fix(install): make Windows zip extraction resilient (issue #603)#713

Merged
sang-neo03 merged 1 commit into
mainfrom
fix/install-windows-zipfile
Apr 29, 2026
Merged

fix(install): make Windows zip extraction resilient (issue #603)#713
sang-neo03 merged 1 commit into
mainfrom
fix/install-windows-zipfile

Conversation

@sang-neo03

@sang-neo03 sang-neo03 commented Apr 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Windows postinstall used powershell -Command Expand-Archive, which fails on real-world environments outside the maintainer's machine:

  • Issue Windows install fails from PowerShell 7 (Microsoft Store) because postinstall hardcodes powershell + `Expand-Archive #603: Microsoft.PowerShell.Archive is a script module (.psm1). Any environment where its loading is blocked — Microsoft Store pwsh 7 injecting a WindowsApps path into PSModulePath, or ExecutionPolicy Restricted from a GPO — causes Expand-Archive to fail with module could not be loaded, breaking install.
  • Path corruption: archive / destination paths were string-interpolated into PowerShell single-quoted strings without escaping. A single quote in TEMP (legal Windows path char) breaks parsing entirely.
  • Silent failures: stdio: "ignore" swallowed the underlying PowerShell error, leaving users with Command failed: powershell ... and no clue why.

Changes

scripts/install.js — replace inline Expand-Archive with a new extractZipWindows():

  1. Primary path: Add-Type -AssemblyName System.IO.Compression.FileSystem + [ZipFile]::ExtractToDirectory(...). Calls .NET BCL directly, doesn't traverse PSModulePath, doesn't load script modules. Available on .NET 4.5+ — Win 8 / Server 2012 by default, broadly on Win 7 SP1.
  2. Fallback: Expand-Archive -LiteralPath for hosts without .NET 4.5 but with PowerShell 5.0+.
  3. Path safety: archive / dest paths are passed through $env:LARK_CLI_ARCHIVE and $env:LARK_CLI_DEST instead of string interpolation. Env-var expansion is value-only, so quotes / wildcards / spaces in the path can't corrupt parsing. -LiteralPath adds belt-and-suspenders for Expand-Archive.
  4. Error visibility: $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.
  5. Hardening: both invocations use -NoProfile -ExecutionPolicy Bypass to defend against user profile and system policy interference.

If both paths fail, the thrown Error includes both the .NET attempt's message and the Expand-Archive fallback'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>:

  • Issue Windows install fails from PowerShell 7 (Microsoft Store) because postinstall hardcodes powershell + `Expand-Archive #603 trigger (shadow Microsoft.PowerShell.Archive in PSModulePath + PSExecutionPolicyPreference=Restricted):
    • Original install.js from main: fails with the exact Failed to install lark-cli: Command failed: powershell -Command Expand-Archive ... error from the issue report.
    • Patched install.js: succeeds (added 7 packages in 16s, lark-cli version 1.0.21).
  • Single-quote in TEMP (C:\Users\Administrator\AppData\Local\Temp\lark'quote-h1test):
    • Original: fails (MissingEndParenthesisInMethodCall).
    • Patched: succeeds.
  • Fallback path end-to-end (Node script forces primary to throw, runs fallback against an existing mkdtempSync destination): succeeds, files extracted.
  • Normal environment, no regression: succeeds in 16s.
  • Local unit tests: 16/16 pass (node --test scripts/install.test.js).

Related Issues

Summary by CodeRabbit

  • Refactor
    • Improved Windows installation reliability with enhanced archive extraction handling. The system now implements multiple fallback strategies to ensure more dependable package installation across various Windows configurations, reducing potential installation failures.

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.
@github-actions github-actions Bot added the size/M Single-domain feat or fix with limited business impact label Apr 29, 2026
@coderabbitai

coderabbitai Bot commented Apr 29, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Windows ZIP extraction logic in scripts/install.js is refactored into a new extractZipWindows helper function. The helper attempts extraction using System.IO.Compression.ZipFile.ExtractToDirectory, then falls back to PowerShell's Expand-Archive -Force if that fails, with combined error messages on total failure.

Changes

Cohort / File(s) Summary
Windows ZIP Extraction Helper
scripts/install.js
Introduces extractZipWindows helper that establishes robust extraction fallback logic: attempts native .NET compression first, retries with PowerShell Expand-Archive -Force on failure, and throws combined error messages if both methods fail. Removes direct PowerShell invocation from main install() function and replaces with helper call.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

size/M

Suggested reviewers

  • liangshuo-1

Poem

🐰 A ZIP that wouldn't unpack on PowerShell's stage,
Now tries the native way, then PowerShell's rage,
Two methods, one victory, the installer's new way,
Windows installations just brighter today! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: making Windows zip extraction resilient and references the related issue (#603).
Description check ✅ Passed The PR description is comprehensive and well-structured, covering summary, detailed changes, thorough test plan, and related issues, exceeding template requirements.
Linked Issues check ✅ Passed The PR fully addresses all coding requirements from issue #603: primary extraction via .NET BCL avoiding module loading, fallback via Expand-Archive with -LiteralPath, safe path passing through environment variables, error visibility via proper stdio configuration, and hardening with -NoProfile -ExecutionPolicy Bypass.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the Windows zip extraction resilience issue (#603); the refactoring of scripts/install.js with the new extractZipWindows() helper is entirely within the stated objectives.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/install-windows-zipfile

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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5dd4302c-7627-48be-8146-2be5a524e5cd

📥 Commits

Reviewing files that changed from the base of the PR and between 7752afa and 7f2149b.

📒 Files selected for processing (1)
  • scripts/install.js

Comment thread scripts/install.js
@github-actions

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@7f2149b9da87172ee379f8cab56785442e132959

🧩 Skill update

npx skills add larksuite/cli#fix/install-windows-zipfile -y -g

@codecov

codecov Bot commented Apr 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 64.14%. Comparing base (7752afa) to head (7f2149b).
⚠️ Report is 1 commits behind head on main.

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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sang-neo03
sang-neo03 merged commit 4181925 into main Apr 29, 2026
29 of 31 checks passed
@sang-neo03
sang-neo03 deleted the fix/install-windows-zipfile branch April 29, 2026 09:50
HomyeeKing pushed a commit to HomyeeKing/cli that referenced this pull request May 6, 2026
) (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.
tuxedomm pushed a commit to zhumiaoxin/cli that referenced this pull request Jun 6, 2026
) (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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/M Single-domain feat or fix with limited business impact

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows install fails from PowerShell 7 (Microsoft Store) because postinstall hardcodes powershell + `Expand-Archive

2 participants