Skip to content

Git Repo Updater

Purpose: Docker container running Alpine Linux that automates and improves upon much of the script mentioned in the Git Repo Updater document. It offers the additional benefits of checking for updates every 5 seconds instead of every 60 seconds. It also accepts environment variables to provide credentials and notification settings, and can have an infinite number of monitored repositories.

Deployment

You can find the current up-to-date Gitea repository that includes the docker-compose.yml and .env files that you need to deploy everything here

docker-compose.yml
version: '3.3'
services:
    git-repo-updater:
        privileged: true
        container_name: git-repo-updater
        env_file:
            - stack.env
        image: git.bunny-lab.io/container-registry/git-repo-updater:latest
        volumes:
           - /srv/containers:/srv/containers
           - /srv/containers/git-repo-updater/Repo_Cache:/root/Repo_Cache
        restart: always

.env
# Gitea Credentials
GIT_USERNAME=nicole.rappe
GIT_PASSWORD=USE-AN-APP-PASSWORD

# NTFY Push Notification Server URL
NTFY_URL=https://ntfy.cyberstrawberry.net/git-repo-updater

# Repository/Destination Pairs (Add as Many as Needed)
REPO_01="https://${GIT_USERNAME}:${GIT_PASSWORD}@git.bunny-lab.io/bunny-lab/docs.git,/srv/containers/material-mkdocs/docs/docs"
REPO_02="https://${GIT_USERNAME}:${GIT_PASSWORD}@git.bunny-lab.io/GitOps/servers.bunny-lab.io.git,/srv/containers/homepage-docker"

Build / Development

If you want to learn how the container was assembled, the related build files are located here

Dockerfile
# Use Alpine as the base image of the container
FROM alpine:latest

# Install necessary packages
RUN apk --no-cache add git curl rsync

# Add script
COPY repo_watcher.sh /repo_watcher.sh
RUN chmod +x /repo_watcher.sh

#Create Directory to store Repositories
RUN mkdir -p /root/Repo_Cache

# Start script (Alpine uses /bin/sh instead of /bin/bash)
CMD ["/bin/sh", "-c", "/repo_watcher.sh"]

repo_watcher.sh
#!/bin/sh

# Function to process each repo-destination pair
process_repo() {
    FULL_REPO_URL=$1
    DESTINATION=$2

    # Extract the URL without credentials for logging and notifications
    CLEAN_REPO_URL=$(echo "$FULL_REPO_URL" | sed 's/https:\/\/[^@]*@/https:\/\//')

    # Directory to hold the repository locally
    REPO_DIR="/root/Repo_Cache/$(basename $CLEAN_REPO_URL .git)"

    # Clone the repo if it doesn't exist, or navigate to it if it does
    if [ ! -d "$REPO_DIR" ]; then
        curl -d "Cloning: $CLEAN_REPO_URL" $NTFY_URL
        git clone "$FULL_REPO_URL" "$REPO_DIR" > /dev/null 2>&1
    fi
    cd "$REPO_DIR" || exit

    # Fetch the latest changes
    git fetch origin main > /dev/null 2>&1

    # Check if the local repository is behind the remote
    LOCAL=$(git rev-parse @)
    REMOTE=$(git rev-parse @{u})

    if [ "$LOCAL" != "$REMOTE" ]; then
        curl -d "Updating: $CLEAN_REPO_URL" $NTFY_URL
        git pull origin main > /dev/null 2>&1
        rsync -av --delete --exclude '.git/' ./ "$DESTINATION" > /dev/null 2>&1
    fi
}

# Main loop
while true; do
    # Iterate over each environment variable matching 'REPO_[0-9]+'
    env | grep '^REPO_[0-9]\+=' | while IFS='=' read -r name value; do
        # Split the value by comma and read into separate variables
        OLD_IFS="$IFS"          # Save the original IFS
        IFS=','                 # Set IFS to comma for splitting
        set -- $value           # Set positional parameters ($1, $2, ...)
        REPO_URL="$1"           # Assign first parameter to REPO_URL
        DESTINATION="$2"        # Assign second parameter to DESTINATION
        IFS="$OLD_IFS"          # Restore original IFS

        process_repo "$REPO_URL" "$DESTINATION"
    done

    # Wait for 5 seconds before the next iteration
    sleep 5
done