Added runsamples.py which allows the caller to run all the sample applications in a given directory.
diff --git a/runsamples.py b/runsamples.py
new file mode 100644
index 0000000..ef559ee
--- /dev/null
+++ b/runsamples.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+"""Execute all sample applications.
+
+Runs over all the sample applications, determines their type (App Engine,
+Django, or a command-line application), and then runs them checking for a good
+return status in the case of command-line applications and a 200 OK response in
+the case of the App Engine and Django samples.
+"""
+import gflags
+import httplib2
+import logging
+import os
+import signal
+import subprocess
+import sys
+import time
+
+FLAGS = gflags.FLAGS
+
+gflags.DEFINE_list('samples_to_skip', ['latitude'],
+    'A comma separated list of project directory names to be skipped.')
+
+gflags.DEFINE_enum('logging_level', 'INFO',
+    ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
+    'Set the level of logging detail.')
+
+gflags.DEFINE_string('app_engine_dir', '../google_appengine/',
+    'Directory where Google App Engine is installed.')
+
+gflags.DEFINE_string('sample_root', 'samples/oauth2',
+    'The root directory for all the samples.')
+
+
+def main(argv):
+  try:
+    argv = FLAGS(argv)
+  except gflags.FlagsError, e:
+    print '%s\\nUsage: %s ARGS\\n%s' % (e, argv[0], FLAGS)
+    sys.exit(1)
+
+  logging.getLogger().setLevel(getattr(logging, FLAGS.logging_level))
+
+  for dirname in os.listdir(FLAGS.sample_root):
+    if dirname in FLAGS.samples_to_skip:
+      logging.debug('Skipping ' + fulldirname + ' (blacklist)')
+      continue
+    fulldirname = os.path.join(FLAGS.sample_root, dirname)
+    filelist = os.listdir(fulldirname)
+    if 'settings.py' in filelist and 'manage.py' in filelist:
+      logging.info(fulldirname + ' [Django]')
+      proc = subprocess.Popen(
+          [os.path.join(fulldirname, 'manage.py'),
+          'runserver'])
+      # Now just wait, because Django actually spawns a sub-process that does
+      # the I/O and does something funky with stdout so we can't read it and
+      # figure out when it is started.
+      time.sleep(3)
+      h = httplib2.Http()
+      resp, content = h.request('http://localhost:8000/')
+      assert(200 == resp.status)
+      time.sleep(1)
+      logging.debug('Django ppid: %d', proc.pid)
+      # Find and kill the sub-process manage.py forked.
+      findpids = subprocess.Popen(['ps', '--ppid', str(proc.pid), 'o', 'pid',],
+          stdout=subprocess.PIPE)
+      for p in findpids.stdout.readlines():
+        if 'PID' not in p:
+          os.kill(int(p), signal.SIGINT)
+      os.kill(proc.pid, signal.SIGINT)
+      proc.wait()
+    elif 'app.yaml' in filelist:
+      logging.info(fulldirname + ' [App Engine]')
+      proc = subprocess.Popen(
+          [os.path.join(FLAGS.app_engine_dir, 'dev_appserver.py'),
+          fulldirname],
+          stdout=subprocess.PIPE,
+          stderr=subprocess.STDOUT)
+      line = proc.stdout.readline()
+      logging.debug('READ: ' + line)
+      while '] Running application' not in line:
+        line = proc.stdout.readline()
+        logging.debug('READ: ' + line)
+      h = httplib2.Http()
+      resp, content = h.request('http://localhost:8080/')
+      assert(200 == resp.status)
+      time.sleep(1)
+      os.kill(proc.pid, signal.SIGINT)
+      proc.wait()
+    else:
+      logging.info(fulldirname + ' [Command-line]')
+      for filename in os.listdir(fulldirname):
+        if filename.endswith('.py'):
+          logging.info('Running: ' + filename)
+          proc = subprocess.Popen(['python',
+            os.path.join(fulldirname, filename)])
+          returncode = proc.wait()
+          assert(returncode == 0)
+
+
+if __name__ == '__main__':
+  main(sys.argv)
+
diff --git a/samples/appengine/gflags.py b/samples/appengine/gflags.py
new file mode 120000
index 0000000..5a2ff94
--- /dev/null
+++ b/samples/appengine/gflags.py
@@ -0,0 +1 @@
+../../gflags.py
\ No newline at end of file
diff --git a/samples/appengine/gflags_validators.py b/samples/appengine/gflags_validators.py
new file mode 120000
index 0000000..25d8ce8
--- /dev/null
+++ b/samples/appengine/gflags_validators.py
@@ -0,0 +1 @@
+../../gflags_validators.py
\ No newline at end of file