apkbuild-lint 4.01 KB
Newer Older
Leo's avatar
Leo committed
1 2 3 4 5 6 7
#!/bin/sh
# alint APKBUILD - scan APKBUILD template for common mistakes
#
# Adapted from xlint from Void Linux's xtools to Alpine Linux
# https://github.com/leahneukirchen/xtools/
#
# Required packages (names are Alpine Linux pkgs):
Leo's avatar
Leo committed
8
# busybox - for sed, tr, sort and other simple utiltiies
Leo's avatar
Leo committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
# grep - for grep with -P

export LC_ALL=C

scan() {
	local rx="$1" msg="$2"
	grep -P -Hn -e "$rx" "$apkbuild" |
		sed "s/^\([^:]*:[^:]*:\)\(.*\)/\1 $msg/"
}

variables=$(echo -n "#.*
_.*
startdir
srcdir
pkgdir
subpkgdir
builddir
arch
depends
depends_dev
checkdepends
giturl
install
.*.pre-install
.*.post-install
.*.pre-upgrade
.*.post-upgrade
.*.pre-deinstall
.*.post-deinstall
install_if
license
makedepends
md5sums
sha256sums
sha512sums
options
pkgdesc
pkggroups
pkgname
pkgrel
pkgusers
pkgver
provides
provider_priority
replaces
replaces_priority
source
subpackages
triggers
ldpath
url" | tr '\n' '|')

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
default_builddir_value() {
	[ "$SKIP_DEFAULT_BUILDDIR_VALUE" ] && return 0
	if [ "$builddir" = "/$pkgname-$pkgver" ]; then
		scan '^builddir=' "builddir can be removed as it is the default value"
	fi
}

unnecessary_return_1() {
	[ "$SKIP_UNNECESSARY_RETURN_1" ] && return 0
	scan '\|\| return 1' "|| return 1 is not required as set -e is used"
}

pkgname_quoted() {
	[ "$SKIP_PKGNAME_QUOTED" ] && return 0
	scan '^pkgname="[^$]+"' "pkgname must not be quoted"
}

pkgver_quoted() {
	[ "$SKIP_PKGVER_QUOTED" ] && return 0
	scan '^pkgver="[^$]+"' "pkgver must not be quoted"
}

empty_variable() {
	[ "$SKIP_EMPTY_VARIABLE" ] && return 0
	scan '^[ =]*=(|""|''|)$' "variable set to empty string: \2"
}

custom_variable() {
	[ "$SKIP_CUSTOM_VARIABLE" ] && return 0
	scan '^(?!\s*^('"$variables"'))[^\s=-]+=' \
		"prefix custom variable with _: \2"
}

indent_tabs() {
	[ "$SKIP_INDENT_TABS" ] && return 0
	scan '^  ' "indent with tabs"
}

trailing_whitespace() {
	[ "$SKIP_TRAILING_WHITESPACE" ] && return 0
	scan '[\t ]$' "trailing whitespace"
}

backticks_usage() {
	[ "$SKIP_BACKTICKS_USAGE" ] && return 0
	scan '[^\\]`' "use \$() instead of backticks"
}

function_keyword() {
	[ "$SKIP_FUNCTION_KEYWORD" ] && return 0
	scan '^\t*function\b' 'do not use the function keyword'
}

space_before_function_parenthesis() {
	[ "$SKIP_SPACE_BEFORE_FUNCTION_PARENTHESIS" ] && return 0
	scan '^\t*[^ ]*  *\(\)' 'do not use space before function parenthesis'
}

space_after_function_parenthesis() {
	[ "$SKIP_SPACE_AFTER_FUNCTION_PARENTHESIS" ] && return 0
	scan '^\t*[^ ]*\(\)(|   *){' 'use one space after function parenthesis'
}

newline_opening_brace() {
	[ "$SKIP_NEWLINE_OPENING_BRACE" ] && return 0
	scan '^\t*[^ ]*\(\)$' 'do not use a newline before function opening brace'
}

superfluous_cd_builddir() {
	[ "$SKIP_SUPERFLUOUS_CD_BUILDDIR" ] && return 0
	sed -n "/^$1() {/,/^}/{p;=}" "$apkbuild" | grep -m 1 -B 1 'cd "$builddir"$' | head -1 | while read -r l; do
Leo's avatar
Leo committed
132
		[ -z "$l" ] && continue
133 134 135 136
		printf "$apkbuild:$l: cd \"\$builddir\" can be removed in phase '$1'\n"
	done
}

Leo's avatar
Leo committed
137 138 139
ret=0
for apkbuild; do
	if [ -f "$apkbuild" ]; then
Leo's avatar
Leo committed
140 141 142

	# Try to guess the repo, first see if our working directory is where
	# the repo is located
Leo's avatar
Leo committed
143 144 145
	_repo="${PWD%/*}"
	_repo="${repo%/*}"
	_repo="${repo##*/}"
Leo's avatar
Leo committed
146 147 148
	case "$repo" in
		main|community|testing|unmaintained) ;;
		# Then have the path given to use be used
Leo's avatar
Leo committed
149 150 151 152 153
		*)
			_repo="${apkbuild%/*}"
			_repo="${repo%/*}"
			_repo="${repo##*/}"
			;;
Leo's avatar
Leo committed
154 155 156
	esac

	# Source apkbuild, we need some nice values
Leo's avatar
Leo committed
157
	srcdir="" . "$apkbuild" 2>/dev/null
Leo's avatar
Leo committed
158
	default_builddir_value &
Leo's avatar
Leo committed
159

Leo's avatar
Leo committed
160 161 162 163 164 165 166 167 168 169 170 171
	unnecessary_return_1 &
	pkgname_quoted &
	pkgver_quoted &
	empty_variable &
	custom_variable &
	indent_tabs &
	trailing_whitespace &
	backticks_usage &
	function_keyword &
	space_before_function_parenthesis &
	space_after_function_parenthesis &
	newline_opening_brace &
172

173
	if [ -z "$SKIP_DUPLICATE_PACKAGE" ]; then
Leo's avatar
Leo committed
174
		duplicate_package &
175
	fi
176

Leo's avatar
Leo committed
177 178
	# Don't perform these checks on packages from main
	if ! [ -z "${apkbuild##*main/*}" ]; then
179
	for phase in prepare build check package; do
Leo's avatar
Leo committed
180
		superfluous_cd_builddir "$phase" &
Leo's avatar
Leo committed
181 182
	done
	fi
Leo's avatar
Leo committed
183
	wait
Leo's avatar
Leo committed
184 185 186 187 188
	else
	echo no such apkbuild "$apkbuild" 1>&2
	fi | sort -t: -n -k2 | grep . && ret=1
done
exit $ret