#!/bin/bash
# aur-fetch - retrieve build files from the AUR
[[ -v AUR_DEBUG ]] && set -o xtrace
argv0=fetch
AUR_LOCATION=${AUR_LOCATION:-https://aur.archlinux.org}
XDG_CONFIG_HOME=${XDG_CONFIG_HOME:-$HOME/.config}
PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[1]}(): }'

# default options
clone=1 recurse=0 sync=no

results() {
    local mode=$1 prev=$2 current=$3 path=$4 dest=$5

    if [[ -w $dest ]]; then
        printf >> "$dest" '%s:%s:%s:file://%s\n' "$mode" "$prev" "$current" "$path"
    fi
}

usage() {
    cat <<! | base64 -d
ICAgICAgICAgICAgIC4tLX5+LF9fCjotLi4uLiwtLS0tLS0tYH5+Jy5fLicKIGAtLCwsICAs
XyAgICAgIDsnflUnCiAgXywtJyAsJ2AtX187ICctLS4KIChfLyd+fiAgICAgICcnJycoOwoK
!
    plain >&2 'usage: %s [-S] [--] pkgname...' "$argv0"
    exit 1
}

source /usr/share/makepkg/util/message.sh
source /usr/share/makepkg/util/parseopts.sh

if [[ ! -v NO_COLOR ]] && [[ ! -v AUR_DEBUG ]]; then
    [[ -t 2 ]] && colorize
fi

opt_short='rS'
opt_long=('sync:' 'results:' 'existing' 'recurse')
opt_hidden=('dump-options')

if ! parseopts "$opt_short" "${opt_long[@]}" "${opt_hidden[@]}" -- "$@"; then
    usage
fi
set -- "${OPTRET[@]}"

unset results_file
while true; do
    case "$1" in
        --existing)
            clone=0 ;;
        -S)
            sync=auto ;;
        --sync)
            shift
            case $1 in
                auto|reset|rebase|no)
                    sync=$1 ;;
                *)
                    error '%s: invalid --sync option: %s' "$argv0" "$1"
                    usage ;;
            esac ;;
        --results)
            shift; results_file=$1 ;;
        ## deprecated options
        --recurse|-r)
            recurse=1 ;;
        --dump-options)
            printf -- '--%s\n' "${opt_long[@]}" ${AUR_DEBUG+"${opt_hidden[@]}"}
            printf -- '%s' "${opt_short}" | sed 's/.:\?/-&\n/g'
            exit ;;
        --) shift; break ;;
    esac
    shift
done

# Single hyphen to denote input taken from stdin
stdin=0
if (( $# == 1 )) && [[ $1 == "-" || $1 == "/dev/stdin" ]]; then
    stdin=1
fi

if (( ! $# )); then
    error '%s: no pkgname given' "$argv0"
    exit 1
fi

if [[ -v results_file ]]; then
    results_file=$(realpath -- "$results_file")
    : >"$results_file" || exit 1 # truncate file
fi

if (( recurse )); then
    aur depends --pkgbase "$@" # stdin handled by aur-depends
elif (( stdin )); then
    tee # noop
else
    printf '%s\n' "$@"
fi | while read -r pkg; do
    unset -f git

    if [[ -d $pkg/.git ]]; then
        # Avoid issues with filesystem boundaries (#274)
        git() { command git -C "$pkg" "$@"; }

        # Retrieve new upstream revisions
        if git fetch --verbose >&2; then
            fetch_head=$(git rev-parse FETCH_HEAD)
        else
            exit 1
        fi

        # HEAD before git rebase or git reset
        prev_head=$(git rev-parse HEAD)

        if [[ -v results_file ]]; then
            results 'fetch' "$prev_head" "$fetch_head" "$PWD/$pkg" "$results_file"
        fi

        if [[ $sync == 'auto' ]]; then
            if [[ $(git config --get --type bool aurutils.rebase) == 'true' ]]; then
                plain >&2 'aurutils.rebase is set for %s' "$pkg"

                if ! git merge-base --is-ancestor 'HEAD@{upstream}' HEAD; then
                    sync=rebase
                fi

            elif [[ $prev_head != $(git rev-parse 'HEAD@{upstream}') ]]; then
                sync=reset
            fi # else sync = no
        fi

        case $sync in
            rebase)
                if [[ $(git config --get --type bool rebase.autoStash) != 'true' ]]; then
                    git reset --hard 'HEAD'
                fi
                git rebase --verbose
                ;;
            reset)
                git reset --hard 'HEAD@{upstream}'
                ;;
        esac >&2 || {
            error '%s: %s: failed to %s repository' "$argv0" "$sync" "$pkg"
            exit 1
        }

        head=$(git rev-parse HEAD)

        if [[ -v results_file ]] && [[ $sync != 'no' ]]; then
            results "$sync" "$prev_head" "$head" "$PWD/$pkg" "$results_file"
        fi

    # Otherwise, try to clone anew
    elif (( clone )) && git clone "$AUR_LOCATION/$pkg"; then
        head=$(git -C "$pkg" rev-parse HEAD)

        if [[ -v results_file ]]; then
            results 'clone' '0' "$head" "$PWD/$pkg" "$results_file"
        fi

    elif (( clone )); then
        error '%s: %s: failed to clone repository' "$argv0" "$pkg"
        exit 1
    fi
done

# vim: set et sw=4 sts=4 ft=sh:
