Compare commits
15 Commits
d2b24e3896
...
f70bffed37
Author | SHA1 | Date | |
---|---|---|---|
f70bffed37 | |||
979dfd892f | |||
ab6dce0c2c | |||
aff447c1b6 | |||
f7e9c3e232 | |||
d168c5bf54 | |||
31d88f9345 | |||
ccf54b98d7 | |||
59ad2b5b4d | |||
0c74f1676c | |||
5e06ebd822 | |||
929a323cf0 | |||
86bb72f201 | |||
54437456ae | |||
c7a6d08665 |
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
.venv
|
||||||
*.html
|
*.html
|
||||||
cache/
|
cache/
|
||||||
config/
|
config/
|
||||||
|
|
|
@ -123,6 +123,8 @@ Notes
|
||||||
pip install -e .[llfuse]
|
pip install -e .[llfuse]
|
||||||
pyinstaller --clean --noconfirm scripts/borg.exe.spec
|
pyinstaller --clean --noconfirm scripts/borg.exe.spec
|
||||||
|
|
||||||
|
Then see `dist/borg.exe`. Confirm the version with `dist/borg.exe --version`.
|
||||||
|
|
||||||
*Note:* This uses the deprecated `llfuse` instead of the newer `pyfuse3`.
|
*Note:* This uses the deprecated `llfuse` instead of the newer `pyfuse3`.
|
||||||
`pyfuse3` doesn't work because, at minimum, it pulls in `trio` which
|
`pyfuse3` doesn't work because, at minimum, it pulls in `trio` which
|
||||||
requires `ssl` which is explicitly excluded by
|
requires `ssl` which is explicitly excluded by
|
||||||
|
|
122
backup.py
122
backup.py
|
@ -10,7 +10,9 @@ import re
|
||||||
import sys
|
import sys
|
||||||
import stat
|
import stat
|
||||||
import time
|
import time
|
||||||
|
import select
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import threading
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import typing
|
import typing
|
||||||
|
@ -105,19 +107,17 @@ class Backup:
|
||||||
self.dry_run = dry_run
|
self.dry_run = dry_run
|
||||||
self.root_seen: dict[bytes, bool] = {}
|
self.root_seen: dict[bytes, bool] = {}
|
||||||
|
|
||||||
# All logged messages, with severity
|
# Saved log messages
|
||||||
self.logs: list[tuple[str, str]] = []
|
self.logs: list[tuple[str, str]] = []
|
||||||
|
|
||||||
def out(self, path: bytes):
|
def out(self, path: bytes):
|
||||||
self.outfile.write(path + (b'\n' if self.dry_run else b'\0'))
|
self.outfile.write(path + (b'\n' if self.dry_run else b'\0'))
|
||||||
|
|
||||||
def log(self, letter: str, msg: str):
|
def log(self, letter: str, msg: str, bold: bool=False):
|
||||||
colors = { 'E': 31, 'W': 33, 'I': 36 };
|
colors = { 'E': 31, 'W': 33, 'I': 36 };
|
||||||
if letter in colors:
|
c = colors[letter] if letter in colors else 0
|
||||||
c = colors[letter]
|
b = "" if bold else "\033[22m"
|
||||||
else:
|
sys.stderr.write(f"\033[1;{c}m{letter}:{b} {msg}\033[0m\n")
|
||||||
c = 0
|
|
||||||
sys.stderr.write(f"\033[1;{c}m{letter}:\033[22m {msg}\033[0m\n")
|
|
||||||
self.logs.append((letter, msg))
|
self.logs.append((letter, msg))
|
||||||
|
|
||||||
def run(self, outfile: typing.IO[bytes]):
|
def run(self, outfile: typing.IO[bytes]):
|
||||||
|
@ -132,10 +132,10 @@ class Backup:
|
||||||
if not stat.S_ISDIR(st.st_mode):
|
if not stat.S_ISDIR(st.st_mode):
|
||||||
raise NotADirectoryError
|
raise NotADirectoryError
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
self.log('W', f"ignoring root, does not exist: {pstr(root)}")
|
self.log('E', f"root does not exist: {pstr(root)}")
|
||||||
continue
|
continue
|
||||||
except NotADirectoryError:
|
except NotADirectoryError:
|
||||||
self.log('W', f"ignoring root, not a directory: {pstr(root)}")
|
self.log('E', f"root is not a directory: {pstr(root)}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.log('I', f"processing root {pstr(root)}")
|
self.log('I', f"processing root {pstr(root)}")
|
||||||
|
@ -219,8 +219,11 @@ class Backup:
|
||||||
for entry in it:
|
for entry in it:
|
||||||
self.scan(path=entry.path, parent_st=st)
|
self.scan(path=entry.path, parent_st=st)
|
||||||
|
|
||||||
except PermissionError as e:
|
except (FileNotFoundError,
|
||||||
self.log('E', f"can't read {pstr(path)}")
|
IsADirectoryError,
|
||||||
|
NotADirectoryError,
|
||||||
|
PermissionError) as e:
|
||||||
|
self.log('E', f"can't read {pstr(path)}: {str(e)}")
|
||||||
return
|
return
|
||||||
|
|
||||||
def main(argv: list[str]):
|
def main(argv: list[str]):
|
||||||
|
@ -229,6 +232,7 @@ def main(argv: list[str]):
|
||||||
def humansize(string):
|
def humansize(string):
|
||||||
return humanfriendly.parse_size(string)
|
return humanfriendly.parse_size(string)
|
||||||
|
|
||||||
|
# Parse args
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
prog=argv[0],
|
prog=argv[0],
|
||||||
description="Back up the local system using borg",
|
description="Back up the local system using borg",
|
||||||
|
@ -239,6 +243,8 @@ def main(argv: list[str]):
|
||||||
help="Config file", default=str(base / "config.yaml"))
|
help="Config file", default=str(base / "config.yaml"))
|
||||||
parser.add_argument('-b', '--borg',
|
parser.add_argument('-b', '--borg',
|
||||||
help="Borg command", default=str(base / "borg.sh"))
|
help="Borg command", default=str(base / "borg.sh"))
|
||||||
|
parser.add_argument('-N', '--notify',
|
||||||
|
help="Notify command", default=str(base / "notify.sh"))
|
||||||
parser.add_argument('-n', '--dry-run', action="store_true",
|
parser.add_argument('-n', '--dry-run', action="store_true",
|
||||||
help="Just print log output, don't run borg")
|
help="Just print log output, don't run borg")
|
||||||
parser.add_argument('-d', '--debug', action="store_true",
|
parser.add_argument('-d', '--debug', action="store_true",
|
||||||
|
@ -247,13 +253,17 @@ def main(argv: list[str]):
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
config = Config(args.config)
|
config = Config(args.config)
|
||||||
|
|
||||||
|
# Run backup
|
||||||
backup = Backup(config, args.dry_run)
|
backup = Backup(config, args.dry_run)
|
||||||
|
captured_output: list[bytes] = []
|
||||||
|
|
||||||
if args.dry_run:
|
if args.dry_run:
|
||||||
if args.debug:
|
if args.debug:
|
||||||
backup.run(sys.stdout.buffer)
|
backup.run(sys.stdout.buffer)
|
||||||
else:
|
else:
|
||||||
with open(os.devnull, "wb") as out:
|
with open(os.devnull, "wb") as out:
|
||||||
backup.run(out)
|
backup.run(out)
|
||||||
|
sys.stdout.flush()
|
||||||
else:
|
else:
|
||||||
borg = subprocess.Popen([args.borg,
|
borg = subprocess.Popen([args.borg,
|
||||||
"create",
|
"create",
|
||||||
|
@ -265,13 +275,34 @@ def main(argv: list[str]):
|
||||||
"--compression", "zstd,3",
|
"--compression", "zstd,3",
|
||||||
"--paths-from-stdin",
|
"--paths-from-stdin",
|
||||||
"--paths-delimiter", "\\0",
|
"--paths-delimiter", "\\0",
|
||||||
"::'{hostname}-{now:%Y%m%d-%H%M%S}'"],
|
"::{hostname}-{now:%Y%m%d-%H%M%S}"],
|
||||||
stdin=subprocess.PIPE)
|
stdin=subprocess.PIPE,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
if borg.stdin is None:
|
if borg.stdin is None:
|
||||||
raise Exception("no pipe")
|
raise Exception("no pipe")
|
||||||
|
|
||||||
|
# Use a thread to capture output
|
||||||
|
def reader_thread(fh):
|
||||||
|
os.set_blocking(fh.fileno(), False)
|
||||||
|
while True:
|
||||||
|
ready = select.select([fh.fileno()], [], [])
|
||||||
|
if not len(ready[0]):
|
||||||
|
break
|
||||||
|
data = fh.read(8192)
|
||||||
|
if not len(data):
|
||||||
|
break
|
||||||
|
sys.stdout.buffer.write(data)
|
||||||
|
sys.stdout.flush()
|
||||||
|
captured_output.append(data)
|
||||||
|
fh.close()
|
||||||
|
reader = threading.Thread(target=reader_thread, args=(borg.stdout,))
|
||||||
|
reader.daemon = True
|
||||||
|
reader.start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Give borg some time to start, just to clean up stdout
|
# Give borg some time to start, just to clean up stdout
|
||||||
time.sleep(2)
|
time.sleep(1)
|
||||||
backup.run(borg.stdin)
|
backup.run(borg.stdin)
|
||||||
except BrokenPipeError:
|
except BrokenPipeError:
|
||||||
sys.stderr.write(f"broken pipe\n")
|
sys.stderr.write(f"broken pipe\n")
|
||||||
|
@ -281,14 +312,69 @@ def main(argv: list[str]):
|
||||||
except BrokenPipeError:
|
except BrokenPipeError:
|
||||||
pass
|
pass
|
||||||
borg.wait()
|
borg.wait()
|
||||||
|
reader.join()
|
||||||
ret = borg.returncode
|
ret = borg.returncode
|
||||||
if ret < 0:
|
if ret < 0:
|
||||||
sys.stderr.write(f"error: process exited with signal {-ret}\n")
|
backup.log('E', f"borg exited with signal {-ret}")
|
||||||
return 1
|
|
||||||
elif ret != 0:
|
elif ret != 0:
|
||||||
sys.stderr.write(f"error: process exited with return code {ret}\n")
|
backup.log('E', f"borg exited with return code {ret}")
|
||||||
return ret
|
|
||||||
|
|
||||||
|
# See if we had any errors
|
||||||
|
warnings = sum(1 for (letter, msg) in backup.logs if letter == 'W')
|
||||||
|
errors = sum(1 for (letter, msg) in backup.logs if letter == 'E')
|
||||||
|
|
||||||
|
def plural(num: int, word: str) -> str:
|
||||||
|
suffix = "" if num == 1 else "s"
|
||||||
|
return f"{num} {word}{suffix}"
|
||||||
|
|
||||||
|
warnmsg = plural(warnings, "warning") if warnings else None
|
||||||
|
errmsg = plural(errors, "error") if errors else None
|
||||||
|
|
||||||
|
if not warnings and not errors:
|
||||||
|
backup.log('I', f"backup successful", bold=True)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if warnmsg:
|
||||||
|
backup.log('W', f"reported {warnmsg}", bold=True)
|
||||||
|
if errors:
|
||||||
|
backup.log('E', f"reported {errmsg}", bold=True)
|
||||||
|
|
||||||
|
# Send a notification of errors
|
||||||
|
email = backup.config.notify_email
|
||||||
|
if email and not args.dry_run:
|
||||||
|
backup.log('I', f"sending error notification to {email}")
|
||||||
|
|
||||||
|
# Show all of our warnings and errors. Use a ">" prefix
|
||||||
|
# so warnings and errors get highlighted by the mail reader.
|
||||||
|
body = [ "Logs from backup.py:" ]
|
||||||
|
for (letter, msg) in backup.logs:
|
||||||
|
if letter == "E" or letter == "W":
|
||||||
|
prefix = ">"
|
||||||
|
else:
|
||||||
|
prefix = " "
|
||||||
|
body.append(f"{prefix}{letter}: {msg}")
|
||||||
|
body_text = "\n".join(body).encode()
|
||||||
|
|
||||||
|
# Followed by borg output
|
||||||
|
body_text += b"\n\nBorg output:\n" + b"".join(captured_output)
|
||||||
|
|
||||||
|
# Subject summary
|
||||||
|
if errmsg and warnmsg:
|
||||||
|
summary = f"{errmsg}, {warnmsg}"
|
||||||
|
elif errors:
|
||||||
|
summary = errmsg
|
||||||
|
else:
|
||||||
|
summary = warnmsg
|
||||||
|
|
||||||
|
# Call notify.sh
|
||||||
|
res = subprocess.run([args.notify, summary, email], input=body_text)
|
||||||
|
if res.returncode != 0:
|
||||||
|
backup.log('E', f"failed to send notification")
|
||||||
|
errors += 1
|
||||||
|
|
||||||
|
# Exit with an error code if we had any errors
|
||||||
|
if errors:
|
||||||
|
return 1
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
21
borg.sh
Executable file
21
borg.sh
Executable file
|
@ -0,0 +1,21 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
. "$(dirname "$0")"/vars.sh
|
||||||
|
|
||||||
|
export BORG_PASSCOMMAND="cat ${BORG_DIR}/passphrase"
|
||||||
|
export BORG_BASE_DIR=${BORG_DIR}
|
||||||
|
export BORG_CACHE_DIR=${BORG_DIR}/cache
|
||||||
|
export BORG_CONFIG_DIR=${BORG_DIR}/config
|
||||||
|
if [ "$1" = "--rw" ] ; then
|
||||||
|
if [ "$BORG_RW_KEY_ADDED" != "1" ] ; then
|
||||||
|
echo "=== Need SSH key passphrase. Check Bitwarden for:"
|
||||||
|
echo "=== borg $HOSTNAME / read-write SSH key"
|
||||||
|
fi
|
||||||
|
export BORG_RSH="ssh -F $SSH/config -o BatchMode=no -o PreferredAuthentication=publickey -i $SSH/id_ecdsa"
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
export BORG_RSH="ssh -F $SSH/config -i $SSH/id_ecdsa_appendonly"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "${BORG_BIN}" "$@"
|
|
@ -72,38 +72,32 @@ setup_venv()
|
||||||
pipenv install
|
pipenv install
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create wrapper to execute borg
|
# Create shell script with environment variables
|
||||||
create_borg_wrapper()
|
create_borg_vars()
|
||||||
{
|
{
|
||||||
BORG=${BORG_DIR}/borg.sh
|
VARS=${BORG_DIR}/vars.sh
|
||||||
|
|
||||||
|
# These variables are used elsewhere in this script
|
||||||
BORG_REPO="ssh://${BACKUP_USER}@${BACKUP_HOST}/./${BACKUP_REPO}"
|
BORG_REPO="ssh://${BACKUP_USER}@${BACKUP_HOST}/./${BACKUP_REPO}"
|
||||||
|
BORG=${BORG_DIR}/borg.sh
|
||||||
SSH=$BORG_DIR/ssh
|
SSH=$BORG_DIR/ssh
|
||||||
|
|
||||||
cat >"$BORG" <<EOF
|
cat >"$VARS" <<EOF
|
||||||
#!/bin/sh
|
export BACKUP_USER=${BACKUP_USER}
|
||||||
|
export BACKUP_HOST=${BACKUP_HOST}
|
||||||
|
export BACKUP_REPO=${BACKUP_REPO}
|
||||||
|
export HOSTNAME=$(hostname)
|
||||||
export BORG_REPO=${BORG_REPO}
|
export BORG_REPO=${BORG_REPO}
|
||||||
|
export BORG_HOST_ID=${HOSTID}
|
||||||
export BORG_PASSCOMMAND="cat ${BORG_DIR}/passphrase"
|
export BORG_PASSCOMMAND="cat ${BORG_DIR}/passphrase"
|
||||||
export BORG_HOST_ID=${HOSTID}
|
export BORG_HOST_ID=${HOSTID}
|
||||||
export BORG_BASE_DIR=${BORG_DIR}
|
export BORG_DIR=${BORG_DIR}
|
||||||
export BORG_CACHE_DIR=${BORG_DIR}/cache
|
export SSH=${SSH}
|
||||||
export BORG_CONFIG_DIR=${BORG_DIR}/config
|
export BORG=${BORG}
|
||||||
if [ "\$1" = "--rw" ] ; then
|
export BORG_BIN=${BORG_BIN}
|
||||||
if [ "$BORG_RW_KEY_ADDED" != "1" ] ; then
|
|
||||||
echo "=== Need SSH key passphrase. Check Bitwarden for:"
|
|
||||||
echo "=== borg $(hostname) / read-write SSH key"
|
|
||||||
fi
|
|
||||||
export BORG_RSH="ssh -F $SSH/config -o BatchMode=no -i $SSH/id_ecdsa"
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
export BORG_RSH="ssh -F $SSH/config -i $SSH/id_ecdsa_appendonly"
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec "${BORG_BIN}" "\$@"
|
|
||||||
EOF
|
EOF
|
||||||
chmod +x "$BORG"
|
|
||||||
if ! "$BORG" -h >/dev/null ; then
|
if ! "$BORG" -h >/dev/null ; then
|
||||||
error "Can't run the new borg wrapper; does borg work?"
|
error "Can't run the borg wrapper; does borg work?"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -136,6 +130,8 @@ configure_ssh()
|
||||||
log "Creating SSH keys"
|
log "Creating SSH keys"
|
||||||
ssh-keygen -N "" -t ecdsa \
|
ssh-keygen -N "" -t ecdsa \
|
||||||
-C "backup-appendonly@$HOSTID" -f "$SSH/id_ecdsa_appendonly"
|
-C "backup-appendonly@$HOSTID" -f "$SSH/id_ecdsa_appendonly"
|
||||||
|
ssh-keygen -N "" -t ecdsa \
|
||||||
|
-C "backup-notify@$HOSTID" -f "$SSH/id_ecdsa_notify"
|
||||||
ssh-keygen -N "$PASS_SSH" -t ecdsa \
|
ssh-keygen -N "$PASS_SSH" -t ecdsa \
|
||||||
-C "backup@$HOSTID" -f "$SSH/id_ecdsa"
|
-C "backup@$HOSTID" -f "$SSH/id_ecdsa"
|
||||||
|
|
||||||
|
@ -173,7 +169,8 @@ EOF
|
||||||
# Copy SSH keys to the server's authorized_keys file, removing any
|
# Copy SSH keys to the server's authorized_keys file, removing any
|
||||||
# existing keys with this HOSTID.
|
# existing keys with this HOSTID.
|
||||||
log "Setting up SSH keys on remote host"
|
log "Setting up SSH keys on remote host"
|
||||||
cmd="borg/borg serve --restrict-to-repository ~/$BACKUP_REPO"
|
REMOTE_BORG="borg/borg"
|
||||||
|
cmd="$REMOTE_BORG serve --restrict-to-repository ~/$BACKUP_REPO"
|
||||||
|
|
||||||
keys=".ssh/authorized_keys"
|
keys=".ssh/authorized_keys"
|
||||||
backup="${keys}.old-$(date +%Y%m%d-%H%M%S)"
|
backup="${keys}.old-$(date +%Y%m%d-%H%M%S)"
|
||||||
|
@ -182,14 +179,14 @@ EOF
|
||||||
run_ssh_command "if cmp -s $backup $keys; then rm $backup ; fi"
|
run_ssh_command "if cmp -s $backup $keys; then rm $backup ; fi"
|
||||||
run_ssh_command "cat >> .ssh/authorized_keys" <<EOF
|
run_ssh_command "cat >> .ssh/authorized_keys" <<EOF
|
||||||
command="$cmd --append-only",restrict $(cat "$SSH/id_ecdsa_appendonly.pub")
|
command="$cmd --append-only",restrict $(cat "$SSH/id_ecdsa_appendonly.pub")
|
||||||
command="borg/notify.sh",restrict $(cat "$SSH/id_ecdsa_appendonly.pub")
|
command="borg/notify.sh",restrict $(cat "$SSH/id_ecdsa_notify.pub")
|
||||||
command="$cmd",restrict $(cat "$SSH/id_ecdsa.pub")
|
command="$cmd",restrict $(cat "$SSH/id_ecdsa.pub")
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Test that everything worked
|
# Test that everything worked
|
||||||
log "Testing SSH login with new key"
|
log "Testing SSH login with new key"
|
||||||
if ! ssh -F "$SSH/config" -i "$SSH/id_ecdsa_appendonly" -T \
|
if ! ssh -F "$SSH/config" -i "$SSH/id_ecdsa_appendonly" -T \
|
||||||
"${BACKUP_USER}@${BACKUP_HOST}" borg --version </dev/null ; then
|
"${BACKUP_USER}@${BACKUP_HOST}" "$REMOTE_BORG" --version </dev/null ; then
|
||||||
error "Logging in with a key failed -- is server set up correctly?"
|
error "Logging in with a key failed -- is server set up correctly?"
|
||||||
fi
|
fi
|
||||||
log "Remote connection OK!"
|
log "Remote connection OK!"
|
||||||
|
@ -297,7 +294,7 @@ git_setup()
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "Committing local changes to git"
|
log "Committing local changes to git"
|
||||||
git add README.md borg-backup.service borg-backup.timer borg.sh
|
git add README.md borg-backup.service borg-backup.timer vars.sh
|
||||||
git commit -a -m "autocommit after initial setup on $(hostname)"
|
git commit -a -m "autocommit after initial setup on $(hostname)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,7 +304,7 @@ log " Backup server user: ${BACKUP_USER}"
|
||||||
log " Repository path: ${BACKUP_REPO}"
|
log " Repository path: ${BACKUP_REPO}"
|
||||||
|
|
||||||
setup_venv
|
setup_venv
|
||||||
create_borg_wrapper
|
create_borg_vars
|
||||||
generate_keys
|
generate_keys
|
||||||
configure_ssh
|
configure_ssh
|
||||||
create_repo
|
create_repo
|
||||||
|
|
26
notify.sh
Executable file
26
notify.sh
Executable file
|
@ -0,0 +1,26 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
. "$(dirname "$0")"/vars.sh
|
||||||
|
|
||||||
|
# Send notification email using a script on the backup host
|
||||||
|
# First argument is our hostname, second argument is destination;
|
||||||
|
# mail body is provided on stdin.
|
||||||
|
|
||||||
|
if tty -s ; then
|
||||||
|
echo 'Refusing to read mail body from terminal'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SUMMARY="$1"
|
||||||
|
EMAIL="$2"
|
||||||
|
|
||||||
|
# Remote notify.sh wants subject as first line, not as an argument,
|
||||||
|
# since it's a bit messy to pass complex strings through ssh command
|
||||||
|
# lines.
|
||||||
|
( echo "backup $HOSTNAME: $SUMMARY" ; cat ) | \
|
||||||
|
ssh \
|
||||||
|
-F "$SSH/config" \
|
||||||
|
-i "$SSH/id_ecdsa_notify" \
|
||||||
|
"$BACKUP_USER@$BACKUP_HOST" \
|
||||||
|
borg/notify.sh "$EMAIL"
|
8
prune.sh
8
prune.sh
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
BORG="$(dirname "$0")/borg.sh --rw"
|
|
||||||
set -e
|
set -e
|
||||||
|
. "$(dirname "$0")"/vars.sh
|
||||||
|
|
||||||
if [ "$BORG_RW_KEY_ADDED" != "1" ] ; then
|
if [ "$BORG_RW_KEY_ADDED" != "1" ] ; then
|
||||||
echo "Re-executing under a new ssh agent"
|
echo "Re-executing under a new ssh agent"
|
||||||
|
@ -9,10 +9,10 @@ if [ "$BORG_RW_KEY_ADDED" != "1" ] ; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "=== Please enter SSH key passphrase. Check Bitwarden for:"
|
echo "=== Please enter SSH key passphrase. Check Bitwarden for:"
|
||||||
echo "=== borg basis / read-write SSH key"
|
echo "=== borg $HOSTNAME / read-write SSH key"
|
||||||
ssh-add -v "$(realpath "$(dirname "$0")")/ssh/id_ecdsa"
|
ssh-add -v "$(realpath "$(dirname "$0")")/ssh/id_ecdsa"
|
||||||
|
|
||||||
$BORG prune \
|
$BORG --rw prune \
|
||||||
--verbose \
|
--verbose \
|
||||||
--progress \
|
--progress \
|
||||||
--stats \
|
--stats \
|
||||||
|
@ -21,6 +21,6 @@ $BORG prune \
|
||||||
--keep-weekly=8 \
|
--keep-weekly=8 \
|
||||||
--keep-monthly=-1
|
--keep-monthly=-1
|
||||||
|
|
||||||
$BORG compact \
|
$BORG --rw compact \
|
||||||
--verbose \
|
--verbose \
|
||||||
--progress
|
--progress
|
||||||
|
|
Loading…
Reference in New Issue
Block a user