termux-packages/scripts/lint-packages.sh
Leonid Pliushch 8701462951 add new linter script & fix packages where needed
Linter will check packages submitted for CI build. If error was found,
build will not be allowed.

To ensure that all build.sh scripts are consistent and have at least
minimal allowed quality, linter is permanently enabled for all branches
and pull requests.
2019-08-14 14:27:55 +03:00

433 lines
10 KiB
Bash
Executable File

#!/usr/bin/env bash
set -e -u
REPO_DIR=$(realpath "$(dirname "$0")/../")
PACKAGES_DIR="$REPO_DIR/packages"
check_package_license() {
local pkg_licenses=$1
local license
local license_ok=true
IFS=","
for license in $pkg_licenses; do
license=$(echo "$license" | sed -r 's/^\s*(\S+(\s+\S+)*)\s*$/\1/')
case "$license" in
AFL-2.1|AFL-3.0|AGPL-V3|APL-1.0|APSL-2.0|Apache-1.0|Apache-1.1);;
Apache-2.0|Artistic-License-2.0|Attribution|BSD|"BSD 2-Clause");;
"BSD 3-Clause"|"BSD New"|"BSD Simplified"|BSL-1.0|Bouncy-Castle);;
CA-TOSL-1.1|CC0-1.0|CDDL-1.0|CDDL-1.1|CPAL-1.0|CPL-1.0|CPOL);;
CPOL-1.02|CUAOFFICE-1.0|CeCILL-1|CeCILL-2|CeCILL-2.1|CeCILL-B);;
CeCILL-C|Codehaus|Copyfree|Day|Day-Addendum|ECL2|EPL-1.0|EPL-2.0);;
EUDATAGRID|EUPL-1.1|EUPL-1.2|Eiffel-2.0|Entessa-1.0);;
Facebook-Platform|Fair|Frameworx-1.0|GPL-2.0|GPL-3.0|GPL-3.0-only);;
GPL-3.0-or-later|Go|HSQLDB|Historical|IBMPL-1.0|IJG|IPAFont-1.0);;
ISC|IU-Extreme-1.1.1|ImageMagick|JA-SIG|JSON|JTidy|LGPL-2.0);;
LGPL-2.1|LGPL-3.0|LPPL-1.0|Libpng|Lucent-1.02|MIT|MPL-2.0|MS-PL);;
MS-RL|MirOS|Motosoto-0.9.1|Mozilla-1.1|Multics|NASA-1.3|NAUMEN);;
NCSA|NOSL-3.0|NTP|NUnit-2.6.3|NUnit-Test-Adapter-2.6.3|Nethack);;
Nokia-1.0a|OCLC-2.0|OSL-3.0|OpenLDAP|OpenSSL|Openfont-1.1);;
Opengroup|PHP-3.0|PostgreSQL|"Public Domain"|"Public Domain - SUN");;
PythonPL|PythonSoftFoundation|QTPL-1.0|RPL-1.5|Real-1.0|RicohPL);;
SUNPublic-1.0|Scala|SimPL-2.0|Sleepycat|Sybase-1.0|TMate|UPL-1.0);;
Unicode-DFS-2015|Unlicense|UoI-NCSA|"VIM License"|VovidaPL-1.0|W3C);;
WTFPL|Xnet|ZLIB|ZPL-2.0|wxWindows);;
*)
license_ok=false
break
;;
esac
done
IFS=$old_ifs
if $license_ok; then
return 0
else
return 1
fi
}
lint_package() {
local package_script
local package_name
package_script=$1
package_name=$(basename "$(dirname "$package_script")")
echo "================================================================"
echo
echo "Package: $package_name"
echo
echo -n "Syntax check: "
local syntax_errors
syntax_errors=$(bash -n "$package_script" 2>&1)
if [ -n "$syntax_errors" ]; then
echo "FAILED"
echo
echo "$syntax_errors"
echo
return 1
else
echo "PASS"
fi
echo
# Fields checking is done in subshell since we will source build.sh.
(set +e +u
local pkg_lint_error
# Certain fields may be API-specific.
# Using API 24 here.
TERMUX_PKG_API_LEVEL=24
. "$REPO_DIR/scripts/properties.sh"
. "$package_script"
pkg_lint_error=false
echo -n "TERMUX_PKG_HOMEPAGE: "
if [ -n "$TERMUX_PKG_HOMEPAGE" ]; then
if ! grep -qP '^https://.+' <<< "$TERMUX_PKG_HOMEPAGE"; then
echo "NON-HTTPS"
else
echo "PASS"
fi
else
echo "NOT SET"
pkg_lint_error=true
fi
echo -n "TERMUX_PKG_DESCRIPTION: "
if [ -n "$TERMUX_PKG_DESCRIPTION" ]; then
str_length=$(($(wc -c <<< "$TERMUX_PKG_DESCRIPTION") - 1))
if [ $str_length -gt 100 ]; then
echo "TOO LONG (allowed: 100 characters max)"
else
echo "PASS"
fi
unset str_length
else
echo "NOT SET"
pkg_lint_error=true
fi
echo -n "TERMUX_PKG_LICENSE: "
if [ -n "$TERMUX_PKG_LICENSE" ]; then
if [ "$TERMUX_PKG_LICENSE" = "custom" ]; then
echo "CUSTOM"
elif [ "$TERMUX_PKG_LICENSE" = "non-free" ]; then
echo "NON-FREE"
else
if check_package_license "$TERMUX_PKG_LICENSE"; then
echo "PASS"
else
echo "INVALID"
pkg_lint_error=true
fi
fi
else
echo "NOT SET"
pkg_lint_error=true
fi
if [ -n "$TERMUX_PKG_API_LEVEL" ]; then
echo -n "TERMUX_PKG_API_LEVEL: "
if grep -qP '^[1-9][0-9]$' <<< "$TERMUX_PKG_API_LEVEL"; then
if [ $TERMUX_PKG_API_LEVEL -lt 24 ] || [ $TERMUX_PKG_API_LEVEL -gt 28 ]; then
echo "INVALID (allowed: number in range 24 - 28)"
pkg_lint_error=true
else
echo "PASS"
fi
else
echo "INVALID (allowed: number in range 24 - 28)"
pkg_lint_error=true
fi
fi
echo -n "TERMUX_PKG_VERSION: "
if [ -n "$TERMUX_PKG_VERSION" ]; then
echo "PASS"
else
echo "NOT SET"
pkg_lint_error=true
fi
if [ -n "$TERMUX_PKG_REVISION" ]; then
echo -n "TERMUX_PKG_REVISION: "
if grep -qP '^[1-9](\d{1,8})?$' <<< "$TERMUX_PKG_REVISION"; then
echo "PASS"
else
echo "INVALID (allowed: number in range 1 - 999999999)"
pkg_lint_error=true
fi
fi
if [ -n "$TERMUX_PKG_SKIP_SRC_EXTRACT" ]; then
echo -n "TERMUX_PKG_SKIP_SRC_EXTRACT: "
if [ "$TERMUX_PKG_SKIP_SRC_EXTRACT" = "true" ] || [ "$TERMUX_PKG_SKIP_SRC_EXTRACT" = "false" ]; then
echo "PASS"
else
echo "INVALID (allowed: true / false)"
pkg_lint_error=true
fi
fi
if [ -n "$TERMUX_PKG_SRCURL" ]; then
echo -n "TERMUX_PKG_SRCURL: "
urls_ok=true
for url in "${TERMUX_PKG_SRCURL[@]}"; do
if [ -n "$url" ]; then
if ! grep -qP '^https://.+' <<< "$url"; then
echo "NON-HTTPS"
urls_ok=false
break
fi
else
echo "NOT SET (one of the array elements)"
urls_ok=false
pkg_lint_error=true
break
fi
done
unset url
if $urls_ok; then
echo "PASS"
fi
unset urls_ok
echo -n "TERMUX_PKG_SHA256: "
if [ -n "$TERMUX_PKG_SHA256" ]; then
if [ "${#TERMUX_PKG_SRCURL[@]}" -eq "${#TERMUX_PKG_SHA256[@]}" ]; then
sha256_ok=true
for sha256 in "${TERMUX_PKG_SHA256[@]}"; do
if ! grep -qP '^[0-9a-fA-F]{64}$' <<< "${sha256}" && [ "$sha256" != "SKIP_CHECKSUM" ]; then
echo "MALFORMED (SHA-256 should contain 64 hexadecimal numbers)"
sha256_ok=false
pkg_lint_error=true
break
fi
done
unset sha256
if $sha256_ok; then
echo "PASS"
fi
unset sha256_ok
else
echo "LENGTHS OF 'TERMUX_PKG_SRCURL' AND 'TERMUX_PKG_SHA256' ARE NOT EQUAL"
pkg_lint_error=true
fi
else
echo "NOT SET"
pkg_lint_error=true
fi
else
if [ "$TERMUX_PKG_SKIP_SRC_EXTRACT" != "true" ] && ! declare -F termux_step_extract_package > /dev/null 2>&1; then
echo "TERMUX_PKG_SRCURL: NOT SET (set TERMUX_PKG_SKIP_SRC_EXTRACT to 'true' if no sources downloaded)"
pkg_lint_error=true
fi
fi
if [ -n "$TERMUX_PKG_METAPACKAGE" ]; then
echo -n "TERMUX_PKG_METAPACKAGE: "
if [ "$TERMUX_PKG_METAPACKAGE" = "true" ] || [ "$TERMUX_PKG_METAPACKAGE" = "false" ]; then
echo "PASS"
else
echo "INVALID (allowed: true / false)"
pkg_lint_error=true
fi
fi
if [ -n "$TERMUX_PKG_ESSENTIAL" ]; then
echo -n "TERMUX_PKG_ESSENTIAL: "
if [ "$TERMUX_PKG_ESSENTIAL" = "true" ] || [ "$TERMUX_PKG_ESSENTIAL" = "false" ]; then
echo "PASS"
else
echo "INVALID (allowed: true / false)"
pkg_lint_error=true
fi
fi
if [ -n "$TERMUX_PKG_NO_STATICSPLIT" ]; then
echo -n "TERMUX_PKG_NO_STATICSPLIT: "
if [ "$TERMUX_PKG_NO_STATICSPLIT" = "true" ] || [ "$TERMUX_PKG_NO_STATICSPLIT" = "false" ]; then
echo "PASS"
else
echo "INVALID (allowed: true / false)"
pkg_lint_error=true
fi
fi
if [ -n "$TERMUX_PKG_BUILD_IN_SRC" ]; then
echo -n "TERMUX_PKG_BUILD_IN_SRC: "
if [ "$TERMUX_PKG_BUILD_IN_SRC" = "true" ] || [ "$TERMUX_PKG_BUILD_IN_SRC" = "false" ]; then
echo "PASS"
else
echo "INVALID (allowed: true / false)"
pkg_lint_error=true
fi
fi
if [ -n "$TERMUX_PKG_HAS_DEBUG" ]; then
echo -n "TERMUX_PKG_HAS_DEBUG: "
if [ "$TERMUX_PKG_HAS_DEBUG" = "true" ] || [ "$TERMUX_PKG_HAS_DEBUG" = "false" ]; then
echo "PASS"
else
echo "INVALID (allowed: true / false)"
pkg_lint_error=true
fi
fi
if [ -n "$TERMUX_PKG_PLATFORM_INDEPENDENT" ]; then
echo -n "TERMUX_PKG_PLATFORM_INDEPENDENT: "
if [ "$TERMUX_PKG_PLATFORM_INDEPENDENT" = "true" ] || [ "$TERMUX_PKG_PLATFORM_INDEPENDENT" = "false" ]; then
echo "PASS"
else
echo "INVALID (allowed: true / false)"
pkg_lint_error=true
fi
fi
if [ -n "$TERMUX_PKG_HOSTBUILD" ]; then
echo -n "TERMUX_PKG_HOSTBUILD: "
if [ "$TERMUX_PKG_HOSTBUILD" = "true" ] || [ "$TERMUX_PKG_HOSTBUILD" = "false" ]; then
echo "PASS"
else
echo "INVALID (allowed: true / false)"
pkg_lint_error=true
fi
fi
if [ -n "$TERMUX_PKG_FORCE_CMAKE" ]; then
echo -n "TERMUX_PKG_FORCE_CMAKE: "
if [ "$TERMUX_PKG_FORCE_CMAKE" = "true" ] || [ "$TERMUX_PKG_FORCE_CMAKE" = "false" ]; then
echo "PASS"
else
echo "INVALID (allowed: true / false)"
pkg_lint_error=true
fi
fi
if [ -n "$TERMUX_PKG_RM_AFTER_INSTALL" ]; then
echo -n "TERMUX_PKG_RM_AFTER_INSTALL: "
file_path_ok=true
while read -r file_path; do
[ -z "$file_path" ] && continue
if grep -qP '^(\.\.)?/' <<< "$file_path"; then
echo "INVALID (file path should be relative to prefix)"
file_path_ok=false
pkg_lint_error=true
break
fi
done <<< "$TERMUX_PKG_RM_AFTER_INSTALL"
unset file_path
if $file_path_ok; then
echo "PASS"
fi
unset file_path_ok
fi
if [ -n "$TERMUX_PKG_CONFFILES" ]; then
echo -n "TERMUX_PKG_CONFFILES: "
file_path_ok=true
while read -r file_path; do
[ -z "$file_path" ] && continue
if grep -qP '^(\.\.)?/' <<< "$file_path"; then
echo "INVALID (file path should be relative to prefix)"
file_path_ok=false
pkg_lint_error=true
break
fi
done <<< "$TERMUX_PKG_CONFFILES"
unset file_path
if $file_path_ok; then
echo "PASS"
fi
unset file_path_ok
fi
if $pkg_lint_error; then
exit 1
else
exit 0
fi
)
local ret=$?
echo
return "$ret"
}
linter_main() {
local package_counter=0
local problems_found=false
local package_script
for package_script in "$@"; do
if ! lint_package "$package_script"; then
problems_found=true
break
fi
package_counter=$((package_counter + 1))
done
if $problems_found; then
echo "================================================================"
echo
echo "A problem has been found in '$(realpath --relative-to="$REPO_DIR" "$package_script")'."
echo "Checked $package_counter packages before first error was detected."
echo
echo "================================================================"
return 1
else
echo "================================================================"
echo
echo "Checked $package_counter packages."
echo "Everything seems ok."
echo
echo "================================================================"
fi
return 0
}
if [ $# -eq 0 ]; then
linter_main "$PACKAGES_DIR"/*/build.sh || exit 1
else
linter_main "$@" || exit 1
fi