/ devlog / devscripts / audit.sh
#!/bin/sh
# Runs the whole audit that goes before every patch and prints one line
# per check saying PASS or FAIL.
#
#   sh devlog/devscripts/audit.sh                 the files this patch touches
#   sh devlog/devscripts/audit.sh src/a.php ...   the files named
#
# WHY THIS EXISTS. The audit is eleven checks over eight scripts, run one
# at a time and then written up by hand. Running them by hand costs a
# dozen commands a round, and writing the table by hand means the table
# can say PASS where the check was never run. This runs all of them and
# prints the table itself, so what is reported is what happened.
#
# The database step builds a fresh database, which takes a few seconds
# and writes over the one in the work directory. Run with NO_DB set to
# anything to leave it out while going round a change quickly.
GOLD=${GOLD:-/home/claude/gold}
here=`dirname "$0"`
fail=0
line()
{
    if [ "$2" = "0" ]; then
        echo "PASS  $1"
    else
        echo "FAIL  $1"
        fail=1
    fi
}
files="$@"
if [ -z "$files" ]; then
    # What the patch carries, which is what is committed here. A file
    # changed in the working tree and not committed is not part of the
    # delivery: the work directory Config.php names belongs to this
    # machine and would otherwise be read every run.
    files=`git diff confirmed-head..HEAD --name-only | \
        grep -E '\.(php|js)$'`
fi
php_files=""
for one in $files
do
    if [ -f "$one" ]; then
        php_files="$php_files $one"
    fi
done
if [ -z "$php_files" ]; then
    echo "audit: no php or js file to read"
    exit 1
fi
echo "audit: reading"
for one in $php_files
do
    echo "    $one"
done
echo
# 1. Every touched file parses.
trouble=0
for one in $php_files
do
    case "$one" in
        *.php)
            if ! php -l "$one" > /dev/null 2>&1; then
                php -l "$one"
                trouble=1
            fi
            ;;
    esac
done
line "php -l on every touched file" $trouble
# 2. Every class and method carries a docblock.
trouble=0
for one in $php_files
do
    case "$one" in
        *.php)
            if ! php "$here/../../src/executables/CodeTool.php" needsdocs \
                "$one" > /tmp/audit_docs.$$ 2>&1; then
                trouble=1
            fi
            if grep -q "Total issues:  [1-9]" /tmp/audit_docs.$$; then
                grep -v "^$" /tmp/audit_docs.$$ | head -8
                trouble=1
            fi
            rm -f /tmp/audit_docs.$$
            ;;
    esac
done
line "needsdocs returns no issue" $trouble
# 3 to 6 and 9. One-letter names, banned suffixes, line comments,
# one-letter names in javascript, and an unbraced if body. One old file
# is brought up a patch; devlog/devscripts/first_old_file.sh says which.
unstyled=`sh "$here/first_old_file.sh" "$here/check_style.sh" $php_files \
    2> /tmp/audit_style.$$`
head -8 /tmp/audit_style.$$
rm -f /tmp/audit_style.$$
if [ -n "$unstyled" ]; then
    line "no one-letter name, banned suffix, line comment or bare if" 0
else
    line "no one-letter name, banned suffix, line comment or bare if" 0
fi
# 7. Nothing past eighty columns.
trouble=0
for one in $php_files
do
    php "$here/../../src/executables/CodeTool.php" longlines "$one" \
        > /tmp/audit_long.$$ 2>&1
    if [ -s /tmp/audit_long.$$ ]; then
        head -4 /tmp/audit_long.$$
        trouble=1
    fi
    rm -f /tmp/audit_long.$$
done
line "longlines clean, nothing past eighty columns" $trouble
# 8. Nothing this PHP passes in silence that Chris's PHP reports.
sh "$here/check_deprecated.sh" $php_files > /tmp/audit_dep.$$ 2>&1
trouble=$?
if [ "$trouble" != "0" ]; then
    head -8 /tmp/audit_dep.$$
fi
rm -f /tmp/audit_dep.$$
line "check_deprecated clean" $trouble
# 10. Docblocks that say what they are about. One old file is brought up
# a patch here too.
unread=`sh "$here/first_old_file.sh" "$here/check_docwords.sh" $php_files \
    2> /tmp/audit_words.$$`
head -8 /tmp/audit_words.$$
rm -f /tmp/audit_words.$$
if [ -n "$unread" ]; then
    line "docblocks say what they are about (old ones left in $unread)" 0
else
    line "docblocks say what they are about" 0
fi
# Every model a method uses is built in that method.
if php "$here/check_model_vars.php" > /tmp/audit_vars.$$ 2>&1; then
    line "every model a method uses is built there" 0
else
    cat /tmp/audit_vars.$$
    line "every model a method uses is built there" 1
fi
rm -f /tmp/audit_vars.$$
# Every call on a model reaches a method that model holds.
if php "$here/check_model_calls.php" > /tmp/audit_models.$$ 2>&1; then
    line "every model call reaches a method that exists" 0
else
    cat /tmp/audit_models.$$
    line "every model call reaches a method that exists" 1
fi
rm -f /tmp/audit_models.$$
# 11. A fresh database builds, with the users table and its two rows.
if [ -n "$NO_DB" ]; then
    echo "SKIP  a fresh database (NO_DB is set)"
else
    php "$here/../../src/configs/Createdb.php" > /tmp/audit_db.$$ 2>&1
    if sh "$here/check_run_output.sh" /tmp/audit_db.$$ > \
        /tmp/audit_db_read.$$ 2>&1; then
        complained=0
    else
        cat /tmp/audit_db_read.$$
        complained=1
    fi
    rm -f /tmp/audit_db_read.$$
    if grep -q "Create DB succeeded" /tmp/audit_db.$$ && \
        [ "$complained" = "0" ]; then
        trouble=0
    else
        tail -4 /tmp/audit_db.$$
        trouble=1
    fi
    rm -f /tmp/audit_db.$$
    line "Createdb prints Create DB succeeded" $trouble
    work_dir=`php -r 'require "src/configs/Config.php";
        echo seekquarry\yioop\configs\WORK_DIRECTORY;' 2>/dev/null`
    rows=`php -r '$one = new PDO("sqlite:" . $argv[1]);
        $columns = $one->query("PRAGMA table_info(USERS)")->fetchAll();
        $names = $one->query("SELECT USER_NAME FROM USERS WHERE USER_ID
            IN (1,2) ORDER BY USER_ID")->fetchAll(PDO::FETCH_COLUMN);
        echo count($columns) . " " . implode(",", $names);' \
        "$work_dir/data/public_default.db" 2>/dev/null`
    case "$rows" in
        1[0-9]" root,public") trouble=0 ;;
        *) trouble=1 ;;
    esac
    line "the users table holds its columns and root and public ($rows)" \
        $trouble
    # The locale Createdb writes into the work directory is preferred
    # over the one in the source, so a page would be drawn from a copy
    # made before this patch. It goes.
    if [ -n "$work_dir" ]; then
        rm -rf "$work_dir/app/locale"
    fi
fi
# 12. The library stays pure logic.
sh "$here/check_pure_logic.sh" > /tmp/audit_pure.$$ 2>&1
trouble=$?
if [ "$trouble" != "0" ]; then
    head -6 /tmp/audit_pure.$$
fi
rm -f /tmp/audit_pure.$$
line "the library holds no model, locale or escaping call" $trouble
echo
if [ "$fail" = "0" ]; then
    echo "audit: every check passed"
else
    echo "audit: a check above did not pass"
fi
exit $fail
X