#!/bin/sh
set -euC
##/// git-gitolite 1.0
#////
#//// Manage the counterpart of a local Git repository on a remote gitolite
#//// host.
#////
###// usage:
#//// git-gitolite help
#//// git-gitolite info
#//// git-gitolite init <license> [<description> [<references>]]
#//// git-gitolite remote
#//// git-gitolite head
#//// git-gitolite publish
#//// git-gitolite unpublish
#//// git-gitolite rm
#//// git-gitolite <command> [<args>...]
#//// git-gitolite --help|-h
#//// git-gitolite --version
#////
###// commands:
####/ help
#//// Runs the gitolite 'help' command.
####/ info
#//// Runs the gitolite 'info' command.
####/ init
#//// Creates and commits, separately, readme and license files.
#//// A note about the <license> (which should be an SPDX identifier), as
#//// well as <description> and <references> if given, is inserted into the
#//// readme. The readme is opened in the editor configured for git
#//// for review before each commit. This command then (re)sets the remote
#//// URL, creates the remote repository and pushes.
####/ remote
#//// (Re)sets the remote URL.
####/ head
#//// Resets the remote HEAD to point to the same ref as the local HEAD,
#//// making it the default view on a web server.
####/ publish
#//// Grants read permissions to 'gitweb', making it viewable through a
#//// web server.
####/ unpublish
#//// Revokes read permissions from 'gitweb', preventing it from being
#//// viewable through a web server.
####/ rm
#//// (Unlocks and) removes the remote repository.
####/ <command>
#//// Perform a gitolite command, filling in the host and repository name
#//// automatically. See `git-gitolite help` for a list of commands.
#////
###// environment variables:
####/ GIT_GITOLITE_USER
#//// The gitolite user under which repositories are found.
#//// [default: $USER]
####/ GIT_GITOLITE_HOST
#//// The host to connect to.
#//// [default: git.$(dnsdomainname)]
####/ GIT_GITOLITE_REMOTE
#//// The git remote used to identify the gitolite server.
#//// [default: origin]
## Messages
prog="$(basename "$0")"
help() { sed -n 's|^#[#/]*/ \?||p' "$0"; exit 0; }
version() { help | awk '/^$/{++p;next}p==0'; exit 0; }
usage() { help | awk '/^$/{++p;next}p==2'; exit 0; }
parse() { printf '%s: error: %s\n' "$prog" "$1"; usage; exit 1; } >&2
error() { printf '%s: error: %s\n' "$prog" "$1"; exit 1; } >&2
warning() { printf '%s: warning: %s\n' "$prog" "$1"; } >&2
opt() { [ $# -gt 1 ] || parse "option '$1' value not provided"; }
arg() { [ $# -gt 1 ] || parse "argument '$1' not provided"; }
argend() { [ $# -eq 0 ] || parse "unrecognized argument '$1'"; }
## Parse special options
case "${1:-}"
in
'-h'|'--help') help; ;;
'--version') version; ;;
esac
## Parse required arguments
arg 'command' "$@"; command="$1"; shift;
case "$command" in
'init')
## Parse required arguments
arg 'license' "$@"; license="$1"; shift;
## Parse optional arguments
description='<DESCRIPTION>'
references=''
[ $# -eq 0 ] || { description="$1"; shift; }
[ $# -eq 0 ] || { references="$1"; shift; }
;;
esac
## Parse unrecognized arguments
case "$command" in
'help'|'info'|'init'|'remote'|'head'|'publish'|'unpublish'|'rm')
argend
;;
esac
## Parse environment variables
user="${GIT_GITOLITE_USER:-"$USER"}"
host="${GIT_GITOLITE_HOST:-"git.$(dnsdomainname)"}"
remote="${GIT_GITOLITE_REMOTE:-"origin"}"
## ssh
ssh()
{
command ssh -T "git@$host" "$@"
}
## Repository-independent commands
case "$command" in
'info'|'help') ssh "$command" "$@"; exit; ;;
esac
## Helpers
toplevel="$(git rev-parse --show-toplevel)"
project="$(basename "$toplevel")"
repo="$user/$project"
url="https://$host/$repo"
remote_url="$(git remote get-url "$remote" 2> /dev/null || true)"
remote_repo="$(printf '%s\n' "$remote_url" | sed 's#^\(.\+://\)\?[^/]\+/##')"
head="$(git rev-parse --abbrev-ref HEAD 2> /dev/null || true)"
licenses='https://github.com/github/choosealicense.com/raw/gh-pages/_licenses'
remote()
{
git remote remove "$remote" 2> /dev/null || true
git remote add "$remote" "$url"
}
commit()
{
message="$1"; shift;
file="$1"; shift;
printf '%s\n' "$@" >> "$file"
eval "$(git var GIT_EDITOR)" '"$file"'
git add "$file"
git commit -m "$message"
}
fmt()
{
printf '%s\n' "$@" \
| command fmt --width=79 --goal=79
}
## Repository initialization commands
[ "$remote_url" = "" ] || [ "$remote_url" = "$url" ] || \
warning "remote '$remote_url' does not match '$url'"
case "$command" in
'init')
# Readme.
commit 'Add readme' "$toplevel/README.md" \
"# [\`$project\`][]" \
'' \
${description:+"$description"} \
${description:+""} \
"[\`$project\`]: $url" \
${references:+"$references"}
# License.
license_raw="$(curl -sL "$licenses/$license.txt")"
license_title="$(
printf '%s' "$license_raw" \
| awk -F': ' '/^title:/{print $2; exit; }'
)"
license_body="$(
printf '%s' "$license_raw" \
| awk 's>=2&&!/^$/{b++};b;/^---$/{s++}' \
| sed \
-e 's/\[year\]/'"$(date +%Y)"'/' \
-e 's/\[fullname\]/'"$(git config user.name)"'/' \
)"
printf '%s\n' "$license_body" >| "$toplevel/LICENSE"
git add "$toplevel/LICENSE"
commit 'Add license' "$toplevel/README.md" \
'' \
'## License' \
'' \
"$(fmt \
"Licensed under the [$license_title][] unless otherwise noted, see" \
'the [`LICENSE`][] file.' \
)" \
'' \
"[$license_title]: https://choosealicense.com/licenses/$license" \
'[`LICENSE`]: LICENSE'
# Remote.
remote
ssh create "$repo"
git push
exit
;;
'remote')
remote
exit
;;
esac
## Repository-dependent commands
[ "$remote_repo" ] || \
error "remote repo for '$remote' is not defined (run 'git-gitolite init'?)"
case "$command" in
'head') ssh symbolic-ref "$remote_repo" HEAD "refs/heads/$head"; ;;
'publish') ssh perms "$remote_repo" + READERS 'gitweb'; ;;
'unpublish') ssh perms "$remote_repo" - READERS 'gitweb'; ;;
'rm') ssh D unlock "$remote_repo";
ssh D rm "$remote_repo"; ;;
*) ssh "$command" "$remote_repo" "$@"; ;;
esac