#!/bin/bash
#
# y2m - a tool to help with the checkout of the YaST git repos
#
# Author: J. Daniel Schmidt <jdsn@suse.de>
# Date: 2012/05/17
# License: GPL2
# Homepage: http://github.com/yast/yast-meta.git

# Enable 'set -x' to get debug output (commands and arguments as they are executed):
#set -x

# Make sure to have a clean environment:
PATH="/sbin:/usr/sbin:/usr/bin:/bin"
LC_ALL="POSIX"
LANG="POSIX"
umask 022

# The return value of a pipeline is the status of
# the last command to exit with a non-zero status,
# or zero if no command exited with a non-zero status:
set -o pipefail

MY_NAME=${0##*/}

Y2MCONF=~/.y2m

# At https://github.com/yast see a repository
# e.g. https://github.com/yast/yast-yast2 which shows those URLs:
# For "SSH": git@github.com:yast/yast-yast2.git
# For "Git Read-Only": git://github.com/yast/yast-yast2.git
GITHUBBASEURL_SSH=git@github.com
GITHUBBASEURL_GitReadOnly=git://github.com

get_repos()
{
  for orgname in "$@"
  do for page in 1 2 3 4 5
     do # workaround for github's silly pagination
        curl -s "https://api.github.com/orgs/${orgname}/repos?page=${page}&per_page=100" | grep "\"name\":" | sed -e "s/^.*name\": \"\(.*\)\",.*$/\1/"
     done
  done
}

list_repos()
{
  get_repos $@ |  sed -n -e ":a" -e "$ s/\n/,/gp;N;b a" | sed -e 's/"//g' -e 's/,/ /g'
}

usage()
{
  echo
  echo "Y2M - the YaST2 meta tool"
  echo
  echo "This is a simple tool to help you to checkout all YaST modules, while"
  echo "maintaining the directory structure of the former svn repo."
  echo
  echo "With this tool you can"
  echo " * checkout all or just your favorite modules"
  echo " * switch all (or favorite) modules to a specific branch"
  echo " * run git pull on all (or favorite) modules"
  echo "with just one command."
  echo
  echo "Note: There is no need to use this tool."
  echo "      You can do everything manually as well."
  echo
  echo "Usage: $MY_NAME <command> [<modules>,..]"
  echo
  echo "commands"
  echo "    li|list      : list modules in 'yast' and 'libyui' repos"
  echo "    cl|clone     : run git clone for <modules>"
  echo "    ro|read-only : run git clone in read-only mode for <modules>"
  echo "    up|pull      : run git pull for a <modules>"
  echo "    co|br <name> : run git checkout to switch to branch <name> for <modules>"
  echo "modules"
  echo "    'ALL' : applies to all current known yast modules"
  echo "    'FAV' : applies to only your favorite modules, set in $Y2MCONF"
  echo "    <lowercase string> : applies to the named modules"
  echo
  echo "Reserved Branch and Tag Name Prefixes"
  echo
  echo "In order to maintain a certain consistency between the modules there are"
  echo "some prefixes that you should *not* use for your own branches/tags."
  echo "They are reserved for openSUSE or SLE releases."
  echo "Reserved prefixed for branches and tags:"
  echo " * 'openSUSE-'"
  echo " * 'SLE-'"
  echo " * 'Code-'"
  echo "See also: http://en.opensuse.org/YaST_SVN_to_GIT"
  echo
  exit ${1:-1}
}

createconfig()
{
  if [ -z "$1" ]
  then
    echo "Error: No path name given. Please enter a path name."
    exit 1
  fi

  if ! mkdir -p "$1"
  then
    echo "Error: Could not create the base directory: $1"
    exit 1
  fi

  cat > $Y2MCONF <<EOYCONF
#
# .y2m config file
#
# This file holds some basic information for the y2m tool
#

#
# Base path where all the checkouts of all modules will be located
# while maintaining the directory structure of the former svn.
Y2MBASE="$1"

#
# Your favorite modules (you either are the maintainer or just want to regularly track them)
# List of space separated module names including their new prefix:
#  e.g. "yast-core yast-network yast-installation libyui-qt"
Y2MFAVORITES=""
EOYCONF
}

function missingconf()
{
  echo "No config for y2m exists."
  echo "In the $Y2MCONF file things like the base checkout directory need to be set."
  echo "I will now create a config. Press Ctrl-C to abort."
  echo
  echo "Within the base path all YaST modules will be checked out, while maintaining"
  echo "the same directory structure of the former svn repo."
  read -p "Please enter base path: " Y2MBASE
  createconfig $Y2MBASE
}

[ -z "$1" -o "help" = "$1" ] && usage 0

#TODO: cache the module name information somewhere and just update them e.g. once a week or on request
#      this would speed up the script run a lot as it would save two curl https requests
echo
echo "Getting repos for 'yast'..."
YMODULESALL="$( list_repos yast )"
echo "Getting repos for 'libyui'..."
LMODULESALL="$( list_repos libyui )"
echo "...got repos"

CMD=$1
shift

[ ! -f $Y2MCONF ] && missingconf
[ ! -f $Y2MCONF ] && exit 1
# overwrite variables that are set in the config
. $Y2MCONF

case $CMD in
  li|list) echo "Modules in 'yast' repo:"
           echo "$YMODULESALL" | fold -s -w 78
           echo "Modules in 'libyui' repo:"
           echo "$LMODULESALL" | fold -s -w 78
           exit 0;;
  cl|clone) CMD=cl;;
  ro|read-only) CMD=ro;;
  up|pull)  CMD=up;;
  br|co) CMD=co
         YBRANCH=$1
         shift;;
  *)     echo "Error: Unknown command: $CMD"
         echo "Run '$0 help' for more information."
         exit 1;;
esac

MODULE=$1
MODULESALL=$@
shift

MODULESACTIVE=""
case $MODULE in
  ALL)
       if [ $CMD = 'cl' -o $CMD = 'ro' ]
       then
         # ALL for cloning means: all remote repos
         MODULESACTIVE="$YMODULESALL $LMODULESALL"
       else
         # ALL for the other commands means: all local repos
         MODULESACTIVE=`ls $Y2MBASE/ | sed -e "s/^/yast-/"`
         #FIXME: all names are mapped to yast-
         #TODO:  use the dynamically fetched repo lists to automatically map them correctly
       fi
       ;;
  FAV) MODULESACTIVE=$Y2MFAVORITES
       ;;
  [a-z0-9]*)
       MODULESACTIVE=$MODULESALL
       ;;
esac

if test -n "$MODULESACTIVE"
then echo "Running '$MY_NAME $CMD' for those modules:"
     echo "$MODULESACTIVE" | fold -s -w 78
else echo "Error: No modules found."
     echo "Run '$0 help' for more information."
     exit 1
fi

test -d "$Y2MBASE" && pushd $Y2MBASE &>/dev/null

for M in $MODULESACTIVE
do
  #HARDCODED exceptions that don't match schema
  case ${M} in
    y2r) ORG=yast
      ;;
    ruby-ui) ORG=libyui
      ;;
    ycp-killer) ORG=yast
      ;;
    *) ORG=${M%%-*}
      ;;
  esac

  case $ORG in
    yast) MODDIRNAME=${M#yast-}
        ;;
    libyui) MODDIRNAME=${M}
        ;;
    y2r) MODDIRNAME=${M}
        ORG="yast"
	;;
    ruby) MODDIRNAME=${M}
	ORG="libyui"
        ;;
    *)  echo "Error: the organization '$ORG' is not supported."
        echo "Please prefix the module names correctly  (yast-*, libyui-*)."
        exit 1
        ;;
  esac

  #TODO: also support the common plain module names without the prefixes "yast-" and "libyui-"
  #      lookup the passed module names in the two lists and set the variables accordingly
  #      it would make the script more comfortable to use

  if [ -z "$MODDIRNAME" ]
  then
    echo "Error: could not parse the module name '$M'."
    echo "Please prefix the module names correctly (yast-*, libyui-*)."
  fi
  case $CMD in
    cl) if test -d $MODDIRNAME
        then echo "Repo already cloned: $MODDIRNAME"
             continue
        fi
        # pass directory name to clone command to maintain the original svn directory structure
        echo
        echo "Cloning ${GITHUBBASEURL_SSH}:${ORG}/${M}..."
        git clone ${GITHUBBASEURL_SSH}:${ORG}/${M} $MODDIRNAME
        echo "...cloned $M in directory $MODDIRNAME"
    ;;
    ro) if test -d $MODDIRNAME
        then echo "Repo already exists: $MODDIRNAME"
             continue
        fi
        # pass directory name to clone command to maintain the original svn directory structure
        echo
        echo "Read-only cloning ${GITHUBBASEURL_SSH}:${ORG}/${M}..."
        git clone ${GITHUBBASEURL_GitReadOnly}/${ORG}/${M} $MODDIRNAME
        echo "...cloned $M in directory $MODDIRNAME"
    ;;
    up) if ! test -d $MODDIRNAME
        then echo "Repo does not exist: $MODDIRNAME"
             continue
        fi
        pushd $MODDIRNAME >/dev/null
        git pull
        popd >/dev/null
    ;;
    co) if ! test -d $MODDIRNAME
        then echo "Repo does not exist: $MODDIRNAME"
             continue
        fi
        pushd $MODDIRNAME >/dev/null
        git checkout $YBRANCH
        popd >/dev/null
    ;;
    *)  echo "Abort: No code for command '$CMD'. Please contact the author of this script."
        test -d "$Y2MBASE" && popd &>/dev/null
        exit 1
    ;;
  esac
done

test -d "$Y2MBASE" && popd &>/dev/null

