#!/bin/bash # mkupdate-with-scores # # This script generates, tests, and publishes rule updates for stable release # versions. It does the following: # # - retrieves the latest gernerated scores for new active.list rules # - checks out the trunk revision of code that those scores were generated for # - generates an update tarball and associated sha1, sha256, sha512, and asc files # - checks out each of the 3.3 stable release tagged versions, builds and # installs that version (in a tmp dir) and then installs the above generated # update using sa-update --install to make sure it works with each version # - if all goes well, it copies the update files to the update www directory, # updates the dns zone files and schedules (using the at queue) an update # of the zone soa and rndc reload using the tick_zone_serial script # # This script is similar to the run_part2 script used for trunk rule updates. # # Update May 19, 2011: this script now also takes a fourth parameter to do a # reversion to an existing rule update. This is useful for releasing an # emergency update to correct a bad update that was automatically (or # otherwise) released. # # The script also takes three initial parameters that are used for testing # purposes. The first is a root prefix for testing. The second is a keydir. # The third is a flag to update the local svn co of this script's directory. # # If https://svn.apache.org/repos/asf/spamassassin/trunk/rulesrc/scores/DISABLE-AUTOMATIC-UPDATES # exists then DNS updates will be skipped so that update publishing is # effectively disabled. Note that generated updates will still be visible # on the mirrors but will not be published in DNS for sa-update clients. # ALSO NOTE that this only applies to update generation. DNS *will* be # update when the script is run with a fourth parameter to revert to an # existing version/revision update. set -e set -x umask 022 PROGDIR=`dirname $0` [[ "$PROGDIR" = "." ]] && PROGDIR=`pwd` PROGNAME=`basename $0 .sh` HOST=`hostname -f` SVN_BASEURL="https://svn.apache.org/repos/asf/spamassassin" TMPDIR="/usr/local/spamassassin/automc/tmp/${PROGNAME}" UPDATEDIR="/var/www/automc.spamassassin.org/updates" KEYDIR="/usr/local/spamassassin/automc/key" UPDATE_BUILD_DIR=0 REVERT_REVISION=0 # if $1 is present redirect output files to a test directory structure if [ ${#1} -gt 1 ]; then UPDATEDIR=$1$UPDATEDIR DNSDIR=$1$DNSDIR # make the test directory structure mkdir -p $UPDATEDIR mkdir -p $DNSDIR fi if [ ${#2} -gt 1 ]; then KEYDIR=$2 fi if [ ${#3} -gt 1 ]; then UPDATE_BUILD_DIR=1 fi if [ $4 ]; then REVERT_REVISION=$4 fi echo "UPDATEDIR=$UPDATEDIR" echo "DNSDIR=$DNSDIR" echo "KEYDIR=$KEYDIR" echo "REVERT_REVISION=$REVERT_REVISION" test_version() { SA_VERSION=$1 SA_SVN_PATH=$2 # to heck with dealing with svn update failures rm -rf release_$SA_VERSION # test the release on the version(s) of spamassassin the update is meant for svn co $SVN_BASEURL/$SA_SVN_PATH release_$SA_VERSION cd release_$SA_VERSION # fix for newer perl and <3.4.2: Unescaped left brace in regex is illegal here in regex; # marked by <-- HERE in m/^(.{ <-- HERE ,200}).*$/ at lib/Mail/SpamAssassin/PerMsgStatus.pm line 921 perl -p -i -e 's/\.\{,200/.{200/g' lib/Mail/SpamAssassin/PerMsgStatus.pm # also do requires ./ perl -p -i -e 's%= "version\.h\.pl"%= "./version.h.pl"%g' spamc/configure.pl # need rules directory or build won't work if [ ! -e rules ]; then cp -a ../trunk/rules .; fi # Fix for <4.0 - enable_compat/WelcomeListSubject not yet supported if [[ $SA_VERSION =~ ^3 ]]; then perl -p -i -e 's/^\s*(enable_compat)/#$1/' rules/init.pre perl -p -i -e 's/WelcomeListSubject/WhiteListSubject/g' rules/v310.pre fi perl Makefile.PL PREFIX=$TMPDIR/release_$SA_VERSION < /dev/null make make install ./sa-update --install $TMPDIR/${REVISION}.tar.gz 2>&1 || { RV=$? echo "FATAL: sa-update test failed for version $SA_VERSION (r$REVISION) [$RV]" exit $RV } cd .. rm -rf release_$SA_VERSION $TMPDIR/release_$SA_VERSION return 0 } make_rule_update_from_trunk() { cd $TMPDIR # to heck with dealing with svn update failures rm -rf trunk trunk-rulesrc-scores # get the latest scores for new rules svn co $SVN_BASEURL/trunk/rulesrc/scores trunk-rulesrc-scores # get the revision number of the rules # TODO: have the script that make 72_scores.cf include a revision number #REVISION=`head -1 trunk-rulesrc-scores/72_scores.cf | cut -d" " -f6` REVISION=`head -1 trunk-rulesrc-scores/scores-set* | cut -d" " -f9 | sort -rn | head -1` svn co --revision=$REVISION $SVN_BASEURL/trunk trunk cd trunk if [ $UPDATE_BUILD_DIR ]; then svn up build fi perl Makefile.PL PREFIX=$TMPDIR/trunk < /dev/null make make test TEST_FILES="t/basic_lint.t t/basic_lint_without_sandbox.t t/basic_meta.t" cd .. cp trunk-rulesrc-scores/72_scores.cf trunk/rules/72_scores.cf # note: one of set0 or set1 stats might be incorrect (not all of their rules # are included in the update) I can't remember if we eliminate dropped # rules in generate-new-scores or not (we run the sets in a particular # order for some reason) cp trunk-rulesrc-scores/stats-set0 trunk/rules/STATISTICS-set0-72_scores.cf.txt cp trunk-rulesrc-scores/stats-set1 trunk/rules/STATISTICS-set1-72_scores.cf.txt cp trunk-rulesrc-scores/stats-set2 trunk/rules/STATISTICS-set2-72_scores.cf.txt cp trunk-rulesrc-scores/stats-set3 trunk/rules/STATISTICS-set3-72_scores.cf.txt cd trunk/rules # remove files we don't want to ship in updates # remember that 3KB == 1GB of traffic on the mirrors as of Jan 1, 2010 rm -f 70_sandbox.cf 70_inactive.cf STATISTICS-set?.txt mkdir -p $TMPDIR/trunk/etc/mail/spamassassin #cp *.pre *.cf *.txt languages user_prefs.template $TMPDIR/trunk/etc/mail/spamassassin/. ../spamassassin --lint -D tar -cvf $TMPDIR/${REVISION}.tar *.cf *.txt languages user_prefs.template # 5% better compression than gzip zopfli $TMPDIR/${REVISION}.tar cd $TMPDIR tar -ztvf ${REVISION}.tar.gz shasum -a 1 ${REVISION}.tar.gz >${REVISION}.tar.gz.sha1 shasum -a 256 ${REVISION}.tar.gz >${REVISION}.tar.gz.sha256 shasum -a 512 ${REVISION}.tar.gz >${REVISION}.tar.gz.sha512 gpg --batch --homedir $KEYDIR -bas ${REVISION}.tar.gz || exit $? } copy_existing_update_for_reversion_testing() { cp "$UPDATEDIR/$REVERT_REVISION.tar.gz"* "$TMPDIR/" || { echo "Could not copy existing revision $REVERT_REVISION, to temporary testing directory, aborting!" exit 1 } } check_for_disable-automatic-update_file_in_svn() { rm -rf $TMPDIR/svn-scores-latest # checkout the latest scores directory svn co $SVN_BASEURL/trunk/rulesrc/scores/ $TMPDIR/svn-scores-latest AUTOUPDATESDISABLED=0 if [ -f $TMPDIR/svn-scores-latest/DISABLE-AUTOMATIC-UPDATES ]; then echo "Auto-updates have been previously disabled... continuing with manual update reversion" AUTOUPDATESDISABLED=1 fi return $AUTOUPDATESDISABLED } disable_auto_update_publishing_and_get_new_update_revision_number() { date > $TMPDIR/svn-scores-latest/DISABLE-AUTOMATIC-UPDATES echo "Automatic sa-update rule update publishing has been disabled via the revert-stable-update script." >> $TMPDIR/svn-scores-latest/DISABLE-AUTOMATIC-UPDATES echo "Current stable update is being reverted to update $REVERT_REVISION." >> $TMPDIR/svn-scores-latest/DISABLE-AUTOMATIC-UPDATES echo "Update version $REVERT_REVISION will be republished using this commit's revision number as the new version number." >> $TMPDIR/svn-scores-latest/DISABLE-AUTOMATIC-UPDATES echo "To re-enable updates: publish the latest update using the 'revert-stable-update' script (this is optional)" >> $TMPDIR/svn-scores-latest/DISABLE-AUTOMATIC-UPDATES echo "and then delete this file from SVN to re-enable DNS publishing of generated updates." >> $TMPDIR/svn-scores-latest/DISABLE-AUTOMATIC-UPDATES if [ $AUTOUPDATESDISABLED ]; then svn add $TMPDIR/svn-scores-latest/DISABLE-AUTOMATIC-UPDATES fi svn ci $TMPDIR/svn-scores-latest/* -m "sa-update auto-update disabled; reversion to version $REVERT_REVISION in progress; version $REVERT_REVISION will be republished as the same version number of this commit revision number" > $TMPDIR/NEW-REVERT-REVISION 2>&1 set +e grep revision $TMPDIR/NEW-REVERT-REVISION if [ $? -ne 0 ]; then echo "Failed to obtain a new revision number to use as the new update version number." exit 7 fi set -e REVISION=`cat $TMPDIR/NEW-REVERT-REVISION | grep revision | cut -d" " -f3 | cut -d "." -f1` echo "New update version/revision will be $REVISION" } svn_path_exists() { SVNINFO=`svn info $1 2>&1 || true` # W170000 == non-existent revision if [[ $SVNINFO =~ W170000: ]]; then return 1; fi # Hardfail on any other error if [[ ! $SVNINFO =~ Revision: ]]; then echo "FATAL: svn info failed: $SVNINFO" exit 1 fi return 0 } rm -rf $TMPDIR mkdir $TMPDIR cd $TMPDIR if [ $REVERT_REVISION -eq 0 ]; then set +e check_for_disable-automatic-update_file_in_svn AUTOUPDATESDISABLED=$? set -e # generate a rule update using rules from trunk at a revision # that we have generated scores for make_rule_update_from_trunk else if [ ! -f $UPDATEDIR/$REVERT_REVISION.tar.gz -a ! -f $UPDATEDIR/$REVERT_REVISION.tar.gz.asc -a ! -f $UPDATEDIR/$REVERT_REVISION.tar.gz.sha1 -a ! -f $UPDATEDIR/$REVERT_REVISION.tar.gz.sha256 -a ! $UPDATEDIR/$REVERT_REVISION.tar.gz.sha512 ]; then echo "Could not find update files for update revision $REVERT_REVISION, aborting." exit 8 fi set +e check_for_disable-automatic-update_file_in_svn AUTOUPDATESDISABLED=$? set -e disable_auto_update_publishing_and_get_new_update_revision_number echo "Copying existing version/revision $REVERT_REVISION to new version/revision $REVISION for testing." copy_existing_update_for_reversion_testing fi # test to make sure it works with sa-update --install TESTED_VERSIONS="" # Iterate through all found 3.4 versions (3.4.1 - 3.4.x) MAJOR_VERS=3 MINOR_VERS=4 for (( UPDATE_VERS=1; 1; UPDATE_VERS++ )); do REL=${MAJOR_VERS}_${MINOR_VERS}_${UPDATE_VERS} if ! svn_path_exists $SVN_BASEURL/tags/spamassassin_release_${REL}; then break; fi test_version ${REL} tags/spamassassin_release_${REL} && TESTED_VERSIONS="$TESTED_VERSIONS ${REL}" done # And lastly the HEAD if svn_path_exists $SVN_BASEURL/branches/${MAJOR_VERS}.${MINOR_VERS}; then test_version ${MAJOR_VERS}_${MINOR_VERS}_HEAD branches/${MAJOR_VERS}.${MINOR_VERS} && TESTED_VERSIONS="$TESTED_VERSIONS ${MAJOR_VERS}_${MINOR_VERS}_HEAD" fi # Iterate through all found 4.0 versions (4.0.0 - 4.0.x) MAJOR_VERS=4 MINOR_VERS=0 for (( UPDATE_VERS=0; 1; UPDATE_VERS++ )); do REL=${MAJOR_VERS}_${MINOR_VERS}_${UPDATE_VERS} if ! svn_path_exists $SVN_BASEURL/tags/spamassassin_release_${REL}; then break; fi test_version ${REL} tags/spamassassin_release_${REL} && TESTED_VERSIONS="$TESTED_VERSIONS ${REL}" done # And lastly the HEAD if svn_path_exists $SVN_BASEURL/branches/${MAJOR_VERS}.${MINOR_VERS}; then test_version ${MAJOR_VERS}_${MINOR_VERS}_HEAD branches/${MAJOR_VERS}.${MINOR_VERS} && TESTED_VERSIONS="$TESTED_VERSIONS ${MAJOR_VERS}_${MINOR_VERS}_HEAD" fi echo "VERSIONS UPDATE PASSED ON: $TESTED_VERSIONS" # publish update if [[ ! -z "$TESTED_VERSIONS" ]]; then ( chmod 644 $TMPDIR/$REVISION.tar.gz* && # Atomic copy rsync -a $TMPDIR/$REVISION.tar.gz* $UPDATEDIR/ ) || exit 5 if [ $AUTOUPDATESDISABLED -eq 1 -a $REVERT_REVISION -eq 0 ]; then echo "DNS updating disabled (auto update publishing disabled), skipping DNS reload" else # Wait 20 minutes for the mirrors to update via rsync sleep 1200 # Newer versions >= 3.4.1 of SpamAssassin are CNAME'd to 3.3.3 /usr/local/bin/updateDNS.sh 3.3.3.updates TXT $REVISION RC=$? if [[ "$RC" -ne 2 ]]; then # sa-update for these older versions doesn't support CNAME'ing /usr/local/bin/updateDNS.sh 0.4.3.updates TXT $REVISION /usr/local/bin/updateDNS.sh 2.3.3.updates TXT $REVISION /usr/local/bin/updateDNS.sh 1.3.3.updates TXT $REVISION /usr/local/bin/updateDNS.sh 0.3.3.updates TXT $REVISION fi fi fi