v0.3.4: one implementation of apply/revert (patch/mw_patch.py)
The "strip the TRUECLOUD_PATCH block" logic existed twice -- in apply.sh's heredoc and in an inline heredoc in uninstall.sh -- and the uninstall copy was the untested one. That is precisely how the two could have drifted apart, with apply.sh reverting one set of files and uninstall.sh another. Both now call patch/mw_patch.py. 17 new tests cover it, including that revert_nested never touches restic.py: that file carries a TRUECLOUD_PATCH block too, but it belongs to the providers module, and removing it would silently break B2 backups. apply.sh imports it fail-safe -- on ImportError the backend patch is skipped and middlewared starts stock, which is this script's whole design principle. The import uses sys.path.append, never insert(0): prepending would give patch/ precedence over the stdlib for that interpreter, so a future patch/json.py would shadow the real json module and break the boot. Also: the README's create_task.py example still taught `--password <secret>`, which is how a security fix quietly fails to land. It now shows --password-stdin. 132 tests, ruff and shellcheck clean.
This commit is contained in:
+5
-48
@@ -3,7 +3,7 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="0.3.3"
|
||||
VERSION="0.3.4"
|
||||
|
||||
PATCH_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
_HOOK_COMMENT='TrueCloud provider patch (S3/B2)'
|
||||
@@ -98,54 +98,11 @@ echo ""
|
||||
# would then remove the boot hook and report success while leaving every patch
|
||||
# applied. Strip our appended blocks explicitly.
|
||||
|
||||
# Same implementation apply.sh uses (patch/mw_patch.py) — a second shell copy of
|
||||
# this would be the untested one.
|
||||
echo "Reverting any file-level patches ..."
|
||||
python3 - <<'PYEOF'
|
||||
import os
|
||||
|
||||
try:
|
||||
import middlewared
|
||||
except ImportError:
|
||||
print(" middlewared not importable — nothing to revert.")
|
||||
raise SystemExit(0)
|
||||
|
||||
mw = os.path.dirname(os.path.abspath(middlewared.__file__))
|
||||
targets = [
|
||||
os.path.join(mw, "rclone", "remote", "b2.py"),
|
||||
os.path.join(mw, "plugins", "cloud_backup", "restic.py"),
|
||||
os.path.join(mw, "plugins", "cloud_backup", "sync.py"),
|
||||
os.path.join(mw, "plugins", "cloud", "crud.py"),
|
||||
os.path.join(mw, "plugins", "cloud", "snapshot.py"),
|
||||
]
|
||||
|
||||
reverted = []
|
||||
for path in targets:
|
||||
try:
|
||||
with open(path, encoding="utf-8") as fh:
|
||||
content = fh.read()
|
||||
except OSError:
|
||||
continue
|
||||
idx = content.find("\n# TRUECLOUD_PATCH")
|
||||
if idx == -1:
|
||||
continue
|
||||
try:
|
||||
with open(path, "w", encoding="utf-8") as fh:
|
||||
fh.write(content[:idx].rstrip("\n") + "\n")
|
||||
reverted.append(os.path.basename(path))
|
||||
except OSError as e:
|
||||
print(f" WARNING: could not revert {path}: {e}")
|
||||
|
||||
nested = os.path.join(mw, "plugins", "cloud", "_truecloud_nested.py")
|
||||
try:
|
||||
os.unlink(nested)
|
||||
reverted.append("_truecloud_nested.py")
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
if reverted:
|
||||
print(" Reverted: " + ", ".join(reverted))
|
||||
else:
|
||||
print(" Nothing to revert (overlay already removed, or never patched).")
|
||||
PYEOF
|
||||
python3 "$PATCH_DIR/patch/mw_patch.py" revert-all || \
|
||||
echo " WARNING: could not revert file-level patches."
|
||||
echo ""
|
||||
|
||||
# ── Unmount nested-snapshot staging trees ─────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user