From 7a39d8fbd164d2df04b98b96f8b6e50a4c6984ee Mon Sep 17 00:00:00 2001 From: archipelago Date: Mon, 31 Aug 2026 14:50:55 -0400 Subject: [PATCH] fix(settings): sort What's New history newest-first --- .../src/views/settings/AccountInfoSection.vue | 74 +++++++------- scripts/sync-whats-new.py | 96 ++++++++++++++----- 2 files changed, 111 insertions(+), 59 deletions(-) diff --git a/neode-ui/src/views/settings/AccountInfoSection.vue b/neode-ui/src/views/settings/AccountInfoSection.vue index 8ba9c973..6803366a 100644 --- a/neode-ui/src/views/settings/AccountInfoSection.vue +++ b/neode-ui/src/views/settings/AccountInfoSection.vue @@ -362,42 +362,6 @@ init()
- -
-
- v1.7.107-alpha - July 20, 2026 -
-
-

Wi-Fi setup now heals itself on older nodes. Some nodes set up before a mid-year fix couldn't connect to a Wi-Fi network from the screen — it failed with a permissions error — because the piece that lets the node manage networking on your behalf was missing. Nodes now put that piece in place automatically on startup, so "scan, pick a network, type the password, connect" works without reinstalling.

-

Your node rejoins the mesh faster after an update. Applying this update briefly restarts the mesh service, and previously a node could sit disconnected from other nodes for up to five minutes before it retried. It now notices the restart and reconnects within seconds.

-

Behind the scenes: fixed the installer image build so it no longer stops on a component that was removed from the product, and so it correctly includes the private relay it was meant to bundle — two separate faults that had been failing the build.

-
-
- -
-
- v1.7.51-alpha - April 30, 2026 -
-
-

Stack installs now adopt containers that already exist instead of failing on them — a repair or reinstall over leftover containers completes, and the adopted container's readiness is waited on like any fresh start.

-

Failed installs come with evidence: the install path waits for its containers, and when one doesn't become healthy it captures that container's logs, so the error on screen names the real culprit instead of a bare timeout.

-

Bitcoin RPC bindings are ensured as part of install, and the startup self-heal path gained additional ground for already-deployed nodes.

-
-
- -
-
- v1.7.50-alpha - April 30, 2026 -
-
-

The OTA bridge older nodes needed: deployed binaries only knew how to apply two artifacts (the backend binary and the frontend archive), so the scripts, app specs and docker assets newer releases carry never reached them. This release packs those payloads inside the frontend tarball — the one channel old binaries do apply — and the new backend promotes them into /opt once it starts.

-

Runtime payloads are staged into timestamped directories and promoted atomically; a failed extraction cleans up its staging area instead of leaving half-written state for the next update to trip over.

-

This is the release that un-sticks the fleet's update pipeline: from here on, an OTA can carry more than the two artifacts, and app installs on updated nodes use the specs that match their backend.

-
-
@@ -550,7 +514,6 @@ init()

Known gaps, unchanged from the last release: three voice-assistant ports remain open without authentication. Non-browser clients — phone apps for Vaultwarden, Home Assistant or Jellyfin, and git over the web — meet the login page and need an access token. The 5x real-node lifecycle gate was not run for this release.

-
@@ -598,6 +561,7 @@ init()

Known gaps, disclosed rather than buried: non-browser clients — phone apps for Vaultwarden, Home Assistant or Jellyfin, and git over the web — meet the login page and need an access token. Three voice-assistant ports remain open without authentication; the correct fix puts them on a private network with the assistant. The 5x real-node lifecycle gate was not run for this release.

+
@@ -862,6 +826,18 @@ init()

Behind the scenes: fixed the installer image build so it no longer stops on a component that was removed from the product, and so it correctly includes the private relay it was meant to bundle.

+ +
+
+ v1.7.107-alpha + July 20, 2026 +
+
+

Wi-Fi setup now heals itself on older nodes. Some nodes set up before a mid-year fix couldn't connect to a Wi-Fi network from the screen — it failed with a permissions error — because the piece that lets the node manage networking on your behalf was missing. Nodes now put that piece in place automatically on startup, so "scan, pick a network, type the password, connect" works without reinstalling.

+

Your node rejoins the mesh faster after an update. Applying this update briefly restarts the mesh service, and previously a node could sit disconnected from other nodes for up to five minutes before it retried. It now notices the restart and reconnects within seconds.

+

Behind the scenes: fixed the installer image build so it no longer stops on a component that was removed from the product, and so it correctly includes the private relay it was meant to bundle — two separate faults that had been failing the build.

+
+
@@ -1581,6 +1557,30 @@ init()

Grafana lifecycle actions repair missing rootless host listeners on port 3000, and Debian 13 install paths pull security updates from trixie-security during image/install creation.

+ +
+
+ v1.7.51-alpha + April 30, 2026 +
+
+

Stack installs now adopt containers that already exist instead of failing on them — a repair or reinstall over leftover containers completes, and the adopted container's readiness is waited on like any fresh start.

+

Failed installs come with evidence: the install path waits for its containers, and when one doesn't become healthy it captures that container's logs, so the error on screen names the real culprit instead of a bare timeout.

+

Bitcoin RPC bindings are ensured as part of install, and the startup self-heal path gained additional ground for already-deployed nodes.

+
+
+ +
+
+ v1.7.50-alpha + April 30, 2026 +
+
+

The OTA bridge older nodes needed: deployed binaries only knew how to apply two artifacts (the backend binary and the frontend archive), so the scripts, app specs and docker assets newer releases carry never reached them. This release packs those payloads inside the frontend tarball — the one channel old binaries do apply — and the new backend promotes them into /opt once it starts.

+

Runtime payloads are staged into timestamped directories and promoted atomically; a failed extraction cleans up its staging area instead of leaving half-written state for the next update to trip over.

+

This is the release that un-sticks the fleet's update pipeline: from here on, an OTA can carry more than the two artifacts, and app installs on updated nodes use the specs that match their backend.

+
+
diff --git a/scripts/sync-whats-new.py b/scripts/sync-whats-new.py index 20980e2e..06ced39f 100755 --- a/scripts/sync-whats-new.py +++ b/scripts/sync-whats-new.py @@ -67,9 +67,53 @@ def undated_versions(): return found -def existing_versions(): - text = MODAL.read_text() - return set(re.findall(r"", text)) +def ordered_versions(): + """Return modal versions in display order (top to bottom).""" + return re.findall(r"", MODAL.read_text()) + + +def version_key(version): + match = re.match(r"v(\d+)\.(\d+)\.(\d+)", version) + return tuple(map(int, match.groups())) + + +def sort_modal_blocks(): + """Sort complete release-note blocks newest-first, preserving gaps.""" + lines = MODAL.read_text().splitlines(keepends=True) + marker = re.compile(r"^\s*\s*$") + blocks = [] + + for start, line in enumerate(lines): + match = marker.match(line) + if not match: + continue + depth = 0 + opened = False + for index in range(start + 1, len(lines)): + for tag in re.findall(r"]*>", lines[index]): + if tag.startswith("" line in the file). - lines = MODAL.read_text().splitlines(keepends=True) - marker = re.compile(r"^\s*\s*$") - idx = next((i for i, ln in enumerate(lines) if marker.match(ln)), None) - if idx is None: - print("ERROR: could not find an existing version block marker in the modal.", - file=sys.stderr) - return 2 + if missing: + # Insert before the first block; the full sort below makes this safe even + # when a historical hand-written block was accidentally left at the top. + lines = MODAL.read_text().splitlines(keepends=True) + marker = re.compile(r"^\s*\s*$") + idx = next((i for i, ln in enumerate(lines) if marker.match(ln)), None) + if idx is None: + print("ERROR: could not find an existing version block marker in the modal.", + file=sys.stderr) + return 2 + lines.insert(idx, "".join(render_block(e) for e in missing)) + MODAL.write_text("".join(lines)) + print(f"Inserted {len(missing)} block(s): {names}") - # newest-first: sort missing by their order in `entries` (already newest-first) - block_text = "".join(render_block(e) for e in missing) - lines.insert(idx, block_text) - MODAL.write_text("".join(lines)) - print(f"Inserted {len(missing)} block(s): {names}") + if sort_modal_blocks(): + print("Sorted What's New blocks newest-first.") return 0