geenforce: gate publishing on the library version, not on the manifest's own
The publish gate exists because a minor version bump that NARROWS behaviour is not backward compatible: _CmmVersion arrived in lib 2.6, and an older lib does not know the field, so every gated entry looks unfiltered and it installs every PC-DMIS version it cannot detect, on every CMM, in one cycle. It was comparing the fleet's reported library versions against manifestversion. That is the manifest's own 'Version' field. For a share-imported manifest the two numbering schemes happen to coincide; for a scope authored in code they do not, and seed_display_scope writes '2.0' - which every kiosk exceeds. So the gate passed on the scope that most needed it. A scope now declares minlibversion. Unset, the requirement is DERIVED from what the manifest actually uses, so a scope written before this column existed is still judged on its contents rather than on a number that says nothing about the library. Only features that narrow behaviour belong in that table; an addition an old lib ignores harmlessly needs no floor. manifestversion remains the last fallback, which preserves what share-imported manifests already relied on.
This commit is contained in:
@@ -116,6 +116,45 @@ def hosts_below_libversion(scopename, phase, required):
|
||||
return sorted(set(behind)), floor
|
||||
|
||||
|
||||
# Manifest features and the client library that first understood them. A
|
||||
# library older than this does not merely skip the feature, it MISREADS the
|
||||
# entry: 2.6 added _CmmVersion, and an older lib treats a version-gated entry as
|
||||
# ungated and installs every PC-DMIS build it cannot detect.
|
||||
#
|
||||
# Only features that NARROW behaviour belong here. An addition an old lib
|
||||
# ignores harmlessly does not need a floor.
|
||||
LIBVERSION_FEATURES = (
|
||||
('cmmversion', '2.6'),
|
||||
)
|
||||
|
||||
|
||||
def required_libversion(scope):
|
||||
"""Lowest client library that may enforce this scope.
|
||||
|
||||
An explicit minlibversion wins. Otherwise it is derived from the features
|
||||
the draft actually uses, so a scope written before this column existed is
|
||||
still gated on what it contains rather than on manifestversion - which is
|
||||
the manifest's own 'Version' field and says nothing about the library.
|
||||
|
||||
Falls back to manifestversion when nothing else applies, preserving the
|
||||
behaviour share-imported manifests already relied on, where the two
|
||||
numbering schemes do coincide.
|
||||
"""
|
||||
declared = (scope.minlibversion or '').strip()
|
||||
if declared:
|
||||
return declared
|
||||
|
||||
floor = None
|
||||
for attribute, version in LIBVERSION_FEATURES:
|
||||
if any(getattr(entry, attribute, None) for entry in scope.entries):
|
||||
candidate = parse_libversion(version)
|
||||
if candidate and (floor is None or candidate > floor[0]):
|
||||
floor = (candidate, version)
|
||||
if floor:
|
||||
return floor[1]
|
||||
return scope.manifestversion
|
||||
|
||||
|
||||
def _installed_app_model():
|
||||
"""Lazily import the computers plugin's ComputerInstalledApp.
|
||||
|
||||
@@ -184,11 +223,11 @@ def publish_scope(scopename, phase, notes=None, publishedby=None, force=False):
|
||||
raise ValueError(f'No scope {scopename}/{phase}')
|
||||
|
||||
if not force:
|
||||
behind, floor = hosts_below_libversion(
|
||||
scopename, phase, scope.manifestversion)
|
||||
required = required_libversion(scope)
|
||||
behind, floor = hosts_below_libversion(scopename, phase, required)
|
||||
if behind:
|
||||
raise LibVersionTooOldError(
|
||||
scopename, scope.manifestversion, floor or 'unknown', behind)
|
||||
scopename, required, floor or 'unknown', behind)
|
||||
|
||||
text = scope_to_json(scope)
|
||||
maxversion = db.session.query(
|
||||
|
||||
Reference in New Issue
Block a user