Setup and marker fixes: correct ARP entry, clear the entry a ZIP install invalidates, refresh the marker on update - #214
Conversation
…ZIP install invalidates
Two consequences of {app} not following the answer file's installPath. Both are
verified on the lab server, not only in contract tests.
Apps & Features showed InstallLocation = {app}, which is where the uninstaller
ended up rather than where NodePilot was installed: /ANSWERFILE skips the
directory page, so {app} keeps DefaultDirName while the adapter installs to
installPath. On a lab install to C:\Program Files\NodePilotC the entry pointed at
C:\Program Files\NodePilot, a directory holding the uninstaller and nothing else.
ssPostInstall now corrects it from the installation marker, the same source the
uninstaller reads. Best-effort by construction: an exception in ssPostInstall
does not change the exit code, so a failed write simply leaves Inno's own value.
The GUI setup keeps its uninstaller inside the install directory, so running the
ZIP installer over a setup installation - both are documented ways to install the
same product - deleted it and left an Add/Remove Programs entry Windows can
neither run nor clear. That is how the lab machine came to carry two dead
entries. Sparing the file is not available: Assert-NodePilotExtractedFiles
requires the install directory to hold exactly the signed artifact and nothing
else, which is what stops a binary being swapped before the service executes it -
the first attempt at this fix spared unins*/deploy and broke that check
immediately. The now-dead entry is removed instead, and only when its uninstaller
lived in the directory just emptied and is really gone.
Known limitation, unchanged: the installation marker is a single key, so a host
running more than one instance keeps only the most recently installed one, and
uninstalling any instance clears it for all. The uninstaller falls back to the
old defaults when it is absent.
Tests: contracts for the corrected ARP write and its marker source, and for the
stale-entry removal including both narrowing conditions - without them the check
would pass on a script that deletes every uninstall entry on the machine.
Only Install-NodePilot.ps1 wrote HKLM\SOFTWARE\NodePilot\Server, so its Version
kept naming the last INSTALL and every script-driven update was invisible in it.
That value is what the setup wizard puts on its mode page ("NodePilot <version>
is already installed in <path>") and the obvious thing for an inventory to read.
Measured in the lab before the fix: binaries updated from 1.2.6-rc1 to the 1.2.5
artifact - ProductVersion really changed - while the marker still claimed
1.2.6-rc1. After: marker and binary move together.
Version only. Path, service name, provider and port are not changed by an update
and are already correct. Guarded on InstallPath because the marker is a single
machine-wide key: on a host running more than one instance it describes whichever
was installed last, and stamping this update's version onto another instance's
marker is worse than leaving it stale. A write failure stays a warning - it costs
discoverability, not a working installation.
|
Added the remaining marker point to this branch (
Verified on CM1:
Previously the marker column would have stayed at
|
The two consequences of Inno's
{app}not following the answer file'sinstallPath— the remaining open point from the CM1 freeze test. Both verified on the lab server.Apps & Features named the wrong directory
/ANSWERFILEskips the directory page, so{app}keepsDefaultDirNamewhile the adapter installs toinstallPath. Inno fillsInstallLocationwith{app}, so the entry pointed at a directory holding the uninstaller and nothing else.Measured on a lab install to
C:\Program Files\NodePilotC:InstallLocationC:\Program Files\NodePilot\C:\Program Files\NodePilotC\InstallPathC:\Program Files\NodePilotCssPostInstallcorrects it from the installation marker — the same source the uninstaller reads. Best-effort by construction: an exception there does not change the exit code (as the[Files]note already records), so a failed write just leaves Inno's own value, which is today's behaviour.A ZIP install over a setup installation left a dead entry
The GUI setup keeps
unins000.exeinside the install directory.Install-NodePilot.ps1empties that directory, so the entry pointing at it survives with no uninstaller behind it — Windows can then neither run nor clear it. This is exactly how the lab machine came to carry two dead entries at the start of the freeze test.Sparing the uninstaller is not available.
Assert-NodePilotExtractedFilesrequires the install directory to hold exactly the signed artifact and nothing else — that check is what stops a binary being swapped before the service executes it. The first attempt at this fix sparedunins*anddeploy\and broke it on the first live run:So the entry that has just become dead is removed instead, and only under both conditions:
UninstallStringpoints into the directory just emptied, andAn entry that still works, or one belonging elsewhere, is left alone. Without both conditions this would quietly become "delete every uninstall entry on the machine", so both are pinned by contract tests.
Known limitation, unchanged
The installation marker is a single key (
HKLM\SOFTWARE\NodePilot\Server). A host running more than one instance keeps only the most recently installed one, and uninstalling any instance clears it for all — observed again in this round. The uninstaller falls back to the old defaults when it is absent. Fixing that means keying the marker per service name, which is a larger change than this one.Verified live on CM1
installPath→ ARPInstallLocationnames the product directory./healthz/ready200), all 174 workflows intact, and the dead entry gone instead of orphaned.Ran:
Test-DeploymentTemplates.ps1,Test-SetupAdapter.ps1. No product code outsidedeploy/is touched, so the backend suites are unaffected and covered by CI.