Commit Graph
56 Commits
Author SHA1 Message Date
flan 2c9adc1ce5 Fix five quality findings; expand recovery and restore documentation
patch_ui.py:
- Write Angular bundle atomically via tmp + os.replace, matching the pattern
  already used by _record_status. Prevents a corrupt bundle if the write is
  interrupted mid-boot.

sitecustomize.py:
- Tighten _record_status count barrier comment to name _Finder._targets as
  the canonical count, making the coupling visible to future editors.

uninstall.sh:
- Verify middlewared restarted cleanly after uninstall, with journalctl
  guidance on failure — matching recover.sh's existing pattern.

apply.sh:
- Change second line of site-packages error block from WARNING: prefix
  (misleading for an instructional message) to a plain Run: hint.

create_task.py:
- Guard __doc__ against None in epilog extraction so -OO does not crash.

README.md:
- Split Emergency recovery into three named subsections: middlewared won't
  start, web UI is blank or broken (corrupt bundle recovery), and backend
  verify shows FAIL. Each gives direct commands and escalation steps.
- Add Restoring from a TrueCloud Backup section: finding the restic binary,
  gathering credentials, provider-specific env var setup for B2 and S3,
  listing and restoring snapshots, and operational notes on restore hygiene.
- Clarify that hook_status.json is written once both target modules have
  loaded (not necessarily at the instant middlewared starts).
2026-06-15 03:23:31 +00:00
flan ebca3f99cc Reduce accidental complexity across three files
sitecustomize.py:
- Replace _PATCHES dispatch dict with if/elif in exec_module; removes
  coupling between dispatch and _record_status's count barrier
- Drop _record_status count barrier entirely; both patches fire within
  milliseconds during the same import sequence, write-on-every-call is safe
- Replace _broken_url regex with str.partition + startswith checks; same
  semantics, no regex knowledge required to read
- Replace @staticmethod decorator inside plain function with explicit
  staticmethod() assignment; decorator form creates a descriptor object,
  not a callable, which confuses readers expecting class-body usage

apply.sh:
- Inline warn/ok helpers; each was one echo with a prefix, the indirection
  cost more than the abstraction saved
- Collapse patch_ui.py if/else (whose if branch was a no-op comment) to
  a single || fallback line

create_task.py:
- Remove vestigial (_client, _args) params from cmd_verify; it was pulled
  out of dispatch, the params were never used
- Replace 3-entry dispatch dict with if/elif; dict implied a uniform calling
  convention that verify already broke
2026-06-15 03:08:52 +00:00
flan 8f24725078 Add kill switch and recover.sh for emergency recovery
If the patch ever prevents middlewared from starting, users now have a
clear escape hatch that requires no knowledge of Python internals:

  bash /data/truecloud-patch/recover.sh

Or at a bare shell prompt:

  touch /data/truecloud-patch/disabled
  systemctl restart middlewared

sitecustomize.py checks for /data/truecloud-patch/disabled at Python
startup and skips the import hook entirely when the file exists.
apply.sh does the same so the PREINIT script also does nothing on reboot.

recover.sh is copied to /data/truecloud-patch/ by install.sh so it is
available even without the original repo directory.

README gains an "Emergency recovery" section above Troubleshooting.
2026-06-15 02:35:55 +00:00
flan 2d32c81485 Fix infinite recursion in import hook and stdout contamination in apply.sh
sitecustomize.py: exec_module was calling importlib.util.find_spec(fullname)
after Python had already added the module to sys.modules. find_spec
short-circuits to sys.modules[name].__spec__, which is our own wrapper spec,
so real_spec.loader.exec_module(module) immediately called exec_module again
— infinite recursion. Fix: resolve the real file spec in find_spec (before
sys.modules is populated) and store it on the Loader. exec_module now uses
the stored spec directly with no find_spec call.

Also move _mark_done into the finally block so a module that fails to load
doesn't trigger infinite retry loops on subsequent import attempts.

apply.sh: warn() writes to stdout. Inside $(find_mw_python), stdout is
captured by the command substitution, so any warn() call folded its text
into $PYTHON — causing every subsequent "$PYTHON" invocation to fail. Fix:
emit the fallback warning to stderr (>&2) so it goes to the log without
being captured.
2026-06-15 02:25:51 +00:00
flan 0a54bcba9d Harden for Python 3.12+, add disclaimer, audit for robustness
- sitecustomize.py: replace deprecated find_module/load_module with
  find_spec/exec_module (required for Python 3.12+ / TrueNAS SCALE 25.x)
- apply.sh: remove set -e (PREINIT must not fail catastrophically);
  detect middlewared's actual Python binary instead of assuming python3;
  log rotation to avoid unbounded growth; independent failure per step
- patch_ui.py: detect multiple bundle matches; include TrueNAS version
  in pattern-not-found warning; better MARKER specificity
- uninstall.sh: mirror Python detection logic from apply.sh
- README: lead with Storj $5→$50 price context; prominent unsupported
  disclaimer; Python version compatibility matrix; post-update checklist
- Add MIT LICENSE
2026-06-15 02:13:24 +00:00
flan 21b9333324 Initial implementation: extend TrueCloud Backup to S3 and B2 providers
Patches middlewared at runtime via sitecustomize.py (no file edits to /usr/)
and widens the UI credential dropdown from Storj-only to S3+B2+Storj.
Persists across TrueNAS updates via PREINIT initshutdownscript stored in DB.
2026-06-15 02:02:17 +00:00