00001
00002
00003 import roslib; roslib.load_manifest("job_generation")
00004 import os
00005 import optparse
00006 import rosdistro
00007 import rospkg
00008 import rospkg.distro
00009 import hudson
00010 import urllib
00011 import time
00012 import subprocess
00013 import yaml
00014
00015 BOOTSTRAP_SCRIPT = """
00016 cat > $WORKSPACE/script.sh <<DELIM
00017 #!/usr/bin/env bash
00018 set -o errexit
00019 echo "_________________________________BEGIN SCRIPT______________________________________"
00020 sudo apt-get install bzr --yes
00021 sudo apt-get install ros-ROSDISTRO-ros --yes
00022 source /opt/ros/ROSDISTRO/setup.sh
00023
00024 export INSTALL_DIR=/tmp/install_dir
00025 export WORKSPACE=/tmp/ros
00026 export ROS_TEST_RESULTS_DIR=/tmp/ros/test_results
00027 export JOB_NAME=$JOB_NAME
00028 export BUILD_NUMBER=$BUILD_NUMBER
00029 export HUDSON_URL=$HUDSON_URL
00030 export ROS_PACKAGE_PATH=\$INSTALL_DIR/ros_release:\$ROS_PACKAGE_PATH
00031
00032 mkdir -p \$INSTALL_DIR
00033 cd \$INSTALL_DIR
00034
00035 wget --no-check-certificate http://code.ros.org/svn/ros/installers/trunk/hudson/hudson_helper
00036 chmod +x hudson_helper
00037 svn co https://code.ros.org/svn/ros/stacks/ros_release/trunk ros_release
00038 """
00039
00040 SHUTDOWN_SCRIPT = """
00041 echo "_________________________________END SCRIPT_______________________________________"
00042 DELIM
00043
00044 set -o errexit
00045
00046 rm -rf $WORKSPACE/test_results
00047 rm -rf $WORKSPACE/test_output
00048
00049 wget --no-check-certificate https://code.ros.org/svn/ros/stacks/ros_release/trunk/hudson/scripts/run_chroot.py -O $WORKSPACE/run_chroot.py
00050 chmod +x $WORKSPACE/run_chroot.py
00051 cd $WORKSPACE && $WORKSPACE/run_chroot.py --distro=UBUNTUDISTRO --arch=ARCH --ramdisk --hdd-scratch=/home/rosbuild/install_dir --script=$WORKSPACE/script.sh --ssh-key-file=/home/rosbuild/rosbuild-ssh.tar
00052 """
00053
00054 BOOTSTRAP_SCRIPT_OSX = """
00055 echo "_________________________________BEGIN SCRIPT______________________________________"
00056 source /Users/rosbuild/ros_bootstrap/setup.bash
00057 export ROS_PACKAGE_PATH=$WORKSPACE/ros_release:$ROS_PACKAGE_PATH
00058
00059 wget --no-check-certificate http://code.ros.org/svn/ros/installers/trunk/hudson/hudson_helper -O $WORKSPACE/hudson_helper
00060 chmod +x $WORKSPACE/hudson_helper
00061 svn co https://code.ros.org/svn/ros/stacks/ros_release/trunk $WORKSPACE/ros_release
00062 """
00063
00064 SHUTDOWN_SCRIPT_OSX = """
00065 echo "_________________________________END SCRIPT_______________________________________"
00066 """
00067
00068
00069
00070 ARCHES = ['amd64', 'i386']
00071
00072
00073 UBUNTU_DISTRO_MAP = os_test_platform = {
00074 'testing': ['lucid', 'maverick'],
00075 'unstable': ['lucid', 'oneiric'],
00076 'electric': ['lucid', 'maverick', 'natty', 'oneiric'],
00077 'diamondback': ['lucid', 'maverick', 'natty'],
00078 'cturtle': ['lucid', 'maverick', 'karmic'],
00079 }
00080
00081
00082 SERVER = 'http://build.willowgarage.com'
00083
00084
00085
00086 CONFIG_PATH = 'http://wgs24.willowgarage.com/hudson-html/hds.xml'
00087
00088
00089 EMAIL_TRIGGER="""
00090 <hudson.plugins.emailext.plugins.trigger.WHENTrigger>
00091 <email>
00092 <recipientList></recipientList>
00093 <subject>$PROJECT_DEFAULT_SUBJECT</subject>
00094 <body>$PROJECT_DEFAULT_CONTENT</body>
00095 <sendToDevelopers>SEND_DEVEL</sendToDevelopers>
00096 <sendToRecipientList>true</sendToRecipientList>
00097 <contentTypeHTML>false</contentTypeHTML>
00098 <script>true</script>
00099 </email>
00100 </hudson.plugins.emailext.plugins.trigger.WHENTrigger>
00101 """
00102
00103
00104 hudson_scm_managers = {'svn':"""
00105 <scm class="hudson.scm.SubversionSCM">
00106 <locations>
00107 <hudson.scm.SubversionSCM_-ModuleLocation>
00108 <remote>STACKURI</remote>
00109 <local>STACKNAME</local>
00110 </hudson.scm.SubversionSCM_-ModuleLocation>
00111 </locations>
00112 <useUpdate>false</useUpdate>
00113 <doRevert>false</doRevert>
00114 <excludedRegions></excludedRegions>
00115 <includedRegions></includedRegions>
00116 <excludedUsers></excludedUsers>
00117 <excludedRevprop></excludedRevprop>
00118 <excludedCommitMessages></excludedCommitMessages>
00119 </scm>
00120 """,
00121 'hg':"""
00122 <scm class="hudson.plugins.mercurial.MercurialSCM">
00123 <source>STACKURI</source>
00124 <modules></modules>
00125 <subdir>STACKNAME</subdir>
00126 <clean>false</clean>
00127 <forest>false</forest>
00128 <branch>STACKBRANCH</branch>
00129 </scm>
00130 """,
00131 'bzr':"""
00132 <scm class="hudson.plugins.bazaar.BazaarSCM">
00133 <source>STACKURI STACKNAME</source>
00134 <clean>false</clean>
00135 </scm>
00136 """,
00137 'git':"""
00138
00139 <scm class="hudson.plugins.git.GitSCM">
00140 <configVersion>1</configVersion>
00141 <remoteRepositories>
00142 <org.spearce.jgit.transport.RemoteConfig>
00143 <string>origin</string>
00144 <int>5</int>
00145
00146 <string>fetch</string>
00147 <string>+refs/heads/*:refs/remotes/origin/*</string>
00148 <string>receivepack</string>
00149 <string>git-upload-pack</string>
00150 <string>uploadpack</string>
00151 <string>git-upload-pack</string>
00152
00153 <string>url</string>
00154 <string>STACKURI</string>
00155 <string>tagopt</string>
00156 <string></string>
00157 </org.spearce.jgit.transport.RemoteConfig>
00158 </remoteRepositories>
00159 <branches>
00160
00161 <hudson.plugins.git.BranchSpec>
00162 <name>STACKBRANCH</name>
00163 </hudson.plugins.git.BranchSpec>
00164 </branches>
00165 <localBranch></localBranch>
00166 <mergeOptions/>
00167 <recursiveSubmodules>false</recursiveSubmodules>
00168 <doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
00169
00170 <authorOrCommitter>Hudson</authorOrCommitter>
00171 <clean>false</clean>
00172 <wipeOutWorkspace>false</wipeOutWorkspace>
00173 <buildChooser class="hudson.plugins.git.util.DefaultBuildChooser"/>
00174 <gitTool>Default</gitTool>
00175 <submoduleCfg class="list"/>
00176 <relativeTargetDir>STACKNAME</relativeTargetDir>
00177
00178 <excludedRegions></excludedRegions>
00179 <excludedUsers></excludedUsers>
00180 </scm>
00181 """
00182 }
00183
00184 def stack_to_deb(stack, rosdistro):
00185 return '-'.join(['ros', rosdistro, str(stack).replace('_','-')])
00186
00187 def stacks_to_debs(stack_list, rosdistro):
00188 if not stack_list or len(stack_list) == 0:
00189 return ''
00190 return ' '.join([stack_to_deb(s, rosdistro) for s in stack_list])
00191
00192
00193 def stack_to_rosinstall(stack_obj, branch):
00194 try:
00195 return yaml.dump(rosdistro.stack_to_rosinstall(stack_obj, branch, anonymous=True))
00196 except rosdistro.DistroException, ex:
00197 print str(ex)
00198 return ''
00199
00200
00201 def stacks_to_rosinstall(stack_list, stack_map, branch):
00202 res = ''
00203 for s in stack_list:
00204 if s in stack_map:
00205 res += stack_to_rosinstall(stack_map[s], branch)
00206 else:
00207 print 'Stack "%s" is not in stack list. Not adding this stack to rosinstall file'%s
00208 return res
00209
00210
00211
00212 def get_depends_one(stack):
00213 name = '%s-%s'%(stack.name, stack.version)
00214 url = urllib.urlopen('https://code.ros.org/svn/release/download/stacks/%s/%s/%s.yaml'%(stack.name, name, name))
00215 conf = url.read()
00216 if '404 Not Found' in conf:
00217 print 'Could not get dependencies of stack %s'%stack.name
00218 return []
00219 depends = yaml.load(conf)['depends']
00220 if depends:
00221 return depends
00222 else:
00223 print 'Stack %s does not have any dependencies'%stack.name
00224 return []
00225
00226 def get_depends_all(distro_obj, stack_name, depends_all):
00227 start_depth = len(depends_all)
00228 print start_depth, " depends all ", stack_name
00229 if not stack_name in depends_all:
00230 depends_all.append(stack_name)
00231 try:
00232 for d in get_depends_one(distro_obj.stacks[stack_name]):
00233 get_depends_all(distro_obj, d, depends_all)
00234 except KeyError, ex:
00235 print "Exception when processing %s. Key %s is not in distro_obj.stacks: %s"%(stack_name, ex, ", ".join([s for s in distro_obj.stacks]))
00236 print "depends_all is %s"%(', '.join(depends_all))
00237 raise ex
00238 print start_depth, " DEPENDS_ALL ", stack_name, " end depth ", len(depends_all)
00239
00240 def get_environment():
00241 my_env = os.environ
00242 my_env['WORKSPACE'] = os.getenv('WORKSPACE', '')
00243 my_env['INSTALL_DIR'] = os.getenv('INSTALL_DIR', '')
00244
00245 my_env['HOME'] = os.path.expanduser('~')
00246 my_env['JOB_NAME'] = os.getenv('JOB_NAME', '')
00247 my_env['BUILD_NUMBER'] = os.getenv('BUILD_NUMBER', '')
00248 my_env['ROS_TEST_RESULTS_DIR'] = os.getenv('ROS_TEST_RESULTS_DIR', my_env['WORKSPACE']+'/test_results')
00249 my_env['PWD'] = os.getenv('WORKSPACE', '')
00250 return my_env
00251
00252
00253 def get_options(required, optional):
00254 parser = optparse.OptionParser()
00255 print "required: ", required, "optional: ", optional
00256 ops = required + optional
00257 print "parsing opts:", ops, "done"
00258
00259 if 'os' in ops:
00260 parser.add_option('--os', dest = 'os', default='ubuntu', action='store',
00261 help='OS name')
00262 if 'rosdistro' in ops:
00263 parser.add_option('--rosdistro', dest = 'rosdistro', default=None, action='store',
00264 help='Ros distro name')
00265 if 'stack' in ops:
00266 parser.add_option('--stack', dest = 'stack', default=None, action='append',
00267 help='Stack name')
00268 if 'email' in ops:
00269 parser.add_option('--email', dest = 'email', default=None, action='store',
00270 help='Email address to send results to')
00271 if 'arch' in ops:
00272 parser.add_option('--arch', dest = 'arch', default=None, action='append',
00273 help='Architecture to test')
00274 if 'ubuntu' in ops:
00275 parser.add_option('--ubuntu', dest = 'ubuntu', default=None, action='append',
00276 help='Ubuntu distribution to test')
00277 if 'repeat' in ops:
00278 parser.add_option('--repeat', dest = 'repeat', default=0, action='store',
00279 help='How many times to repeat the test')
00280 if 'source-only' in ops:
00281 parser.add_option('--source-only', dest = 'source_only', default=False, action='store_true',
00282 help="Build everything from source, don't use Debian packages")
00283 if 'delete' in ops:
00284 parser.add_option('--delete', dest = 'delete', default=False, action='store_true',
00285 help='Delete jobs from Hudson')
00286 if 'wait' in ops:
00287 parser.add_option('--wait', dest = 'wait', default=False, action='store_true',
00288 help='Wait for running jobs to finish to reconfigure them')
00289 if 'rosinstall' in ops:
00290 parser.add_option('--rosinstall', dest = 'rosinstall', default=None, action='store',
00291 help="Specify the rosinstall file that refers to unreleased code.")
00292 if 'overlay' in ops:
00293 parser.add_option('--overlay', dest = 'overlay', default=None, action='store',
00294 help='Create overlay file')
00295 if 'variant' in ops:
00296 parser.add_option('--variant', dest = 'variant', default=None, action='store',
00297 help="Specify the variant to create a rosinstall for")
00298 if 'database' in ops:
00299 parser.add_option('--database', dest = 'database', default=None, action='store',
00300 help="Specify database file")
00301
00302 print "done setting up parser"
00303
00304 (options, args) = parser.parse_args()
00305 print "run parser"
00306
00307
00308 if 'repeat' in ops:
00309 options.repeat = int(options.repeat)
00310
00311
00312 for r in required:
00313 if not eval('options.%s'%r):
00314 print 'You need to specify "--%s"'%r
00315 return (None, args)
00316
00317
00318 if 'email' in ops and options.email and not '@' in options.email:
00319 options.email = options.email + '@willowgarage.com'
00320
00321
00322 print "check if the rosdistro exists:"
00323
00324 if 'rosdistro' in ops and (not options.rosdistro or not options.rosdistro in UBUNTU_DISTRO_MAP.keys()):
00325 print 'You provided an invalid "--rosdistro %s" argument. Options are %s'%(options.rosdistro, UBUNTU_DISTRO_MAP.keys())
00326 return (None, args)
00327
00328 print "check if the stack exists"
00329
00330 if 'stack' in ops and options.stack:
00331 distro_obj = rospkg.distro.load_distro(rospkg.distro.distro_uri(options.rosdistro))
00332 for s in options.stack:
00333 if not s in distro_obj.stacks:
00334 print 'Stack "%s" does not exist in the %s distro file.'%(s, options.rosdistro)
00335 print 'You need to add this stack to the rosdistro file'
00336 return (None, args)
00337
00338 print "checking for variants"
00339
00340 if 'variant' in ops and options.variant:
00341 distro_obj = rospkg.distro.load_distro(rospkg.distro.distro_uri(options.rosdistro))
00342 if not options.variant in distro_obj.variants:
00343 print 'Variant "%s" does not exist in the %s distro file.'%(options.variant, options.rosdistro)
00344 return (None, args)
00345
00346 return (options, args)
00347
00348
00349 def schedule_jobs(jobs, wait=False, delete=False, start=False, hudson_obj=None):
00350
00351 if not hudson_obj:
00352 info = urllib.urlopen(CONFIG_PATH).read().split(',')
00353 hudson_obj = hudson.Hudson(SERVER, info[0], info[1])
00354
00355 finished = False
00356 while not finished:
00357 jobs_todo = {}
00358 for job_name in jobs:
00359 exists = hudson_obj.job_exists(job_name)
00360
00361
00362 if exists and hudson_obj.job_is_running(job_name):
00363 jobs_todo[job_name] = jobs[job_name]
00364 print "Not reconfiguring running job %s because it is still running"%job_name
00365
00366
00367
00368 elif delete:
00369 if exists:
00370 hudson_obj.delete_job(job_name)
00371 print " - Deleting job %s"%job_name
00372
00373
00374 elif exists:
00375 hudson_obj.reconfig_job(job_name, jobs[job_name])
00376 if start:
00377 hudson_obj.build_job(job_name)
00378 print " - %s"%job_name
00379
00380
00381 elif not exists:
00382 hudson_obj.create_job(job_name, jobs[job_name])
00383 if start:
00384 hudson_obj.build_job(job_name)
00385 print " - %s"%job_name
00386
00387 if wait and len(jobs_todo) > 0:
00388 jobs = jobs_todo
00389 jobs_todo = {}
00390 time.sleep(10.0)
00391 else:
00392 finished = True
00393
00394
00395
00396 def get_rosdistro_file(rosdistro):
00397 return 'https://code.ros.org/svn/release/trunk/distros/%s.rosdistro'%rosdistro
00398
00399
00400
00401 def get_email_triggers(when, send_devel=True):
00402 triggers = ''
00403 for w in when:
00404 trigger = EMAIL_TRIGGER
00405 trigger = trigger.replace('WHEN', w)
00406 if send_devel:
00407 trigger = trigger.replace('SEND_DEVEL', 'true')
00408 else:
00409 trigger = trigger.replace('SEND_DEVEL', 'false')
00410 triggers += trigger
00411 return triggers
00412
00413
00414 def get_job_name(jobtype, rosdistro, stack_name, ubuntu, arch):
00415 if len(stack_name) > 50:
00416 stack_name = stack_name[0:46]+'_...'
00417 return "_".join([jobtype, rosdistro, stack_name, ubuntu, arch])
00418
00419
00420 def ensure_dir(f):
00421 d = os.path.dirname(f)
00422 if not os.path.exists(d):
00423 os.makedirs(d)
00424
00425 def write_file(filename, msg):
00426 ensure_dir(filename)
00427 with open(filename, 'w') as f:
00428 f.write(msg)
00429
00430
00431 def generate_email(message, env):
00432 print message
00433 write_file(env['WORKSPACE']+'/build_output/buildfailures.txt', message)
00434 write_file(env['WORKSPACE']+'/test_output/testfailures.txt', '')
00435 write_file(env['WORKSPACE']+'/build_output/buildfailures-with-context.txt', '')
00436
00437
00438
00439 def call(command, env=None, message='', ignore_fail=False):
00440 res = ''
00441 err = ''
00442 try:
00443 print message+'\nExecuting command "%s"'%command
00444 helper = subprocess.Popen(command.split(' '), stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, env=env)
00445 res, err = helper.communicate()
00446 print str(res)
00447 print str(err)
00448 if helper.returncode != 0:
00449 raise Exception
00450 return res
00451 except Exception:
00452 if not ignore_fail:
00453 message += "\n=========================================\n"
00454 message += "Failed to execute '%s'"%command
00455 message += "\n=========================================\n"
00456 message += str(res)
00457 message += "\n=========================================\n"
00458 message += str(err)
00459 message += "\n=========================================\n"
00460 if env:
00461 message += "ROS_PACKAGE_PATH = %s\n"%env['ROS_PACKAGE_PATH']
00462 message += "ROS_ROOT = %s\n"%env['ROS_ROOT']
00463 message += "PYTHONPATH = %s\n"%env['PYTHONPATH']
00464 message += "\n=========================================\n"
00465 generate_email(message, env)
00466 raise Exception
00467
00468
00469 def get_sys_info():
00470 arch = 'i386'
00471 if '64' in call('uname -mrs'):
00472 arch = 'amd64'
00473 ubuntudistro = call('lsb_release -a').split('Codename:')[1].strip()
00474 return (arch, ubuntudistro)