commit 5d927bc5731e798256a4c627ad4b1ccb6578e8ca Author: Knut Ahlers Date: Fri Mar 10 17:50:48 2023 +0100 Initial version diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1401058 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +FROM bash:5 as prefetch + +ARG DUMB_INIT_VERSION=1.2.5 + +RUN set -ex \ + && apk --no-cache add \ + curl \ + && curl -sSfLo /dumb-init "https://github.com/Yelp/dumb-init/releases/download/v${DUMB_INIT_VERSION}/dumb-init_${DUMB_INIT_VERSION}_x86_64" \ + && chmod 0755 /dumb-init + + +FROM bash:5 + +RUN set -ex \ + && apk --no-cache add \ + coreutils \ + curl \ + git \ + grep \ + openssh + +COPY --from=prefetch /dumb-init /usr/local/bin/ +COPY docker-entrypoint.sh /usr/local/bin/ + +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] + diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100755 index 0000000..f240578 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,124 @@ +#!/usr/local/bin/dumb-init bash +set -euo pipefail + +: ${BRANCH:=master} # Branch to work with when syncing +: ${CHOWN_UID:=} # ID of the user to chown the repo files to +: ${CHOWN_GID:=${CHOWN_UID}} # ID of the group to chown the repo files to +: ${INTERVAL:=300} # How often to sync with remote +: ${LOCAL_DIR:=/data} # Where to find the data to backup +: ${PING_DOWN:=} # Send a ping (HTTP GET) to this URL when an exit-error ocurred +: ${PING_MAX_TIME:=5} # Time in seconds to timeout the ping request +: ${PING_UP:=} # Send a ping (HTTP GET) to this URL when backup finished successfully +: ${REMOTE:=} # Remote to sync the branch to + +function error() { + if [[ -n $PING_DOWN ]]; then + curl -sS -m ${PING_MAX_TIME} -o /dev/null "${PING_DOWN}" + fi + + log E "$@" +} + +function fatal() { + log F "$@" + exit 1 +} + +function info() { + log I "$@" +} + +function log() { + local level=$1 + shift + echo "[$(date +%H:%M:%S)][$level] $@" >&2 +} + +function main() { + pushd "${LOCAL_DIR}" + + info "Marking local dir save..." + git config --global --add safe.directory "$(pwd)" + + case "${1:-help}" in + sync) + run_sync || fatal "Backup failed." + ;; + + restore) + run_restore || failed "Restore failed." + ;; + + *) + usage + fatal "Action ${1:-help} called" + ;; + esac +} + +function run_restore() { + if [[ -d .git ]]; then + info "Found .git directory, skipping restore." + return 0 + fi + + info "Initializing empty git repository..." + git init -b ${BRANCH} + + info "Setting up remote..." + git remote add origin "${REMOTE}" + git branch -u origin "${BRANCH}" + + info "Fetching remote to reset..." + git fetch ${REMOTE} ${BRANCH} || { + error "Fetch failed (exit $?)" + continue + } + + info "Resetting to remote state..." + git reset --hard FETCH_HEAD +} + +function run_sync() { + while true; do + next_run=$((INTERVAL - $(date +%s) % INTERVAL)) + info "Sleeping ${next_run}s to next sync..." + sleep ${next_run} + + info "Fetching remote to rebase..." + git fetch ${REMOTE} ${BRANCH} || { + error "Fetch failed (exit $?)" + continue + } + + info "Rebasing upon remote..." + git rebase FETCH_HEAD || { + error "Rebase failed (exit $?)" + continue + } + [[ -z $CHOWN_UID ]] || chown -R ${CHOWN_UID}:${CHOWN_GID} . + + info "Pushing to remote..." + git push ${REMOTE} ${BRANCH} || { + error "Push failed (exit $?)" + continue + } + + if [[ -n $PING_UP ]]; then + curl -sS -m ${PING_MAX_TIME} -o /dev/null "${PING_UP}" + fi + done +} + +function usage() { + cat >&2 < +EOF +} + +main "$@"