home · contact · privacy
Have orgbackup auto-append to .config/orgsync/servers on --init. master
authorPlom Heller <plom@plomlompom.com>
Mon, 28 Sep 2026 21:58:35 +0000 (23:58 +0200)
committerPlom Heller <plom@plomlompom.com>
Mon, 28 Sep 2026 21:58:35 +0000 (23:58 +0200)
CLAUDE.md
home/user/.local/bin/lib/orgsync.py
home/user/.local/bin/orgbackup

index 4f7f06c3b3529979eca5d3f367c6a24fe2a6181d..dd37d925c27859b9918a46d2bb10d6b5187a17c0 100644 (file)
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -847,8 +847,10 @@ in `.profile.d/` as `*.sh`, sourced by the loop line in `~/.profile`.
   on the one switched to. Borg archives rather than git or a live-sync tool,
   since `~/org`'s contents and changes don't follow software-development
   patterns and syncing follows machine switches. Every server listed in
-  `~/.config/orgsync/servers` (untracked; one per line, `#`/blank lines skipped)
-  holds an independent repo (see "`setup_borg_server.sh`"), each pushed to
+  `~/.config/orgsync/servers` (untracked; one per line, `#`/blank lines skipped;
+  `orgbackup --init <server>` appends its server there unless listed, the
+  other desktops' lists are edited by hand) holds an independent repo (see
+  "`setup_borg_server.sh`"), each pushed to
   separately — never copy a repo between servers, which would duplicate its
   ID and encryption nonce state. Shared code in `lib/orgsync.py` (below);
   `borgbackup` is in `to_install/t490s`.
@@ -860,10 +862,14 @@ in `.profile.d/` as `*.sh`, sourced by the loop line in `~/.profile`.
     --init <server>` (refuses to overwrite) and copied by hand to every
     other desktop, plus one copy kept off all desktops and servers: losing
     every copy makes that server's backups unreadable. Never tracked
-    (secrets). Borg 2's `repo-create --other-repo` may allow one key for all;
-    revisit on migrating. The passphrase, the same for all repos, is asked
-    once per run into `BORG_PASSPHRASE`, unless that or `BORG_PASSCOMMAND` is
-    already set.
+    (secrets). The server list is deliberately not derived from these
+    filenames: a listed server lacking its keyfile here (e.g. one forgot to
+    copy) gets a skip warning instead of silently dropping out of the
+    guards below, and a server is disabled by editing the list, not by
+    moving a secret around. Borg 2's `repo-create --other-repo` may allow
+    one key for all; revisit on migrating. The passphrase, the same for all
+    repos, is asked once per run into `BORG_PASSPHRASE`, unless that or
+    `BORG_PASSCOMMAND` is already set.
   - SSH: `BORG_RSH` (`ssh -o StrictHostKeyChecking=accept-new`) logs in as
     `borg` with whatever key `ssh-agent` offers (see `ssh-agent.sh` above).
     `accept-new`: a never-contacted server's host key is accepted rather
index 1000f9eb70808a1712a4c6c02b0309e9ace0e00d..f08547badd7133123a9a7b7325ff24aebdc19f73 100644 (file)
@@ -159,15 +159,20 @@ def ensure_passphrase(
     environ['BORG_PASSPHRASE'] = passphrase
 
 
+def server_names(
+        ) -> list[str]:
+    'Read PATH_SERVERS: one server per line; blank, "#" lines skipped.'
+    lines = [line.strip() for line
+             in PATH_SERVERS.read_text(encoding='utf8').splitlines()]
+    return [line for line in lines if line and not line.startswith('#')]
+
+
 def load_servers(
         ) -> list[Server]:
-    'Read PATH_SERVERS: one server per line; blank, "#" lines skipped.'
+    'Servers listed in PATH_SERVERS, fail if no such file.'
     if not PATH_SERVERS.exists():
         raise SyncError(f'no server list at {PATH_SERVERS}')
-    lines = [line.strip() for line
-             in PATH_SERVERS.read_text(encoding='utf8').splitlines()]
-    return [Server(line) for line in lines
-            if line and not line.startswith('#')]
+    return [Server(name) for name in server_names()]
 
 
 def probe_servers(
index ae9955f9efb2ca6494d5fd70d1e7bbeb135086a6..cd1f41698530231402d8b218675496acb5325b03 100755 (executable)
@@ -4,9 +4,9 @@ from datetime import datetime, timezone
 from socket import gethostname
 from lib.argparsing import ArgParser
 from lib.orgsync import (
-        DIRNAME_ORG, PATH_HOME, PATH_ORG, Server, SyncError, current_manifest,
-        ensure_passphrase, load_state, newest_archive, probe_servers,
-        run_against_syncerror, save_state, warn)
+        DIRNAME_ORG, PATH_HOME, PATH_ORG, PATH_SERVERS, Server, SyncError,
+        current_manifest, ensure_passphrase, load_state, newest_archive,
+        probe_servers, run_against_syncerror, save_state, server_names, warn)
 
 APP_DESC = 'Back up ~/org to all reachable servers.'
 
@@ -29,9 +29,19 @@ def main(
         print(f'{server.name}: creating repo, keyfile at {server.path_key} …')
         server.init()
         server.probe()
+        if not (PATH_SERVERS.exists() and server.name in server_names()):
+            servers_text = (PATH_SERVERS.read_text(encoding='utf8')
+                            if PATH_SERVERS.exists() else '')
+            print(f'{server.name}: adding to server list at {PATH_SERVERS} …')
+            PATH_SERVERS.parent.mkdir(parents=True, exist_ok=True)
+            separator = ('\n' if servers_text
+                         and not servers_text.endswith('\n') else '')
+            with PATH_SERVERS.open('a', encoding='utf8') as file:
+                file.write(f'{separator}{server.name}\n')
         print(f'{server.name}: repo created.\n'
-              'Copy that keyfile to every other desktop, and keep one copy'
-              ' off all desktops and servers (e.g. printed: it is text).')
+              'Copy that keyfile to every other desktop (and list the server'
+              ' there), and keep one copy off all desktops and servers (e.g.'
+              ' printed: it is text).')
         return
 
     if not PATH_ORG.is_dir():