#/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" # 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> $BASEDIR/$p_name.err) & } # So scripts can know we're still running (for autoupdater) # TODO needs to be reworked so multiple instances can run at the same time https://tildegit.org/southerntofu/git-build.sh/issues/13 touch $BASEDIR/.LOCK # 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 # TODO: better locking mechanism rm $BASEDIR/.LOCK