diff --git a/CHANGELOG.md b/CHANGELOG.md index 9763bca..f084282 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -97,6 +97,14 @@ worse than no alert, because one day it carries a security fix. ### Changed +- **The minimum supported TrueNAS is stated, and enforced: 24.10.** TrueCloud Backup + does not exist before it — `plugins/cloud_backup/` is simply absent — so the patch + had nothing to attach to and would have done nothing at all, silently, while the + user believed their backups were configured. `install.sh` now reads + `system.version` and refuses, naming the reason. A version it cannot *parse* is a + warning, not a refusal: declining to install over a string we failed to read would + be a worse failure than the one being prevented. + - **A stable release may not leave work stranded under `## Unreleased`.** Either it is finished and belongs in the release, or the release is premature. Candidates are exempt: an rc may legitimately have work queued behind it. diff --git a/README.md b/README.md index bcd2a5c..a52f661 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,14 @@ # truenas-truecloud-patch +> ### Requires **TrueNAS SCALE 24.10 or newer** +> +> TrueCloud Backup — the feature this patch extends — **does not exist before +> 24.10**. There is nothing here to install on an older release, and `install.sh` +> will refuse. +> +> Verified on **24.10**, **25.04** and **25.10**. Not yet compatible with the +> unreleased **26.0** (see [TrueNAS compatibility](#truenas-compatibility)). + Extends TrueNAS SCALE's **TrueCloud Backup** feature to: - work with S3-compatible providers and native Backblaze B2, instead of Storj only; @@ -436,9 +445,20 @@ Two different things must survive two different events: so nothing registered there can run before it. 2. **Pools import** (`ix-zfs.service`), making `/mnt/` — and this repository — available. -3. **`apply.sh` runs** (`ix-preinit.service`): mounts the writable overlay - (upper layer in `/run`), patches `b2.py` and `restic.py` on disk inside it, - patches the UI bundle, and writes `apply.log` and `hook_status.json`. +3. **`apply.sh` runs** (`ix-preinit.service`). Before it patches anything it runs + the **compatibility preflight** ([`tools/compat.py`](tools/compat.py)) against + the middlewared that is *actually installed*, and **any module whose assumptions + no longer hold is not applied** — see [TrueNAS + compatibility](#truenas-compatibility). What survives that check gets applied: + it mounts the writable overlay (upper layer in `/run`), patches `b2.py` and + `restic.py` on disk inside it, patches the UI bundle, and writes `apply.log` and + `hook_status.json`. + + An incompatible module is skipped **for this boot only**. It is not the kill + switch: install a release that supports your TrueNAS and the patch re-applies + itself on the next boot, with no manual step. (The kill switch is permanent and + is set only when TrueNAS has made the patch *unnecessary* — a different + situation, and the opposite conclusion.) 4. **A deferred restart is scheduled.** The middlewared that is running imported the stock modules in step 1 and never re-imports, so the on-disk patch alone is not enough. `apply.sh` detects it was invoked by middlewared @@ -476,7 +496,7 @@ then run `install.sh` from there: ```bash # Replace /mnt/tank with your pool name -git clone https://github.com/sudolulo/truenas-truecloud-patch.git \ +git clone https://git.onetick.ninja/flan/truenas-truecloud-patch.git \ /mnt/tank/truenas-truecloud-patch cd /mnt/tank/truenas-truecloud-patch bash install.sh @@ -560,7 +580,7 @@ detonate on the next reboot. That is not hypothetical: **v0.0.4 shipped exactly such a bug and took all 54 apps on a box down.** The manual step *is* the safety gate. If you want convenience, watch the -[releases feed](https://github.com/sudolulo/truenas-truecloud-patch/releases); +[releases feed](https://git.onetick.ninja/flan/truenas-truecloud-patch/releases); don't automate the pull. ## Update alerts @@ -580,6 +600,17 @@ WARNING. The CHANGELOG's own section headings are the signal, and a security fix anywhere in the range escalates the whole span — a docs-only release on top of a security fix still reports as security. +**Release candidates never alert.** They are invisible to `update.sh` and to the +alert, which both take the newest plain `vX.Y.Z` tag. That is what lets debugging +happen in `-rc` tags instead of in your notification bell — see +[Releasing](#releasing). + +The changelog is read from whichever forge `origin` points at, derived from the +remote rather than hard-coded. That is not cosmetic: when the changelog cannot be +read, the alert deliberately fires **anyway** rather than risk hiding a security +fix — so a wrong URL would not silence the alert, it would make it fire on *every* +release, including the documentation-only ones this section promises to suppress. + ### How it works, and why it's built this way TrueNAS **cannot raise an alert from the CLI** — `midclt` exposes only @@ -720,7 +751,8 @@ python3 /mnt/tank/truenas-truecloud-patch/patch/create_task.py verify | What you see | What it means | |---|---| | `[OK] providers`, `[OK]`/`[SKIP] nested_snapshots` | Fine. Nothing to do. | -| `WARNING: … pattern not found` (UI) | The Angular bundle changed. The UI dropdown reverts to Storj-only, but **backups keep working** — create tasks with `create_task.py` meanwhile, and [open an issue](https://github.com/sudolulo/truenas-truecloud-patch/issues) with your TrueNAS version. | +| `WARNING: … pattern not found` (UI) | The Angular bundle changed. The UI dropdown reverts to Storj-only, but **backups keep working** — create tasks with `create_task.py` meanwhile, and [open an issue](https://git.onetick.ninja/flan/truenas-truecloud-patch/issues) with your TrueNAS version. | +| `WARNING: truecloud-patch is NOT COMPATIBLE with this TrueNAS version` | This TrueNAS changed middleware underneath the patch, and the named module was **deliberately not applied** — see `incompatible.json` for exactly which assumption broke. TrueNAS is left stock, so nothing is half-patched. Check [TrueNAS compatibility](#truenas-compatibility), then `bash update.sh` once a release supports your version; it re-applies itself on the next boot. This is **not** the kill switch and needs no manual reset. | | `[FAIL] providers` | **Your B2/S3 backups will not run.** middlewared is fine, but the credential/URL handling is gone. Open an issue with your version. | | `[FAIL] nested_snapshots` | The stock guard is back, so tasks with `snapshot = true` on a nested dataset will fail validation. Turn the option off on those tasks until it's fixed. | @@ -842,7 +874,7 @@ If a module shows `[FAIL]`: risk. 4. **If the detail says the module doesn't exist**, a TrueNAS update renamed or restructured the internal API. - [Open an issue](https://github.com/sudolulo/truenas-truecloud-patch/issues) + [Open an issue](https://git.onetick.ninja/flan/truenas-truecloud-patch/issues) with your TrueNAS version number and the full verify output. --- diff --git a/install.sh b/install.sh index c94d0ed..308bf87 100755 --- a/install.sh +++ b/install.sh @@ -88,6 +88,44 @@ if [ "$(id -u)" -ne 0 ]; then exit 1 fi +# ── Minimum TrueNAS version ─────────────────────────────────────────────────── +# +# TrueCloud Backup -- the feature this whole project extends -- was introduced in +# 24.10. On anything older, `plugins/cloud_backup/` does not exist at all: there is +# no restic, no cloud_backup task type, and nothing for the patch to attach to. It +# would not break the box, it would simply do nothing, silently, while the user +# believed their backups were configured. Say no clearly instead. +# +# A version we cannot PARSE is not a version we may refuse on: warn and continue. +# Refusing to install over a string we failed to read would be a worse failure than +# the one being prevented. +MIN_TRUENAS="24.10" + +_tc_version_raw="" +if command -v midclt &>/dev/null; then + _tc_version_raw=$(midclt call system.version 2>/dev/null || true) # TrueNAS-25.10.4 +fi +# Both spellings are real: modern releases report `TrueNAS-25.10.4`, older ones +# `TrueNAS-SCALE-24.04.2` -- and the SCALE- form is used by exactly the versions +# this gate exists to turn away, so failing to parse it would let them through. +_tc_version=$(printf '%s' "$_tc_version_raw" \ + | sed -n 's/^TrueNAS-\(SCALE-\)\{0,1\}\([0-9]\{1,\}\.[0-9]\{1,\}\).*/\2/p') + +if [ -z "$_tc_version" ]; then + echo "WARNING: could not determine the TrueNAS version" \ + "${_tc_version_raw:+(got '${_tc_version_raw}')}." + echo "WARNING: this patch requires TrueNAS SCALE ${MIN_TRUENAS} or newer. Continuing anyway." + echo "" +elif [ "$(printf '%s\n%s\n' "$MIN_TRUENAS" "$_tc_version" | sort -V | head -1)" != "$MIN_TRUENAS" ]; then + echo "ERROR: TrueNAS ${_tc_version} is too old — this patch requires ${MIN_TRUENAS} or newer." >&2 + echo "" >&2 + echo " TrueCloud Backup does not exist before ${MIN_TRUENAS}, so there is nothing" >&2 + echo " here for the patch to extend. Upgrade TrueNAS first." >&2 + exit 1 +else + echo "TrueNAS ${_tc_version} (minimum ${MIN_TRUENAS}) — ok" +fi + if ! command -v midclt &>/dev/null; then echo "ERROR: midclt not found. Run this script on TrueNAS SCALE." >&2 exit 1