#!/usr/bin/python # vim:sw=4 import sys import os import shutil import time import popen2 import traceback SKIP_LOCALES = () SKIP_PDF_LOCALES = ('ru', 'zh') MAIL_DESTINATION = "svnbook-dev@red-bean.com" MAIL_SENDER = "svnbook-build-daemon@red-bean.com" MAIL_SENDER_NAME = "Svnbook Build Daemon" DROPSPOT_URL = "http://svnbook.red-bean.com/nightly" def format_duration(seconds): seconds = int(seconds) hours = seconds / 3600 minutes = seconds % 3600 / 60 seconds = seconds % 60 return ((hours and "%dh " % hours or "") + (minutes and "%dm " % minutes or "") + "%ds" % seconds) def sendmail(subject, body): try: outerrfp, infp = popen2.popen4( ['/usr/sbin/sendmail', '-f', MAIL_SENDER, MAIL_DESTINATION]) infp.write("Subject: %s\n" % subject) infp.write("To: %s\n" % MAIL_DESTINATION) infp.write("From: %s <%s>\n" % (MAIL_SENDER_NAME, MAIL_SENDER)) infp.write("\n") infp.write(body) infp.close() output = outerrfp.read() if len(output): sys.stderr.write("MTA output when sending email (%s):\n" % subject) sys.stderr.write(outerrfp.read()) except IOError: etype, value, tb = sys.exc_info() sys.stderr.write("Failed sending email (%s):\n" % subject) traceback.print_exception(etype, value, tb) sys.stderr.write("\n") sys.stderr.write("Email body:\n") sys.stderr.write(body) if len(sys.argv) < 3: sys.stderr.write("""Usage: %s SRC-DIR TGT-DIR [--dryrun] Crawl SRC-DIR looking for book translations, building distributions of them, and exploding those distributions into TGT-DIR. """ % (os.path.basename(sys.argv[0]))) sys.exit(1) BOOKSRC = os.path.abspath(sys.argv[1]) DROPSPOT = os.path.abspath(sys.argv[2]) DRYRUN = (len(sys.argv) > 3) # Update the working copy if DRYRUN: print "SVN-Update: %s" % BOOKSRC else: os.system('svn up -q ' + BOOKSRC) # Timestamp build_begin_time = time.time() # Find translations locales = [] built_locales = [] kids = os.listdir(BOOKSRC) for kid in kids: full_path = os.path.join(BOOKSRC, kid) if os.path.isfile(full_path): continue if os.path.exists(os.path.join(full_path, 'book')) \ and os.path.exists(os.path.join(full_path, 'Makefile')): locales.append(kid) # Build the locales for i in SKIP_LOCALES: try: locales.remove(i) except ValueError: pass locales.sort() cwd = os.getcwd() for locale in locales: # Calculate some paths locale_dir = os.path.join(BOOKSRC, locale) temp_dir = os.path.join(locale_dir, '__TEMPINSTALL__') build_log = os.path.join(DROPSPOT, 'nightly-build.%s.log' % (locale)) dropspot_locale_path = os.path.join(DROPSPOT, locale) # Figger out which book formats to build book_formats = ['html', 'html-chunk', 'html-arch', 'html-chunk-arch', 'pdf', ] if locale in SKIP_PDF_LOCALES: book_formats.remove('pdf') try: # Build make_cmd = (['make', 'INSTALL_SUBDIR=__TEMPINSTALL__', 'clean', 'valid'] + map(lambda x: 'install-%s' % x, book_formats)) if os.path.isdir(temp_dir): if DRYRUN: print "Erase: %s" % (temp_dir) else: shutil.rmtree(temp_dir) os.chdir(locale_dir) try: cmd = " ".join(make_cmd) if DRYRUN: print "Run: %s" % (cmd) else: child = popen2.Popen4(cmd) logfp = open(build_log, 'w', 1) while 1: data = child.fromchild.readline() if not data: break logfp.write(data) exitcode = child.wait() if exitcode: raise RuntimeError("make exited with error %d" % exitcode) finally: os.chdir(cwd) # Move stuff into place. if os.path.isdir(dropspot_locale_path): if DRYRUN: print "Erase: %s" % (dropspot_locale_path) else: shutil.rmtree(dropspot_locale_path) if DRYRUN: print "Move into place: %s -> %s" % (temp_dir, dropspot_locale_path) else: os.rename(temp_dir, dropspot_locale_path) built_locales.append(locale) except: if DRYRUN: print "Send failure email: %s" % (locale) else: sendmail("Nightly Build Failure Alert: '%s'" % locale, "The nightly svnbook build for the '%s' locale\n" "has failed. Please investigate.\n" "\n" "%s/nightly-build.%s.log\n" "\n" "-- The Svnbook Build Daemon.\n" % (locale, DROPSPOT_URL, locale)) # Timestamp build_end_time = time.time() # Write out index.html if DRYRUN: print "Write index.html:" fp = sys.stdout else: fp = open(os.path.join(DROPSPOT, 'index.html'), 'w') fp.write("""