#!/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