Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ GitHub Releases page; `0.8.0` is the new starting line.

## Unreleased

- **Windows upgrade version display fix.** In-place upgrades no longer show a stale version number or re-trigger the update prompt. Inno Setup now wipes `_internal` before installing new files, preventing old `dist-info` directories from accumulating and causing `importlib.metadata` to report the previous version.

## 0.31.0 (2026-06-02)

### What changed in this release
Expand Down
10 changes: 10 additions & 0 deletions packages/windows-installer/installer.iss
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ Name: "modifypath"; Description: "Add Pythinker to your PATH"; \
Name: "modifypathmachine"; Description: "Add Pythinker to the system PATH"; \
GroupDescription: "Shell integration:"; Check: IsAdminInstallMode

[InstallDelete]
; Remove the entire _internal directory before installing new files so that
; in-place upgrades do not accumulate stale version-stamped dist-info dirs
; (e.g. pythinker_code-0.28.0.dist-info alongside 0.31.0.dist-info).
; importlib.metadata.version() picks the first match it finds, which is the
; alphabetically-lower (older) version, causing the UI to display a stale
; version number and re-trigger the update prompt after every upgrade.
; _internal is 100% app payload — no user data lives there.
Type: filesandordirs; Name: "{app}\_internal"

[Files]
Source: "..\..\dist\pythinker\*"; DestDir: "{app}"; \
Flags: ignoreversion recursesubdirs createallsubdirs
Expand Down
8 changes: 8 additions & 0 deletions tests/test_release_update_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,14 @@ def test_windows_installer_signs_update_artifacts_when_credentials_are_available
assert re.search(r"NewPath\s*:=\s*Param\s*\+\s*';'\s*\+\s*OrigPath", installer_script)
assert not re.search(r"NewPath\s*:=\s*OrigPath\s*\+\s*';'\s*\+\s*Param", installer_script)
assert not re.search(r"StringChangeEx\(\s*OrigPath\s*,\s*Param\s*,", installer_script)
# [InstallDelete] must wipe _internal before [Files] runs so that in-place
# upgrades never accumulate stale dist-info dirs (pythinker_code-0.28.0.dist-info
# alongside 0.31.0 makes importlib.metadata pick the old version first).
assert re.search(
r"\[InstallDelete\].*filesandordirs.*\{app\}\\_internal",
installer_script,
re.DOTALL,
)

# Signing only the final setup executable leaves Smart App Control and AV
# heuristics to inspect unsigned bundled/native helper files. Keep signing
Expand Down
Loading