#!/bin/bash
#-------------------------------------------------------------------------------
# Copyright (C) 2006-2021 British Crown (Met Office) & Contributors.
#
# This file is part of FCM, tools for managing and building source code.
#
# FCM is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# FCM is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with FCM. If not, see <http://www.gnu.org/licenses/>.
#-------------------------------------------------------------------------------
# NAME
#   pre-commit
#
# SYNOPSIS
#   pre-commit REPOS TXN
#
# ARGUMENTS
#   REPOS - the path to the Subversion repository
#   TXN - the commit transaction
#
# DESCRIPTION
#   This script performs pre-commit check, including:
#   1. Path-based permission check using "svnperms.py", if
#      "$REPOS/hooks/svnperms.conf" exists.
#   2. Size check. Transaction should occupy less than 10MB, or the number of
#      MB specified in "$REPOS/hooks/pre-commit-size-threshold.conf".
#   3. Runs "$REPOS/hooks/pre-commit-custom" if it exists.
#
# ENVIRONMENT VARIABLES
#   FCM_SVN_HOOK_ADMIN_EMAIL
#     The name of the admin team. (Default is "Admin".)
#-------------------------------------------------------------------------------
set -eu

REPOS=$1
TXN=$2

export PATH=${PATH:-'/usr/local/bin:/bin:/usr/bin'}:$(dirname $0)
THIS=$(basename $0)
USER=${USER:-$(whoami)}
NAME=$(basename "$REPOS")
LOG_TXN="$REPOS/log/$THIS-$TXN.log"

begin() {
    local NOW=$(date -u +%FT%H:%M:%SZ)
    local AUTHOR=$(svnlook author -t "$TXN" "$REPOS")
    echo "$NOW+ $TXN by $AUTHOR"
    svnlook changed -t "$TXN" "$REPOS"
}

main() {
    local ADMIN_EMAIL=${FCM_SVN_HOOK_ADMIN_EMAIL:-Admin}

    # Check size.
    local MB=1048576
    local THRESHOLD=10
    local SIZE_THRESHOLD_FILE="$REPOS/hooks/$THIS-size-threshold.conf"
    if [[ -f "$SIZE_THRESHOLD_FILE" && -r "$SIZE_THRESHOLD_FILE" ]]; then
        THRESHOLD=$(<"$SIZE_THRESHOLD_FILE")
    fi
    local TXN_FILE="$REPOS/db/txn-protorevs/$TXN.rev"
    local SIZE=$(du -b "$TXN_FILE" | cut -f 1)
    if ((SIZE > THRESHOLD * MB)); then
        SIZE=$(du -h "$TXN_FILE" | cut -f 1)
        echo "$NAME@$TXN: changeset size ${SIZE}B exceeds ${THRESHOLD}MB." >&2
        echo "Email $ADMIN_EMAIL if you need to bypass this restriction." >&2
        return 1
    elif ((SIZE > MB)); then
        # Log any changesets bigger than 1MB
        SIZE=$(du -h "$TXN_FILE" | cut -f 1)
        echo "$NAME@$TXN: changeset size ${SIZE}B exceeds 1MB." >&2
    fi

    # Check permission.
    local PERM_CONFIG="$REPOS/hooks/svnperms.conf"
    if [[ -r "$PERM_CONFIG" && -s "$PERM_CONFIG" ]]; then
        svnperms.py -r "$REPOS" -t "$TXN" -f "$PERM_CONFIG" || return $?
    elif [[ -L "$PERM_CONFIG" ]]; then
        echo "$NAME: permission configuration file not found." >&2
        echo "$ADMIN_EMAIL has been notified." >&2
        return 1
    fi

    # Verify owner of any new branches, if relevant
    local COMMIT_CONFIG="${REPOS}/hooks/commit.conf"
    if grep -q 'verify-branch-owner' "$COMMIT_CONFIG" 2>/dev/null; then
        pre-commit-verify-branch-owner "$REPOS" "$TXN" || return $?
    fi

    # Custom checking, if required
    local CUSTOM_HOOK="$REPOS/hooks/$THIS-custom"
    if [[ -x "$CUSTOM_HOOK" ]]; then
        "$CUSTOM_HOOK" "$REPOS" "$TXN" || return $?
    fi
}

RET_CODE=0
begin 1>"${LOG_TXN}" 2>&1
LOG_TXN_SIZE="$(stat -c '%s' "${LOG_TXN}")"
if ! main 1>>"${LOG_TXN}" 2>&1; then
    if [[ -n ${FCM_SVN_HOOK_ADMIN_EMAIL:-} ]]; then
        FROM=
        if [[ -n ${FCM_SVN_HOOK_NOTIFICATION_FROM:-} ]]; then
            FROM="-r${FCM_SVN_HOOK_NOTIFICATION_FROM:-}"
        fi
        mail "$FROM" -s "[$THIS] $REPOS@$TXN" \
            "$FCM_SVN_HOOK_ADMIN_EMAIL" <"$LOG_TXN" || true
    fi
    cat "$LOG_TXN"
    cat "$LOG_TXN" >&2
    RET_CODE=1
elif [[ "${LOG_TXN_SIZE}" != "$(stat -c '%s' "${LOG_TXN}")" ]]; then
    cat "$LOG_TXN"
fi
rm -f "$LOG_TXN"
exit $RET_CODE
