#!/bin/bash

# sbodeathrow by B. Watson <urchlay@slackware.uk>, WTFPL.
# You shouldn't have to edit anything but SBO_GITROOT to make
# this work (or just set it in the environment).

# Default for $THRESH, in days. Don't change this; change THRESH, below.
DEFTHRESH=180

#######################################################################
#### Configuration:

# Where you keep your local git tree (which you must create yourself,
# and keep up to date)
SBO_GITROOT="${SBO_GITROOT:-/home/urchlay/sbogit}"

# Builds orphaned >= $THRESH days ago will be removed (or just listed)
THRESH=${THRESH:-${DEFTHRESH}}

#### End of config
#######################################################################

SELF="$( basename $0 )"

# For use with urchlay's git hooks. Ignore these if you don't
# know what they're for.
export NOAUTOCOMMIT=1
export SBOLINT=no

usage() {
cat <<EOF
$SELF - remove orphaned builds from the SBo repo.

$SELF looks for builds that have been orphaned for more that $DEFTHRESH
days, and (optionally) removes them with "git rm -rf", each in its own
git commit.

Builds that have dependees will not be removed, unless all the
dependees have also been orphaned for >= $DEFTHRESH days. These builds
will instead be listed on stderr, along with the dependee and its
maintainer's name/email.

Options:
  --remove   Actually remove the builds. Without this, builds that
             would be removed are listed on stdout, but not removed.
  --help     Show this help message.

Environment:
  THRESH - threshold (in days) for deleting orphans. Default is $DEFTHRESH.
    Please don't change this in production; it's only provided for testing.

  SBO_GITROOT - where the local git clone of the SBo tree lives. This
    script will *not* create the tree nor keep it up-to-date; use e.g.
    "git clone" to create it, and "git pull" before each run.
    Currently set to: $SBO_GITROOT
EOF
}

die() {
  echo "$SELF: fatal: $@" 2>&1
  exit 1
}

warn() {
  echo "$SELF: $@" 2>&1
}

set_cat_prg() {
  category="$( echo $1 | cut -d/ -f1 )"
  prgnam="$( echo $1 | cut -d/ -f2 )"
}

print_maint_info() {
  local name="$( grep ^MAINTAINER $1/*.info | cut -d'"' -f2 )"
  local email="$( grep ^EMAIL $1/*.info | cut -d'"' -f2 )"
  echo "$name <$email>" 1>&2
}

# Return true if it's safe to remove the build $category/$prgnam.
# It's not safe if it has dependees, unless all the dependees are
# also going to be removed during this run.
safe_to_remove() {
  local catprg="$category/$prgnam"
  # hoorex adds a trailing space...
  local d="$( hoorex -l $prgnam | sed 's, *$,,' )"

  if [ "$d" != "$catprg" ]; then
    for i in $d; do
      if [ "$i" != "$catprg" ]; then
        if echo $INFOS | grep -q "\\<$i\\/"; then
          #echo "$catprg: dependee $i exists, but is to be removed" 1>&2
          : # NOP, don't print a message
        else
          echo -n "$catprg: can't remove due to dependee:" 1>&2
          echo -n "  $i: " 1>&2
          print_maint_info $i
          return 1 # false
        fi
      fi
    done
  fi
  return 0 # remember, 0 is 'true' in bash!
}

# Die if hoorex isn't installed, or doesn't support --auto option
check_hoorex_version() {
  local hoohelp="$( hoorex --help || echo MISSING )"
  [ "$hoohelp" = "MISSING" ] && die "hoorex not installed"
  echo "$hoohelp" | grep -q -- --auto || \
    die "hoorex too old (needs -a/--auto option; upgrade to >=0.10.4)"
}

# With --remove, remove a build. Otherwise, print a message to stdout
# letting us know it would be removed.
remove_build() {
  local catprg="$1"
  if [ "$REMOVE" = "1" ]; then
    git rm -rf "$catprg" || die "$catprg: git rm command failed"
    git commit -m"$catprg: Removed (orphaned >$THRESH days)." || die "$catprg: git commit failed"
    warn "removed $catprg"
  else
    echo "$catprg"
  fi
}

### main()
case "$1" in
  "") ;; # NOP
  --remove) REMOVE=1 ;;
  --help)   usage; exit 0 ;;
  *) die "unknown argument $1, try --help" ;;
esac

check_hoorex_version

cd $SBO_GITROOT || die "edit script or set SBO_GITROOT in environment"
[ -d academic/ -a -d system/ -a -d .git/ ] || \
  die "$SBO_GITROOT is not a SBo git tree (set SBO_GITROOT)"

case "$THRESH" in
  [0-9]*) ;; # NOP
  *) die "invalid THRESH '$THRESH' (non-numeric)"
esac

hoorex -a -s `pwd` >/dev/null || die "hoorex failed"

thresh_sec="$( date -d "$THRESH days ago" +%s )"
case "$thresh_sec" in
  [1-9][0-9]*) ;; # OK, NOP
  *) die "failed to get thresh_sec, coreutils too old or bad THRESH?" ;;
esac

INFOS="$( git grep -l '^EMAIL="NOEMAIL"' '*/*/*.info' )"

if [ "$INFOS" = "" ]; then
  # nothing to do!
  warn "nothing to do; no orphaned builds in the repo at $SBO_GITROOT"
  exit 0
fi

for infofile in $INFOS; do
  set_cat_prg $infofile
  orphaned_sec="$( git blame --porcelain -L/MAINTAINER/,+1 $infofile | grep ^author-time | cut -d' ' -f2 )"
  if [ "$orphaned_sec" -lt "$thresh_sec" ]; then
    if safe_to_remove; then
      remove_build "$category/$prgnam"
    fi
  fi
done

exit 0
