Skip to content

Fix Compare-InstallerSnapshots.ps1 StrictMode crash on empty UninstallEntries - #1008

Open
johnml1135 wants to merge 2 commits into
mainfrom
fix/compare-installer-snapshots-strictmode
Open

Fix Compare-InstallerSnapshots.ps1 StrictMode crash on empty UninstallEntries#1008
johnml1135 wants to merge 2 commits into
mainfrom
fix/compare-installer-snapshots-strictmode

Conversation

@johnml1135

@johnml1135 johnml1135 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Compare-InstallerSnapshots.ps1 crashed under Set-StrictMode whenever a
before/after snapshot had zero UninstallEntries. Collect-InstallerSnapshot
serializes an empty list as {} (a Windows PowerShell ConvertTo-Json
quirk), and the script read .DisplayName directly off that property-less
object, which throws under strict mode. This surfaced while gathering
installer evidence: a silent MSI install whose before-snapshot legitimately
had 0 uninstall entries made the diff tool abort instead of producing a
report.

The fix extracts DisplayName through a new Get-DisplayNameList helper
that tolerates $null, a single unwrapped object, and the empty {}
object, skipping any entry without a usable DisplayName. Everything else
about the diff -- registry and file comparison -- is untouched.

Where to look

  • Get-DisplayNameList (scripts/Agent/Compare-InstallerSnapshots.ps1) --
    the null/empty/unwrapped-object handling is the whole fix.
  • Both call sites (before- and after-snapshot UninstallEntries) now go
    through the same helper, so the two snapshots can't drift in how they
    handle a missing entry.

Deliberately not here

  • Collect-InstallerSnapshot's {}-for-empty-list serialization is
    unchanged; hardening the producer to always emit [] is a separate,
    reasonable follow-up.

Verification

  • Ran the script against a synthetic before/after pair with an empty {}
    UninstallEntries (the exact shape that crashed): exits 0, produces the
    expected diff.
  • gitlint --ignore body-is-missing --commits origin/main..HEAD -- clean.
  • All 6 CI checks pass.

Reading this a year from now -- start here

The one thing worth knowing beyond the pitch above: the {}-for-empty-list
quirk lives in Collect-InstallerSnapshot's JSON serialization, not in this
script. This script tolerates it rather than fixing the producer, so any
other consumer of a snapshot file needs the same tolerance.

Preflight review details

See .review/summary.md on the reviewing machine for the full pass:
no contract/API impact, no open findings, verification steps as listed
above.


This change is Reviewable

@github-actions

This comment has been minimized.

@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown

NUnit Tests

    1 files   -     1      1 suites   - 1   9m 28s ⏱️ - 5m 3s
5 761 tests ±    0  5 680 ✅ +   38  81 💤 ± 0  0 ❌  - 38 
5 770 runs   - 5 770  5 689 ✅  - 5 615  81 💤  - 81  0 ❌  - 74 

Results for commit 1051486. ± Comparison against base commit 7f93348.

♻️ This comment has been updated with latest results.

@codecov-commenter

codecov-commenter commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 40.33%. Comparing base (f505729) to head (1051486).
⚠️ Report is 41 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1008      +/-   ##
==========================================
+ Coverage   32.97%   40.33%   +7.35%     
==========================================
  Files        1202     1542     +340     
  Lines      278291   365073   +86782     
  Branches    37166    40233    +3067     
==========================================
+ Hits        91776   147237   +55461     
- Misses     158649   188551   +29902     
- Partials    27866    29285    +1419     

see 501 files with indirect coverage changes

🚀 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.

johnml1135 and others added 2 commits August 14, 2026 02:59
Compare-InstallerSnapshots.ps1 runs under Set-StrictMode -Version
Latest and read $p.DisplayName directly for every item in a
snapshot's UninstallEntries. When a snapshot has no uninstall
entries, Collect-InstallerSnapshot serializes the empty list as {}
(a Windows PowerShell ConvertTo-Json quirk), so ConvertFrom-Json
yields a single property-less object. Reading .DisplayName on it
threw "The property 'DisplayName' cannot be found on this object",
aborting the diff (observed after a silent MSI install whose
before-snapshot had 0 entries).

Extract DisplayName via a helper that tolerates $null, a single
unwrapped object, and the empty {} object, skipping entries without
a usable DisplayName.

Verified: re-running against the real before/after snapshots that
triggered the crash now completes (exit 0) and writes
diff-before-vs-after-install.txt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The comment above Get-DisplayNameList restated the function's name,
named Collect-InstallerSnapshot as a cross-file provenance pointer,
and ran well past the repo's 200-character implementation-comment
budget. Trim it to the single non-obvious fact a reader needs: which
input shapes StrictMode would otherwise throw on.
@johnml1135
johnml1135 force-pushed the fix/compare-installer-snapshots-strictmode branch from b256892 to 1051486 Compare August 14, 2026 07:03
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.

2 participants