Skip to content
fossil-skin Artifact [5318cc59cf]
Login

Artifact [5318cc59cf]

Artifact 5318cc59cf5198107a24ead8dda522e591d06c7f097a3646b174484e11f7dfa2:


#!/usr/bin/env bash
# Keep /var/fossil/repos/<name>.fossil as a full-history mirror of a GitHub repo.
#
#   git-mirror.sh init <name> <git-url> "<project description>"
#   git-mirror.sh sync [<name>...]      # default: every mirror under /var/fossil/mirror
#
# State per mirror lives in /var/fossil/mirror/<name>/: a bare git clone plus the
# git and fossil marks files that make `fossil import --git --incremental` resume
# where the last run stopped. The crontab entry runs `sync` hourly.
set -euo pipefail

REPOS=/var/fossil/repos
MIRROR=/var/fossil/mirror
LOG=$MIRROR/sync.log

export_git() {  # name, extra fast-export args...
  local name=$1; shift
  git -C "$MIRROR/$name/git" fast-export --all --signed-tags=strip \
      --tag-of-filtered-object=drop "$@"
}

init() {
  local name=$1 url=$2 desc=${3:-}
  local dir=$MIRROR/$name repo=$REPOS/$name.fossil
  [ -e "$dir" ] && { echo "mirror $name already exists at $dir" >&2; exit 1; }
  mkdir -p "$dir"
  git clone --quiet --bare "$url" "$dir/git"

  if [ -e "$repo" ]; then
    # Keep the old repo's wiki pages and move the old file out of the served directory.
    mkdir -p "$dir/wiki-from-old"
    fossil wiki list -R "$repo" | while IFS= read -r page; do
      fossil wiki export "$page" "$dir/wiki-from-old/$page.md" -R "$repo"
    done
    mkdir -p "$MIRROR/retired"
    mv "$repo" "$MIRROR/retired/$name.$(date +%Y%m%d%H%M%S).fossil"
  fi

  export_git "$name" --export-marks="$dir/git.marks" \
    | fossil import --git --export-marks="$dir/fossil.marks" "$repo" > /dev/null
  local now; now=$(date +%s)
  fossil sql -R "$repo" "REPLACE INTO config(name,value,mtime) VALUES('project-name','$name',$now)"
  [ -n "$desc" ] && fossil sql -R "$repo" \
    "REPLACE INTO config(name,value,mtime) VALUES('project-description','$(printf %s "$desc" | sed "s/'/''/g")',$now)"
  if [ -d "$dir/wiki-from-old" ]; then
    for f in "$dir/wiki-from-old"/*.md; do
      [ -e "$f" ] || continue
      page=$(basename "$f" .md)
      fossil wiki create "$page" "$f" --mimetype text/x-markdown -R "$repo" > /dev/null
    done
  fi
  echo "$url" > "$dir/url"
  echo "initialised $name: $(fossil info -R "$repo" | grep check-ins)"
}

sync_one() {
  local name=$1
  local dir=$MIRROR/$name repo=$REPOS/$name.fossil
  git -C "$dir/git" fetch --quiet --prune origin '+refs/heads/*:refs/heads/*' '+refs/tags/*:refs/tags/*'
  local before after
  before=$(fossil info -R "$repo" | awk '/check-ins/{print $2}')
  export_git "$name" --import-marks="$dir/git.marks" --export-marks="$dir/git.marks" \
    | fossil import --git --incremental --import-marks="$dir/fossil.marks" \
        --export-marks="$dir/fossil.marks" "$repo" > /dev/null
  after=$(fossil info -R "$repo" | awk '/check-ins/{print $2}')
  echo "$(date -u +%FT%TZ) $name: $before -> $after check-ins" >> "$LOG"
}

case ${1:-} in
  init) shift; init "$@" ;;
  sync) shift
    if [ $# -eq 0 ]; then
      names=()
      for d in "$MIRROR"/*/git; do [ -d "$d" ] && names+=("$(basename "$(dirname "$d")")"); done
      set -- "${names[@]}"
    fi
    for n in "$@"; do sync_one "$n"; done ;;
  *) sed -n '2,9p' "$0"; exit 2 ;;
esac