borg_public/get-aquila-modules.sh
2023-05-29 10:41:03 +02:00

617 lines
16 KiB
Bash
Executable File

#!/bin/sh
#+
# ARES/HADES/BORG Package -- ./get-aquila-modules.sh
# Copyright (C) 2014-2020 Guilhem Lavaux <guilhem.lavaux@iap.fr>
# Copyright (C) 2009-2020 Jens Jasche <jens.jasche@fysik.su.se>
#
# Additional contributions from:
# Guilhem Lavaux <guilhem.lavaux@iap.fr> (2023)
#
#+
C_DEFAULT=$(printf "\033[0m")
C_WHITE=$(printf "\033[1m")
C_GREEN=$(printf "\033[92m")
C_RED=$(printf "\033[91m")
C_BG_RED=$(printf "\033[41m")
C_BG_GREEN=$(printf "\033[42m")
errormsg() {
msg=$1
printf "${C_BG_RED}${msg}${C_DEFAULT}\n"
}
abort() {
errormsg "$1"
exit 1
}
trap "printf \"${C_DEFAULT}\"\n" SIGTERM EXIT
print_help()
{
cat<<EOF
This is the get-aquila-module helper script. It clones and updates the modules
common to the collaboration. By default it uses SSH protocol. We advise using
a SSH key to avoid being asked too much about your password.
--hooks Setup git hooks
--clone Clone missing modules
--https USER Use HTTPS protocol instead of SSH
--pull Pull all the modules
--push Push all modules to origin (including root)
--help This information message
--send-pack H D R Send packs of ref R to remote ssh server (host H, directory D)
--local-merge Use local GIT database to merge the reference to checked
out branches
--update-copyright Update copyright notices
--update-indent Update indentation
--status Show git status
--report Create a git version report for reproductibility
--branch-set Setup branches of default modules
directory D (including modules)
--purge Purge the content of modules (DANGEROUS)
Developer tools:
--tags Tags all submodule with current version in ARES
Experimental / not working:
--work-tree B D Create a complete worktree of branch B in
EOF
}
# ===========================================
# get current branch in git repo
function parse_git_branch() {
BRANCH=`git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/'`
if [ ! "${BRANCH}" == "" ]
then
STAT=`parse_git_dirty`
echo "(${BRANCH}${STAT})"
else
echo ""
fi
}
# ===========================================
# get current status of git repo
function parse_git_dirty {
status=`git status 2>&1 | tee`
dirty=`printf "${status}" 2> /dev/null | grep "modified:" &> /dev/null; echo "$?"`
untracked=`printf "${status}" 2> /dev/null | grep "Untracked files" &> /dev/null; echo "$?"`
ahead=`printf "${status}" 2> /dev/null | grep "Your branch is ahead of" &> /dev/null; echo "$?"`
newfile=`printf "${status}" 2> /dev/null | grep "new file:" &> /dev/null; echo "$?"`
renamed=`printf "${status}" 2> /dev/null | grep "renamed:" &> /dev/null; echo "$?"`
deleted=`printf "${status}" 2> /dev/null | grep "deleted:" &> /dev/null; echo "$?"`
bits=''
if [ "${renamed}" == "0" ]; then
bits=">${bits}"
fi
if [ "${ahead}" == "0" ]; then
bits="*${bits}"
fi
if [ "${newfile}" == "0" ]; then
bits="+${bits}"
fi
if [ "${untracked}" == "0" ]; then
bits="?${bits}"
fi
if [ "${deleted}" == "0" ]; then
bits="x${bits}"
fi
if [ "${dirty}" == "0" ]; then
bits="!${bits}"
fi
if [ ! "${bits}" == "" ]; then
echo " ${bits}"
else
echo ""
fi
}
# ===========================================
# Check whether current git is dirty
check_dirty()
{
if test $force != 0; then
return
fi
d=$1
r=$(
cd $d;
git status --porcelain | grep '^ M'
)
if test x"$r" != x""; then
echo "Module ${d} is not clean."
exit 1
fi
}
indent_out()
{
echo "${C_RED} | "
echo " ->"
sed 's/\r/\n/' | sed "s/^/${C_RED} |${C_DEFAULT} /"
}
# ===========================================
# Send notice to user
#
warning_msg() {
if [ "x$operation" != xreport ]; then
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
echo "This script can be run only by Aquila members."
echo "if your bitbucket login is not accredited the next operations will fail."
echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
fi
}
if ! command -v git 2>&1 > /dev/null; then
echo "Git is required."
exit 1
fi
# ===========================================
# Argument parsing
#
operation=''
prefix=git@bitbucket.org:
force=0
be_quiet=0
while test $# -gt 0; do
key="$1"
case $key in
--clone)
operation="clone"
;;
--hooks)
operation="hooks"
;;
--https)
user="$2"
prefix="https://${user}@bitbucket.org/"
shift
;;
--pull)
operation="pull"
;;
--local-merge)
operation="local_merge"
[ -z "$2" ] && abort "No source provided"
source_ref="$2"
shift 1
;;
--send-pack)
operation="send"
[ -z "$2" ] && abort "No host provided"
[ -z "$3" ] && abort "No directory provided"
[ -z "$4" ] && abort "No git ref provided"
target_host="$2"
target_directory="$3"
target_ref="$4"
shift 3
;;
--push)
operation="push"
;;
--purge)
operation="purge"
;;
--update-copyright)
operation="update_copyright"
;;
--update-indent)
operation="update_indent"
;;
--force)
force=1
;;
--status)
operation="status"
;;
--report)
operation="report"
;;
--branch-set)
operation="branch_set"
;;
--work-tree)
operation="worktree"
[ -z "$2" ] && abort "No branch provided"
[ -z "$3" ] && abort "No directory provided"
branch_work=$2
branch_dir=$3
shift 2
;;
--tags)
operation="tags"
;;
--push-tags)
operation="push_tags"
;;
-q)
be_quiet=1
;;
-h|--h|--he|--hel|--help)
print_help
exit 0
;;
esac
shift
done
if [ "$be_quiet" == "0" ]; then
warning_msg
fi
# ===========================================
if test "x${operation}" = "x"; then
errormsg "The operation to do is missing."
print_help
exit 1
fi
export prefix
# ===========================================
#
# Read the information about Aquila modules
read_submodules() {
local submod
submod=""
while read; do
if test "x${REPLY}" = "x"; then
continue
fi
set -- $REPLY;
submod="${submod} $1,$2,$3"
done < $1
echo ${submod}
}
close_master() {
trap - SIGINT
ssh -O exit -o ${cpath} ${target_host}
exit 1
}
get_current_branch() {
if git branch --show-current 2> /dev/null; then
return 0
else
git branch 2> /dev/null | grep -E '^\*' | awk '{print $2; }'
return $?
fi
}
setup_hooks() {
top=$1
HOOK_NAMES="applypatch-msg pre-applypatch post-applypatch pre-commit prepare-commit-msg commit-msg post-commit pre-rebase post-checkout post-merge pre-receive update post-receive post-update pre-auto-gc"
# assuming the script is in a bin directory, one level into the repo
HOOK_DIR=$(git rev-parse --show-toplevel)/.git/hooks
for hook in $HOOK_NAMES; do
# If the hook already exists, is executable, and is not a symlink
if [ ! -h $HOOK_DIR/$hook -a -x $HOOK_DIR/$hook ]; then
mv $HOOK_DIR/$hook $HOOK_DIR/$hook.local
fi
# create the symlink, overwriting the file if it exists
# probably the only way this would happen is if you're using an old version of git
# -- back when the sample hooks were not executable, instead of being named ____.sample
ln -s -f ${top}/build_tools/hooks/hooks-wrapper $HOOK_DIR/$hook
done
echo ${top} > ${HOOK_DIR}/ares_top.cfg
}
check_hooks() {
printf "Checking whether git hooks are there... "
HOOK_DIR=$(git rev-parse --show-toplevel)/.git/hooks
if [ ! -e ${HOOK_DIR}/ares_top.cfg ]; then
echo "It looks not. Installing..."
$SHELL $0 --hooks
else
echo "It looks so. Skipping."
fi
}
submodules=$(read_submodules .aquila-modules)
# ===========================================
# ===========================================
# Execute the requested operation
#
case $operation in
tags)
ares_version=$(git describe)
release_msg="Release ${ares_version}"
for i in $submodules; do
IFS=","
set -- $i;
sub_git=$1
sub=$2
branch=$3
echo "${C_WHITE}Tagging ${sub_git}${C_DEFAULT} into extra/${sub} with ${ares_version} (${release_msg})"
(cd extra/$sub; git tag -f -a -m "${release_msg}" ${ares_version}) | indent_out
done
;;
local_merge)
echo "${C_GREEN}Merging from ${source_ref}${C_DEFAULT}"
current_branch=$(get_current_branch)
echo "Current branch is ${current_branch}"
git merge ${source_ref}/${current_branch}
for i in $submodules; do
IFS=","
set -- $i;
sub_git=$1
sub=$2
branch=$3
echo "${C_GREEN}Merging $sub with ${source_ref}/$branch...${C_DEFAULT}"
([ -e extra/$sub ] && (cd extra/$sub; git merge "$source_ref/$branch" ) ) || exit 1
done
;;
send)
echo "${C_WHITE}Sending all packs...${C_DEFAULT}"
cpath="ControlPath=$(pwd)/mux-packs"
(
trap close_master SIGHUP SIGINT SIGTERM SIGKILL
ssh -nNf -o ControlMaster=yes -o "$cpath" $target_host
export GIT_SSH_COMMAND="ssh -o $cpath"
echo "${C_GREEN}Uploading $sub...${C_DEFAULT}"
current_branch=$(get_current_branch)
git send-pack "$target_host:$target_directory" "$target_ref/$current_branch"
# Wait a bit for SSH to shutdonw the tunnel between each git command. Some
# server are setup to only allow one tunnel at a time.
sleep 0.5
for i in $submodules; do
IFS=","
set -- $i;
sub_git=$1
sub=$2
branch=$3
echo "${C_GREEN}Uploading $sub...${C_DEFAULT}"
[ -e extra/$sub ] && (cd extra/$sub; git send-pack "$target_host:$target_directory/extra/$sub" "$target_ref/$branch")
sleep 0.5
done
ssh -O exit -o ${cpath} ${target_host}
)
;;
pull)
echo "${C_WHITE}Pulling root...${C_DEFAULT}"
( git pull 2>&1 ) | indent_out
for i in $submodules; do
IFS=","
set -- $i;
sub_git=$1
sub=$2
branch=$3
echo "${C_WHITE}Pulling ${sub_git}${C_DEFAULT} into extra/${sub}"
[ -e extra/$sub ] && (cd extra/$sub; git pull 2>&1 ) | indent_out
done
;;
push)
for i in $submodules; do
IFS=","
set -- $i;
sub_git=$1
sub=$2
branch=$3
echo "${C_WHITE}Pushing ${sub_git}${C_DEFAULT}..."
[ -e extra/$sub ] && (cd extra/$sub; git push origin ${branch} 2>&1 ) | indent_out
done
git push origin
;;
push_tags)
for i in $submodules; do
IFS=","
set -- $i;
sub_git=$1
sub=$2
branch=$3
echo "${C_WHITE}Pushing ${sub_git}${C_DEFAULT}..."
[ -e extra/$sub ] && (cd extra/$sub; git push --tags origin 2>&1 ) | indent_out
done
git push --tags origin
;;
purge)
for i in $submodules; do
IFS=","
set -- $i;
sub_git=$1
sub=$2
branch=$3
[ -e extra/$sub ] && (
cd extra/${sub};
if [ -z "$(git status --porcelain)" ]; then
# Working directory clean
cd ..
echo "Purging ${sub}"
rm -fr ${sub}
else
echo "Not empty. Skipping"
fi
)
done
;;
clone)
for i in $submodules; do
IFS=","
set -- $i;
sub_git=$1
sub=$2
if [[ $sub_git =~ ^bb:(.*)$ ]]; then
sub_git=${BASH_REMATCH[1]}
else
sub_git=bayesian_lss_team/${sub_git}.git
fi
echo "Cloning ${sub_git} into extra/${sub}"
(
cd extra/
if ! test -e ${sub}; then
git clone ${prefix}${sub_git} ${sub}
fi
) 2>&1 | indent_out
done
;;
hooks)
ares_topdir=$(pwd)
echo "... setting up root"
setup_hooks ${ares_topdir}
for i in $submodules; do
IFS=","
set -- $i;
sub_git=$1
sub=$2
if [[ $sub_git =~ ^bb:(.*)$ ]]; then
sub_git=${BASH_REMATCH[1]}
else
sub_git=bayesian_lss_team/${sub_git}.git
fi
echo "... setting up ${sub}"
IFS=$'\n\t '
(
cd extra/${sub}
setup_hooks ${ares_topdir}
)
done
;;
update_copyright)
check_dirty .
python3 build_tools/gather_sources.py
for i in $submodules; do
IFS=","
set -- $i;
sub_git=$1
sub=$2
branch=$3
echo "Updating ${sub}"
check_dirty extra/${sub}
[ -e extra/$sub ] && (cd extra/${sub}; python3 ../../build_tools/gather_sources.py)
done
;;
update_indent)
if ! command -v clang-format; then
errormsg "Could not find clang-format in the PATH. It is required for reindentation."
exit 1
fi
CF=$(which clang-format)
for i in $submodules; do
IFS=","
set -- $i;
sub_git=$1
sub=$2
branch=$3
if test -d extra/${sub}; then
find extra/${sub} -name "*.[ch]pp" | xargs ${CF} -style=file -i
fi
done
;;
branch_set)
echo "Setting up branches for modules..."
for i in ${submodules}; do
IFS=","
set -- $i;
sub_git=$1
sub=$2
branch=$3
echo "-- Switching ${sub} to ${branch}"
if ! [ -e extra/$sub ]; then
echo "${C_RED}Directory extra/$sub does not exist. Failing.${C_DEFAULT}"
exit 1
fi
( cd extra/${sub};
if ! (git checkout ${branch} > /dev/null 2>&1); then
errormsg "Problem changing branch on ${sub}"
exit 1
fi
)
done
;;
report)
echo "GIT report"
for i in . extra/*; do
if test x$(basename $i) = xdemo; then
continue
fi
(
cd $i
h=$(git rev-parse HEAD)
mname=$(if [ "$i" == "." ]; then echo "root"; else echo $(basename $i); fi)
status=`git status 2>&1 | tee`
dirty=`printf "${status}" 2> /dev/null | grep "modified:" &> /dev/null; echo "$?"`
code=""
if [ "$dirty" == "0" ] ; then
code='(?)'
fi
echo "- Module: ${mname}, Version${code}: ${h}"
)
done
;;
worktree)
base=$(pwd)
git worktree add "${branch_dir}" "${branch_work}" || abort "Invalid worktree arguments"
work_submodules=$(read_submodules ${branch_dir}/.aquila-modules)
for i in ${work_submodules}; do
IFS=","
set -- $i;
sub_git=$1
sub=$2
branch=$3
( cd "extra/$sub"; git worktree add ${base}/${branch_dir}/extra/${sub} "${branch}" || abort "Invalid state" ) || abort "Could not setup ${branch} for ${sub}"
done
;;
status)
echo "Checking GIT status..."
echo
for i in . extra/*; do
if test x$(basename $i) = xdemo; then
continue
fi
(
cd $i
# https://stackoverflow.com/questions/1593051/how-to-programmatically-determine-the-current-checked-out-git-branch
branch_name="$(git symbolic-ref HEAD 2>/dev/null)" || branch_name="(unnamed branch)" # detached HEAD
branch_name=${branch_name##refs/heads/}
# --
if [ "$i" == "." ]; then
printf "Root tree\t (branch ${branch_name}) :"
else
printf "Module $(basename $i)\t(branch ${branch_name}) :"
fi
status=`git status 2>&1 | tee`
dirty=`printf "${status}" 2> /dev/null | grep "modified:" &> /dev/null; echo "$?"`
untracked=`printf "${status}" 2> /dev/null | grep "Untracked files" &> /dev/null; echo "$?"`
ahead=`printf "${status}" 2> /dev/null | grep "Your branch is ahead of" &> /dev/null; echo "$?"`
newfile=`printf "${status}" 2> /dev/null | grep "new file:" &> /dev/null; echo "$?"`
renamed=`printf "${status}" 2> /dev/null | grep "renamed:" &> /dev/null; echo "$?"`
deleted=`printf "${status}" 2> /dev/null | grep "deleted:" &> /dev/null; echo "$?"`
code=$(
[ "$dirty" == "0" ] && echo " dirty"
[ "$untracked" == "0" ] && echo " untracked. Are you sure all files are under git supervision ?"
[ "$ahead" == "0" ] && echo " ahead. Run git push ?"
[ "$newfile" == "0" ] && echo " newfile. Please git commit."
[ "$renamed" == "0" ] && echo " renamed. Please git commit."
[ "$deleted" == "0" ] && echo " deleted. Please git commit.")
if [ "x$code" == "x" ]; then
printf "$C_GREEN good. All clear. $C_DEFAULT\n"
else
printf "$C_RED some things are not right. $C_DEFAULT\n"
printf "${code}\n"
fi
)
done
;;
esac
# ===========================================
if [ $operation != report ]; then
check_hooks
fi