git-build.sh/git-build.sh

222 lines
6.5 KiB
Bash
Executable File

#/bin/bash
# This script was indeed self-updated!
# Catch signals so we don't leave zombie tasks behind if we Ctrl^C
# No idea why but it seems to prevent the script from working? Disable!
#trap "exit" INT TERM ERR
#trap "kill 0" EXIT
BASEDIR="$HOME/.git-build"
# So scripts can know we're still running (for autoupdater)
echo "$BASHPID" > $BASEDIR/.LOCK
# Default logging levels (error is always enabled)
INFO=1
DEBUG=0
# Extract two letters from $LANG
locale="${LANG:0:2}"
# Initialize translations
[ -f i18n/$locale.json ] && locale_strings="$(cat i18n/$locale.json)" || locale_strings="$(cat i18n/en.json)"
# Takes one argument, looks up translation
trans() {
res="$(echo "$locale_strings" | jq ".$1")"
if [[ "$res" = "" ]]; then
echo "ERROR: Failed to translate $1"
exit 1
fi
res=${res#"\""}
res=${res%"\""}
echo -n "$res"
}
# We set custom logging functions so that later we can decorate stuff
warn() {
[[ $INFO = 1 ]] && echo -e "\e[33m$(trans warning)\e[0m$(trans $1)" | envsubst > /dev/stderr
}
info() {
[[ $INFO = 1 ]] && echo "[git-build] $(trans $1)" | envsubst
}
error () {
echo -e "\e[31m$(trans error)\e[0m$(trans $1)" | envsubst > /dev/stderr
}
debug () {
[[ $DEBUG = 1 ]] && echo -e "\e[36m$(trans debug)\e[0m$(trans $1)" | envsubst
}
# Logging is done with the LOG="debug|info|error" environment variable
if [ ! -z $LOG ] && [ "$LOG" != "" ]; then
case $LOG in
"debug"|"DEBUG")
DEBUG=1
;;
"error"|"ERROR")
INFO=0
;;
"info"|"INFO")
# Default settings
;;
*)
warn loglevel_bad
;;
esac
fi
run() {
p_name="$1"
i18n_task="$p_name" info run
# Run in background and redirect output to $p_name.log
(GITBUILDCONF="$CONFDIR" GITBUILDDIR="$BASEDIR" nohup $BASEDIR/$p_name $p_name > $BASEDIR/$p_name.log 2>&1) &
}
# Overriden by -f/--force to force rebuild when no update is available
FORCE=0
# Check if we have host-specific config then LOCAL=0
# TODO: maybe we should just match for $BASEDIR/$HOSTNAME directly? Would allow to fallback to global config for multihost setup where one host is not configured.. but is that desired? Also enables to have more folders in $BASEDIR (maybe logs/)
LOCAL=1
for folder in $BASEDIR/*/; do
# Hidden folders (repos) are ignored by the pattern, so we can assume if a folder is not config/ then there are host-based configs here.
[[ "$(basename $folder)" != "config" ]] && LOCAL=0 && break
done
# Pass either local or global config to the task
[[ LOCAL = 1 ]] && CONFDIR=$BASEDIR/config || CONFDIR="$BASEDIR/$HOSTNAME"
export i18n_config="$CONFDIR"
info config
# Find targeted projects from args and extra arguments
PROJECTS=()
for arg in "$@"; do
if [[ "$arg" = "-f" ]] || [[ "$arg" = "--force" ]]; then
info force_flag
FORCE=1
# Maybe it's a task name and we find a corresponding source?
# TODO: Support source-less tasks https://tildegit.org/southerntofu/git-build.sh/issues/8
elif [ -f $BASEDIR/$arg.source ]; then
export i18n_task="$arg"
debug found_task
PROJECTS+=("$arg")
# Maybe it's a repo URL and we find a corresponding task?
elif matches="$(grep --files-with-matches --word-regexp "$arg" $BASEDIR/*.source)"; then
# Iterate over the files found to extract the task name
IFS= echo "$matches" | while read -r file; do
task_name="$(basename "$file" .source)"
# Make sure we don't have a .source file without name lying around, just in case
if [[ "$task_name" != "" ]]; then
export i18n_task="$task_name" i18n_source="$arg"
debug found_url
PROJECT+=("$task_name")
fi
done
else
export i18n_arg="$arg"
error unknown_arg
exit 1
fi
done
# If no project argument passed, default to all projects
if [[ ${#PROJECTS[*]} = 0 ]]; then
info no_task
# TODO: sourceless tasks
for file in $BASEDIR/*.source; do
# Extract the project name from path
task="$(basename $file .source)"
export i18n_task="$task"
debug found_task
PROJECTS+=("$task")
done
fi
# TODO: sourceless tasks
for p_name in ${PROJECTS[*]}; do
# For translations
export i18n_task="$p_name"
debug start_proc
# Check if task should run on host
if [[ $LOCAL = 0 ]]; then
if [ -f $BASEDIR/$p_name.host ]; then
for_host="$(cat $BASEDIR/$p_name.host)"
if [[ "$for_host" != "$HOSTNAME" ]]; then
# The task is specifically configured for a different host, ignore
export i18n_host="$for_host"
debug skipped
continue
fi
fi
# The task has a PROJECT.ignore file in host config, ignore
if [ -f $BASEDIR/$HOSTNAME/$p_name.ignore ]; then
info host_ignored
continue
fi
fi
info process
# TODO: Should be able to switch branch after a repo was cloned
if [ -f $BASEDIR/$p_name.branch ]; then
p_branch="$(cat $BASEDIR/$p_name.branch)"
export i18n_branch="$p_branch"
info to_branch
else
p_branch="master"
export i18n_branch="$p_branch"
debug default_branch
fi
p_dir="$BASEDIR/.$p_name"
if [ ! -d $p_dir ]; then
source="$(cat $BASEDIR/$p_name.source)"
export i18n_source="$source"
info clone
# Don't forget the git submodules!
if ! git clone --recursive "$source" "$p_dir"; then
error clone_failed
exit 1
fi
cd $p_dir
if [[ "$p_branch" != "master" ]]; then
debug checkout
if ! git checkout "$p_branch"; then
error checkout_failed
exit 1
fi
fi
run $p_name
# Skip to the next task!
continue
fi
debug already_cloned
cd "$p_dir"
# Refresh remote before comparing with local
git fetch --quiet origin
git diff --quiet remotes/origin/$p_branch
if [[ $? != 0 ]]; then
info pull
# Update all submodules, for now only when the main repo changed (TODO)
if ! git pull --quiet --recurse-submodules; then
error pull_failed
exit 1
fi
run $p_name
# If no update was found, we can still force rebuild with -f/--force
elif [[ $FORCE = 1 ]]; then
debug forcing
run $p_name
else
debug no_update
fi
done
# Check the PID in lockfile is still ours, we don't want
# to remove the lockfile if another git-build "owns" it
if [[ "$(cat $BASEDIR/.LOCK)" = "$BASHPID" ]]; then
rm $BASEDIR/.LOCK
fi