123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990 | #!/bin/sh
# Usage: new-repo [-b BRANCH] [-d DESCRIPTION] [-m MIRROR]... DIR
#
# Sets up the bare repository DIR (for example /var/git/foo.git) for
# Repo Renderer, creating it if it is not there. Run it as the user that
# owns the repositories. On an existing repository it wires it up again.
#
# -b BRANCH the default branch of a new repository (default main)
# -d DESCRIPTION the one-line description the site shows
# -m MIRROR a git URL to push to after every push; repeatable
#
# It refuses branch deletions (force pushes are still allowed), links
# the repository's post-receive hook to the one beside this script (or
# installed under share/repo-render), and records the mirrors in the
# repository's git config, where the hook reads them. Every
# repository runs that one hook, so installing a newer one upgrades them
# all. Remove a mirror with
# git -C DIR config --unset repo-render.mirror URL
# and give mirrors an ssh key with
# git -C DIR config core.sshCommand "ssh -o BatchMode=yes -i KEY"
set -eu
die() { echo "new-repo: $*" >&2; exit 1; }
usage() { die "usage: new-repo [-b BRANCH] [-d DESCRIPTION] [-m MIRROR]... DIR"; }
# The hook ships beside this script, or under share/ when installed. The
# link names it by its absolute path, wherever this runs from.
here=$(cd "$(dirname "$0")" && pwd -P)
hook=""
for d in "$here" "$here/../share/repo-render"; do
if [ -f "$d/post-receive" ]; then
hook=$(cd "$d" && pwd -P)/post-receive
break
fi
done
[ -n "$hook" ] || die "no post-receive hook beside $0"
[ -x "$hook" ] || die "$hook is not executable"
branch="" description="" mirrors=""
while getopts b:d:m: opt; do
case $opt in
b) branch=$OPTARG ;;
d) description=$OPTARG ;;
m)
case $OPTARG in
""|-*|*[[:space:][:cntrl:]]*) die "mirror $OPTARG is not a usable git URL (no spaces, no leading -)" ;;
esac
mirrors="$mirrors
$OPTARG"
;;
*) usage ;;
esac
done
shift $((OPTIND - 1))
[ $# -eq 1 ] || usage
dir=$1
case $(basename "$dir") in
[A-Za-z0-9]*.git) ;;
*) die "$dir: the repository's directory must be NAME.git" ;;
esac
case $(basename "$dir" .git) in
*[!A-Za-z0-9._-]*) die "$dir: a name has only letters, digits and . _ -" ;;
esac
if [ -e "$dir" ]; then
[ -z "$branch" ] || die "$dir already exists, and -b only applies to a new repository"
[ "$(git -C "$dir" rev-parse --is-bare-repository 2>/dev/null)" = true ] || die "$dir is not a bare git repository"
verb=rewired
else
git init --quiet --bare --initial-branch="${branch:-main}" "$dir"
verb=created
fi
git -C "$dir" config receive.denyDeletes true
git -C "$dir" config receive.denyNonFastForwards false
printf '%s\n' "$mirrors" | while read -r m; do
[ -n "$m" ] || continue
git -C "$dir" config --get-all repo-render.mirror | grep -qxF -- "$m" || git -C "$dir" config --add repo-render.mirror "$m"
done
[ -z "$description" ] || printf '%s\n' "$description" >"$dir/description"
# The link is made beside the hook and renamed over it, so a push
# meanwhile finds one or the other.
mkdir -p "$dir/hooks"
tmp=$dir/hooks/.post-receive.$$
rm -f "$tmp"
ln -s "$hook" "$tmp"
mv -f "$tmp" "$dir/hooks/post-receive"
echo "$verb $dir"
|