|  |  | @ -9,6 +9,28 @@ def retryWithDelay(int maxRetries, int delay, Closure body) { | 
			
		
	
		
		
			
				
					
					|  |  |  |   throw Exception("Failed after ${maxRetries} retries") |  |  |  |   throw Exception("Failed after ${maxRetries} retries") | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | // check if started by timer: https://stackoverflow.com/questions/43516025/how-to-handle-nightly-build-in-jenkins-declarative-pipeline | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | @NonCPS | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | def isJobStartedByTimer() { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   def startedByTimer = false | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   try { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     def buildCauses = currentBuild.rawBuild.getCauses() | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     for ( buildCause in buildCauses ) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       if (buildCause != null) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         def causeDescription = buildCause.getShortDescription() | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         echo "shortDescription: ${causeDescription}" | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         if (causeDescription.contains("Started by timer")) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |           startedByTimer = true | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   } catch(theError) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     echo "Error getting build cause" | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   return startedByTimer | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | def device(String ip, String step_label, String cmd) { |  |  |  | def device(String ip, String step_label, String cmd) { | 
			
		
	
		
		
			
				
					
					|  |  |  |   withCredentials([file(credentialsId: 'id_rsa', variable: 'key_file')]) { |  |  |  |   withCredentials([file(credentialsId: 'id_rsa', variable: 'key_file')]) { | 
			
		
	
		
		
			
				
					
					|  |  |  |     def ssh_cmd = """ |  |  |  |     def ssh_cmd = """ | 
			
		
	
	
		
		
			
				
					|  |  | @ -151,15 +173,31 @@ def build_release(String channel_name) { | 
			
		
	
		
		
			
				
					
					|  |  |  |       deviceStage("build git", "tici-needs-can", [], [ |  |  |  |       deviceStage("build git", "tici-needs-can", [], [ | 
			
		
	
		
		
			
				
					
					|  |  |  |         ["build ${channel_name}", "RELEASE_BRANCH=${channel_name} $SOURCE_DIR/release/build_release.sh"], |  |  |  |         ["build ${channel_name}", "RELEASE_BRANCH=${channel_name} $SOURCE_DIR/release/build_release.sh"], | 
			
		
	
		
		
			
				
					
					|  |  |  |       ]) |  |  |  |       ]) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   ) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | def build_casync_release(String channel_name, def release) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   def extra_env = release ? "RELEASE=1" : "" | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   return deviceStage("build casync", "tici-needs-can", [], [ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     ["build", "${extra_env} BUILD_DIR=/data/openpilot CASYNC_DIR=/data/casync/openpilot $SOURCE_DIR/release/create_casync_build.sh"], | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     ["create manifest", "$SOURCE_DIR/release/create_release_manifest.py /data/openpilot /data/manifest.json && cat /data/manifest.json"], | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     ["upload and cleanup", "PYTHONWARNINGS=ignore $SOURCE_DIR/release/upload_casync_release.py /data/casync && rm -rf /data/casync"], | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   ]) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | def build_stage() { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   return parallel ( | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     'nightly': { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       build_release("nightly", true); | 
			
		
	
		
		
			
				
					
					|  |  |  |     }, |  |  |  |     }, | 
			
		
	
		
		
			
				
					
					|  |  |  |     "${channel_name} (casync)": { |  |  |  |     'master': { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       deviceStage("build casync", "tici-needs-can", [], [ |  |  |  |       build_release("master", false); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         ["build", "RELEASE=1 BUILD_DIR=/data/openpilot CASYNC_DIR=/data/casync/openpilot $SOURCE_DIR/release/create_casync_build.sh"], |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         ["create manifest", "$SOURCE_DIR/release/create_release_manifest.py /data/openpilot /data/manifest.json && cat /data/manifest.json"], |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         ["upload and cleanup", "PYTHONWARNINGS=ignore $SOURCE_DIR/release/upload_casync_release.py /data/casync && rm -rf /data/casync"], |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |       ]) |  |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     }, |  |  |  |     }, | 
			
		
	
		
		
			
				
					
					|  |  |  |     "publish agnos": { |  |  |  |     'publish agnos': { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |       pcStage("publish agnos") { |  |  |  |       pcStage("publish agnos") { | 
			
		
	
		
		
			
				
					
					|  |  |  |         sh "release/create_casync_agnos_release.py /tmp/casync/agnos /tmp/casync_tmp" |  |  |  |         sh "release/create_casync_agnos_release.py /tmp/casync/agnos /tmp/casync_tmp" | 
			
		
	
		
		
			
				
					
					|  |  |  |         sh "PYTHONWARNINGS=ignore ${env.WORKSPACE}/release/upload_casync_release.py /tmp/casync" |  |  |  |         sh "PYTHONWARNINGS=ignore ${env.WORKSPACE}/release/upload_casync_release.py /tmp/casync" | 
			
		
	
	
		
		
			
				
					|  |  | @ -168,7 +206,6 @@ def build_release(String channel_name) { | 
			
		
	
		
		
			
				
					
					|  |  |  |   ) |  |  |  |   ) | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | node { |  |  |  | node { | 
			
		
	
		
		
			
				
					
					|  |  |  |   env.CI = "1" |  |  |  |   env.CI = "1" | 
			
		
	
		
		
			
				
					
					|  |  |  |   env.PYTHONWARNINGS = "error" |  |  |  |   env.PYTHONWARNINGS = "error" | 
			
		
	
	
		
		
			
				
					|  |  | @ -183,12 +220,23 @@ node { | 
			
		
	
		
		
			
				
					
					|  |  |  |                          'testing-closet*', 'hotfix-*'] |  |  |  |                          'testing-closet*', 'hotfix-*'] | 
			
		
	
		
		
			
				
					
					|  |  |  |   def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*') |  |  |  |   def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*') | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   if (env.BRANCH_NAME != 'master') { |  |  |  |   def nightlyBranch = "master" | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     properties([ |  |  |  | 
 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         disableConcurrentBuilds(abortPrevious: true) |  |  |  |   def props = []; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     ]) |  |  |  | 
 | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   if (env.BRANCH_NAME == nightlyBranch) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     props.add(pipelineTriggers([ | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       pollSCM('* * * * *'), // every commit | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       cron('0 2 * * *')     // and at 2am every night | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     ])) | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   if (env.branch != "master") { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     props.add(disableConcurrentBuilds(abortPrevious: true)) | 
			
		
	
		
		
			
				
					
					|  |  |  |   } |  |  |  |   } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |   properties(props); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |   try { |  |  |  |   try { | 
			
		
	
		
		
			
				
					
					|  |  |  |     if (env.BRANCH_NAME == 'devel-staging') { |  |  |  |     if (env.BRANCH_NAME == 'devel-staging') { | 
			
		
	
		
		
			
				
					
					|  |  |  |       build_release("release3-staging") |  |  |  |       build_release("release3-staging") | 
			
		
	
	
		
		
			
				
					|  |  | @ -199,74 +247,79 @@ node { | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |     if (!env.BRANCH_NAME.matches(excludeRegex)) { |  |  |  |     if (!env.BRANCH_NAME.matches(excludeRegex)) { | 
			
		
	
		
		
			
				
					
					|  |  |  |     parallel ( |  |  |  |       parallel ( | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       // tici tests |  |  |  |         // tici tests | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       'onroad tests': { |  |  |  |         'onroad tests': { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         deviceStage("onroad", "tici-needs-can", [], [ |  |  |  |           deviceStage("onroad", "tici-needs-can", [], [ | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           // TODO: ideally, this test runs in master-ci, but it takes 5+m to build it |  |  |  |             // TODO: ideally, this test runs in master-ci, but it takes 5+m to build it | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           //["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR $SOURCE_DIR/scripts/retry.sh ./build_devel.sh"], |  |  |  |             //["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR $SOURCE_DIR/scripts/retry.sh ./build_devel.sh"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["build openpilot", "cd selfdrive/manager && ./build.py"], |  |  |  |             ["build openpilot", "cd selfdrive/manager && ./build.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["check dirty", "release/check-dirty.sh"], |  |  |  |             ["check dirty", "release/check-dirty.sh"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["onroad tests", "pytest selfdrive/test/test_onroad.py -s"], |  |  |  |             ["onroad tests", "pytest selfdrive/test/test_onroad.py -s"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["time to onroad", "pytest selfdrive/test/test_time_to_onroad.py"], |  |  |  |             ["time to onroad", "pytest selfdrive/test/test_time_to_onroad.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         ]) |  |  |  |           ]) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       }, |  |  |  |         }, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       'HW + Unit Tests': { |  |  |  |         'HW + Unit Tests': { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         deviceStage("tici-hardware", "tici-common", ["UNSAFE=1"], [ |  |  |  |           deviceStage("tici-hardware", "tici-common", ["UNSAFE=1"], [ | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["build", "cd selfdrive/manager && ./build.py"], |  |  |  |             ["build", "cd selfdrive/manager && ./build.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test pandad", "pytest selfdrive/boardd/tests/test_pandad.py"], |  |  |  |             ["test pandad", "pytest selfdrive/boardd/tests/test_pandad.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test power draw", "pytest -s system/hardware/tici/tests/test_power_draw.py"], |  |  |  |             ["test power draw", "pytest -s system/hardware/tici/tests/test_power_draw.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test encoder", "LD_LIBRARY_PATH=/usr/local/lib pytest system/loggerd/tests/test_encoder.py"], |  |  |  |             ["test encoder", "LD_LIBRARY_PATH=/usr/local/lib pytest system/loggerd/tests/test_encoder.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test pigeond", "pytest system/ubloxd/tests/test_pigeond.py"], |  |  |  |             ["test pigeond", "pytest system/ubloxd/tests/test_pigeond.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test manager", "pytest selfdrive/manager/test/test_manager.py"], |  |  |  |             ["test manager", "pytest selfdrive/manager/test/test_manager.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         ]) |  |  |  |           ]) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       }, |  |  |  |         }, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       'loopback': { |  |  |  |         'loopback': { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         deviceStage("loopback", "tici-loopback", ["UNSAFE=1"], [ |  |  |  |           deviceStage("loopback", "tici-loopback", ["UNSAFE=1"], [ | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["build openpilot", "cd selfdrive/manager && ./build.py"], |  |  |  |             ["build openpilot", "cd selfdrive/manager && ./build.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test boardd loopback", "pytest selfdrive/boardd/tests/test_boardd_loopback.py"], |  |  |  |             ["test boardd loopback", "pytest selfdrive/boardd/tests/test_boardd_loopback.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         ]) |  |  |  |           ]) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       }, |  |  |  |         }, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       'camerad': { |  |  |  |         'camerad': { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         deviceStage("AR0231", "tici-ar0231", ["UNSAFE=1"], [ |  |  |  |           deviceStage("AR0231", "tici-ar0231", ["UNSAFE=1"], [ | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["build", "cd selfdrive/manager && ./build.py"], |  |  |  |             ["build", "cd selfdrive/manager && ./build.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test camerad", "pytest system/camerad/test/test_camerad.py"], |  |  |  |             ["test camerad", "pytest system/camerad/test/test_camerad.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test exposure", "pytest system/camerad/test/test_exposure.py"], |  |  |  |             ["test exposure", "pytest system/camerad/test/test_exposure.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         ]) |  |  |  |           ]) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         deviceStage("OX03C10", "tici-ox03c10", ["UNSAFE=1"], [ |  |  |  |           deviceStage("OX03C10", "tici-ox03c10", ["UNSAFE=1"], [ | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["build", "cd selfdrive/manager && ./build.py"], |  |  |  |             ["build", "cd selfdrive/manager && ./build.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test camerad", "pytest system/camerad/test/test_camerad.py"], |  |  |  |             ["test camerad", "pytest system/camerad/test/test_camerad.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test exposure", "pytest system/camerad/test/test_exposure.py"], |  |  |  |             ["test exposure", "pytest system/camerad/test/test_exposure.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         ]) |  |  |  |           ]) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       }, |  |  |  |         }, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       'sensord': { |  |  |  |         'sensord': { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         deviceStage("LSM + MMC", "tici-lsmc", ["UNSAFE=1"], [ |  |  |  |           deviceStage("LSM + MMC", "tici-lsmc", ["UNSAFE=1"], [ | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["build", "cd selfdrive/manager && ./build.py"], |  |  |  |             ["build", "cd selfdrive/manager && ./build.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test sensord", "pytest system/sensord/tests/test_sensord.py"], |  |  |  |             ["test sensord", "pytest system/sensord/tests/test_sensord.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         ]) |  |  |  |           ]) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         deviceStage("BMX + LSM", "tici-bmx-lsm", ["UNSAFE=1"], [ |  |  |  |           deviceStage("BMX + LSM", "tici-bmx-lsm", ["UNSAFE=1"], [ | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["build", "cd selfdrive/manager && ./build.py"], |  |  |  |             ["build", "cd selfdrive/manager && ./build.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test sensord", "pytest system/sensord/tests/test_sensord.py"], |  |  |  |             ["test sensord", "pytest system/sensord/tests/test_sensord.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         ]) |  |  |  |           ]) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       }, |  |  |  |         }, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       'replay': { |  |  |  |         'replay': { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         deviceStage("model-replay", "tici-replay", ["UNSAFE=1"], [ |  |  |  |           deviceStage("model-replay", "tici-replay", ["UNSAFE=1"], [ | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["build", "cd selfdrive/manager && ./build.py"], |  |  |  |             ["build", "cd selfdrive/manager && ./build.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["model replay", "selfdrive/test/process_replay/model_replay.py"], |  |  |  |             ["model replay", "selfdrive/test/process_replay/model_replay.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         ]) |  |  |  |           ]) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       }, |  |  |  |         }, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       'tizi': { |  |  |  |         'tizi': { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         deviceStage("tizi", "tizi", ["UNSAFE=1"], [ |  |  |  |           deviceStage("tizi", "tizi", ["UNSAFE=1"], [ | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["build openpilot", "cd selfdrive/manager && ./build.py"], |  |  |  |             ["build openpilot", "cd selfdrive/manager && ./build.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test boardd loopback", "SINGLE_PANDA=1 pytest selfdrive/boardd/tests/test_boardd_loopback.py"], |  |  |  |             ["test boardd loopback", "SINGLE_PANDA=1 pytest selfdrive/boardd/tests/test_boardd_loopback.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test pandad", "pytest selfdrive/boardd/tests/test_pandad.py"], |  |  |  |             ["test pandad", "pytest selfdrive/boardd/tests/test_pandad.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test amp", "pytest system/hardware/tici/tests/test_amplifier.py"], |  |  |  |             ["test amp", "pytest system/hardware/tici/tests/test_amplifier.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test hw", "pytest system/hardware/tici/tests/test_hardware.py"], |  |  |  |             ["test hw", "pytest system/hardware/tici/tests/test_hardware.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |           ["test qcomgpsd", "pytest system/qcomgpsd/tests/test_qcomgpsd.py"], |  |  |  |             ["test qcomgpsd", "pytest system/qcomgpsd/tests/test_qcomgpsd.py"], | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |         ]) |  |  |  |           ]) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |       }, |  |  |  |         }, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  |       ) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     ) |  |  |  |     } | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     if (env.BRANCH_NAME == nightlyBranch && isJobStartedByTimer()) { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       stage('build release') { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         build_stage() | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |       } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  |   } catch (Exception e) { |  |  |  |   } catch (Exception e) { | 
			
		
	
		
		
			
				
					
					|  |  |  |     currentBuild.result = 'FAILED' |  |  |  |     currentBuild.result = 'FAILED' | 
			
		
	
	
		
		
			
				
					|  |  | 
 |