Install-FromManifest: PCTypes alias map for rename reorg

Phase 1 of the gea-shopfloor-* rename per project-shopfloor-rename-reorg.
Manifests can use either old names (Standard, Standard-Machine, CMM,
Keyence, etc.) or new names (gea-shopfloor-collections,
gea-shopfloor-cmm, gea-shopfloor-keyence, etc.) interchangeably.

Equivalence sets defined inline. Each set is a list of names that all
match the same identity. The match logic resolves the current PC's
identity AND each PCTypes entry into their alias sets, then matches
if the sets intersect.

Standard maps to all three new shopfloor variants (collections,
nocollections, common) so an existing PCTypes=['Standard'] manifest
entry still applies when PC pc-type.txt becomes any of the three.
Standard-Machine maps to (collections, nocollections) only since
Timeclock subtype is now collapsed under common.

Smoke-tested on win11 VM as SYSTEM via qga: dispatcher run with
PCType='gea-shopfloor-collections' against the existing common
manifest (Standard-only PCTypes filters) fires Oracle / FMS hosts pin
correctly. Same run with PCType='Standard' PCSubType='Machine' fires
identically.

Phases 3+4 (repo folder renames + startnet.cmd menu reorg) deferred to
the next session - high breakage risk, must ship atomically.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-05-03 10:15:57 -04:00
parent 395d045cdf
commit 5fe7e7767f

View File

@@ -38,11 +38,16 @@ $ErrorActionPreference = 'Continue'
# logged; manifests tagged with a newer MINOR are fine. # logged; manifests tagged with a newer MINOR are fine.
# #
# Changelog: # Changelog:
# 2.3 - PCTypes filter accepts old (Standard, Standard-Machine, CMM, ...)
# and new (gea-shopfloor-collections, gea-shopfloor-cmm, ...) names
# interchangeably via alias sets. Transitional for the rename reorg.
# 2.2 - added TargetMachineNumbers filter (reads C:\Enrollment\machine-number.txt
# then falls back to DNC registry HKLM\...\GE Aircraft Engines\DNC\General\MachineNo)
# 2.1 - added TargetHostnames filter (exact + -like wildcards) # 2.1 - added TargetHostnames filter (exact + -like wildcards)
# 2.0 - initial Stage 2a: PS1/BAT/File/Registry/INF action types, # 2.0 - initial Stage 2a: PS1/BAT/File/Registry/INF action types,
# Always/MarkerFile/ValueMatches/pnputil detection, PCTypes filter # Always/MarkerFile/ValueMatches/pnputil detection, PCTypes filter
$LIB_MANIFEST_MAJOR = 2 $LIB_MANIFEST_MAJOR = 2
$LIB_MANIFEST_MINOR = 1 $LIB_MANIFEST_MINOR = 3
$logDir = Split-Path -Parent $LogFile $logDir = Split-Path -Parent $LogFile
if (-not (Test-Path $logDir)) { if (-not (Test-Path $logDir)) {
@@ -360,14 +365,67 @@ function Invoke-InstallerAction {
# Both filters are ANDed so they compose: scope to a type AND a hostname # Both filters are ANDed so they compose: scope to a type AND a hostname
# subset, or either alone. Case-insensitive throughout. # subset, or either alone. Case-insensitive throughout.
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# PCTypes alias map for the 2026-05-03 rename reorg. Manifests may use
# either old names (Standard, Standard-Machine, CMM, etc.) or new names
# (gea-shopfloor-collections, gea-shopfloor-cmm, etc.). Each entry below
# is a set of names that all match the same identity. The match logic
# resolves the current PC's identity AND each PCTypes entry into their
# alias sets, then matches if the sets intersect. See
# project-shopfloor-rename-reorg memory for the full rename plan.
$script:_pcTypeAliasGroups = @(
@('Standard', 'gea-shopfloor-collections', 'gea-shopfloor-nocollections', 'gea-shopfloor-common'),
@('Standard-Machine', 'gea-shopfloor-collections', 'gea-shopfloor-nocollections'),
@('Standard-Timeclock', 'gea-shopfloor-common'),
@('CMM', 'gea-shopfloor-cmm'),
@('Keyence', 'gea-shopfloor-keyence'),
@('Lab', 'gea-shopfloor-common'),
@('WaxAndTrace', 'gea-shopfloor-waxtrace'),
@('Genspect', 'gea-shopfloor-genspect'),
@('Display', 'gea-shopfloor-display'),
@('Heattreat', 'gea-shopfloor-heattreat')
)
# Returns every alias set (each itself a string array) that contains $name.
# Multiple groups can return the same name (e.g. "Standard" appears in the
# super-group covering all three new shopfloor variants AND has its own
# subtype-specific groups - by design, matches widely).
function Get-PCTypeAliasSets {
param([string]$Name)
$hits = @()
foreach ($g in $script:_pcTypeAliasGroups) {
foreach ($n in $g) {
if ($n -ieq $Name) { $hits += ,$g; break }
}
}
return ,$hits
}
function Test-PCTypeMatches { function Test-PCTypeMatches {
param($App, [string]$Type, [string]$SubType) param($App, [string]$Type, [string]$SubType)
if (-not $App.PCTypes -or $App.PCTypes.Count -eq 0) { return $true } if (-not $App.PCTypes -or $App.PCTypes.Count -eq 0) { return $true }
if (-not $Type) { return $true } if (-not $Type) { return $true }
# Build the set of strings the CURRENT PC matches: bare PCType,
# "Type-SubType", and every alias-set member of either.
$myNames = New-Object System.Collections.Generic.HashSet[string]([System.StringComparer]::OrdinalIgnoreCase)
[void]$myNames.Add($Type)
if ($SubType) { [void]$myNames.Add("$Type-$SubType") }
foreach ($n in @($Type, "$Type-$SubType") | Where-Object { $_ }) {
foreach ($g in (Get-PCTypeAliasSets -Name $n)) {
foreach ($alias in $g) { [void]$myNames.Add($alias) }
}
}
foreach ($t in $App.PCTypes) { foreach ($t in $App.PCTypes) {
if ($t -eq '*') { return $true } if ($t -eq '*') { return $true }
if ($t -eq $Type) { return $true } if ($myNames.Contains($t)) { return $true }
if ($SubType -and $t -eq "$Type-$SubType") { return $true } # Manifest entry's PCTypes value may itself be an alias - expand it
# and check overlap with the PC's identity set.
foreach ($g in (Get-PCTypeAliasSets -Name $t)) {
foreach ($alias in $g) {
if ($myNames.Contains($alias)) { return $true }
}
}
} }
return $false return $false
} }
@@ -387,6 +445,47 @@ function Test-HostnameMatches {
return $false return $false
} }
# Machine-number filter. Stable identifier tied to the bay; survives PC
# replacement at the same machine. Source of truth = the value the tech
# entered at the PXE menu, persisted to C:\Enrollment\machine-number.txt
# by startnet.cmd. Falls back to the DNC registry if that file is missing
# (covers PCs that pre-date this filter being introduced).
$script:_cachedMachineNumber = $null
function Get-CurrentMachineNumber {
if ($null -ne $script:_cachedMachineNumber) { return $script:_cachedMachineNumber }
$candidates = @(
'C:\Enrollment\machine-number.txt'
)
foreach ($p in $candidates) {
if (Test-Path -LiteralPath $p) {
$v = (Get-Content -LiteralPath $p -ErrorAction SilentlyContinue | Select-Object -First 1)
if ($v) { $script:_cachedMachineNumber = $v.Trim(); return $script:_cachedMachineNumber }
}
}
foreach ($r in @(
'HKLM:\SOFTWARE\WOW6432Node\GE Aircraft Engines\DNC\General',
'HKLM:\SOFTWARE\GE Aircraft Engines\DNC\General'
)) {
if (Test-Path $r) {
$p = Get-ItemProperty -Path $r -ErrorAction SilentlyContinue
if ($p.MachineNo) { $script:_cachedMachineNumber = [string]$p.MachineNo; return $script:_cachedMachineNumber }
}
}
$script:_cachedMachineNumber = ''
return ''
}
function Test-MachineNumberMatches {
param($App)
if (-not $App.TargetMachineNumbers -or $App.TargetMachineNumbers.Count -eq 0) { return $true }
$myNumber = Get-CurrentMachineNumber
if (-not $myNumber) { return $false } # entry restricts by machine #, but PC has no machine # -> exclude
foreach ($n in $App.TargetMachineNumbers) {
if ([string]$n -ieq $myNumber) { return $true }
}
return $false
}
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Main loop # Main loop
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -414,6 +513,13 @@ foreach ($app in $config.Applications) {
continue continue
} }
if (-not (Test-MachineNumberMatches -App $app)) {
$myNum = Get-CurrentMachineNumber
Write-InstallLog " TargetMachineNumbers filter: entry targets $($app.TargetMachineNumbers -join ',') but machine number is $(if ($myNum) { $myNum } else { '(none)' }) - skipping"
$pcFiltered++
continue
}
if (Test-AppInstalled -App $app) { if (Test-AppInstalled -App $app) {
Write-InstallLog ' Already installed at expected version - skipping' Write-InstallLog ' Already installed at expected version - skipping'
$skipped++ $skipped++