Audit fixes: a compat verdict must never be able to brick a working box
The audit found the new machinery could do more harm than the bugs it prevents. - apply.sh reused the 'nothing left to do' exit -- which touches the PERMANENT kill switch, cleared only by install.sh, never by update.sh -- for the incompatible case. On TrueNAS 26 (providers ok, nested opt-out) both modules go quiet, so the switch would fire and the release that fixed 26 could never re-enable itself. Retirement and incompatibility now take different exits. - A network blip, a re-export, or a conditional def all read as BROKEN. Each is now 'unknown', which changes nothing, rather than evidence strong enough to disable a module. - 'native' outranked BROKEN everywhere but apply.sh, so a TrueNAS that reworded the guard AND reshaped the functions rendered as good news. - compat.py --tree read B2_BLOCK's own 'restic = True' as native support, so the documented way to check a live box lied on every patched machine. - The signature check was a name-subset test. It passed reorders, kw-only conversions, and added required params -- and it had already passed a real bug: restic_backup takes 4 args on 24.10/25.04, and the wrapper forwarded 5. Nested backups have been raising TypeError on those releases the whole time. The wrapper now forwards *args/**kwargs. - release.sh --promote was unreachable: it died if the tag existed, the gate died if it did not. The tests hid it by always tagging first.
This commit is contained in:
@@ -307,3 +307,33 @@ def test_guard_is_relaxed_only_after_traversal_is_installed():
|
||||
src.index("patch_file(crud_py, CRUD_BLOCK)"),
|
||||
]
|
||||
assert order == sorted(order), "crud.py must be patched last"
|
||||
|
||||
|
||||
class TestWrappersDoNotHardcodeStockArity:
|
||||
"""iX changes the tail of these signatures between releases.
|
||||
|
||||
SYNC_BLOCK used to spell out `(middleware, job, cloud_backup, dry_run, rate_limit)`
|
||||
and forward all five. But 24.10 and 25.04 declare only four -- `rate_limit` arrived
|
||||
in 25.10 -- so every nested backup on those two releases raised
|
||||
`TypeError: restic_backup() takes 4 positional arguments but 5 were given`.
|
||||
It shipped broken and nothing noticed, because the compat check at the time only
|
||||
asked whether the parameter NAMES still appeared somewhere in the signature.
|
||||
|
||||
Forwarding *args/**kwargs makes the wrapper indifferent to a trailing parameter
|
||||
being added or dropped, which is the only part iX actually churns.
|
||||
"""
|
||||
|
||||
def test_restic_backup_forwards_rather_than_naming_stock_params(self):
|
||||
block = extract_blocks()["SYNC_BLOCK"]
|
||||
assert "async def restic_backup(middleware, job, cloud_backup, *args, **kwargs)" in block
|
||||
assert "_tc_orig_restic_backup(middleware, job, cloud_backup, *args, **kwargs)" in block
|
||||
|
||||
# Comments stripped: the block's own commentary explains the rate_limit
|
||||
# history, and that must not be mistaken for the code re-declaring it.
|
||||
code = "\n".join(
|
||||
line for line in block.splitlines()
|
||||
if not line.lstrip().startswith("#")
|
||||
)
|
||||
assert "rate_limit" not in code, (
|
||||
"naming a trailing stock parameter re-introduces the arity bug"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user