diff --git a/playbook/shopfloor-setup/CMM/lib/Install-FromManifest.ps1 b/playbook/shopfloor-setup/CMM/lib/Install-FromManifest.ps1 index 86b3f07..fcea812 100644 --- a/playbook/shopfloor-setup/CMM/lib/Install-FromManifest.ps1 +++ b/playbook/shopfloor-setup/CMM/lib/Install-FromManifest.ps1 @@ -123,6 +123,22 @@ function Test-AppInstalled { "File" { return Test-Path $App.DetectionPath } + "FileVersion" { + # Compare a file's VersionInfo.FileVersion against the + # manifest's expected value. Used for version-pinned MSI/EXE + # installs where existence alone doesn't tell you whether + # the right release is on disk. Exact string match - the + # manifest must carry the exact version the vendor stamps + # into the binary. + if (-not (Test-Path $App.DetectionPath)) { return $false } + if (-not $App.DetectionValue) { + Write-InstallLog " FileVersion detection requires DetectionValue - treating as not installed" "WARN" + return $false + } + $actual = (Get-Item $App.DetectionPath -ErrorAction Stop).VersionInfo.FileVersion + if (-not $actual) { return $false } + return ($actual -eq $App.DetectionValue) + } "Hash" { # Compare SHA256 of the on-disk file against the manifest's # expected value. Used for content-versioned files that do not diff --git a/playbook/shopfloor-setup/Standard/lib/Install-FromManifest.ps1 b/playbook/shopfloor-setup/Standard/lib/Install-FromManifest.ps1 index 5a3e8dd..1022cfb 100644 --- a/playbook/shopfloor-setup/Standard/lib/Install-FromManifest.ps1 +++ b/playbook/shopfloor-setup/Standard/lib/Install-FromManifest.ps1 @@ -111,6 +111,23 @@ function Test-AppInstalled { "File" { return Test-Path $App.DetectionPath } + "FileVersion" { + # Compare a file's VersionInfo.FileVersion against the + # manifest's expected value. Used for version-pinned MSI/EXE + # installs where existence alone doesn't tell you whether + # the right release is on disk (e.g. eDNC 6.4.3 vs 6.4.4 + # both leave NTLARS.exe in the same path). Exact string + # match - the manifest must carry the exact version the + # vendor stamps into the binary. + if (-not (Test-Path $App.DetectionPath)) { return $false } + if (-not $App.DetectionValue) { + Write-InstallLog " FileVersion detection requires DetectionValue - treating as not installed" "WARN" + return $false + } + $actual = (Get-Item $App.DetectionPath -ErrorAction Stop).VersionInfo.FileVersion + if (-not $actual) { return $false } + return ($actual -eq $App.DetectionValue) + } "Hash" { # Compare SHA256 of the on-disk file against the manifest's # expected value. Used for content-versioned files that do not diff --git a/playbook/shopfloor-setup/Standard/machineapps-manifest.template.json b/playbook/shopfloor-setup/Standard/machineapps-manifest.template.json index c7cdfe3..820e993 100644 --- a/playbook/shopfloor-setup/Standard/machineapps-manifest.template.json +++ b/playbook/shopfloor-setup/Standard/machineapps-manifest.template.json @@ -14,13 +14,14 @@ "DetectionValue": "REPLACE_WITH_PINNED_UDC_VERSION" }, { - "_comment": "eDNC 6.4.3. Ships with NTLARS bundled (NTLARS.exe lands at C:\\Program Files (x86)\\Dnc\\Common\\ as part of the same install), so no separate NTLARS entry is needed. SITESELECTED encodes the site (was a recurring bug in early shopfloor-setup scripts that omitted it). Adjust to your site's value if not West Jefferson. Detection uses File on the NTLARS binary: catches the case where eDNC is installed but the sub-components we actually care about are missing. DisplayVersion detection via Registry would be tighter but the x86 uninstall key path for eDNC varies across 6.x releases.", + "_comment": "eDNC. Ships with NTLARS bundled (NTLARS.exe lands at C:\\Program Files (x86)\\Dnc\\Common\\ as part of the same install), so no separate NTLARS entry is needed. SITESELECTED encodes the site (was a recurring bug in early shopfloor-setup scripts that omitted it). Adjust to your site's value if not West Jefferson. Detection uses FileVersion on DncMain.exe so version upgrades actually fire (File-existence detection on NTLARS would skip the upgrade because the file already exists from the prior version). Update workflow: drop the new MSI on the SFLD share, bump DetectionValue + Installer in this manifest to the new vendor-stamped FileVersion, and the next user logon installs it.", "Name": "eDNC (bundles NTLARS)", "Installer": "eDNC-6.4.3.msi", "Type": "MSI", "InstallArgs": "/qn /norestart ALLUSERS=1 REBOOT=ReallySuppress SITESELECTED=\"West Jefferson\"", - "DetectionMethod": "File", - "DetectionPath": "C:\\Program Files (x86)\\Dnc\\Common\\NTLARS.exe" + "DetectionMethod": "FileVersion", + "DetectionPath": "C:\\Program Files (x86)\\Dnc\\bin\\DncMain.exe", + "DetectionValue": "6.4.3" }, { "_comment": "Custom eMxInfo.txt (site-specific eDNC config). No vendor installer - the secret file lives on the SFLD share alongside the eDNC MSI. Install-eMxInfo.cmd copies it to both 32-bit and 64-bit eDNC Program Files paths. Hash detection catches both 'file missing' and 'file is a stale version'. Yearly rotation procedure: drop the new eMxInfo.txt on the share, recompute its SHA256 (PowerShell: (Get-FileHash .\\eMxInfo.txt -Algorithm SHA256).Hash), paste the new hash into DetectionValue here, save. Every Machine PC catches up on the next user logon. Content-sensitive: eMxInfo.txt must NEVER be committed to git (already in .gitignore).", diff --git a/playbook/shopfloor-setup/common/lib/Install-FromManifest.ps1 b/playbook/shopfloor-setup/common/lib/Install-FromManifest.ps1 index 5a3e8dd..1022cfb 100644 --- a/playbook/shopfloor-setup/common/lib/Install-FromManifest.ps1 +++ b/playbook/shopfloor-setup/common/lib/Install-FromManifest.ps1 @@ -111,6 +111,23 @@ function Test-AppInstalled { "File" { return Test-Path $App.DetectionPath } + "FileVersion" { + # Compare a file's VersionInfo.FileVersion against the + # manifest's expected value. Used for version-pinned MSI/EXE + # installs where existence alone doesn't tell you whether + # the right release is on disk (e.g. eDNC 6.4.3 vs 6.4.4 + # both leave NTLARS.exe in the same path). Exact string + # match - the manifest must carry the exact version the + # vendor stamps into the binary. + if (-not (Test-Path $App.DetectionPath)) { return $false } + if (-not $App.DetectionValue) { + Write-InstallLog " FileVersion detection requires DetectionValue - treating as not installed" "WARN" + return $false + } + $actual = (Get-Item $App.DetectionPath -ErrorAction Stop).VersionInfo.FileVersion + if (-not $actual) { return $false } + return ($actual -eq $App.DetectionValue) + } "Hash" { # Compare SHA256 of the on-disk file against the manifest's # expected value. Used for content-versioned files that do not