11#! groovy
2+ pipeline {
23
3- /**
4- * Clone a repository from either a private URL (if provided) or from dependency.repos config.
5- *
6- * @param repoName Target directory name for the clone
7- * @param privateUrl Private repo URL (empty string to use public)
8- * @param privateBranch Branch to use for private repo
9- * @param publicUrl Public URL from dependency.repos
10- * @param publicVersion Version/branch from dependency.repos
11- * @param credentialId Jenkins SSH credential ID
12- * @param recursive Whether to clone recursively (default: false)
13- * @param fetchTags Whether to fetch tags after clone (default: false)
14- */
15- def cloneRepo (Map args ) {
16- def repoName = args. repoName
17- def privateUrl = args. privateUrl ?: ' '
18- def privateBranch = args. privateBranch ?: ' main'
19- def publicUrl = args. publicUrl
20- def publicVersion = args. publicVersion ?: ' main'
21- def credentialId = args. credentialId
22- def recursive = args. recursive ?: false
23- def fetchTags = args. fetchTags ?: false
24-
25- def recursiveFlag = recursive ? ' --recursive' : ' '
26- def fetchTagsCmd = fetchTags ? " && cd ${ repoName} && git fetch --tags" : ' '
27-
28- if (privateUrl) {
29- echo " Cloning ${ repoName} from internal: ${ privateUrl} (branch: ${ privateBranch} )"
30- sshagent(credentials : [credentialId]) {
31- sh """
32- git clone ${ recursiveFlag} --branch ${ privateBranch} --single-branch ${ privateUrl} ${ repoName} ${ fetchTagsCmd}
33- """
34- }
35- } else {
36- echo " Cloning ${ repoName} from dependency.repos: ${ publicUrl} (version: ${ publicVersion} )"
37- sh """
38- git clone ${ recursiveFlag} --branch ${ publicVersion} --single-branch ${ publicUrl} ${ repoName} ${ fetchTagsCmd}
39- """
4+ libraries {
5+ lib(' fe-pipeline-steps' )
406 }
41- }
427
43- pipeline {
448 agent {
45- label ' docker'
9+ node {
10+ label ' fepc-lx010'
11+ }
4612 }
4713
4814 triggers {
@@ -52,121 +18,161 @@ pipeline {
5218 parameters {
5319 string(name : ' libfrankaRepoUrl' ,
5420 defaultValue :
' ssh://[email protected] :7999/moctrl/libfranka.git' ,
55- description : ' SSH URL to clone libfranka from internal repo. Leave empty to use the github remote. ' )
21+ description : ' SSH URL to clone libfranka' )
5622
5723 string(name : ' libfrankaBranch' ,
5824 defaultValue : ' main' ,
5925 description : ' Branch or tag to checkout for libfranka.' )
6026
6127 string(name : ' frankaDescriptionRepoUrl' ,
6228 defaultValue :
' ssh://[email protected] :7999/moctrl/franka_description.git' ,
63- description : ' SSH URL to clone franka_description from internal repo. Leave empty to use the github remote .' )
29+ description : ' SSH URL to clone franka_description.' )
6430
6531 string(name : ' frankaDescriptionBranch' ,
6632 defaultValue : ' humble' ,
6733 description : ' Branch or tag to checkout for franka_description. Defaults to the same ROS_DISTRO branch of franka_ros2.' )
6834
69- string(name : ' sshCredentialId' ,
70- defaultValue : ' git_ssh' ,
71- description : ' Jenkins credential ID for SSH key to access internal Bitbucket repos.' )
35+ booleanParam(name : ' executeHardwareTestsOnRobot' ,
36+ defaultValue : true ,
37+ description : " Run the franka_ros2 tests on the real hardware" )
38+
39+ string(name : " robotIp" ,
40+ defaultValue : " 172.16.0.1" ,
41+ description : " The static IP of the robot to run tests onto" )
42+ }
43+
44+ environment {
45+ HOME = " ${ WORKSPACE} " // for cleaning up ros logs
7246 }
7347
48+
7449 stages {
7550 stage(' Get Ready' ) {
51+ options { skipDefaultCheckout true }
7652 steps {
77- cleanWs()
78- checkout scm
53+ script{
54+ cleanWs()
55+ }
56+ checkout([
57+ $class : ' GitSCM' ,
58+ branches : scm. branches,
59+ userRemoteConfigs : scm. userRemoteConfigs,
60+ extensions : [
61+ [$class : ' RelativeTargetDirectory' , relativeTargetDir : ' src' ] // needed to put everything under src as expected by colcon/rosdep
62+ ]
63+ ])
7964 script {
8065 notifyBitbucket()
81- currentBuild. displayName = " [libfranka: ${ params.libfrankaRepoUrl ? params. libfrankaBranch : 'private' } , franka_description: ${ params.frankaDescriptionRepoUrl ? params. frankaDescriptionBranch : 'private' } ]"
66+ currentBuild. displayName = " [libfranka: ${ params.libfrankaBranch} , franka_description: ${ params.frankaDescriptionBranch} ]"
8267 }
83- sh ' rm -rf build log install libfranka franka_description'
8468 }
8569 }
8670
8771 stage(' Fetch Dependencies' ) {
72+ options { skipDefaultCheckout true }
8873 steps {
89- script {
90- sh ' rm -rf libfranka franka_description'
91-
92- // Read defaults from dependencies.repos so we can follow the versions defined there
93- def repos = readYaml file : ' dependency.repos'
94- def repoMap = repos?. repositories ?: [:]
95-
96- // Helper to get repo config with defaults
97- def getRepoConfig = { name , defaultUrl ->
98- def cfg = repoMap[name] ?: [:]
99- return [
100- url : cfg. url ?: defaultUrl,
101- version : cfg. version ?: ' main'
102- ]
103- }
74+ sh ' echo "=== Workspace structure ===" && ls -la'
75+ dir(' src' ) {
76+ script {
10477
105- def libfrankaCfg = getRepoConfig(' libfranka' , ' https://github.com/frankarobotics/libfranka.git' )
106- def frankaDescCfg = getRepoConfig(' franka_description' , ' https://github.com/frankarobotics/franka_description.git' )
107-
108- // Clone libfranka
109- cloneRepo(
110- repoName : ' libfranka' ,
111- privateUrl : params. libfrankaRepoUrl,
112- privateBranch : params. libfrankaBranch,
113- publicUrl : libfrankaCfg. url,
114- publicVersion : libfrankaCfg. version,
115- credentialId : params. sshCredentialId,
116- recursive : true ,
117- fetchTags : true
118- )
119-
120- // Clone franka_description - default to the same branch as franka_ros2 if not specified, which is common for ROS packages
121- cloneRepo(
122- repoName : ' franka_description' ,
123- privateUrl : params. frankaDescriptionRepoUrl,
124- privateBranch : params. frankaDescriptionBranch,
125- publicUrl : frankaDescCfg. url,
126- publicVersion : frankaDescCfg. version,
127- credentialId : params. sshCredentialId
128- )
129-
130- // Stash cloned external dependencies so later stages (running in another agent/container)
131- // can restore them even if the workspace is wiped or a different node is used.
132- // useDefaultExcludes: false is required to include .git directories for version detection
133- stash name : ' external_deps' , includes : ' libfranka/**,franka_description/**' , useDefaultExcludes : false
134-
135- sh ' echo "=== Workspace structure ===" && ls -la'
78+ def repos = readYaml file : ' dependency.repos'
79+ def repoMap = repos?. repositories ?: [:]
80+
81+ // Clone/update all 3rd party dependencies
82+ repoMap. each {repoName , cfg ->
83+ def url = cfg. url
84+ def version = cfg. version ?: ' main'
85+ if (repoName != ' libfranka' && repoName != ' franka_description' ){
86+ sh """
87+ if [ -d ${ repoName} ]; then
88+ cd ${ repoName}
89+ git checkout ${ version}
90+ git pull
91+ cd ..
92+ else
93+ git clone --depth 1 --branch ${ version} ${ url} ${ repoName}
94+ fi
95+ """
96+ }
97+ }
98+
99+ sshagent([' git_ssh' ]){
100+ sh """
101+
102+ if [ -d "libfranka" ]; then
103+ cd libfranka
104+ git fetch --all --tags
105+ git checkout ${ params.libfrankaBranch}
106+ git pull
107+ cd ..
108+ else
109+ git clone --branch ${ params.libfrankaBranch} ${ params.libfrankaRepoUrl}
110+ cd libfranka
111+ git config submodule.common.url ssh://[email protected] :7999/moctrl/libfranka-common.git 112+ git submodule update --init --recursive --depth 1
113+ cd ..
114+ fi
115+
116+ if [ -d "franka_description" ]; then
117+ cd franka_description
118+ git fetch --all --tags
119+ git checkout ${ params.frankaDescriptionBranch}
120+ git pull
121+ cd ..
122+ else
123+ git clone --depth 1 --branch ${ params.frankaDescriptionBranch} ${ params.frankaDescriptionRepoUrl}
124+ fi
125+ """
126+ }
127+ }
128+ sh ' echo "=== src structure ===" && ls -la'
136129 }
137130 }
138131 }
139132
140133 stage(' Build' ) {
134+ options { skipDefaultCheckout true }
141135 agent {
142136 dockerfile {
137+ dir ' src'
143138 reuseNode true
144139 }
145140 }
141+ environment {
142+ ROBOT_IP = " ${ params.robotIp} "
143+ }
146144 steps {
147- // Clean any existing files before unstashing to avoid permission conflicts
148- sh ' rm -rf libfranka franka_description'
149- // Restore external deps cloned in the Fetch Dependencies stage
150- unstash ' external_deps'
151145 sh '''
152146 . /opt/ros/$ROS_DISTRO/setup.sh
153147 echo "=== Workspace structure ===" && ls -la
154- colcon build --cmake-args -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCHECK_TIDY=ON -DBUILD_TESTS=OFF
148+ colcon build \
149+ --base-paths src \
150+ --cmake-args -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
151+ -DCHECK_TIDY=ON \
152+ -DBUILD_TESTS=OFF \
153+ -DBUILD_TESTING=ON \
154+ -DROBOT_IP=$ROBOT_IP
155155 '''
156156 }
157157 }
158158
159+ // Run the unit tests (no hardware tests)
159160 stage(' Test' ) {
161+ options { skipDefaultCheckout true }
160162 agent {
161163 dockerfile {
164+ dir ' src'
162165 reuseNode true
163166 }
164167 }
165168 steps {
166169 sh '''
167- . /opt/ros/$ROS_DISTRO/setup.sh
168170 . install/setup.sh
169- colcon test --packages-ignore hardware_interface realtime_tools libfranka controller_manager integration_launch_testing --event-handlers console_direct+
171+ colcon test \
172+ --base-paths src \
173+ --packages-select-regex '^franka_(?!bringup$|gripper$)' \
174+ --event-handlers console_direct+ \
175+ --ctest-args --exclude-regex test_hardware
170176 colcon test-result --verbose
171177 '''
172178 }
@@ -176,11 +182,41 @@ pipeline {
176182 }
177183 }
178184 }
185+ stage(" Hardware Test" ){
186+ // re-enable this when https://franka.atlassian.net/browse/BFFTRAC-2632 is fixed
187+ // when { expression { params.executeHardwareTestsOnRobot } }
188+ when { expression { false } }
189+ options { skipDefaultCheckout true }
190+ agent {
191+ dockerfile {
192+ dir ' src'
193+ reuseNode true
194+ args ' --network host ' +
195+ ' --cap-add=sys_nice ' +
196+ ' --ulimit rtprio=99 ' +
197+ ' --ulimit rttime=-1 ' +
198+ ' --ulimit memlock=8428281856 ' +
199+ ' --security-opt=seccomp=unconfined'
200+ }
201+ }
202+
203+ steps {
204+ script {
205+ echo " Checking connectivity to Master Controller..."
206+ sh " ping -c 5 ${ params.robotIp} "
207+ sh """
208+ . install/setup.sh
209+ chmod +x ./src/franka_ros2/scripts/*.sh
210+ ./src/franka_ros2/scripts/run_hardware_tests.sh ${ params.robotIp}
211+ """
212+ }
213+ }
214+ }
179215 }
180216 post {
181217 always {
182- cleanWs()
183218 script {
219+ cleanWs()
184220 notifyBitbucket()
185221 }
186222 }
0 commit comments