diff options
Diffstat (limited to 'sbopkglint')
-rwxr-xr-x | sbopkglint | 490 |
1 files changed, 490 insertions, 0 deletions
diff --git a/sbopkglint b/sbopkglint new file mode 100755 index 0000000..c7f88f7 --- /dev/null +++ b/sbopkglint @@ -0,0 +1,490 @@ +#!/bin/bash + +: <<EOF +=pod + +=head1 NAME + +sbopkglint - check Slackware binary packages for common errrors. + +=head1 SYNOPSIS + +B<sbopkglint> [-k] [-i] [package.t?z ...] + +=head1 DESCRIPTION + +B<sbopkglint> installs a Slackware package to a temporary directory, then +examines the contents. It finds lots of common problems that aren't +always noticed by SBo script maintainers or the admins. + +This is for built packages. If you want to lint your build scripts, +use B<sbolint>(1) instead. + +With no package arguments, it looks for a SlackBuild in the current +directory, extracts the PRINT_PACKAGE_NAME information, and tries +to find a package in $OUTPUT (/tmp by default). If found, it checks +that package. It's up to you to know whether the package needs to be +rebuilt (e.g. if you've edited the SlackBuild since the package was +built). + +With arguments, it checks the given packages. These must be +supported Slackware package files (.tgz, .txz, .tlz, etc). There's no +requirement that these have to be SBo packages, but a couple of the +tests (e.g. the check for $PRGNAM.SlackBuild in the doc dir) might not +apply to non-SBo builds. + +Diagnostics will be logged to stdout and stderr. Exit status will +be 0 if all tests passed, non-zero otherwise. + +This script must run as root. If you run it as a normal user, it tries +to re-execute itself via sudo(8). + +=head1 OPTIONS + +=over 4 + +=item B<-k> + +Keep the temporary package install directory instead of deleting it on exit. + +=item B<-i> + +Disable the "useless-looking install instructions" test. This is +intended for SBo admins mass-linting a ton of packages. INSTALL in +the doc dir is something that exists in thousands of existing builds, +and it's not a major problem. New builds and updates should be linted +without this option, however. + +=item B<--help> + +Show the short built-in help. + +=item B<--doc> + +View this documentation using perldoc(1), which generally uses your +pager (e.g. less(1) or more(1)) to display it. + +=item B<--man> + +Convert this documentation to a man page, on stdout. + +=back + +=head1 EXIT STATUS + +0 (success) if all tests passed for all packages, non-zero if there +were any test failures (or if installpkg failed for some reason). + +=head1 FILES + +=over 4 + +=item B<sbolint.d/*.t.sh> + +These are the actual tests. They're installed to +I<PREFIX>/share/sbo-maintainer-tools, and they're sourced by +B<sbopkglint> at runtime. Each test script begins with (hopefully) +useful comments that go into more detail than the documentation here. + +=back + +=head1 TESTS + +=head2 basic-sanity + +=over 4 + +=item B<-> + +Top-level directories inside the package must be recognized ones, +such as /bin /etc /usr /opt. Packages shouldn't be installing files +in /tmp, /dev, or /home... and they really shouldn't be inventing +new top-level directories. + +=item B<-> + +The documentation directory must exist and be correctly named, as +/usr/doc/$PRGNAM-$VERSION. It must contain $PRGNAM.SlackBuild, too. + +=item B<-> + +The directories /usr/local, /usr/share/doc, /usr/share/man, /usr/etc +are not allowed in SBo packages. + +=item B<-> + +Some directories (e.g. /usr/bin) may not contain subdirectories. + +=item B<-> + +Some directories (e.g. /usr/share) must *only* contain subdirectories. + +=item B<-> + +Some directories (e.g. /usr/man, /usr/share/applications) must not +contain files with executable permissions. /usr/doc is not in this +list; neither is /etc (too many existing packages install +x files +there). + +=item B<-> + +Broken symlinks may not exist. + +=item B<-> + +Absolute symlinks may not exist (they should be converted to +relative symlinks). This may seem like nitpicking, but packages +may be installed somewhere besides / (the root dir) with the +-root option to installpkg. If /usr/bin/foo is a link to /usr/bin/bar, +it should be a link to just bar. + +=back + +=head2 docs + +=over 4 + +=item B<-> + +Documentation must be installed to /usr/doc/$PRGNAM-$VERSION. If +there's any other directory under /usr/doc, it's incorrect. Some +builds use mis-named doc directories (if it hasn't been fixed by +now, an example is gcc5, which installs docs to /usr/doc/gcc-$VERSION +when it should be gcc5-$VERSION). + +=item B<-> + +Documentation files must be readable by everyone, and owned by root:root. + +=item B<-> + +Doc dir shouldn't contain empty files (0 bytes in length). + +=item B<-> + +Doc dir shouldn't contain install instructions. Specifically, files +named INSTALL, INSTALL.*, or install.txt are flagged (it's impossible +to make this test 100% perfect). + +=back + +=head2 noarch + +=over 4 + +=item B<-> + +If a package has its architecture set to "noarch", it must not contain +any ELF binaries/libraries. + +=back + +=head2 arch + +=over 4 + +=item B<-> + +If a package has its architecture set to i?86 or x86_64, all ELF +binaries/libraries must be for the correct arch (no 32-bit code in +64-bit packages, and vice versa). + +=item B<-> + +If a package is i?86, it must not contain /usr/lib64. + +=item B<-> + +If a package is x86_64 and contains shared libraries, they must be +in /lib64 or /usr/lib64 (not /lib or /usr/lib). + +=back + +=head2 lafiles + +=over 4 + +=item B<-> + +Packages are no longer allowed to contain libtool archive files (.la) +in /lib, /lib64, /usr/lib, or /usr/lib64. However, subdirectories +such as /usr/lib64/someprogram/ are not checked, since some applications +which use plugins actually use the .la files. + +=back + +=head2 manpages + +=over 4 + +=item B<-> + +All man pages must be readable by everyone, and owned by root:root. + +=item B<-> + +All man pages must be gzipped. + +=item B<-> + +All man pages must be in /usr/man/man[1-9n] or /usr/man/<lang>/man[1-9n]. + +=item B<-> + +Man page directories must be mode 755, owned by root:root. + +=item B<-> + +The section numbers in man page filenames must match the section number +in the directory name (e.g. /usr/man/man1/ls.1.gz is OK, +/usr/man/man1/tetris.6.gz is an error). + +=item B<-> + +Man pages must actually be man pages (troff markup). + +=back + +=head2 desktop + +=over 4 + +=item B<-> + +If there are .desktop files, doinst.sh must run update-desktop-database. + +=item B<-> + +.desktop files must be mode 644, owned by root:root. Slackware's KDE +packages actually break this rule (they install executable .desktop +files), but SBo packages are not allowed to. + +=item B<-> + +Only .desktop files are allowed in /usr/share/applications. + +=item B<-> + +.desktop files must be valid, according to the desktop-file-validate +command. Only actual errors count; warnings don't cause this test to +fail. + +=back + +=head2 newconfig + +=over 4 + +=item B<-> + +Any files (outside of /usr/doc) with names ending in .new are flagged. +This might be a bit too restrictive (possibly only check /etc and +/usr/share?) + +=back + +=head2 doinst + +=over 4 + +=item B<-> + +If there are icons in /usr/share/icons, .desktop files in /usr/share/applications, +or glib2 schemas in /usr/share/glib-2.0/schemas, there must be a doinst.sh +with appropriate command(s), e.g. update-desktop-database, gtk-update-icon-cache, +glib-compile-schemas. + +=back + +=head1 BUGS + +Probably many. This is still a work in progress. + +One known problem is that the same file can fail multiple tests. E.g. +if you have a man page that's installed executable, it will fail both +the basic-sanity test and the manpages test. This isn't really a huge +problem, so it might not be fixed any time soon. + +=head1 AUTHOR + +B. Watson <urchlay@slackware.uk>, AKA Urchlay on Libera IRC. + +=head1 SEE ALSO + +B<sbolint>(1) + +=cut +EOF + +# lint a binary Slackware package. primarily intended for use with +# SBo packages, but could be used for any Slack pkg. + +# this must be run as root, as it installs the package in a temp dir, +# with "installpkg -root". if it's not running as root, it tries to +# run itself via sudo. + +SELF="$( basename $0 )" + +usage() { + cat 1>&2 <<EOF +$SELF - check SBo binary packages for various problems + +Usage: + $0 [-k] [-i] [/path/to/package-file] [...] + $0 --doc | --man + +Options: +-k Keep (don't delete) the package install directory at exit. +-i Do not check for INSTALL in the doc dir. +--doc See the full documentation in your pager. +--man Convert the full documentation to a man page, on stdout. + +With no package arguments, it looks for a SlackBuild in the current +directory, extracts the PRINT_PACKAGE_NAME information, and tries to +find a package in \$OUTPUT (/tmp by default). + +With arguments, it checks the given packages. These must be supported +Slackware package files (.tgz, .txz, .tlz, etc). + +Diagnostics will be logged to stdout and stderr. Exit status will +be 0 if all tests passed, non-zero otherwise. + +This script must run as root. If you run it as a normal user, it tries +to re-execute itself via sudo(8). +EOF +} + +while true; do + case "$1" in + --doc) exec perldoc "$0" ;; + --man) exec pod2man --stderr -s1 -csbo-maintainer-tools -r0.4 "$0" ;; + -k) KEEP=1 ; shift ;; + -i) INSTALL_DOCS_OK=1 ; shift;; + -h*|--h*) usage; exit 0 ;; + -*) echo "$SELF: invalid option '$1', try '$SELF --help'" ; exit 1 ;; + *) break ;; + esac +done + +# where the test scripts live, space-separated list. +SBOPKGLINT_PATH=${SBOPKGLINT_PATH:-"./sbopkglint.d /usr/share/sbo-maintainer-tools/sbopkglint.d /usr/local/share/sbo-maintainer-tools/sbopkglint.d"} + +if [ "$(id -u)" != "0" ]; then + exec sudo \ + TMP="$TMP" \ + OUTPUT="$OUTPUT" \ + INSTALL_DOCS_OK="$INSTALL_DOCS_OK" \ + KEEP="$KEEP" \ + SBOPKGLINT_PATH="$SBOPKGLINT_PATH" \ + "$0" "$@" +fi + +warn() { + [ "$warncount" = "0" ] && echo + : $(( warncount ++ )) + echo "--- $@" 1>&2 +} + +die() { + warn "$@" + exit 1 +} + +TMP=${TMP:-/tmp} +OUTPUT=${OUTPUT:-$TMP} + +exit_status=0 + +if [ -n "$1" ]; then + packages="$@" +else + cnt="$( /bin/ls *.SlackBuild | wc -l )" + case "$cnt" in + 0) die "No argument given and no SlackBuild script in current dir" ;; + 1) ;; # OK + *) die "Multiple SlackBuild scripts in current dir" ;; + esac + script="$( /bin/ls *.SlackBuild )" + if ! grep -q PRINT_PACKAGE_NAME $script; then + die "$script doesn't support PRINT_PACKAGE_NAME" + fi + filename="$( PRINT_PACKAGE_NAME=1 sh $script )" + packages="$OUTPUT/$filename" + if [ ! -e "$packages" ]; then + die "Can't find $packages" + fi +fi + +for testdir in $SBOPKGLINT_PATH; do + [ -d $testdir ] || continue + testdir="$( realpath $testdir )" + break +done +[ -z "$testdir" -o "$testdir/*.t.sh" = '*.t.sh' ] && \ + die "Can't find any tests to run, looked in: $SBOPKGLINT_PATH" + +echo "Using tests from $testdir" + +for package in $packages; do + filename="$( basename $package )" + + ARCH="$( echo $filename | rev | cut -d- -f2 | rev )" + PRGNAM="$( echo $filename | rev | cut -d- -f4- | rev )" + VERSION="$( echo $filename | rev | cut -d- -f3 | rev )" + PKG="$( mktemp -d $TMP/sbopkglint.XXXXXX )" + + echo -n "Installing $package to $PKG..." + /sbin/installpkg -root "$PKG" "$package" &> $PKG/.tmp.$$ + S="$?" + + if [ "$S" != "0" ]; then + echo "FAILED" + cat $PKG/.tmp.$$ + echo "installpkg exited with status $S" + [ "$KEEP" = "" ] && rm -rf $PKG + exit_status=1 + continue + fi + + echo "OK" + rm -f $PKG/.tmp.$$ + + cd "$PKG" + + totalwarns=0 + foundtests=0 + for testscript in $testdir/*.t.sh; do + foundtests=1 + ( + warncount=0 + echo -n "Running test: $( basename $testscript .t.sh )..." + source "$testscript" + if [ "$warncount" = "0" ]; then + echo "OK" + else + echo "FAILED" + echo "$warncount" > .tmp.warncount + fi + ) + if [ -e .tmp.warncount ]; then + warns="$( cat .tmp.warncount )" + : $(( totalwarns += warns )) + fi + rm -f .tmp.warncount + done + + [ "$KEEP" = "" ] && rm -rf "$PKG" + + if [ "$foundtests" = "0" ]; then + die "!!! can't find any tests to run in $testdir." + fi + + if [ "$totalwarns" = "0" ]; then + echo "=== $filename: All tests passed" + else + exit_status=1 + echo "!!! $filename: $totalwarns failures" + fi +done + +exit $exit_status |