283 lines
6.8 KiB
Bash
Executable File
283 lines
6.8 KiB
Bash
Executable File
#!/bin/bash
|
|
set -f
|
|
|
|
VERBOSITY=0
|
|
SUPPORTED_VCS="bzr hg git url"
|
|
RET_UNCLAIMED=3
|
|
RET_SUCCESS=0
|
|
RET_FAIL=1
|
|
DEF_COMMAND="vcs_run"
|
|
|
|
Usage() {
|
|
cat <<EOF
|
|
Usage: ${0##*/} [ options ] repo-url [command [arguments]]
|
|
|
|
obtain repository from repo-url, and execute 'command' with 'arguments'
|
|
|
|
Command will default to '$DEF_COMMAND' in the top level of the repository.
|
|
|
|
options:
|
|
-t | --target DIR checkout branch to DIR [./(basename repo)]
|
|
--vcs-type V repo-url is of type 'V' [auto]
|
|
supported: auto $SUPPORTED_VCS
|
|
-v | --verbose increase verbosity
|
|
-D | --deps attempt to install dependencies if necessary
|
|
|
|
Example:
|
|
* run 'stack.sh' in git://github.com/openstack-dev/devstack.git
|
|
vcs-run --deps git://github.com/openstack-dev/devstack.git stack.sh
|
|
* build cloud-utils
|
|
vcs-run --deps lp:cloud-utils -- ./tools/build-deb -us -uc
|
|
|
|
EOF
|
|
}
|
|
|
|
bad_Usage() { Usage 1>&2; [ $# -eq 0 ] || error "$@"; return 1; }
|
|
error() { echo "$@" 1>&2; }
|
|
debug() {
|
|
local level=${1}; shift;
|
|
[ "${level}" -gt "${VERBOSITY}" ] && return
|
|
error "${@}"
|
|
}
|
|
|
|
has_cmd() {
|
|
command -v "$1" >/dev/null 2>&1
|
|
}
|
|
|
|
get_cmd() {
|
|
# get_cmd(cmd, get_deps, packages)
|
|
# get command 'cmd' if necessary by installing 'packages'
|
|
# if 'get_deps' is false, then return error.
|
|
local cmd="$1" deps="$2"
|
|
shift 2
|
|
has_cmd "$1" && return 0
|
|
$deps || { error "No cmd '$cmd', but nodeps specified"; return 1; }
|
|
apt_install "$@"
|
|
}
|
|
|
|
apt_install() {
|
|
local cmd=""
|
|
cmd=( env DEBIAN_FRONTEND=noninteractive apt-get --quiet
|
|
--assume-yes install "$@" )
|
|
[ "$(id -u)" = "0" ] ||
|
|
cmd=( sudo "${cmd[@]}" )
|
|
debug 1 "installing dependencies:" "${cmd[@]}"
|
|
"${cmd[@]}"
|
|
}
|
|
|
|
vcsget_bzr() {
|
|
# deps type src target cmd
|
|
local deps="$1" rtype="$2" src="$3" target="$4" tmp=""
|
|
if [ "$rtype" = "auto" ]; then
|
|
case "$src" in
|
|
*.bzr|bzr:*|lp:*) :;;
|
|
*)
|
|
if ! [ -d "$src" -a -d "$src/.bzr" ]; then
|
|
return $RET_UNCLAIMED
|
|
fi
|
|
src=$(cd "$src" && pwd) || return $RET_FAIL
|
|
;;
|
|
esac
|
|
fi
|
|
get_cmd bzr "$deps" bzr || return $RET_FAIL
|
|
if [ -z "$target" ]; then
|
|
case "$src" in
|
|
*/*) tmp="${src##*/}";;
|
|
*:*) tmp="${src#*:}";;
|
|
*) tmp="$src"
|
|
esac
|
|
target="${tmp%.bzr}"
|
|
fi
|
|
local cmd="" q="--quiet"
|
|
[ $VERBOSITY -gt 1 ] && q=""
|
|
|
|
if [ -d "$target/.bzr" ]; then
|
|
debug 1 "updating $target: bzr pull ${q:+$q }$src"
|
|
( cd "$target" && bzr pull $q "$src" )
|
|
else
|
|
debug 1 "branching to $target: bzr branch ${q:+$q }$src"
|
|
bzr branch $q "$src" "$target"
|
|
fi
|
|
[ $? -eq 0 ] || return $RET_FAIL
|
|
_RET="$target"
|
|
return 0
|
|
}
|
|
|
|
vcsget_git() {
|
|
# deps type src target cmd
|
|
local deps="$1" rtype="$2" src="$3" target="$4" tmp=""
|
|
if [ "$rtype" = "auto" ]; then
|
|
case "$src" in
|
|
*.git|git:*) :;;
|
|
*)
|
|
if ! [ -d "$src" -a -d "$src/.git" ]; then
|
|
return $RET_UNCLAIMED
|
|
fi
|
|
src=$(cd "$src" && pwd) || return $RET_FAIL
|
|
;;
|
|
esac
|
|
fi
|
|
get_cmd git "$deps" git || return $RET_FAIL
|
|
if [ -z "$target" ]; then
|
|
tmp="${src##*/}"
|
|
target="${tmp%.git}"
|
|
fi
|
|
local q="--quiet"
|
|
[ $VERBOSITY -gt 1 ] && q=""
|
|
if [ -d "$target/.git" ]; then
|
|
debug 1 "updating $target: git pull ${q:+$q }${src}"
|
|
( cd "$target" && git pull $q "$src" )
|
|
else
|
|
debug 1 "cloning to $target: git clone ${q:+$q }$src $target"
|
|
git clone $q "$src" "$target" || return $RET_FAIL
|
|
fi
|
|
[ $? -eq 0 ] || return $RET_FAIL
|
|
_RET="$target"
|
|
return 0
|
|
}
|
|
|
|
vcsget_hg() {
|
|
# deps type src target cmd
|
|
local deps="$1" rtype="$2" src="$3" target="$4" tmp=""
|
|
if [ "$rtype" = "auto" ]; then
|
|
case "$src" in
|
|
*.hg|hg:*) :;;
|
|
*) return $RET_UNCLAIMED;;
|
|
esac
|
|
fi
|
|
get_cmd hg "$deps" mercurial || return $RET_FAIL
|
|
if [ -z "$target" ]; then
|
|
tmp="${src##*/}"
|
|
target="${tmp%.hg}"
|
|
fi
|
|
local quiet="--quiet"
|
|
[ $VERBOSITY -gt 1 ] && quiet=""
|
|
hg clone $quiet "$src" "$target" || return $RET_FAIL
|
|
_RET="$target"
|
|
return 0
|
|
}
|
|
|
|
vcsget_url() {
|
|
# deps type src target cmd
|
|
# if target is not specified, target directory is md5sum
|
|
# of the url. If cmd does not start with a /, then use it
|
|
# as the output filename. If it does start with a /, then
|
|
# store the url in DEF_COMMAND in this directory.
|
|
local deps="$1" rtype="$2" src="$3" target="$4" cmd="$5" tmp=""
|
|
if [ "$rtype" = "auto" ]; then
|
|
case "$src" in
|
|
http://*|https://*) :;;
|
|
*) return $RET_UNCLAIMED;;
|
|
esac
|
|
fi
|
|
get_cmd wget "$deps" wget || return $RET_FAIL
|
|
if [ -z "$target" ]; then
|
|
target=$(echo "$src" | md5sum)
|
|
target=${target% -}
|
|
fi
|
|
|
|
local cmdname="$cmd"
|
|
if [ "${cmd#/}" != "$cmd" ]; then
|
|
cmdname="./$DEF_COMMAND"
|
|
fi
|
|
|
|
local quiet="--quiet"
|
|
[ $VERBOSITY -gt 1 ] && quiet=""
|
|
|
|
mkdir -p "$target" ||
|
|
{ error "failed mkdir -p '$target'"; return $RET_FAIL; }
|
|
debug 1 "wget -O '$target/$cmdname' '$src'"
|
|
wget $quiet -O "$target/$cmdname" "$src" || {
|
|
error "failed wget -O '$target/$cmdname' '$src'"
|
|
return $RET_FAIL
|
|
}
|
|
|
|
_RET="$target"
|
|
return 0
|
|
}
|
|
|
|
main() {
|
|
local short_opts="hDt:v"
|
|
local long_opts="help,deps,target:,vcs-type:,verbose"
|
|
local getopt_out=$(getopt --name "${0##*/}" \
|
|
--options "${short_opts}" --long "${long_opts}" -- "$@") &&
|
|
eval set -- "${getopt_out}" ||
|
|
{ bad_Usage; return; }
|
|
|
|
local cur="" next="" target="" rtype="auto" tmp=""
|
|
local def_target="" deps="" getdeps=false arg0=""
|
|
|
|
while [ $# -ne 0 ]; do
|
|
cur="$1"; next="$2";
|
|
case "$cur" in
|
|
-h|--help) Usage ; exit 0;;
|
|
-D|--deps) getdeps=true;;
|
|
-t|--target) target=$next; shift;;
|
|
--vcs-type) rtype=$next; shift;;
|
|
-v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
|
|
--) shift; break;;
|
|
esac
|
|
shift;
|
|
done
|
|
|
|
[ $# -gt 0 ] || { bad_Usage "must provide at least repo"; return; }
|
|
|
|
src_repo="$1"
|
|
shift
|
|
[ -n "$src_repo" ] || { error "empty source repo?"; return 1; }
|
|
|
|
if [ -n "$target" ]; then
|
|
tmp=$(dirname "${target}")
|
|
[ -d "$tmp" ] || mkdir -p "$tmp" ||
|
|
{ error "failed to create $tmp for '$target'"; return 1; }
|
|
fi
|
|
|
|
if [ $# -eq 0 ]; then
|
|
set -- "$DEF_COMMAND"
|
|
fi
|
|
arg0="$1"
|
|
|
|
local vcs vcslist="${SUPPORTED_VCS}"
|
|
[ "$rtype" = "auto" ] || vcslist="$rtype"
|
|
|
|
local workd=""
|
|
for vcs in $vcslist; do
|
|
has_cmd "vcsget_$vcs" ||
|
|
{ error "unknown vcs type '$vcs'"; return 1; }
|
|
"vcsget_$vcs" "$getdeps" "$rtype" "$src_repo" "$target" "$arg0"
|
|
ret=$?
|
|
case "$ret" in
|
|
$RET_UNCLAIMED) :;; # not claimed
|
|
$RET_SUCCESS) workd="$_RET"; break;;
|
|
*) error "failed to get '$src_repo' of type '$vcs'";
|
|
return $ret;;
|
|
esac
|
|
done
|
|
|
|
[ -d "$workd" ] ||
|
|
{ error "unknown source repo '$src_repo'"; return 1; }
|
|
|
|
cd "$workd" ||
|
|
{ error "failed to enter target dir '$workd'"; return 1; }
|
|
|
|
if [ -f "./$1" ]; then
|
|
if [ ! -x "./$1" ]; then
|
|
debug 1 "adding execute to ./$1"
|
|
chmod ugo+x "./$1" ||
|
|
{ error "failed add execute to ./$1"; return 1; }
|
|
fi
|
|
tmp="./$1"
|
|
shift
|
|
set -- "$tmp" "$@"
|
|
elif ! has_cmd "$1"; then
|
|
error "command '$1' not available anywhere"
|
|
return 1
|
|
fi
|
|
|
|
debug 1 "executing command in $PWD:" "$@"
|
|
exec "$@"
|
|
}
|
|
|
|
main "$@"
|
|
# vi: ts=4 noexpandtab
|