Track manifest apps in the Applications catalog; make CMM gate contextual
All checks were successful
CI / backend (push) Successful in 1m43s
CI / naming (push) Successful in 1s
CI / frontend (push) Successful in 8s

seed-applications: a flask geenforce seed-applications command + service that
reads the imaging-PC-type manifests and creates a core Application for every
installer entry (MSI/EXE/CMD/BAT), so shopdb tracks what GE-Enforce actually
deploys. Idempotent, deduped by appname; File/Registry/PS1/INF config entries
are skipped. Run against the West Jefferson reference: 27 apps tracked (PC-DMIS
2016/2019/2026, eDNC, Oracle Client, Adobe Reader, HostExplorer, the VC++ redist
matrix, Keyence VR-6000, PowerShell, Display Kiosk, ...). 2 tests.

Editor: the CMM version gate (_CmmVersion) now only shows for CMM scopes - it is
metrology-specific, so a printer/common entry form no longer carries the
irrelevant field.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-07-12 21:21:49 -04:00
parent d9d40297b6
commit 796007bcca
4 changed files with 119 additions and 2 deletions

View File

@@ -0,0 +1,54 @@
"""GE-Enforce seeds the core Applications catalog from what the manifests install.
Only installer entries (MSI/EXE/CMD/BAT) become Applications; File/Registry/PS1/
INF config entries do not. Idempotent + deduped by appname.
"""
from plugins.geenforce import service
from shopdb.core.models import Application
MANIFEST = {
'Version': '2.6',
'Applications': [
{'Name': 'eDNC', 'Type': 'MSI', 'Installer': 'apps/edns.msi',
'_comment': 'eDNC (Universal Data Collection). Bundles NTLARS.'},
{'Name': 'UDC', 'Type': 'EXE', 'Installer': 'apps/udc.exe'},
{'Name': 'Config drop', 'Type': 'File', 'Source': 'configs/x.json',
'Destination': 'C:\\x.json'},
{'Name': 'Firewall rule', 'Type': 'PS1', 'Script': 'scripts/fw.ps1'},
{'Name': 'FMS host pin', 'Type': 'Registry', 'RegPath': 'HKLM:\\x',
'RegName': 'y', 'RegValue': 1, 'RegType': 'DWord'},
],
}
def test_seed_only_installer_apps(db):
result = service.seed_applications_from_manifests(
[('gea-shopfloor-collections', 'runtime', MANIFEST)])
service.db.session.commit()
assert set(result['created']) == {'eDNC', 'UDC'}
names = {a.appname for a in Application.query.all()}
assert 'eDNC' in names and 'UDC' in names
# config / script / registry entries are not applications
assert 'Config drop' not in names
assert 'Firewall rule' not in names
assert 'FMS host pin' not in names
edns = Application.query.filter_by(appname='eDNC').first()
assert edns.isinstallable is True
assert edns.installpath == 'apps/edns.msi'
assert edns.appdescription.startswith('eDNC (Universal Data Collection)')
def test_seed_is_idempotent_and_deduped(db):
manifests = [('a', 'runtime', MANIFEST), ('b', 'runtime', MANIFEST)]
first = service.seed_applications_from_manifests(manifests)
service.db.session.commit()
# deduped across the two manifests: 2 created, not 4
assert len(first['created']) == 2
# re-run: nothing new, both already tracked
second = service.seed_applications_from_manifests(manifests)
service.db.session.commit()
assert second['created'] == []
assert set(second['existing']) == {'eDNC', 'UDC'}
assert Application.query.filter(Application.appname.in_(['eDNC', 'UDC'])).count() == 2