diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f719e4 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/restic-go/ diff --git a/restic.sh b/restic.sh new file mode 100755 index 0000000..5954420 --- /dev/null +++ b/restic.sh @@ -0,0 +1,172 @@ +#! /bin/bash + +function main() { + set -e + set -u + + cd "$(dirname "${BASH_SOURCE[0]}")" + export GOPATH="$PWD/restic-go" + + if [ -z "${PASSWORD:-""}" ]; then + read -sp "Password: " PASSWORD + echo "" + fi + + install + + configure + + local action="$1" + shift + case "$action" in + clean ) clean_remote "$@" ;; + backup ) backup "$@" ;; + restore ) restore "$@" ;; + * ) RESTIC "$action" "$@" ;; + esac +} + +function install() { + if ! go version; then + echo "Must install go" >&2 + exit 1 + fi + export GOFLAGS=""; export GO111MODULE=""; export GOPROXY="" + export GOFLAGS="" + mkdir -p "$GOPATH/bin/$(uname)" + for i in ncw/rclone restic/restic; do + local b="${i##*/}" + if [ -e "$GOPATH/bin/$(uname)/$b" ]; then + continue + fi + mkdir -p $GOPATH/src/github.com/${i%/*} + #go get -u "github.com/$i" || true + pushd "$GOPATH/src/github.com/${i%/*}" + git clone --depth 1 --branch master https://github.com/$i + cd ${i##*/} + go get ./... + case "$i" in + restic* ) + go run build.go + mv "./$b" "$GOPATH/bin/$(uname)/$b" + ;; + * ) + go install + mv "$GOPATH/bin/$b" "$GOPATH/bin/$(uname)/$b" + ;; + esac + popd + done +} + +function configure() { + mkdir -p "$PWD/config" + if [ ! -e "$PWD/config/rclone" ]; then + configure_rclone + fi + if [ ! -e "$PWD/config/restic" ]; then + configure_restic + fi +} + +function configure_rclone() { + RCLONE config create remote drive + #RCLONE config create remote-enc crypt \ + # remote remote:cloudly/restic \ + # filename_encryption obfuscate \ + # directory_name_encryption true \ + # password $PASSWORD \ + # password2 "" +} + +function configure_restic() { + RESTIC init + touch "$PWD/config/restic" +} + +function backup() { + local args=() + for arg in "$@"; do + local real="$(realpath "$arg")" + if [ "$real" == "${real#$HOME}" ]; then + log "Cowardly skipping $arg ($real) for not being under \$HOME ($HOME)" + continue + fi + args+=("$real") + done + local extraTags=" " + if [ -n "${TAGS:-""}" ]; then + extraTags="--tag $TAGS" + fi + log "BACKUP ${args[@]}" + RESTIC backup \ + -e "**.sw*" \ + --tag "${real#$HOME/}" \ + $extraTags \ + "${args[@]}" +} + +function restore() { + for arg in "$@"; do + if [ -e "$arg" ] || [ -d "$arg" ]; then + arg="$(realpath "$arg")" + fi + local latest="$(RESTIC snapshots --last --tag "${arg#$HOME/}" | sort -k 2 | grep '20[0-9][0-9].[0-9][0-9].[0-9][0-9]' | tail -n 1)" + local id="$(echo "$latest" | awk '{print $1}')" + local path="$(echo "$latest" | awk '{print $5}')" + local rpath="$(echo "$latest" | awk '{print $6}')" + log "RESTORE $id as $HOME/$path as $HOME/$path-restore" + rm -rf "$HOME/$path-"{dump,restore} 2> /dev/null \ + || sudo rm -rf "$HOME/$path-"{dump,restore} 2> /dev/null \ + || true + mkdir -p "$HOME/$path-dump" + RESTIC restore $id --target "$HOME/$path-dump" + mv "$HOME/$path-dump/${rpath#/}" "$HOME/$path-restore" + rm -rf "$HOME/$path-"{dump,old} \ + || sudo rm -rf "$HOME/$path-"{dump,old} \ + || true + if [ -d "$HOME/$path" ]; then + mv "$HOME/$path" "$HOME/$path-old" + fi + mv "$HOME/$path-restore" "$HOME/$path" + done + if [ -z "${DIRTY:-}" ]; then + clean_remote + fi +} + +function clean_remote() { + RESTIC forget --prune --cleanup-cache --keep-last 2 --group-by host,tags "$@" + RESTIC check +} + +function RCLONE() { + $GOPATH/bin/$(uname)/rclone --config=$PWD/config/rclone --ask-password=false "$@" +} + +function RESTIC() { + RESTIC_PASSWORD=$PASSWORD \ + $GOPATH/bin/$(uname)/restic \ + -r rclone:remote:cloudly/restic-plain/backup \ + -o rclone.program=$GOPATH/bin/$(uname)/rclone \ + -o rclone.args="serve restic --stdio --b2-hard-delete --drive-use-trash=false --config $PWD/config/rclone --bwlimit 26M --ask-password=false" \ + "$@" + + #-r rclone:remote-enc:backup +} + +function log() { + echo "$(date +%H:%M:%S): $@" >&2 +} + +function fatal() { + log "$@" + exit 1 +} + +if [ "$0" == "${BASH_SOURCE[0]}" ]; then + SECONDS=0 + log "Start" + main "$@" + log "Stop ($?): $(python3 -c "print(int(10*$SECONDS/60)/10.0, 'minutes')")" +fi