aboutsummaryrefslogtreecommitdiff
path: root/sbopkglint
diff options
context:
space:
mode:
Diffstat (limited to 'sbopkglint')
-rwxr-xr-xsbopkglint490
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