[sysadmin-bin: 38/168] Put in a delay between emails sent for a single push
- From: Andrea Veri <av src gnome org>
- To: gnome-sysadmin gnome org,commits-list gnome org
- Subject: [sysadmin-bin: 38/168] Put in a delay between emails sent for a single push
- Date: Thu, 24 May 2012 19:54:38 +0000 (UTC)
commit 3158c8c8c559d906efe967d8c0e69ec220fecfe0
Author: Owen W. Taylor <otaylor fishsoup net>
Date: Wed Mar 4 14:13:07 2009 -0500
Put in a delay between emails sent for a single push
In gnome-post-receive-email, when sending out emails, fork off a
subprocess that sends an email, waits 5 seconds, sends another,
and so forth. This (with luck) will improve getting sequential
mails sent out in sequence.
gnome-post-receive-email | 134 ++++++++++++++++++++++++++++++++++++++--------
1 files changed, 111 insertions(+), 23 deletions(-)
---
diff --git a/gnome-post-receive-email b/gnome-post-receive-email
index 940aa82..a96fc30 100755
--- a/gnome-post-receive-email
+++ b/gnome-post-receive-email
@@ -34,6 +34,8 @@ import os
import pwd
from subprocess import Popen, PIPE
import sys
+import tempfile
+import time
# Utility functions for git
# =========================
@@ -215,21 +217,107 @@ def split_lines(str):
else:
return str.split("\n")
-# Open a subprocess.Popen process object for sending mail. Write the
-# mail to process.stdin, and then call close_email()
-def open_email():
- process = Popen(["/usr/sbin/sendmail", "-t"],
- stdout=PIPE, stderr=PIPE, stdin=PIPE)
- return process
+# How long to wait between mails (in seconds); the idea of waiting
+# is to try to make the sequence of mails we send out in order
+# actually get delivered in order. The waiting is done in a forked
+# subprocess and doesn't stall completion of the main script.
+EMAIL_DELAY = 5
+
+# Some line that can never appear in any email we send out
+EMAIL_BOUNDARY="---@@@--- gnome-post-receive-email ---@@@---\n"
+
+# Run in subprocess
+def do_send_emails():
+ email_files = []
+ current_file = None
+ last_line = None
+
+ # Read emails from stdin and write each to a file
+ for line in sys.stdin:
+ if current_file is None:
+ current_file, filename = tempfile.mkstemp(suffix=".mail", prefix="gnome-post-receive-email-")
+ email_files.append(filename)
+
+ if line == EMAIL_BOUNDARY:
+ # Strip the last line if blank; see comment when writing
+ # the email boundary for rationale
+ if last_line.strip() != "":
+ os.write(current_file, last_line)
+ last_line = None
+ os.close(current_file)
+ current_file = None
+ else:
+ if last_line is not None:
+ os.write(current_file, last_line)
+ last_line = line
+
+ if current_file is not None:
+ if last_line is not None:
+ os.write(current_file, last_line)
+ os.close(current_file)
+
+ # We're done interacting with the parent process, the rest happens
+ # asynchronously; send out the emails one by one and remove the
+ # temporary files
+ for i, filename in enumerate(email_files):
+ if i != 0:
+ time.sleep(EMAIL_DELAY)
+
+ f = open(filename, "r")
+ process = Popen(["/usr/sbin/sendmail", "-t"],
+ stdout=None, stderr=None, stdin=f)
+ process.wait()
+ f.close()
+
+ os.remove(filename)
+
+email_file = None
+
+# Start a new outgoing email; returns a file object that the
+# email should be written to. Call end_email() when done
+def start_email():
+ global email_file
+ if email_file is None:
+ email_pipe = os.pipe()
+ pid = os.fork()
+ if pid == 0:
+ # The child
+
+ # Redirect stdin from our pipe
+ os.close(email_pipe[1])
+ os.close(0)
+ os.dup2(email_pipe[0], 0)
+ os.close(email_pipe[0])
+
+ # Redirect stdout/stderr to /dev/null
+ devnull = os.open("/dev/null", os.O_WRONLY)
+ os.close(1)
+ os.dup2(devnull, 1)
+ os.close(2)
+ os.dup2(devnull, 2)
+ os.close(devnull)
+
+ # Fork again to daemonize
+ if os.fork() > 0:
+ sys.exit(0)
+
+ do_send_emails()
+ sys.exit(0)
+
+ email_file = os.fdopen(email_pipe[1], "w")
+ else:
+ # The email might not end with a newline, so add one. We'll
+ # strip the last line, if blank, when emails, so the net effect
+ # is to add a newline to messages without one
+ email_file.write("\n")
+ email_file.write(EMAIL_BOUNDARY)
-def close_email(process):
- output, error = process.communicate()
+ return email_file
- if process.returncode != 0:
- if not quiet and not interactive:
- print >>sys.stderr, error,
- print output,
- raise CalledProcessError(process.returncode, "/usr/sbin/sendmail -t")
+# Finish an email started with start_email
+def end_email():
+ global email_file
+ email_file.flush()
######################################################################
@@ -350,12 +438,12 @@ X-Git-Newrev: %(newrev)s
extra = ""
subject = "[" + projectshort + extra + "] " + self.get_subject()
- mail_process = open_email()
+ mail_out = start_email()
- self.generate_header(mail_process.stdin, subject, include_revs=True, oldrev=self.oldrev, newrev=self.newrev)
- self.generate_body(mail_process.stdin)
+ self.generate_header(mail_out, subject, include_revs=True, oldrev=self.oldrev, newrev=self.newrev)
+ self.generate_body(mail_out)
- close_email(mail_process)
+ end_email()
# Allow multiple emails to be sent - used for branch updates
def send_extra_emails(self):
@@ -488,7 +576,7 @@ class BranchChange(RefChange):
if not commit.id in self.detailed_commits:
continue
- mail_process = open_email()
+ mail_out = start_email()
if self.short_refname == 'master':
branch = ""
@@ -515,16 +603,16 @@ class BranchChange(RefChange):
# for the total branch update. Without a cover email, we are conceptually
# breaking up the update into individual updates for each commit
if self.needs_cover_email:
- self.generate_header(mail_process.stdin, subject, include_revs=False)
+ self.generate_header(mail_out, subject, include_revs=False)
else:
parent = git.rev_parse(commit.id + "^")
- self.generate_header(mail_process.stdin, subject,
+ self.generate_header(mail_out, subject,
include_revs=True,
oldrev=parent, newrev=commit.id)
- mail_process.stdin.flush()
- git.show(commit.id, p=True, stat=True, _outfile=mail_process.stdin)
- close_email(mail_process)
+ mail_out.flush()
+ git.show(commit.id, p=True, stat=True, _outfile=mail_out)
+ end_email()
class BranchCreation(BranchChange):
def get_subject(self):
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]