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`.
--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
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(
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.'
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():