#!/bin/bash # Script to backup git repo(s) to Backblaze B2 # Usage read -r -d '' __usage <<-'EOF' || true Options: -b --b2-bucket [arg] Backblaze B2 bucket name. -i --b2-id [arg] Backblaze B2 account ID. -k --b2-key [arg] Backblaze B2 application key. -g --github-account [arg] GitHub account name. -p --https-password [arg] GitHub account password (for private repos) -t --temp-dir [arg] Dir to use for temp files. -s --use-https Use HTTPS to clone instead of SSH -h --help This page. EOF __helptext='NOTE: --temp-dir defaults to "$(mktemp -d)" if not specified.' # Load BASH3 Boilerplate functions source bash3boilerplate/main.sh # Color fix for rxvt-unicode (tell b3bp we're xterm) if [[ "${TERM}" =~ rxvt-unicode-.*color ]]; then TERM="xterm" fi # Functions function clone_repository() { # Clone the repo using either HTTPS or SSH local repo="${1:-}" local repo_url="" if [[ "${arg_s}" == "1" ]]; then # Use HTTPS repo_url="github.com/${__github_account}/${repo}.git" if [[ "${arg_p:-}" != "" ]]; then repo_url="https://${__github_account}:${arg_p}@${repo_url}" else repo_url="https://${repo_url}" fi else # Use SSH repo_url="git@github.com:${__github_account}/${repo}.git" fi git clone --mirror \ "${repo_url}" \ "${__temp_dir}/${repo}-${__date}.git" } function compress_repository() { # Move to repository dir so relative paths are used in the archive local repo_source="${1:-}" local archive_dest="${2:-}" local repo_path="$(dirname "$(realpath "${repo_source}")")" local repo_source="$(basename "$(realpath "${repo_source}")")" local archive_dest="$(realpath "${archive_dest}")" pushd "${repo_path}" >/dev/null # Create archive tar cpzf "${archive_dest}" "${repo_source}" # Done popd >/dev/null } function find_b2_cmd() { if which backblaze-b2 >/dev/null 2>&1; then # Arch Linux uses this name echo "backblaze-b2" elif which b2 > /dev/null 2>&1; then echo "b2" else __b3bp_log error "B2 command-line tool not found" exit 1 fi } function fix_repository_name() { # Remove '.git' from the name (if present) local repository="${1:-}" if [[ "${repository: -4:4}" == ".git" ]]; then repository="${repository:0: -4}" fi echo "${repository}" } function main() { # Create the __temp_dir mkdir -p "${__temp_dir}" # Authorize B2 account "${__b2_cmd}" authorize-account "${__b2_id}" "${__b2_key}" # Loop over repos for repo in "${@}"; do repo="$(fix_repository_name "${repo}")" echo "Backing up ${repo}" # Clone repo echo "Cloning ${repo}" clone_repository "${repo}" # Compress repo echo "Compressing ${repo}" compress_repository "${__temp_dir}/${repo}-${__date}.git" \ "${__temp_dir}/${repo}-${__date}.git.tgz" # Upload repo echo "Uploading ${repo}" "${__b2_cmd}" upload-file "${__b2_bucket}" \ "${__temp_dir}/${repo}-${__date}.git.tgz" \ "${__github_account}/${repo}-${__date}.git.tgz" # Cleanup /bin/rm "${__temp_dir}/${repo}-${__date}.git.tgz" /bin/rm -rf "${__temp_dir}/${repo}-${__date}.git" # Done echo "Successfully backed up ${repo}" done # Remove __temp_dir (if empty) /bin/rmdir --ignore-fail-on-non-empty "${__temp_dir}" } # Set env __b2_bucket="${arg_b}" __b2_cmd="$(find_b2_cmd)" __b2_id="${arg_i}" __b2_key="${arg_k}" __date="$(date '+%F_%H%M%z')" __github_account="${arg_g}" __temp_dir="${arg_t:-}" if [[ "$__temp_dir" == "" ]]; then __temp_dir="$(mktemp -d)" fi # Check args __run="yes" if [[ "${arg_h}" == "0" ]]; then # Show missing arg warnings only if help arg isn't specified [[ -z "${arg_b:-}" ]] && echo "B2 bucket not specified" && __run="no" [[ -z "${arg_i:-}" ]] && echo "B2 ID not specified" && __run="no" [[ -z "${arg_k:-}" ]] && echo "B2 Key not specified" && __run="no" [[ -z "${arg_g:-}" ]] && echo "GitHub account not specified" && __run="no" [[ "${#}" == "0" ]] && echo "No repos specified" && __run="no" fi # Debug #echo "== Debug ==" #echo "b: [${arg_b}]" #echo "i: [${arg_i}]" #echo "k: [${arg_k}]" #echo "g: [${arg_g}]" #echo "t: [${arg_t}]" #echo "p: [${arg_p}]" #echo "" #echo "s: [${arg_s}]" #echo "h: [${arg_h}]" #echo "" #echo "r: [${@}]" #echo "== Debug ==" # Show help or run backups if [[ "${arg_h}" = 1 ]] || [[ "${__run}" == "no" ]]; then # -h or --help used or missing argument(s), show usage and exit help "Usage: ${__base} [options] repo(s)..." else # Backup repo(s) main "${@}" fi exit 0