#!/bin/bash # Copyright © 2014, 2016 Luke Shumaker # # This work is free. You can redistribute it and/or modify it under the # terms of the Do What The Fuck You Want To Public License, Version 2, # as published by Sam Hocevar. See the COPYING file for more details. # Depends on the 'gitget' and 'libremessages' commands. On Parabola # GNU/Linux-libre, those are the 'gitget' and 'librelib' packages, # respectively. # # For other systems, they both live at: # https://git.parabola.nu/packages/libretools.git/ set -o pipefail set -e . libremessages usage() { print 'Usage %s CONFIG-FILE' "${0##*/}" } main() { if [[ $# != 1 ]]; then usage exit fi declare -g cfg_file="$1" local r=0 while read -r repo; do handle-repo "$repo" || r=$? done < <(cfg-list-repos) return $r } handle-repo() { [[ $# == 1 ]] || panic local repo=$1 local local url upstream downstreams downstream r=0 # read configuration local="$(cfg-get "repo.$repo.local")" url="$(cfg-get "repo.$repo.url")" || true upstream="$(cfg-get "repo.$repo.upstream")" || true downstreams=($(cfg-get-all "repo.$repo.downstream")) || true # download if [[ -n "$upstream" ]]; then download "$upstream" "$local" fi # ensure that $local exists test -f "$local"/HEAD # upload for downstream in "${downstreams[@]}"; do upload "$url" "$local" "$downstream" || r=$? done return $r } download() { [[ $# == 3 ]] || panic local repo=$1 local remote=$2 local local=$3 # download the repository local url url="$(remote "$remote" pull-url)" gitget -f -n "$repo" "$url" "$local" # download the metadata remote "$remote" get-meta > "$local/git-mirror.tmp" git config --file "$local/config" --rename-section git-mirror git-mirror-bak local IFS='=' while read -r key val; do git config --file "$local/config" --add git-mirror."$key" "$val" done < "$local/git-mirror.tmp" rm -f "$local/git-mirror.tmp" git config --file "$local/config" --remove-section git-mirror-bak } upload() { [[ $# == 3 ]] || panic local clonable_url=$1 local local=$2 local remote=$3 # push metadata { printf '%q ' set-meta "${remote#*:}" [[ -z "$clonable_url" ]] || printf '%q ' "mirror=$clonable_url" git config --file "$local/config" --get-regexp '^git-mirror[.]' -z|sed -z 's/ /=/'|xargs -0r printf '%q ' } | account "${remote%%:*}" # push repository local repo_mode repo_mode=$(remote "$remote" repo-mode) if [[ $repo_mode == passive ]]; then local push_url push_url="$(remote "$remote" push-url)" cd "$local" && git push --mirror "$push_url" fi } # Spawn an 'account.type' helper. It will read commands from stdin. account() { [[ $# == 1 ]] || panic local account=$1 local account_type account_type="$(cfg-get "account.$account.type")" { cfg --list -z|sed -zn "s=^account[.]$account[.]=config =p"|grep -z -v '^type='|xargs -r0 printf '%s\n' cat } | git mirror-"$type" "$account" } # `account` is awkward to use; so let's wrap it. remote() { [[ $# > 1 ]] || panic local remote=$1 [[ $remote = *:* ]] local account="${remote%%:*}" local path="${remote#*:}" printf '%q ' "$2" "$path" "${@:3}" | account "$account" } cfg() { git config --file "$cfg_file" "$@" } cfg-get() { [[ $# == 1 ]] || panic cfg --get-all "$1" } cfg-get-all() { [[ $# == 1 ]] || panic cfg --get-all "$1" } cfg-list-repos() { [[ $# == 0 ]] || panic cfg --name-only --get-regexp '^repo[.].*[.].*$' -z|sed -z -e 's|^repo[.]||' -e 's|[.][^.]*$||'|sort -zu|xargs -r0 printf '%s\n' } main "$@"