#!/bin/sh
# Run code linters on Apport.
#
# Test against the source tree when run in the source tree root.

# Copyright (C) 2007 - 2012 Canonical Ltd.
# Author: Martin Pitt <martin.pitt@ubuntu.com>
#
# This program 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 2 of the License, or (at your
# option) any later version.  See http://www.gnu.org/copyleft/gpl.html for
# the full text of the license.

set -eu

PYTHON_FILES="apport apport_python_hook.py data problem_report.py setup.py setuptools_apport tests"
test ! -d debian || PYTHON_FILES="$PYTHON_FILES debian"
PYTHON_SCRIPTS_WITHOUT_APPORT=$(find bin data gtk kde -type f -executable ! -name apport-bug ! -name apport-collect ! -name is-enabled ! -name root_info_wrapper ! -name apport)
PYTHON_SCRIPTS="$PYTHON_SCRIPTS_WITHOUT_APPORT data/apport"

check_hardcoded_names() {
    # assert that there are no hardcoded "Ubuntu" names
    out=$(grep -rw Ubuntu apport/*.py gtk/apport-gtk* kde/* bin/* | grep -v Debian | grep -v X-Ubuntu-Gettext-Domain | grep -v '#.*Ubuntu') || :
    if [ -n "$out" ]; then
        echo "Found hardcoded 'Ubuntu' names, use DistroRelease: field or lsb_release instead:\n\n$out" >&2
        exit 1
    fi
}

run_black() {
    if ! type black >/dev/null 2>&1; then
        echo "Skipping black tests, black is not installed"
        return
    fi
    black_version=$(black --version | head -n1 | cut -d' ' -f 2)
    if test "${black_version%%.*}" -lt 25; then
        echo "Skipping black tests, black $black_version is installed but version >= 25 is needed"
        return
    fi
    echo "Running black..."
    black -C --check --diff ${PYTHON_FILES} ${PYTHON_SCRIPTS}
}

run_isort() {
    if ! type isort >/dev/null 2>&1; then
        echo "Skipping isort tests, isort is not installed"
        return
    fi
    echo "Running isort..."
    isort --check-only --diff ${PYTHON_FILES} ${PYTHON_SCRIPTS}
}

run_mypy() {
    if ! type mypy >/dev/null 2>&1; then
        echo "Skipping mypy tests, mypy is not installed"
        return
    fi
    echo "Running mypy..."
    mypy ${PYTHON_FILES} data/apport
    mypy --scripts-are-modules ${PYTHON_SCRIPTS_WITHOUT_APPORT}
}

run_pycodestyle() {
    if ! type pycodestyle >/dev/null 2>&1; then
        echo "Skipping pycodestyle tests, pycodestyle is not installed"
        return
    fi
    echo "Running pycodestyle..."
    # E101 causes false positive on tests/unit/test_hookutils.py,
    # see https://github.com/PyCQA/pycodestyle/issues/376
    # . catches all *.py modules; we explicitly need to specify the programs
    pycodestyle --max-line-length=88 -r --ignore=E101,E203,W503 ${PYTHON_FILES} ${PYTHON_SCRIPTS}
}

run_pydocstyle() {
    if ! type pydocstyle >/dev/null 2>&1; then
        echo "Skipping pydocstyle tests, pydocstyle is not installed"
        return
    fi
    pydocstyle_version=$(pydocstyle --version)
    pydocstyle_major_version=$(echo "$pydocstyle_version" | cut -d. -f1)
    if test "$pydocstyle_major_version" -lt 6; then
        echo "Skipping pydocstyle tests, pydocstyle $pydocstyle_version is too old"
        return
    fi
    echo "Running pydocstyle..."
    pydocstyle ${PYTHON_FILES} ${PYTHON_SCRIPTS}
}

run_pyflakes() {
    if ! type pyflakes3 >/dev/null 2>&1; then
        echo "Skipping pyflakes3 tests, pyflakes3 is not installed"
        return
    fi
    echo "Running pyflakes3..."
    pyflakes3 ${PYTHON_FILES} ${PYTHON_SCRIPTS}
}

run_pylint() {
    if ! type pylint >/dev/null 2>&1; then
        echo "Skipping pylint tests, pylint is not installed"
        return
    fi
    echo "Running pylint..."
    pylint -j 0 "$@" ${PYTHON_FILES} ${PYTHON_SCRIPTS}
}

if test "${1-}" = "--errors-only"; then
    # Run only linters that can detect real errors (ignore formatting)
    run_mypy
    run_pylint --errors-only
else
    run_black
    run_isort
    run_pycodestyle
    run_pydocstyle
    run_pyflakes
    run_mypy
    run_pylint
    check_hardcoded_names
fi
