├── .github
└── workflows
│ ├── github.yml
│ └── gitlab.yml
├── .gitlab-ci
└── docker-ros.yml
├── CITATION.cff
├── LICENSE
├── README.md
├── action.yml
├── assets
├── banner.png
├── logo-midjourney.png
└── logo.png
├── docker
├── Dockerfile
├── entrypoint.sh
└── recursive_vcs_import.py
└── scripts
├── build.sh
├── ci.sh
└── utils.sh
/.github/workflows/github.yml:
--------------------------------------------------------------------------------
1 | name: GitHub
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | jobs:
12 |
13 | trigger-ros2:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: convictional/trigger-workflow-and-wait@v1.6.5
17 | with:
18 | owner: ika-rwth-aachen
19 | repo: docker-ros-ci
20 | ref: main
21 | workflow_file_name: ros2.yml
22 | client_payload: '{"docker-ros-git-ref": "${{ github.sha }}"}'
23 | github_token: ${{ secrets.DOCKER_ROS_CI_TRIGGER_GITHUB_TOKEN }}
24 |
--------------------------------------------------------------------------------
/.github/workflows/gitlab.yml:
--------------------------------------------------------------------------------
1 | name: GitLab
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | jobs:
12 |
13 | trigger-ros2:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: Trigger pipeline
17 | run: |
18 | curl --silent --fail --request POST --form "token=${{ secrets.DOCKER_ROS_CI_TRIGGER_GITLAB_TOKEN }}" --form "ref=main" --form "variables[DOCKER_ROS_GIT_REF]=${{ github.sha }}" "https://gitlab.ika.rwth-aachen.de/api/v4/projects/1886/trigger/pipeline" | jq -r .id > id
19 | - name: Upload pipeline ID
20 | uses: actions/upload-artifact@v4
21 | with:
22 | name: id_ros2
23 | path: id
24 |
25 | watch-ros2:
26 | runs-on: ubuntu-latest
27 | needs: trigger-ros2
28 | steps:
29 | - name: Get pipeline ID
30 | uses: actions/download-artifact@v4
31 | with:
32 | name: id_ros2
33 | - name: Wait for pipeline completion
34 | run: |
35 | PIPELINE_ID=$(cat id)
36 | while true; do
37 | sleep 30
38 | PIPELINE_STATUS=$(curl --silent --header "PRIVATE-TOKEN: ${{ secrets.DOCKER_ROS_CI_READ_PIPELINE_GITLAB_TOKEN }}" "https://gitlab.ika.rwth-aachen.de/api/v4/projects/1886/pipelines/$PIPELINE_ID" | jq -r .status)
39 | echo "Pipeline status: $PIPELINE_STATUS (https://gitlab.ika.rwth-aachen.de/fb-fi/ops/docker-ros-ci/-/pipelines/$PIPELINE_ID)"
40 | if [[ $PIPELINE_STATUS == "success" ]]; then
41 | break
42 | elif [[ $PIPELINE_STATUS == "failed" ]]; then
43 | exit 1
44 | elif [[ $PIPELINE_STATUS == "canceled" ]]; then
45 | exit 1
46 | fi
47 | done
48 |
--------------------------------------------------------------------------------
/.gitlab-ci/docker-ros.yml:
--------------------------------------------------------------------------------
1 | workflow:
2 | rules:
3 | - if: $CI_PIPELINE_SOURCE == "parent_pipeline" # run child pipeline if triggered by parent pipeline
4 | - if: $CI_PIPELINE_SOURCE == "merge_request_event" # run merge request pipeline if triggered by merge request
5 | - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS # don't run branch pipeline if open merge request exists
6 | when: never
7 | - if: $CI_COMMIT_BRANCH # run branch pipeline if triggered by commit
8 | - if: $CI_COMMIT_TAG # run tag pipeline with specific image tags if triggered by tag
9 |
10 |
11 | variables:
12 | ADDITIONAL_DEBS_FILE: docker/additional-debs.txt # Relative filepath to file containing additional apt deb packages to install
13 | ADDITIONAL_FILES_DIR: docker/additional-files # Relative path to directory containing additional files to copy into image"
14 | ADDITIONAL_PIP_FILE: docker/additional-pip-requirements.txt # Relative filepath to file containing additional pip packages to install
15 | AFTER_DEPENDENCY_INSTALLATION_SCRIPT: docker/custom.sh # Relative filepath to script containing commands to run after dependency installation
16 | BASE_IMAGE: '' # Base image name:tag (required)
17 | BEFORE_DEPENDENCY_IDENTIFICATION_SCRIPT: docker/before_dependency_identification.sh # Relative filepath to script containing commands to run before dependency identification
18 | BEFORE_DEPENDENCY_INSTALLATION_SCRIPT: docker/before_dependency_installation.sh # Relative filepath to script containing commands to run before dependency installation
19 | BLACKLISTED_PACKAGES_FILE: docker/blacklisted-packages.txt # Relative filepath to file containing the blacklisted packages
20 | BUILD_CONTEXT: . # Build context of Docker build process
21 | COMMAND: '' # Launch command of run image (required if target=run)
22 | CMAKE_ARGS: '-DCMAKE_BUILD_TYPE=Release' # CMake arguments to pass to `colcon build`
23 | DISABLE_ROS_INSTALLATION: 'false' # Disable automatic installation of `ros-$ROS_DISTRO-ros-core` package, e.g., if ROS is already installed in `base-image` and package is not available for the OS
24 | DOCKER_ROS_GIT_REF: main # Git ref of *docker-ros* to run in CI
25 | ENABLE_INDUSTRIAL_CI: 'false' # Enable industrial_ci
26 | ENABLE_PUSH_AS_LATEST: 'false' # Push images with tag `latest`/`latest-dev` in addition to the configured image names
27 | ENABLE_RECURSIVE_ADDITIONAL_DEBS: 'false' # Enable recursive discovery of files named `additional-debs-file`
28 | ENABLE_RECURSIVE_ADDITIONAL_PIP: 'false' # Enable recursive discovery of files named `additional-pip-file`
29 | ENABLE_RECURSIVE_AFTER_DEPENDENCY_INSTALLATION_SCRIPT: 'false' # Enable recursive discovery of files named `after-dependency-installation-script`
30 | ENABLE_RECURSIVE_BEFORE_DEPENDENCY_INSTALLATION_SCRIPT: 'false' # Enable recursive discovery of files named `before-dependency-installation-script`
31 | ENABLE_RECURSIVE_BLACKLISTED_PACKAGES: 'false' # Enable recursive discovery of files named `blacklisted-packages-file`
32 | ENABLE_RECURSIVE_VCS_IMPORT: 'true' # Enable recursive discovery of files named `*.repos`
33 | ENABLE_SINGLEARCH_PUSH: 'false' # Enable push of single arch images with [-amd64|-arm64] postfix
34 | ENABLE_SLIM: 'true' # Enable an extra slimmed run image via slim (only if run stage is targeted)
35 | GIT_HTTPS_PASSWORD: ${CI_JOB_TOKEN} # Password for cloning private Git repositories via HTTPS
36 | GIT_HTTPS_SERVER: ${CI_SERVER_HOST} # Server URL (without protocol) for cloning private Git repositories via HTTPS
37 | GIT_HTTPS_USER: gitlab-ci-token # Username for cloning private Git repositories via HTTPS
38 | GIT_SSH_KNOWN_HOST_KEYS: '' # Known SSH host keys for cloning private Git repositories via SSH (may be obtained using `ssh-keyscan`)
39 | GIT_SSH_PRIVATE_KEY: '' # SSH private key for cloning private Git repositories via SSH
40 | IMAGE_NAME: ${CI_REGISTRY_IMAGE} # Image name of run image
41 | IMAGE_TAG: latest # Image tag of run image
42 | PLATFORM: amd64 # Target platform architecture (comma-separated list) [amd64|arm64|...]
43 | REGISTRY_PASSWORD: ${CI_REGISTRY_PASSWORD} # Docker registry password
44 | REGISTRY_USER: ${CI_REGISTRY_USER} # Docker registry username
45 | REGISTRY: ${CI_REGISTRY} # Docker registry to push images to
46 | RMW_IMPLEMENTATION: 'rmw_fastrtps_cpp' # RMW implementation to use (only for ROS 2)
47 | ROS_DISTRO: '' # ROS Distro (required if ROS is not installed in `base-image`)
48 | SLIM_BUILD_ARGS: '--sensor-ipc-mode proxy --continue-after=10 --show-clogs --http-probe=false --include-path /opt/ros --include-path /docker-ros/ws/install' # Arguments to `slim build` (except for `--target` and `--tag`)
49 | TARGET: run # Target stage of Dockerfile (comma-separated list) [dev|run]
50 | VCS_IMPORT_FILE: .repos # Relative filepath to file containing additional repos to install via vcstools (only relevant if ENABLE_RECURSIVE_VCS_IMPORT=false)
51 |
52 | DEV_IMAGE_NAME: ${IMAGE_NAME} # Image name of dev image
53 | DEV_IMAGE_TAG: ${IMAGE_TAG}-dev # Image tag of dev image
54 | SLIM_IMAGE_NAME: ${IMAGE_NAME} # Image name of slim run image
55 | SLIM_IMAGE_TAG: ${IMAGE_TAG}-slim # Image tag of slim run image
56 |
57 | # --------------------
58 |
59 | _RUN_IMAGE: ${IMAGE_NAME}:${IMAGE_TAG}
60 | _DEV_IMAGE: ${DEV_IMAGE_NAME}:${DEV_IMAGE_TAG}
61 | _SLIM_IMAGE: ${SLIM_IMAGE_NAME}:${SLIM_IMAGE_TAG}
62 |
63 | _IMAGE_DEV_CI: ${_DEV_IMAGE}_${CI_COMMIT_REF_SLUG}_ci
64 | _IMAGE_RUN_CI: ${_RUN_IMAGE}_${CI_COMMIT_REF_SLUG}_ci
65 | _IMAGE_SLIM_CI: ${_SLIM_IMAGE}_${CI_COMMIT_REF_SLUG}_ci
66 | _IMAGE_DEV_CI_AMD64: ${_IMAGE_DEV_CI}-amd64
67 | _IMAGE_DEV_CI_ARM64: ${_IMAGE_DEV_CI}-arm64
68 | _IMAGE_RUN_CI_AMD64: ${_IMAGE_RUN_CI}-amd64
69 | _IMAGE_RUN_CI_ARM64: ${_IMAGE_RUN_CI}-arm64
70 | _IMAGE_SLIM_CI_AMD64: ${_IMAGE_SLIM_CI}-amd64
71 | _IMAGE_SLIM_CI_ARM64: ${_IMAGE_SLIM_CI}-arm64
72 | _IMAGE_DEV_LATEST: ${DEV_IMAGE_NAME}:latest-dev
73 | _IMAGE_RUN_LATEST: ${IMAGE_NAME}:latest
74 | _IMAGE_SLIM_LATEST: ${SLIM_IMAGE_NAME}:latest-slim
75 | _IMAGE_DEV_TARGET_TAG: ${_DEV_IMAGE}-${CI_COMMIT_TAG}
76 | _IMAGE_RUN_TARGET_TAG: ${_RUN_IMAGE}-${CI_COMMIT_TAG}
77 | _IMAGE_SLIM_TARGET_TAG: ${_SLIM_IMAGE}-${CI_COMMIT_TAG}
78 | _IMAGE_DEV_TAG: ${DEV_IMAGE_NAME}:${CI_COMMIT_TAG}-dev
79 | _IMAGE_RUN_TAG: ${IMAGE_NAME}:${CI_COMMIT_TAG}
80 | _IMAGE_SLIM_TAG: ${SLIM_IMAGE_NAME}:${CI_COMMIT_TAG}-slim
81 |
82 | GIT_SUBMODULE_STRATEGY: recursive
83 | DOCKER_DRIVER: overlay2
84 | DOCKER_TLS_CERTDIR: /certs
85 | DOCKER_BUILDKIT: 1
86 |
87 |
88 | stages:
89 | - Build dev Images
90 | - Build run Images
91 | - Test ROS Industrial CI
92 | - Slim Images
93 | - Push Multi-Arch Images
94 |
95 |
96 | default:
97 | image: docker:20.10.22-git
98 | services:
99 | - docker:20.10.22-dind
100 | tags:
101 | - privileged
102 | - amd64
103 | before_script:
104 | - echo -e "section_start:`date +%s`:setup_section[collapsed=true]\r\e[0K[docker-ros] Setup docker-ros"
105 | - apk add --update bash
106 | - cd ${BUILD_CONTEXT}
107 | - |-
108 | if [[ ! -d docker/docker-ros ]]; then
109 | mkdir -p docker
110 | git clone --depth=1 https://github.com/ika-rwth-aachen/docker-ros.git docker/docker-ros
111 | cd docker/docker-ros
112 | git fetch origin ${DOCKER_ROS_GIT_REF}
113 | git checkout FETCH_HEAD
114 | cd -
115 | fi
116 | - docker login -u ${REGISTRY_USER} -p ${REGISTRY_PASSWORD} ${REGISTRY}
117 | - docker context create buildx-context
118 | - docker buildx create --use buildx-context
119 | - echo -e "section_end:`date +%s`:setup_section\r\e[0K"
120 |
121 | .build:
122 | script:
123 | - |-
124 | if [[ ${CI_RUNNER_EXECUTABLE_ARCH} != ${_PLATFORM} && ${CI_RUNNER_EXECUTABLE_ARCH} != linux/${_PLATFORM} ]]; then
125 | docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
126 | fi
127 | TARGET=${_TARGET} PLATFORM=${_PLATFORM} ./docker/docker-ros/scripts/ci.sh
128 | echo -e "section_start:`date +%s`:push_section[collapsed=true]\r\e[0K[docker-ros] Push ${IMAGE}"
129 | docker push ${IMAGE}
130 | echo -e "section_end:`date +%s`:push_section\r\e[0K"
131 |
132 | dev-amd64:
133 | stage: Build dev Images
134 | extends: .build
135 | rules:
136 | - if: $PLATFORM =~ /.*amd64.*/ && $TARGET =~ /.*dev.*/
137 | variables:
138 | _PLATFORM: amd64
139 | _TARGET: dev
140 | IMAGE: ${_IMAGE_DEV_CI_AMD64}
141 | ENABLE_SINGLEARCH_PUSH: 'true'
142 | ENABLE_SLIM: 'false'
143 | _IMAGE_POSTFIX: _${CI_COMMIT_REF_SLUG}_ci
144 |
145 | dev-arm64:
146 | stage: Build dev Images
147 | extends: .build
148 | tags: [privileged, arm64]
149 | rules:
150 | - if: $PLATFORM =~ /.*arm64.*/ && $TARGET =~ /.*dev.*/
151 | variables:
152 | _PLATFORM: arm64
153 | _TARGET: dev
154 | IMAGE: ${_IMAGE_DEV_CI_ARM64}
155 | ENABLE_SINGLEARCH_PUSH: 'true'
156 | ENABLE_SLIM: 'false'
157 | _IMAGE_POSTFIX: _${CI_COMMIT_REF_SLUG}_ci
158 |
159 | run-amd64:
160 | stage: Build run Images
161 | extends: .build
162 | needs:
163 | - job: dev-amd64
164 | optional: true
165 | rules:
166 | - if: $PLATFORM =~ /.*amd64.*/ && $TARGET =~ /.*run.*/
167 | variables:
168 | _PLATFORM: amd64
169 | _TARGET: run
170 | IMAGE: ${_IMAGE_RUN_CI_AMD64}
171 | ENABLE_SINGLEARCH_PUSH: 'true'
172 | ENABLE_SLIM: 'false'
173 | _IMAGE_POSTFIX: _${CI_COMMIT_REF_SLUG}_ci
174 |
175 | run-arm64:
176 | stage: Build run Images
177 | extends: .build
178 | tags: [privileged, arm64]
179 | needs:
180 | - job: dev-arm64
181 | optional: true
182 | rules:
183 | - if: $PLATFORM =~ /.*arm64.*/ && $TARGET =~ /.*run.*/
184 | variables:
185 | _PLATFORM: arm64
186 | _TARGET: run
187 | IMAGE: ${_IMAGE_RUN_CI_ARM64}
188 | ENABLE_SINGLEARCH_PUSH: 'true'
189 | ENABLE_SLIM: 'false'
190 | _IMAGE_POSTFIX: _${CI_COMMIT_REF_SLUG}_ci
191 |
192 |
193 | .test:
194 | stage: Test ROS Industrial CI
195 | variables:
196 | UPSTREAM_WORKSPACE: ${BUILD_CONTEXT}/.repos
197 | TARGET_WORKSPACE: ${BUILD_CONTEXT}
198 | ADDITIONAL_DEBS: git
199 | AFTER_INIT_EMBED: git config --global url.https://${GIT_HTTPS_USER}:${GIT_HTTPS_PASSWORD}@${GIT_HTTPS_SERVER}.insteadOf https://${GIT_HTTPS_SERVER}
200 | DOCKER_RUN_OPTS: -u root:root
201 | before_script:
202 | - docker login -u ${REGISTRY_USER} -p ${REGISTRY_PASSWORD} ${REGISTRY}
203 | - apk add --update bash coreutils grep tar
204 | - |-
205 | if [[ ${CI_RUNNER_EXECUTABLE_ARCH} != ${_PLATFORM} && ${CI_RUNNER_EXECUTABLE_ARCH} != linux/${_PLATFORM} ]]; then
206 | docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
207 | fi
208 | - git clone --branch master --depth 1 https://github.com/ros-industrial/industrial_ci.git .industrial_ci
209 | - test -f ${BUILD_CONTEXT}/.repos || echo "repositories:" > ${BUILD_CONTEXT}/.repos
210 | script: .industrial_ci/gitlab.sh
211 |
212 | Test dev-amd64:
213 | extends: .test
214 | needs:
215 | - job: dev-amd64
216 | rules:
217 | - if: $ENABLE_INDUSTRIAL_CI == 'true' && $PLATFORM =~ /.*amd64.*/ && $TARGET =~ /.*dev.*/
218 | variables:
219 | DOCKER_IMAGE: ${_IMAGE_DEV_CI_AMD64}
220 | _PLATFORM: amd64
221 |
222 | Test dev-arm64:
223 | extends: .test
224 | tags: [privileged, arm64]
225 | needs:
226 | - job: dev-arm64
227 | rules:
228 | - if: $ENABLE_INDUSTRIAL_CI == 'true' && $PLATFORM =~ /.*arm64.*/ && $TARGET =~ /.*dev.*/
229 | variables:
230 | DOCKER_IMAGE: ${_IMAGE_DEV_CI_ARM64}
231 | _PLATFORM: arm64
232 |
233 | Test run-amd64:
234 | extends: .test
235 | needs:
236 | - job: run-amd64
237 | rules:
238 | - if: $ENABLE_INDUSTRIAL_CI == 'true' && $PLATFORM =~ /.*amd64.*/ && $TARGET !~ /.*dev.*/
239 | variables:
240 | DOCKER_IMAGE: ${_IMAGE_RUN_CI_AMD64}
241 | _PLATFORM: amd64
242 |
243 | Test run-arm64:
244 | extends: .test
245 | needs:
246 | - job: run-arm64
247 | rules:
248 | - if: $ENABLE_INDUSTRIAL_CI == 'true' && $PLATFORM =~ /.*arm64.*/ && $TARGET !~ /.*dev.*/
249 | variables:
250 | DOCKER_IMAGE: ${_IMAGE_RUN_CI_ARM64}
251 | _PLATFORM: arm64
252 |
253 |
254 | .slim:
255 | stage: Slim Images
256 | before_script:
257 | - apk add --update --upgrade curl tar
258 | - curl -L -o ds.tar.gz ${SLIM_DOWNLOAD_URL}
259 | - tar -xvf ds.tar.gz
260 | - cd dist_linux*
261 | - docker login -u ${REGISTRY_USER} -p ${REGISTRY_PASSWORD} ${REGISTRY}
262 | - docker pull ${FAT_IMAGE}
263 | script:
264 | - ./slim build --target ${FAT_IMAGE} --tag ${SLIM_IMAGE} ${SLIM_BUILD_ARGS}
265 | - docker push ${SLIM_IMAGE}
266 |
267 | Slim run-amd64:
268 | stage: Slim Images
269 | extends: .slim
270 | needs:
271 | - job: run-amd64
272 | optional: true
273 | - job: Test run-amd64
274 | optional: true
275 | rules:
276 | - if: $ENABLE_SLIM == 'true' && $PLATFORM =~ /.*amd64.*/ && $TARGET =~ /.*run.*/
277 | variables:
278 | FAT_IMAGE: ${_IMAGE_RUN_CI_AMD64}
279 | SLIM_IMAGE: ${_IMAGE_SLIM_CI_AMD64}
280 | SLIM_DOWNLOAD_URL: "https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_linux.tar.gz"
281 |
282 | Slim run-arm64:
283 | stage: Slim Images
284 | extends: .slim
285 | tags: [privileged, arm64]
286 | needs:
287 | - job: run-arm64
288 | optional: true
289 | - job: Test run-arm64
290 | optional: true
291 | rules:
292 | - if: $ENABLE_SLIM == 'true' && $PLATFORM =~ /.*arm64.*/ && $TARGET =~ /.*run.*/
293 | variables:
294 | FAT_IMAGE: ${_IMAGE_RUN_CI_ARM64}
295 | SLIM_IMAGE: ${_IMAGE_SLIM_CI_ARM64}
296 | SLIM_DOWNLOAD_URL: "https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_linux_arm64.tar.gz"
297 |
298 |
299 | .push:
300 | needs:
301 | - job: dev-amd64
302 | optional: true
303 | - job: dev-arm64
304 | optional: true
305 | - job: run-amd64
306 | optional: true
307 | - job: run-arm64
308 | optional: true
309 | - job: Test dev-amd64
310 | optional: true
311 | - job: Test dev-arm64
312 | optional: true
313 | - job: Test run-amd64
314 | optional: true
315 | - job: Test run-arm64
316 | optional: true
317 | - job: Slim run-amd64
318 | optional: true
319 | - job: Slim run-arm64
320 | optional: true
321 | rules:
322 | - if: $PLATFORM == '' || $TARGET == ''
323 | when: never
324 | script:
325 | - |-
326 | if [[ "${PLATFORM}" =~ amd64 && "${PLATFORM}" =~ arm64 ]]; then
327 | if [[ "${TARGET}" =~ dev ]]; then
328 | docker manifest create ${IMG_DEV} --amend ${_IMAGE_DEV_CI_AMD64} --amend ${_IMAGE_DEV_CI_ARM64}
329 | docker manifest push ${IMG_DEV}
330 | fi
331 | if [[ "${TARGET}" =~ run ]]; then
332 | docker manifest create ${IMG_RUN} --amend ${_IMAGE_RUN_CI_AMD64} --amend ${_IMAGE_RUN_CI_ARM64}
333 | docker manifest push ${IMG_RUN}
334 | fi
335 | if [[ "${ENABLE_SLIM}" == 'true' && "${TARGET}" =~ run ]]; then
336 | docker manifest create ${IMG_SLIM} --amend ${_IMAGE_SLIM_CI_AMD64} --amend ${_IMAGE_SLIM_CI_ARM64}
337 | docker manifest push ${IMG_SLIM}
338 | fi
339 | elif [[ "${PLATFORM}" =~ amd64 ]]; then
340 | if [[ "${TARGET}" =~ dev ]]; then
341 | docker pull ${_IMAGE_DEV_CI_AMD64}
342 | docker tag ${_IMAGE_DEV_CI_AMD64} ${IMG_DEV}
343 | docker push ${IMG_DEV}
344 | fi
345 | if [[ "${TARGET}" =~ run ]]; then
346 | docker pull ${_IMAGE_RUN_CI_AMD64}
347 | docker tag ${_IMAGE_RUN_CI_AMD64} ${IMG_RUN}
348 | docker push ${IMG_RUN}
349 | fi
350 | if [[ "${ENABLE_SLIM}" == 'true' && "${TARGET}" =~ run ]]; then
351 | docker pull ${_IMAGE_SLIM_CI_AMD64}
352 | docker tag ${_IMAGE_SLIM_CI_AMD64} ${IMG_SLIM}
353 | docker push ${IMG_SLIM}
354 | fi
355 | elif [[ "${PLATFORM}" =~ arm64 ]]; then
356 | if [[ "${TARGET}" =~ dev ]]; then
357 | docker pull ${_IMAGE_DEV_CI_ARM64}
358 | docker tag ${_IMAGE_DEV_CI_ARM64} ${IMG_DEV}
359 | docker push ${IMG_DEV}
360 | fi
361 | if [[ "${TARGET}" =~ run ]]; then
362 | docker pull ${_IMAGE_RUN_CI_ARM64}
363 | docker tag ${_IMAGE_RUN_CI_ARM64} ${IMG_RUN}
364 | docker push ${IMG_RUN}
365 | fi
366 | if [[ "${ENABLE_SLIM}" == 'true' && "${TARGET}" =~ run ]]; then
367 | docker pull ${_IMAGE_SLIM_CI_ARM64}
368 | docker tag ${_IMAGE_SLIM_CI_ARM64} ${IMG_SLIM}
369 | docker push ${IMG_SLIM}
370 | fi
371 | fi
372 |
373 | Push CI:
374 | stage: Push Multi-Arch Images
375 | extends: .push
376 | rules:
377 | - !reference [.push, rules]
378 | - if: $CI_COMMIT_TAG
379 | when: never
380 | - if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
381 | variables:
382 | IMG_DEV: ${_IMAGE_DEV_CI}
383 | IMG_RUN: ${_IMAGE_RUN_CI}
384 | IMG_SLIM: ${_IMAGE_SLIM_CI}
385 |
386 | Push:
387 | stage: Push Multi-Arch Images
388 | extends: .push
389 | rules:
390 | - !reference [.push, rules]
391 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
392 | variables:
393 | IMG_DEV: ${_DEV_IMAGE}
394 | IMG_RUN: ${_RUN_IMAGE}
395 | IMG_SLIM: ${_SLIM_IMAGE}
396 |
397 | Push latest:
398 | stage: Push Multi-Arch Images
399 | extends: .push
400 | rules:
401 | - !reference [.push, rules]
402 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $ENABLE_PUSH_AS_LATEST == 'true'
403 | variables:
404 | IMG_DEV: ${_IMAGE_DEV_LATEST}
405 | IMG_RUN: ${_IMAGE_RUN_LATEST}
406 | IMG_SLIM: ${_IMAGE_SLIM_LATEST}
407 |
408 | Push target tag:
409 | stage: Push Multi-Arch Images
410 | extends: .push
411 | rules:
412 | - !reference [.push, rules]
413 | - if: $CI_COMMIT_TAG
414 | variables:
415 | IMG_DEV: ${_IMAGE_DEV_TARGET_TAG}
416 | IMG_RUN: ${_IMAGE_RUN_TARGET_TAG}
417 | IMG_SLIM: ${_IMAGE_SLIM_TARGET_TAG}
418 |
419 | Push tag:
420 | stage: Push Multi-Arch Images
421 | extends: .push
422 | rules:
423 | - !reference [.push, rules]
424 | - if: $CI_COMMIT_TAG && $ENABLE_PUSH_AS_LATEST == 'true'
425 | variables:
426 | IMG_DEV: ${_IMAGE_DEV_TAG}
427 | IMG_RUN: ${_IMAGE_RUN_TAG}
428 | IMG_SLIM: ${_IMAGE_SLIM_TAG}
429 |
--------------------------------------------------------------------------------
/CITATION.cff:
--------------------------------------------------------------------------------
1 | cff-version: 1.2.0
2 | message: "We hope that our tools can help your research. If this is the case, please cite it using the following metadata."
3 |
4 | title: dorotos
5 | type: software
6 | repository-code: "https://github.com/ika-rwth-aachen/docker-ros"
7 | date-released: 2023-05-28
8 | authors:
9 | - given-names: Jean-Pierre
10 | family-names: Busch
11 | - given-names: Lennart
12 | family-names: Reiher
13 |
14 | preferred-citation:
15 | title: "Enabling the Deployment of Any-Scale Robotic Applications in Microservice-Based Service-Oriented Architectures through Automated Containerization"
16 | type: conference-paper
17 | conference:
18 | name: "2024 IEEE International Conference on Robotics and Automation (ICRA)"
19 | year: 2024
20 | pages: "17650-17656"
21 | doi: "10.1109/ICRA57147.2024.10611586"
22 | url: "https://ieeexplore.ieee.org/document/10611586"
23 | authors:
24 | - given-names: Jean-Pierre
25 | family-names: Busch
26 | orcid: "https://orcid.org/0009-0000-1417-0463"
27 | - given-names: Lennart
28 | family-names: Reiher
29 | orcid: "https://orcid.org/0000-0002-7309-164X"
30 | - given-names: Lutz
31 | family-names: Eckstein
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023-2024 Institute for Automotive Engineering (ika), RWTH Aachen University
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # *docker-ros* – Automated Containerization of ROS Apps
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | *docker-ros* automatically builds minimal container images of ROS applications.
14 |
15 | > [!IMPORTANT]
16 | > This repository is open-sourced and maintained by the [**Institute for Automotive Engineering (ika) at RWTH Aachen University**](https://www.ika.rwth-aachen.de/).
17 | > **DevOps, Containerization and Orchestration of Software-Defined Vehicles** are some of many research topics within our [*Vehicle Intelligence & Automated Driving*](https://www.ika.rwth-aachen.de/en/competences/fields-of-research/vehicle-intelligence-automated-driving.html) domain.
18 | > If you would like to learn more about how we can support your advanced driver assistance and automated driving efforts, feel free to reach out to us!
19 | > :email: ***opensource@ika.rwth-aachen.de***
20 |
21 | - [About](#about)
22 | - [Prerequisites](#prerequisites)
23 | - [Usage](#usage)
24 | - [Build a minimal image for deployment](#build-a-minimal-image-for-deployment)
25 | - [Build development and deployment images](#build-development-and-deployment-images)
26 | - [Build multi-arch images](#build-multi-arch-images)
27 | - [Build deployment image with additional industrial\_ci check](#build-deployment-image-with-additional-industrial_ci-check)
28 | - [Build multi-arch images on arch-specific self-hosted runners in parallel](#build-multi-arch-images-on-arch-specific-self-hosted-runners-in-parallel)
29 | - [Build images locally](#build-images-locally)
30 | - [Advanced Dependencies](#advanced-dependencies)
31 | - [Recursion](#recursion)
32 | - [Package Blacklist](#package-blacklist)
33 | - [Extra System Dependencies (*apt*)](#extra-system-dependencies-apt)
34 | - [Extra System Dependencies (*pip*)](#extra-system-dependencies-pip)
35 | - [Custom Installation Scripts](#custom-installation-scripts)
36 | - [Extra Image Files](#extra-image-files)
37 | - [Additional Information](#additional-information)
38 | - [User Setup](#user-setup)
39 | - [Slim Deployment Image](#slim-deployment-image)
40 | - [Configuration Variables](#configuration-variables)
41 |
42 | We recommend to use *docker-ros* in combination with our other tools for Docker and ROS.
43 | - [*docker-ros-ml-images*](https://github.com/ika-rwth-aachen/docker-ros-ml-images) provides machine learning-enabled ROS Docker images
44 | - [*docker-run*](https://github.com/ika-rwth-aachen/docker-run) is a CLI tool for simplified interaction with Docker images
45 |
46 |
47 | ## About
48 |
49 | *docker-ros* provides a generic [Dockerfile](docker/Dockerfile) that can be used to build development and deployment Docker images for arbitrary ROS packages or package stacks. Building such images can easily be automated by integrating *docker-ros* into CI through the provided [GitHub action](action.yml) or [GitLab CI template](.gitlab-ci/docker-ros.yml). The development image built by *docker-ros* contains all required dependencies and the source code of your ROS-based repository. The deployment image only contains dependencies and the compiled binaries created by building the ROS packages in the repository. *docker-ros* is also able to build multi-arch Docker images for *amd64* and *arm64* architectures. In addition, [*slim*](https://github.com/slimtoolkit/slim) is integrated for slimming Docker image size of the deployment image by up to 30x (see [*Slim Deployment Image*](#slim-deployment-image)).
50 |
51 | The Dockerfile performs the following steps to build these images:
52 | 1. All dependency repositories that are defined in a `.repos` file anywhere in the repository are cloned using [*vcstool*](https://github.com/dirk-thomas/vcstool).
53 | 2. *(optional)* Packages blacklisted in a special file `blacklisted-packages.txt` are removed from the workspace (see [*Advanced Dependencies*](#package-blacklist)).
54 | 3. *(optional)* A special script `before_dependency_installation.sh` is executed to perform arbitrary installation commands, if needed (see [*Advanced Dependencies*](#custom-installation-scripts)).
55 | 4. The ROS dependencies listed in each package's `package.xml` are installed by [*rosdep*](https://docs.ros.org/en/independent/api/rosdep/html/).
56 | 5. *(optional)* Additional apt dependencies from a special file `additional-debs.txt` are installed, if needed (see [*Advanced Dependencies*](#extra-system-dependencies-apt)).
57 | 6. *(optional)* Additional pip requirements from a special file `additional-pip-requirements.txt` are installed, if needed (see [*Advanced Dependencies*](#extra-system-dependencies-pip)).
58 | 7. *(optional)* A special folder `additional-files/` is copied into the images, if needed (see [*Advanced Dependencies*](#extra-image-files)).
59 | 8. *(optional)* A special script `custom.sh` is executed to perform arbitrary installation commands, if needed (see [*Advanced Dependencies*](#custom-installation-scripts)).
60 | 9. *(deployment)* All ROS packages are built using `colcon` (ROS2).
61 | 10. *(deployment)* A custom launch command is configured to run on container start.
62 |
63 | ### Prerequisites
64 |
65 | *docker-ros* is made for automated execution in GitHub or GitLab CI pipelines. For local execution, see [*Build images locally*](#build-images-locally).
66 |
67 | GitHub
68 |
69 | GitHub offers free minutes on GitHub-hosted runners executing GitHub Actions, [see here](https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions). No further setup is required other than integrating *docker-ros* into your repository, see [*Usage*](#usage).
70 |
71 | Note that GitHub is currently only offering Linux runners based on the *amd64* architecture. *docker-ros* can also build multi-arch Docker images solely on the *amd64* platform through emulation, but performance can be improved greatly by deploying [self-hosted runners](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners) for the *arm64* platform.
72 |
73 |
74 |
75 | GitLab
76 |
77 | > [!NOTE]
78 | > - GitLab runners must be based on the Docker executor, [see here](https://docs.gitlab.com/runner/executors/docker.html)
79 | > - GitLab runners must run in privileged mode for Docker-in-Docker, [see here](https://docs.gitlab.com/runner/executors/docker.html#use-docker-in-docker-with-privileged-mode)
80 | > - GitLab runners must be tagged with tags `privileged` and either `amd64` or `arm64` depending on their architecture
81 |
82 | GitLab offers free minutes on GitLab-hosted runners executing GitLab CI pipelines on [gitlab.com](https://gitlab.com), [see here](https://docs.gitlab.com/runner/#use-gitlabcom-saas-runners). On self-hosted GitLab instances, you can set up self-hosted runners, [see here](https://docs.gitlab.com/runner/#use-self-managed-runners).
83 |
84 | Note that GitLab is currently only offering Linux runners based on the *amd64* architecture. *docker-ros* can also build multi-arch Docker images solely on the *amd64* platform through emulation, but performance can be improved greatly by deploying [self-hosted runners](https://docs.gitlab.com/runner/#use-self-managed-runners) for the *arm64* platform.
85 |
86 |
87 |
88 |
89 | ## Usage
90 |
91 | *docker-ros* can easily be integrated into any GitHub or GitLab repository containing ROS packages. Example integrations can be found in the following sections. Configuration options can be found [here](#configuration-variables). For local execution, see [*Build images locally*](#build-images-locally).
92 |
93 | GitHub
94 |
95 | *docker-ros* provides a [GitHub action](action.yml) that can simply be added to a job via the [`jobs..steps[*].uses` keyword](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsuses). A quick start for GitHub Actions is found [here](https://docs.github.com/en/actions/quickstart).
96 |
97 |
98 |
99 | GitLab
100 |
101 | *docker-ros* provides a [GitLab CI template](.gitlab-ci/docker-ros.yml) that can simply be included in a [`.gitlab-ci.yml`](https://docs.gitlab.com/ee/ci/yaml/gitlab_ci_yaml.html) file. A quick start for GitLab CI is found [here](https://docs.gitlab.com/ee/ci/quick_start/).
102 |
103 |
104 |
105 | ### Build a minimal image for deployment
106 |
107 | GitHub
108 |
109 | ```yml
110 | on: push
111 | jobs:
112 | docker-ros:
113 | runs-on: ubuntu-latest
114 | steps:
115 | - uses: ika-rwth-aachen/docker-ros@v1.7.0
116 | with:
117 | base-image: rwthika/ros2:humble
118 | command: ros2 run my_pkg my_node
119 | ```
120 |
121 |
122 |
123 | GitLab
124 |
125 | ```yml
126 | include:
127 | - remote: https://raw.githubusercontent.com/ika-rwth-aachen/docker-ros/v1.7.0/.gitlab-ci/docker-ros.yml
128 |
129 | variables:
130 | BASE_IMAGE: rwthika/ros2:humble
131 | COMMAND: ros2 run my_pkg my_node
132 | ```
133 |
134 |
135 |
136 | ### Build development and deployment images
137 |
138 | GitHub
139 |
140 | ```yml
141 | on: push
142 | jobs:
143 | docker-ros:
144 | runs-on: ubuntu-latest
145 | steps:
146 | - uses: ika-rwth-aachen/docker-ros@v1.7.0
147 | with:
148 | base-image: rwthika/ros2:humble
149 | command: ros2 run my_pkg my_node
150 | target: dev,run
151 | ```
152 |
153 |
154 |
155 | GitLab
156 |
157 | ```yml
158 | include:
159 | - remote: https://raw.githubusercontent.com/ika-rwth-aachen/docker-ros/v1.7.0/.gitlab-ci/docker-ros.yml
160 |
161 | variables:
162 | BASE_IMAGE: rwthika/ros2:humble
163 | COMMAND: ros2 run my_pkg my_node
164 | TARGET: dev,run
165 | ```
166 |
167 |
168 |
169 | ### Build multi-arch images
170 |
171 | GitHub
172 |
173 | ```yml
174 | on: push
175 | jobs:
176 | docker-ros:
177 | runs-on: ubuntu-latest
178 | steps:
179 | - uses: ika-rwth-aachen/docker-ros@v1.7.0
180 | with:
181 | base-image: rwthika/ros2:humble
182 | command: ros2 run my_pkg my_node
183 | target: dev,run
184 | platform: amd64,arm64
185 | ```
186 |
187 |
188 |
189 | GitLab
190 |
191 | ```yml
192 | include:
193 | - remote: https://raw.githubusercontent.com/ika-rwth-aachen/docker-ros/v1.7.0/.gitlab-ci/docker-ros.yml
194 |
195 | variables:
196 | BASE_IMAGE: rwthika/ros2:humble
197 | COMMAND: ros2 run my_pkg my_node
198 | TARGET: dev,run
199 | PLATFORM: amd64,arm64
200 | ```
201 |
202 |
203 |
204 | ### Build deployment image with additional industrial_ci check
205 |
206 | GitHub
207 |
208 | ```yml
209 | on: push
210 | jobs:
211 | docker-ros:
212 | runs-on: ubuntu-latest
213 | steps:
214 | - uses: ika-rwth-aachen/docker-ros@v1.7.0
215 | with:
216 | base-image: rwthika/ros2:humble
217 | command: ros2 run my_pkg my_node
218 | enable-industrial-ci: 'true'
219 | ```
220 |
221 |
222 |
223 | GitLab
224 |
225 | ```yml
226 | include:
227 | - remote: https://raw.githubusercontent.com/ika-rwth-aachen/docker-ros/v1.7.0/.gitlab-ci/docker-ros.yml
228 |
229 | variables:
230 | BASE_IMAGE: rwthika/ros2:humble
231 | COMMAND: ros2 run my_pkg my_node
232 | ENABLE_INDUSTRIAL_CI: 'true'
233 | ```
234 |
235 |
236 |
237 | ### Build multi-arch images on arch-specific self-hosted runners in parallel
238 |
239 | GitHub
240 |
241 | ```yml
242 | on: push
243 | jobs:
244 | docker-ros:
245 | strategy:
246 | matrix:
247 | target: [dev, run]
248 | platform: [amd64, arm64]
249 | runs-on: [self-hosted, "${{ matrix.platform }}"]
250 | steps:
251 | - uses: ika-rwth-aachen/docker-ros@v1.7.0
252 | with:
253 | base-image: rwthika/ros2:humble
254 | command: ros2 run my_pkg my_node
255 | target: ${{ matrix.target }}
256 | platform: ${{ matrix.platform }}
257 | enable-singlearch-push: true
258 | # TODO: manifest
259 | ```
260 |
261 |
262 |
263 | GitLab
264 |
265 | ```yml
266 | # TODO
267 | ```
268 |
269 |
270 |
271 | ### Build images locally
272 |
273 | *docker-ros* can build Docker images locally by executing the [`build.sh`](scripts/build.sh) script.
274 |
275 | 1. Clone *docker-ros* as a Git submodule to `docker/docker-ros` in your repository.
276 | ```bash
277 | # ros-repository/
278 | mkdir -p docker
279 | git submodule add https://github.com/ika-rwth-aachen/docker-ros.git docker/docker-ros
280 | ```
281 | 2. Configure the build using the same [environment variables](#configuration-variables) as used for GitLab CI and run [`build.sh`](scripts/build.sh), e.g.:
282 | ```bash
283 | # ros-repository/
284 | BASE_IMAGE="rwthika/ros2:humble" \
285 | COMMAND="ros2 run my_pkg my_node" \
286 | IMAGE="my-image:latest" \
287 | ./docker/docker-ros/scripts/build.sh
288 | ```
289 | > [!NOTE]
290 | > You can alternatively store your environment variable configuration in a `.env` file:
291 | > ```bash
292 | > # .env
293 | > BASE_IMAGE="rwthika/ros2:humble"
294 | > COMMAND="ros2 run my_pkg my_node"
295 | > IMAGE="my-image:latest"
296 | > ```
297 |
298 |
299 | ## Advanced Dependencies
300 |
301 | In order to keep things organized, we recommend to place all *docker-ros* related files in a `docker` folder on top repository level.
302 |
303 | ### Recursion
304 |
305 | Most of the steps listed in [*About*](#about) and below can be toggled between recursive and non-recursive mode, see [*Configuration Variables*](#configuration-variables). This usually means that not only special files on the top-level are considered (e.g., `docker/additional-requirements.txt`), but also files with the same name (e.g., `additional-requirements.txt`) that are found anywhere in the workspace, even after cloning the upstream repositories in step 1.
306 |
307 | ### Package Blacklist
308 |
309 | If your ROS-based repository (or any of your repository's upstream dependencies, see `.repos`) contains ROS packages that should neither be built nor be used for determining dependencies, you can blacklist those in a special `blacklisted-packages.txt` file.
310 |
311 | Create a file `blacklisted-packages.txt` in your `docker` folder (or configure a different `BLACKLISTED_PACKAGES_FILE`) and list any ROS package name to blacklist.
312 |
313 | ### Extra System Dependencies (*apt*)
314 |
315 | If your ROS-based repository requires system dependencies that cannot be installed by specifying their [rosdep](https://docs.ros.org/en/independent/api/rosdep/html/) keys in a `package.xml`, you can use a special `additional-debs.txt` file.
316 |
317 | Create a file `additional-debs.txt` in your `docker` folder (or configure a different `ADDITIONAL_DEBS_FILE`) and list any other dependencies that need to be installed via *apt*.
318 |
319 | ### Extra System Dependencies (*pip*)
320 |
321 | If your ROS-based repository requires Python dependencies that cannot be installed by specifying their [rosdep](https://docs.ros.org/en/independent/api/rosdep/html/) keys in a `package.xml`, you can use a special `additional-pip-requirements.txt` file.
322 |
323 | Create a file `additional-pip-requirements.txt` in your `docker` folder (or configure a different `ADDITIONAL_PIP_FILE`) and list any other Python dependencies that need to be installed via *pip*.
324 |
325 | ### Custom Installation Scripts
326 |
327 | If your ROS-based repository requires to execute any other installation or pre-/post-installation steps, you can specify multiple custom scripts that are executed during the image building process. See the [configuration variable documentation](#configuration-variables) for the following variables.
328 | - `BEFORE_DEPENDENCY_IDENTIFICATION_SCRIPT`
329 | - `BEFORE_DEPENDENCY_INSTALLATION_SCRIPT`
330 | - `AFTER_DEPENDENCY_INSTALLATION_SCRIPT`
331 |
332 | Create those scripts in your `docker` folder (or configure different filepaths via the specified environment variables).
333 |
334 | ### Extra Image Files
335 |
336 | If you need to have additional files present in the deployment image, you can use a special `additional-files` folder. The folder contents will be copied into the container before the custom installation script `custom.sh` is executed.
337 |
338 | Create a folder `additional-files` in your `docker` folder (or configure a different `ADDITIONAL_FILES_DIR`) and place any files or directories in it. The contents will be copied to `/docker-ros/additional-files` in the image.
339 |
340 |
341 | ## Additional Information
342 |
343 | ### User Setup
344 |
345 | Containers of the provided images start with `root` user by default. If the two environment variables `DOCKER_UID` and `DOCKER_GID` are passed, a new user with the corresponding UID/GID is created on the fly. Most importantly, this features allows to mount and edit files of the host user in the container without having to deal with permission issues.
346 |
347 | ```bash
348 | docker run --rm -it -e DOCKER_UID=$(id -u) -e DOCKER_GID=$(id -g) -e DOCKER_USER=$(id -un) rwthika/ros:latest
349 | ```
350 |
351 | The password of the custom user is set to its username (`dockeruser:dockeruser` by default).
352 |
353 | ### Slim Deployment Image
354 |
355 | *docker-ros* integrates the [*slim*](https://github.com/slimtoolkit/slim) toolkit for minifying container images. *slim* is enabled by default and will, in addition to the `run` deployment image, produce an additional `:latest-slim`-tagged minified image. Note that *slim* removes every single thing not needed for executing the default launch command. To balance image size and out-of-the-box functionality, the `/opt/ros` and `/docker-ros/ws/install` directories are preserved by default. The slimming process can be controlled via the `SLIM_BUILD_ARGS` configuration variable.
356 |
357 |
358 | ## Configuration Variables
359 |
360 | > [!NOTE]
361 | > *GitHub Action input* | *GitLab CI environment variable*
362 |
363 | - **`additional-debs-file` | `ADDITIONAL_DEBS_FILE`**
364 | Relative filepath to file containing additional apt deb packages to install
365 | *default:* `docker/additional-debs.txt`
366 | - **`additional-files-dir` | `ADDITIONAL_FILES_DIR`**
367 | Relative path to directory containing additional files to copy into image
368 | *default:* `docker/additional-files`
369 | - **`additional-pip-file` | `ADDITIONAL_PIP_FILE`**
370 | Relative filepath to file containing additional pip packages to install
371 | *default:* `docker/additional-pip-requirements.txt`
372 | - **`after-dependency-installation-script` | `AFTER_DEPENDENCY_INSTALLATION_SCRIPT`**
373 | Relative filepath to script containing commands to run after dependency installation
374 | *default:* `docker/custom.sh`
375 | - **`base-image` | `BASE_IMAGE`**
376 | Base image `name:tag`
377 | *required*
378 | - **`before-dependency-identification-script` | `BEFORE_DEPENDENCY_IDENTIFICATION_SCRIPT`**
379 | Relative filepath to script containing commands to run before dependency identification
380 | *default:* `docker/before_dependency_identification.sh`
381 | - **`before-dependency-installation-script` | `BEFORE_DEPENDENCY_INSTALLATION_SCRIPT`**
382 | Relative filepath to script containing commands to run before dependency installation
383 | *default:* `docker/before_dependency_installation.sh`
384 | - **`blacklisted-packages-file` | `BLACKLISTED_PACKAGES_FILE`**
385 | Relative filepath to file containing blacklisted packages
386 | *default:* `docker/blacklisted-packages.txt`
387 | - **`build-context` | `BUILD_CONTEXT`**
388 | Build context of Docker build process
389 | *default:* `${{ github.workspace }}` | `.`
390 | - **`command` | `COMMAND`**
391 | Launch command of run image
392 | *required if `target=run`*
393 | - **`cmake-args` | `CMAKE_ARGS`**
394 | CMake arguments to pass to `colcon build`
395 | *default:* `"-DCMAKE_BUILD_TYPE=Release"`
396 | - **`dev-image-name` | `DEV_IMAGE_NAME`**
397 | Image name of dev image
398 | *default:* ``
399 | - **`dev-image-tag` | `DEV_IMAGE_TAG`**
400 | Image tag of dev image
401 | *default:* `-dev`
402 | - **`disable-ros-installation` | `DISABLE_ROS_INSTALLATION`**
403 | Disable automatic installation of `ros-$ROS_DISTRO-ros-core` package
404 | *e.g., if ROS is already installed in `base-image` and package is not available for the OS*
405 | *default:* `false`
406 | - **`-` | `DOCKER_ROS_GIT_REF`**
407 | Git ref of *docker-ros* to run in CI
408 | *default:* `main`
409 | - **`enable-checkout` | `-`**
410 | Enable [*checkout*](https://github.com/actions/checkout) action to (re-)download your repository prior to running the pipeline
411 | *default:* `true`
412 | - **`enable-checkout-submodules` | `-`**
413 | Enable submodules for the [*checkout*](https://github.com/actions/checkout) action (`false`|`true`|`recursive`)
414 | *default:* `recursive`
415 | - **`enable-checkout-lfs` | `-`**
416 | Enable [*Git LFS*](https://git-lfs.com/) support for the [*checkout*](https://github.com/actions/checkout) action
417 | *default:* `true`
418 | - **`enable-industrial-ci` | `ENABLE_INDUSTRIAL_CI`**
419 | Enable [*industrial_ci*](https://github.com/ros-industrial/industrial_ci)
420 | *default:* `false`
421 | - **`enable-push-as-latest` | `ENABLE_PUSH_AS_LATEST`**
422 | Push images with tag `latest`/`latest-dev` in addition to the configured image names
423 | *default:* `false`
424 | - **`enable-singlearch-push` | `ENABLE_SINGLEARCH_PUSH`**
425 | Enable push of single arch images with `-amd64`/`-arm64` postfix
426 | *default:* `false`
427 | - **`enable-recursive-additional-debs` | `ENABLE_RECURSIVE_ADDITIONAL_DEBS`**
428 | Enable recursive discovery of files named `additional-debs-file`
429 | *default:* `false`
430 | - **`enable-recursive-additional-pip` | `ENABLE_RECURSIVE_ADDITIONAL_PIP`**
431 | Enable recursive discovery of files named `additional-pip-file`
432 | *default:* `false`
433 | - **`enable-recursive-after-dependency-installation-script` | `ENABLE_RECURSIVE_AFTER_DEPENDENCY_INSTALLATION_SCRIPT`**
434 | Enable recursive discovery of files named `after-dependency-installation-script`
435 | *default:* `false`
436 | - **`enable-recursive-before-dependency-installation-script` | `ENABLE_RECURSIVE_BEFORE_DEPENDENCY_INSTALLATION_SCRIPT`**
437 | Enable recursive discovery of files named `before-dependency-installation-script`
438 | *default:* `false`
439 | - **`enable-recursive-blacklisted-packages` | `ENABLE_RECURSIVE_BLACKLISTED_PACKAGES`**
440 | Enable recursive discovery of files named `blacklisted-packages-file`
441 | *default:* `false`
442 | - **`enable-recursive-vcs-import` | `ENABLE_RECURSIVE_VCS_IMPORT`**
443 | Enable recursive discovery of files named `*.repos`
444 | *default:* `true`
445 | - **`enable-slim` | `ENABLE_SLIM`**
446 | Enable an extra slimmed run image via [slim](https://github.com/slimtoolkit/slim) (only if `run` stage is targeted)
447 | *default:* `true`
448 | - **`git-https-password` | `GIT_HTTPS_PASSWORD`**
449 | Password for cloning private Git repositories via HTTPS
450 | *default:* `${{ github.token }}` | `$CI_JOB_TOKEN`
451 | - **`git-https-server` | `GIT_HTTPS_SERVER`**
452 | Server URL (without protocol) for cloning private Git repositories via HTTPS
453 | *default:* `github.com` | `$CI_SERVER_HOST:$CI_SERVER_PORT`
454 | - **`git-https-user` | `GIT_HTTPS_USER`**
455 | Username for cloning private Git repositories via HTTPS
456 | *default:* `${{ github.actor }}` | `gitlab-ci-token`
457 | - **`git-ssh-known-host-keys` | `GIT_SSH_KNOWN_HOST_KEYS`**
458 | Known SSH host keys for cloning private Git repositories via SSH (may be obtained using `ssh-keyscan`)
459 | - **`git-ssh-private-key` | `GIT_SSH_PRIVATE_KEY`**
460 | SSH private key for cloning private Git repositories via SSH
461 | - **`image-name` | `IMAGE_NAME`**
462 | Image name of run image
463 | *default:* `ghcr.io/${{ github.repository }}` | `$CI_REGISTRY_IMAGE`
464 | - **`image-tag` | `IMAGE_TAG`**
465 | Image tag of run image
466 | *default:* `latest`
467 | - **`platform` | `PLATFORM`**
468 | Target platform architecture (comma-separated list)
469 | *default:* runner architecture | `amd64`
470 | *supported values:* `amd64`, `arm64`
471 | - **`registry` | `REGISTRY`**
472 | Docker registry to push images to
473 | *default:* `ghcr.io` | `$CI_REGISTRY`
474 | - **`registry-password` | `REGISTRY_PASSWORD`**
475 | Docker registry password
476 | *default:* `${{ github.token }}` | `$CI_REGISTRY_PASSWORD`
477 | - **`registry-user` | `REGISTRY_USER`**
478 | Docker registry username
479 | *default:* `${{ github.actor }}` | `$CI_REGISTRY_USER`
480 | - **`rmw-implementation` | `RMW_IMPLEMENTATION`**
481 | ROS 2 middleware implementation
482 | *default:* `rmw_fastrtps_cpp`
483 | *supported values:* `rmw_zenoh_cpp`, `rmw_fastrtps_cpp`, `rmw_cyclonedds_cpp`, `rmw_gurumdds_cpp`, ...
484 | - **`ros-distro` | `ROS_DISTRO`**
485 | ROS Distro
486 | *required if ROS is not installed in `base-image`*
487 | *supported values:* `rolling`, ..., `noetic`, ...
488 | - **`slim-build-args` | `SLIM_BUILD_ARGS`**
489 | [Arguments to `slim build`](https://github.com/slimtoolkit/slim?tab=readme-ov-file#build-command-options) (except for `--target` and `--tag`)
490 | *default:* `--sensor-ipc-mode proxy --continue-after=10 --show-clogs --http-probe=false --include-path /opt/ros --include-path /docker-ros/ws/install`
491 | - **`slim-image-name` | `SLIM_IMAGE_NAME`**
492 | Image name of slim run image
493 | *default:* ``
494 | - **`slim-image-tag` | `SLIM_IMAGE_TAG`**
495 | Image tag of slim run image
496 | *default:* `-slim`
497 | - **`target` | `TARGET`**
498 | Target stage of Dockerfile (comma-separated list)
499 | *default:* `run`
500 | *supported values:* `dev`, `run`
501 | - **`vcs-import-file` | `VCS_IMPORT_FILE`**
502 | Relative filepath to file containing additional repos to install via vcstools (only relevant if `enable-recursive-vcs-import=false`)
503 | *default:* `.repos`
504 |
--------------------------------------------------------------------------------
/action.yml:
--------------------------------------------------------------------------------
1 | name: "docker-ros"
2 | description: "docker-ros automatically builds development and deployment Docker images for your ROS-based repositories."
3 | branding:
4 | color: blue
5 | icon: package
6 |
7 | inputs:
8 | additional-debs-file:
9 | description: "Relative filepath to file containing additional apt deb packages to install"
10 | default: docker/additional-debs.txt
11 | additional-files-dir:
12 | description: "Relative path to directory containing additional files to copy into image"
13 | default: docker/additional-files
14 | additional-pip-file:
15 | description: "Relative filepath to file containing additional pip packages to install"
16 | default: docker/additional-pip-requirements.txt
17 | after-dependency-installation-script:
18 | description: "Relative filepath to script containing commands to run after dependency installation"
19 | default: docker/custom.sh
20 | base-image:
21 | description: "Base image name:tag"
22 | required: true
23 | before-dependency-identification-script:
24 | description: "Relative filepath to script containing commands to run before dependency identification"
25 | default: docker/before_dependency_identification.sh
26 | before-dependency-installation-script:
27 | description: "Relative filepath to script containing commands to run before dependency installation"
28 | default: docker/before_dependency_installation.sh
29 | blacklisted-packages-file:
30 | description: "Relative filepath to file containing blacklisted packages to remove from workspace"
31 | default: docker/blacklisted-packages.txt
32 | build-context:
33 | description: "Build context of Docker build process"
34 | default: ${{ github.workspace }}
35 | command:
36 | description: "Launch command of run image (required if target=run)"
37 | cmake-args:
38 | description: "CMake arguments to pass to `colcon build`"
39 | default: "-DCMAKE_BUILD_TYPE=Release"
40 | dev-image-name:
41 | description: "Image name of dev image"
42 | default: ghcr.io/${{ github.repository }}
43 | dev-image-tag:
44 | description: "Image tag of dev image"
45 | disable-ros-installation:
46 | description: "Disable automatic installation of `ros-$ROS_DISTRO-ros-core` package, e.g., if ROS is already installed in `base-image` and package is not available for the OS"
47 | default: false
48 | enable-checkout:
49 | description: "Enable checkout action to (re-)download your repository prior to running the pipeline"
50 | default: true
51 | enable-checkout-lfs:
52 | description: "Enable Git LFS support for the checkout action"
53 | default: true
54 | enable-checkout-submodules:
55 | description: "Enable submodules for the checkout action (false|true|recursive)"
56 | default: recursive
57 | enable-industrial-ci:
58 | description: "Enable industrial_ci"
59 | default: false
60 | enable-push-as-latest:
61 | description: "Push images with tag `latest`/`latest-dev` in addition to the configured image names"
62 | default: false
63 | enable-recursive-additional-debs:
64 | description: "Enable recursive discovery of files named `additional-debs-file`"
65 | default: false
66 | enable-recursive-additional-pip:
67 | description: "Enable recursive discovery of files named `additional-pip-file`"
68 | default: false
69 | enable-recursive-after-dependency-installation-script:
70 | description: "Enable recursive discovery of files named `after-dependency-installation-script`"
71 | default: false
72 | enable-recursive-before-dependency-installation-script:
73 | description: "Enable recursive discovery of files named `before-dependency-installation-script`"
74 | default: false
75 | enable-recursive-blacklisted-packages:
76 | description: "Enable recursive discovery of files named `blacklisted-packages-file`"
77 | default: false
78 | enable-singlearch-push:
79 | description: "Enable push of single arch images with [-amd64|-arm64] postfix"
80 | default: false
81 | enable-slim:
82 | description: "Enable an extra slimmed run image via slim (only if run stage is targeted)"
83 | default: true
84 | git-https-password:
85 | description: "Password for cloning private Git repositories via HTTPS"
86 | default: ${{ github.token }}
87 | git-https-server:
88 | description: "Server URL (without protocol) for cloning private Git repositories via HTTPS"
89 | default: "github.com"
90 | git-https-user:
91 | description: "Username for cloning private Git repositories via HTTPS"
92 | default: ${{ github.actor }}
93 | git-ssh-known-host-keys:
94 | description: "Known SSH host keys for cloning private Git repositories via SSH (may be obtained using `ssh-keyscan`)"
95 | git-ssh-private-key:
96 | description: "SSH private key for cloning private Git repositories via SSH"
97 | image-name:
98 | description: "Image name of run image"
99 | default: ghcr.io/${{ github.repository }}
100 | image-tag:
101 | description: "Image tag of run image"
102 | default: latest
103 | platform:
104 | description: "Target platform architecture (comma-separated list) [amd64|arm64|...]"
105 | registry:
106 | description: "Docker registry to push images to"
107 | default: ghcr.io
108 | registry-password:
109 | description: "Docker registry password"
110 | default: ${{ github.token }}
111 | registry-user:
112 | description: "Docker registry username"
113 | default: ${{ github.actor }}
114 | ros-distro:
115 | description: "ROS Distro (required if ROS is not installed in `base-image`)"
116 | rmw-implementation:
117 | description: "RMW implementation to use (only for ROS 2)"
118 | default: rmw_fastrtps_cpp
119 | slim-build-args:
120 | description: "Arguments to `slim build` (except for `--target` and `--tag`)"
121 | default: '--sensor-ipc-mode proxy --continue-after=10 --show-clogs --http-probe=false --include-path /opt/ros --include-path /docker-ros/ws/install'
122 | slim-image-name:
123 | description: "Image name of slim run image"
124 | default: ghcr.io/${{ github.repository }}
125 | slim-image-tag:
126 | description: "Image tag of slim run image"
127 | target:
128 | description: "Target stage of Dockerfile (comma-separated list) [dev|run]"
129 | default: run
130 |
131 |
132 | runs:
133 | using: "composite"
134 | steps:
135 |
136 | - name: Checkout repository
137 | uses: actions/checkout@v3
138 | if: ${{ inputs.enable-checkout == 'true' }}
139 | with:
140 | submodules: ${{ inputs.enable-checkout-submodules }}
141 | lfs: ${{ inputs.enable-checkout-lfs }}
142 |
143 | - name: Set up docker-ros
144 | shell: bash
145 | working-directory: ${{ inputs.build-context }}
146 | run: |
147 | if ! [[ -d "docker/docker-ros" ]]; then
148 | mkdir -p docker
149 | cp -r ${GITHUB_ACTION_PATH} docker/docker-ros
150 | fi
151 |
152 | - name: Prepare setup of QEMU
153 | id: prepare-setup-of-qemu
154 | shell: bash
155 | run: echo "RUNNER_ARCH=$(dpkg --print-architecture)" >> $GITHUB_OUTPUT
156 |
157 | - name: Set up QEMU
158 | if: ${{ steps.prepare-setup-of-qemu.outputs.RUNNER_ARCH != inputs.platform }}
159 | uses: docker/setup-qemu-action@v2
160 |
161 | - name: Login to Docker registry
162 | uses: docker/login-action@v3
163 | with:
164 | registry: ${{ inputs.registry }}
165 | username: ${{ inputs.registry-user }}
166 | password: ${{ inputs.registry-password }}
167 |
168 | - name: Set up Docker buildx
169 | uses: docker/setup-buildx-action@v3
170 |
171 | - name: Enforce lower-case image name
172 | id: image-name
173 | uses: ASzc/change-string-case-action@v6
174 | with:
175 | string: ${{ inputs.image-name }}
176 |
177 | - name: Enforce lower-case dev image name
178 | id: dev-image-name
179 | uses: ASzc/change-string-case-action@v6
180 | with:
181 | string: ${{ inputs.dev-image-name }}
182 |
183 | - name: Enforce lower-case slim image name
184 | id: slim-image-name
185 | uses: ASzc/change-string-case-action@v6
186 | with:
187 | string: ${{ inputs.slim-image-name }}
188 |
189 | - name: Build images
190 | id: build-images
191 | shell: bash
192 | working-directory: ${{ inputs.build-context }}
193 | run: docker/docker-ros/scripts/ci.sh
194 | env:
195 | ADDITIONAL_DEBS_FILE: ${{ inputs.additional-debs-file }}
196 | ADDITIONAL_FILES_DIR: ${{ inputs.additional-files-dir }}
197 | ADDITIONAL_PIP_FILE: ${{ inputs.additional-pip-file }}
198 | AFTER_DEPENDENCY_INSTALLATION_SCRIPT: ${{ inputs.after-dependency-installation-script }}
199 | BASE_IMAGE: ${{ inputs.base-image }}
200 | BEFORE_DEPENDENCY_IDENTIFICATION_SCRIPT: ${{ inputs.before-dependency-identification-script }}
201 | BEFORE_DEPENDENCY_INSTALLATION_SCRIPT: ${{ inputs.before-dependency-installation-script }}
202 | BLACKLISTED_PACKAGES_FILE: ${{ inputs.blacklisted-packages-file }}
203 | COMMAND: ${{ inputs.command }}
204 | CMAKE_ARGS: ${{ inputs.cmake-args }}
205 | DEV_IMAGE_NAME: ${{ steps.dev-image-name.outputs.lowercase }}
206 | DEV_IMAGE_TAG: ${{ inputs.dev-image-tag }}
207 | DISABLE_ROS_INSTALLATION: ${{ inputs.disable-ros-installation }}
208 | ENABLE_RECURSIVE_ADDITIONAL_DEBS: ${{ inputs.enable-recursive-additional-debs }}
209 | ENABLE_RECURSIVE_ADDITIONAL_PIP: ${{ inputs.enable-recursive-additional-pip }}
210 | ENABLE_RECURSIVE_AFTER_DEPENDENCY_INSTALLATION_SCRIPT: ${{ inputs.enable-recursive-after-dependency-installation-script }}
211 | ENABLE_RECURSIVE_BEFORE_DEPENDENCY_INSTALLATION_SCRIPT: ${{ inputs.enable-recursive-before-dependency-installation-script }}
212 | ENABLE_RECURSIVE_BLACKLISTED_PACKAGES: ${{ inputs.enable-recursive-blacklisted-packages }}
213 | ENABLE_RECURSIVE_VCS_IMPORT: ${{ inputs.enable-recursive-vcs-import }}
214 | ENABLE_SLIM: ${{ inputs.enable-slim }}
215 | GIT_HTTPS_PASSWORD: ${{ inputs.git-https-password }}
216 | GIT_HTTPS_SERVER: ${{ inputs.git-https-server }}
217 | GIT_HTTPS_USER: ${{ inputs.git-https-user }}
218 | GIT_SSH_KNOWN_HOST_KEYS: ${{ inputs.git-ssh-known-host-keys }}
219 | GIT_SSH_PRIVATE_KEY: ${{ inputs.git-ssh-private-key }}
220 | IMAGE_NAME: ${{ steps.image-name.outputs.lowercase }}
221 | IMAGE_TAG: ${{ inputs.image-tag }}
222 | PLATFORM: ${{ inputs.platform }}
223 | RMW_IMPLEMENTATION: ${{ inputs.rmw-implementation }}
224 | ROS_DISTRO: ${{ inputs.ros-distro }}
225 | SLIM_BUILD_ARGS: ${{ inputs.slim-build-args }}
226 | SLIM_IMAGE_NAME: ${{ steps.slim-image-name.outputs.lowercase }}
227 | SLIM_IMAGE_TAG: ${{ inputs.slim-image-tag }}
228 | TARGET: ${{ inputs.target }}
229 | VCS_IMPORT_FILE: ${{ inputs.vcs-import-file }}
230 |
231 | - name: Set up industrial_ci
232 | if: ${{ inputs.enable-industrial-ci == 'true' }}
233 | shell: bash
234 | run: test -f ${{ inputs.build-context }}/.repos || echo "repositories:" > ${{ inputs.build-context }}/.repos
235 |
236 | - name: Run industrial_ci
237 | if: ${{ inputs.enable-industrial-ci == 'true' }}
238 | uses: ros-industrial/industrial_ci@master
239 | env:
240 | ADDITIONAL_DEBS: git
241 | AFTER_INIT_EMBED: '[[ -n ${{ inputs.git-https-server }} ]] && git config --global url.https://${{ inputs.git-https-user }}:${{ inputs.git-https-password }}@${{ inputs.git-https-server }}.insteadOf https://${{ inputs.git-https-server }}'
242 | DOCKER_IMAGE: ${{ steps.build-images.outputs.INDUSTRIAL_CI_IMAGE }}
243 | DOCKER_PULL: false
244 | DOCKER_RUN_OPTS: -u root:root
245 | SSH_PRIVATE_KEY: ${{ inputs.git-ssh-private-key }}
246 | SSH_SERVER_HOSTKEYS: ${{ inputs.git-ssh-known-host-keys }}
247 | TARGET_WORKSPACE: ${{ inputs.build-context }}
248 | UPSTREAM_WORKSPACE: ${{ inputs.build-context }}/.repos
249 |
250 | - name: Slugify ref name
251 | id: slugify-ref-name
252 | uses: gacts/github-slug@v1
253 | with:
254 | to-slug: ${{ github.ref_name }}
255 |
256 | - name: Push images
257 | shell: bash
258 | working-directory: ${{ inputs.build-context }}
259 | run: docker/docker-ros/scripts/ci.sh
260 | env:
261 | ADDITIONAL_DEBS_FILE: ${{ inputs.additional-debs-file }}
262 | ADDITIONAL_FILES_DIR: ${{ inputs.additional-files-dir }}
263 | ADDITIONAL_PIP_FILE: ${{ inputs.additional-pip-file }}
264 | AFTER_DEPENDENCY_INSTALLATION_SCRIPT: ${{ inputs.after-dependency-installation-script }}
265 | BASE_IMAGE: ${{ inputs.base-image }}
266 | BEFORE_DEPENDENCY_IDENTIFICATION_SCRIPT: ${{ inputs.before-dependency-identification-script }}
267 | BEFORE_DEPENDENCY_INSTALLATION_SCRIPT: ${{ inputs.before-dependency-installation-script }}
268 | BLACKLISTED_PACKAGES_FILE: ${{ inputs.blacklisted-packages-file }}
269 | COMMAND: ${{ inputs.command }}
270 | CMAKE_ARGS: ${{ inputs.cmake-args }}
271 | DEV_IMAGE_NAME: ${{ steps.dev-image-name.outputs.lowercase }}
272 | DEV_IMAGE_TAG: ${{ inputs.dev-image-tag }}
273 | DISABLE_ROS_INSTALLATION: ${{ inputs.disable-ros-installation }}
274 | ENABLE_RECURSIVE_ADDITIONAL_DEBS: ${{ inputs.enable-recursive-additional-debs }}
275 | ENABLE_RECURSIVE_ADDITIONAL_PIP: ${{ inputs.enable-recursive-additional-pip }}
276 | ENABLE_RECURSIVE_AFTER_DEPENDENCY_INSTALLATION_SCRIPT: ${{ inputs.enable-recursive-after-dependency-installation-script }}
277 | ENABLE_RECURSIVE_BEFORE_DEPENDENCY_INSTALLATION_SCRIPT: ${{ inputs.enable-recursive-before-dependency-installation-script }}
278 | ENABLE_RECURSIVE_BLACKLISTED_PACKAGES: ${{ inputs.enable-recursive-blacklisted-packages }}
279 | ENABLE_RECURSIVE_VCS_IMPORT: ${{ inputs.enable-recursive-vcs-import }}
280 | ENABLE_SINGLEARCH_PUSH: ${{ inputs.enable-singlearch-push }}
281 | ENABLE_SLIM: ${{ inputs.enable-slim }}
282 | GIT_HTTPS_PASSWORD: ${{ inputs.git-https-password }}
283 | GIT_HTTPS_SERVER: ${{ inputs.git-https-server }}
284 | GIT_HTTPS_USER: ${{ inputs.git-https-user }}
285 | GIT_SSH_KNOWN_HOST_KEYS: ${{ inputs.git-ssh-known-host-keys }}
286 | GIT_SSH_PRIVATE_KEY: ${{ inputs.git-ssh-private-key }}
287 | IMAGE_NAME: ${{ steps.image-name.outputs.lowercase }}
288 | IMAGE_TAG: ${{ inputs.image-tag }}
289 | PLATFORM: ${{ inputs.platform }}
290 | RMW_IMPLEMENTATION: ${{ inputs.rmw-implementation }}
291 | ROS_DISTRO: ${{ inputs.ros-distro }}
292 | SLIM_BUILD_ARGS: ${{ inputs.slim-build-args }}
293 | SLIM_IMAGE_NAME: ${{ steps.slim-image-name.outputs.lowercase }}
294 | SLIM_IMAGE_TAG: ${{ inputs.slim-image-tag }}
295 | TARGET: ${{ inputs.target }}
296 | VCS_IMPORT_FILE: ${{ inputs.vcs-import-file }}
297 | _ENABLE_IMAGE_PUSH: true
298 | _IMAGE_POSTFIX: ${{ github.ref != format('refs/heads/{0}', github.event.repository.default_branch) && format('_{0}_ci', steps.slugify-ref-name.outputs.slug) || '' }}
299 |
300 | - name: Push images (as latest)
301 | if: ${{ inputs.enable-push-as-latest == 'true' }}
302 | shell: bash
303 | working-directory: ${{ inputs.build-context }}
304 | run: docker/docker-ros/scripts/ci.sh
305 | env:
306 | ADDITIONAL_DEBS_FILE: ${{ inputs.additional-debs-file }}
307 | ADDITIONAL_FILES_DIR: ${{ inputs.additional-files-dir }}
308 | ADDITIONAL_PIP_FILE: ${{ inputs.additional-pip-file }}
309 | AFTER_DEPENDENCY_INSTALLATION_SCRIPT: ${{ inputs.after-dependency-installation-script }}
310 | BASE_IMAGE: ${{ inputs.base-image }}
311 | BEFORE_DEPENDENCY_IDENTIFICATION_SCRIPT: ${{ inputs.before-dependency-identification-script }}
312 | BEFORE_DEPENDENCY_INSTALLATION_SCRIPT: ${{ inputs.before-dependency-installation-script }}
313 | BLACKLISTED_PACKAGES_FILE: ${{ inputs.blacklisted-packages-file }}
314 | COMMAND: ${{ inputs.command }}
315 | CMAKE_ARGS: ${{ inputs.cmake-args }}
316 | DEV_IMAGE_NAME: ${{ steps.dev-image-name.outputs.lowercase }}
317 | DEV_IMAGE_TAG: latest-dev
318 | DISABLE_ROS_INSTALLATION: ${{ inputs.disable-ros-installation }}
319 | ENABLE_RECURSIVE_ADDITIONAL_DEBS: ${{ inputs.enable-recursive-additional-debs }}
320 | ENABLE_RECURSIVE_ADDITIONAL_PIP: ${{ inputs.enable-recursive-additional-pip }}
321 | ENABLE_RECURSIVE_AFTER_DEPENDENCY_INSTALLATION_SCRIPT: ${{ inputs.enable-recursive-after-dependency-installation-script }}
322 | ENABLE_RECURSIVE_BEFORE_DEPENDENCY_INSTALLATION_SCRIPT: ${{ inputs.enable-recursive-before-dependency-installation-script }}
323 | ENABLE_RECURSIVE_BLACKLISTED_PACKAGES: ${{ inputs.enable-recursive-blacklisted-packages }}
324 | ENABLE_RECURSIVE_VCS_IMPORT: ${{ inputs.enable-recursive-vcs-import }}
325 | ENABLE_SINGLEARCH_PUSH: ${{ inputs.enable-singlearch-push }}
326 | ENABLE_SLIM: ${{ inputs.enable-slim }}
327 | GIT_HTTPS_PASSWORD: ${{ inputs.git-https-password }}
328 | GIT_HTTPS_SERVER: ${{ inputs.git-https-server }}
329 | GIT_HTTPS_USER: ${{ inputs.git-https-user }}
330 | GIT_SSH_KNOWN_HOST_KEYS: ${{ inputs.git-ssh-known-host-keys }}
331 | GIT_SSH_PRIVATE_KEY: ${{ inputs.git-ssh-private-key }}
332 | IMAGE_NAME: ${{ steps.image-name.outputs.lowercase }}
333 | IMAGE_TAG: latest
334 | PLATFORM: ${{ inputs.platform }}
335 | RMW_IMPLEMENTATION: ${{ inputs.rmw-implementation }}
336 | ROS_DISTRO: ${{ inputs.ros-distro }}
337 | SLIM_BUILD_ARGS: ${{ inputs.slim-build-args }}
338 | SLIM_IMAGE_NAME: ${{ steps.slim-image-name.outputs.lowercase }}
339 | SLIM_IMAGE_TAG: latest-slim
340 | TARGET: ${{ inputs.target }}
341 | VCS_IMPORT_FILE: ${{ inputs.vcs-import-file }}
342 | _ENABLE_IMAGE_PUSH: true
343 | _IMAGE_POSTFIX: ${{ github.ref != format('refs/heads/{0}', github.event.repository.default_branch) && format('_{0}_ci', steps.slugify-ref-name.outputs.slug) || '' }}
344 |
--------------------------------------------------------------------------------
/assets/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ika-rwth-aachen/docker-ros/dd00b8cf2c001afa1d2ba87cc5250dd577733080/assets/banner.png
--------------------------------------------------------------------------------
/assets/logo-midjourney.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ika-rwth-aachen/docker-ros/dd00b8cf2c001afa1d2ba87cc5250dd577733080/assets/logo-midjourney.png
--------------------------------------------------------------------------------
/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ika-rwth-aachen/docker-ros/dd00b8cf2c001afa1d2ba87cc5250dd577733080/assets/logo.png
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG BASE_IMAGE
2 |
3 | ############ dependencies ######################################################
4 | FROM ${BASE_IMAGE} AS dependencies
5 | ARG TARGETARCH
6 | ENV TARGETARCH=${TARGETARCH}
7 |
8 | USER root
9 | SHELL ["/bin/bash", "-c"]
10 | ARG DEBIAN_FRONTEND=noninteractive
11 |
12 | # create workspace folder structure
13 | ENV WORKSPACE=/docker-ros/ws
14 | WORKDIR $WORKSPACE
15 | RUN mkdir -p src/target src/upstream src/downstream
16 |
17 | # setup keys and sources.list for ROS packages
18 | ARG ROS_DISTRO
19 | ENV ROS_DISTRO=${ROS_DISTRO}
20 | RUN test -n "$ROS_DISTRO" || (echo "missing build-arg: ROS_DISTRO" && false)
21 | RUN apt-get update && \
22 | apt-get install -y curl gnupg && \
23 | if ! [[ -n $(find /etc/apt/sources.list.d/ -name "*ros2*") ]]; then \
24 | ROS_APT_SOURCE_VERSION=$(curl -s https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | grep -F "tag_name" | awk -F\" '{print $4}') && \
25 | curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo $UBUNTU_CODENAME)_all.deb" && \
26 | apt-get install -y /tmp/ros2-apt-source.deb ; \
27 | fi && \
28 | rm -rf /var/lib/apt/lists/*
29 |
30 | # install ROS bootstrapping tools
31 | ARG DISABLE_ROS_INSTALLATION="false"
32 | RUN apt-get update && \
33 | apt-get install -y \
34 | git \
35 | python3-rosdep \
36 | python3-vcstool && \
37 | if [[ "$DISABLE_ROS_INSTALLATION" != "true" ]]; then \
38 | apt-get install -y ros-${ROS_DISTRO}-ros-core; \
39 | fi && \
40 | rm -rf /var/lib/apt/lists/*
41 |
42 | # copy contents of repository
43 | COPY . src/target
44 |
45 | # run custom script before dependency identification
46 | ARG BEFORE_DEPENDENCY_IDENTIFICATION_SCRIPT="docker/before_dependency_identification.sh"
47 | RUN if [[ -f src/target/${BEFORE_DEPENDENCY_IDENTIFICATION_SCRIPT} ]]; then \
48 | chmod +x src/target/${BEFORE_DEPENDENCY_IDENTIFICATION_SCRIPT} && \
49 | src/target/${BEFORE_DEPENDENCY_IDENTIFICATION_SCRIPT} ; \
50 | fi
51 |
52 | # clone .repos upstream dependencies
53 | ARG GIT_HTTPS_SERVER=
54 | ARG GIT_HTTPS_USER=
55 | ARG GIT_HTTPS_PASSWORD=
56 | RUN if [[ -n ${GIT_HTTPS_SERVER} ]]; then \
57 | git config --global url.https://${GIT_HTTPS_USER}:${GIT_HTTPS_PASSWORD}@${GIT_HTTPS_SERVER}.insteadOf https://${GIT_HTTPS_SERVER} ; \
58 | fi
59 | ARG GIT_SSH_PRIVATE_KEY=
60 | ARG GIT_SSH_KNOWN_HOST_KEYS=
61 | RUN if [[ -n ${GIT_SSH_PRIVATE_KEY} ]]; then \
62 | echo -e ${GIT_SSH_PRIVATE_KEY} > /tmp/ssh_id && \
63 | chmod 400 /tmp/ssh_id && \
64 | git config --global core.sshCommand "ssh -i /.ssh_key" && \
65 | mkdir -p ~/.ssh && \
66 | touch ~/.ssh/known_hosts && \
67 | echo -e ${GIT_SSH_KNOWN_HOST_KEYS} >> ~/.ssh/known_hosts ; \
68 | fi
69 | COPY docker/docker-ros/docker/recursive_vcs_import.py /usr/local/bin
70 | RUN apt-get update && \
71 | apt-get install -y python-is-python3 && \
72 | rm -rf /var/lib/apt/lists/*
73 |
74 | ARG VCS_IMPORT_FILE=".repos"
75 | ARG ENABLE_RECURSIVE_VCS_IMPORT="true"
76 | RUN if [[ $ENABLE_RECURSIVE_VCS_IMPORT == 'true' ]]; then \
77 | /usr/local/bin/recursive_vcs_import.py src src/upstream ; \
78 | elif [[ -f src/target/${VCS_IMPORT_FILE} ]]; then \
79 | vcs import --recursive src/upstream < src/target/${VCS_IMPORT_FILE} ; \
80 | fi
81 |
82 | # remove blacklisted packages from workspace
83 | ARG BLACKLISTED_PACKAGES_FILE="docker/blacklisted-packages.txt"
84 | ARG ENABLE_RECURSIVE_BLACKLISTED_PACKAGES="false"
85 | RUN echo "colcon list -p --base-paths src/ --packages-select \\" >> $WORKSPACE/.remove-packages.sh && \
86 | if [[ $ENABLE_RECURSIVE_BLACKLISTED_PACKAGES == 'true' ]]; then \
87 | find . -type f -name $(basename ${BLACKLISTED_PACKAGES_FILE}) -exec sed '$a\' {} \; | awk '{print " " $0 " \\"}' >> $WORKSPACE/.remove-packages.sh ; \
88 | elif [[ -f src/target/${BLACKLISTED_PACKAGES_FILE} ]]; then \
89 | cat src/target/${BLACKLISTED_PACKAGES_FILE} | awk '{ gsub(/#.*/, ""); gsub(/^[ \t]+|[ \t]+$/, ""); if (NF) print " " $0 " \\" }' >> $WORKSPACE/.remove-packages.sh ; \
90 | fi && \
91 | echo ";" >> $WORKSPACE/.remove-packages.sh && \
92 | chmod +x $WORKSPACE/.remove-packages.sh && \
93 | $WORKSPACE/.remove-packages.sh 2> /dev/null | xargs rm -rf
94 |
95 | # create install script to run in dependencies-install stage
96 | RUN echo "set -e" > $WORKSPACE/.install-dependencies.sh && \
97 | chmod +x $WORKSPACE/.install-dependencies.sh
98 |
99 | # add custom script to run before dependency installation to install script
100 | ARG BEFORE_DEPENDENCY_INSTALLATION_SCRIPT="docker/before_dependency_installation.sh"
101 | ARG ENABLE_RECURSIVE_BEFORE_DEPENDENCY_INSTALLATION_SCRIPT="false"
102 | RUN if [[ $ENABLE_RECURSIVE_BEFORE_DEPENDENCY_INSTALLATION_SCRIPT == 'true' ]]; then \
103 | find . -type f -name $(basename ${BEFORE_DEPENDENCY_INSTALLATION_SCRIPT}) -exec sed '$a\' {} >> $WORKSPACE/.install-dependencies.sh \; ; \
104 | elif [[ -f src/target/${BEFORE_DEPENDENCY_INSTALLATION_SCRIPT} ]]; then \
105 | cat src/target/${BEFORE_DEPENDENCY_INSTALLATION_SCRIPT} >> $WORKSPACE/.install-dependencies.sh && \
106 | echo "" >> $WORKSPACE/.install-dependencies.sh ; \
107 | fi
108 |
109 | # add list of rosdep dependencies to install script
110 | RUN source /opt/ros/$ROS_DISTRO/setup.bash && \
111 | apt-get update && \
112 | (rosdep init || true) && \
113 | rosdep update --rosdistro ${ROS_DISTRO} && \
114 | export OS="ubuntu:$(lsb_release -c | awk '{print $2}')" && \
115 | if [[ "$ROS_DISTRO" = "rolling" && "$OS" = "ubuntu:focal" ]]; then export OS="ubuntu:jammy"; fi && \
116 | set -o pipefail && \
117 | PIP_BREAK_SYSTEM_PACKAGES=1 ROS_PACKAGE_PATH=$(pwd):$ROS_PACKAGE_PATH rosdep install --os $OS -y --simulate --from-paths src --ignore-src \
118 | | sed -E "s/'apt-get install -y ([^']+)' \(alternative 1\/1\)/apt-get install -y \1/" \
119 | | tee -a $WORKSPACE/.install-dependencies.sh && \
120 | rm -rf /var/lib/apt/lists/*
121 |
122 | # add additionally specified apt dependencies to install script
123 | ARG ADDITIONAL_DEBS_FILE="docker/additional-debs.txt"
124 | ARG ENABLE_RECURSIVE_ADDITIONAL_DEBS="false"
125 | RUN echo "apt-get install -y \\" >> $WORKSPACE/.install-dependencies.sh && \
126 | set -o pipefail && \
127 | if [[ $ENABLE_RECURSIVE_ADDITIONAL_DEBS == 'true' ]]; then \
128 | find . -type f -name $(basename ${ADDITIONAL_DEBS_FILE}) -exec sed '$a\' {} \; | awk '{print " " $0 " \\"}' >> $WORKSPACE/.install-dependencies.sh ; \
129 | elif [[ -f src/target/${ADDITIONAL_DEBS_FILE} ]]; then \
130 | cat src/target/${ADDITIONAL_DEBS_FILE} | awk '{ gsub(/#.*/, ""); gsub(/^[ \t]+|[ \t]+$/, ""); if (NF) print " " $0 " \\" }' >> $WORKSPACE/.install-dependencies.sh ; \
131 | fi && \
132 | echo ";" >> $WORKSPACE/.install-dependencies.sh
133 |
134 | # add additionally specified pip dependencies to install script
135 | ARG ADDITIONAL_PIP_FILE="docker/additional-pip-requirements.txt"
136 | ARG ENABLE_RECURSIVE_ADDITIONAL_PIP="false"
137 | RUN echo "pip install pip \\" >> $WORKSPACE/.install-dependencies.sh && \
138 | set -o pipefail && \
139 | if [[ $ENABLE_RECURSIVE_ADDITIONAL_PIP == 'true' ]]; then \
140 | find . -type f -name $(basename ${ADDITIONAL_PIP_FILE}) -exec sed '$a\' {} \; | awk '{print " " $0 " \\"}' >> $WORKSPACE/.install-dependencies.sh ; \
141 | elif [[ -f src/target/${ADDITIONAL_PIP_FILE} ]]; then \
142 | cat src/target/${ADDITIONAL_PIP_FILE} | awk '{ gsub(/#.*/, ""); gsub(/^[ \t]+|[ \t]+$/, ""); if (NF) print " " $0 " \\" }' >> $WORKSPACE/.install-dependencies.sh ; \
143 | fi && \
144 | echo ";" >> $WORKSPACE/.install-dependencies.sh
145 |
146 | # add custom script to run after dependency installation to install script
147 | ARG AFTER_DEPENDENCY_INSTALLATION_SCRIPT="docker/custom.sh"
148 | ARG ENABLE_RECURSIVE_AFTER_DEPENDENCY_INSTALLATION_SCRIPT="false"
149 | RUN if [[ $ENABLE_RECURSIVE_AFTER_DEPENDENCY_INSTALLATION_SCRIPT == 'true' ]]; then \
150 | find . -type f -name $(basename ${AFTER_DEPENDENCY_INSTALLATION_SCRIPT}) -exec sed '$a\' {} >> $WORKSPACE/.install-dependencies.sh \; ; \
151 | elif [[ -f src/target/${AFTER_DEPENDENCY_INSTALLATION_SCRIPT} ]]; then \
152 | cat src/target/${AFTER_DEPENDENCY_INSTALLATION_SCRIPT} >> $WORKSPACE/.install-dependencies.sh ; \
153 | fi
154 |
155 | # log install script
156 | RUN cat $WORKSPACE/.install-dependencies.sh
157 |
158 | # remove additional-files folder from copied repository content to avoid redundancies
159 | ARG ADDITIONAL_FILES_DIR="docker/additional-files"
160 | RUN rm -rf src/target/${ADDITIONAL_FILES_DIR}
161 |
162 | ############ dependencies-install ##############################################
163 | FROM ${BASE_IMAGE} AS dependencies-install
164 | ARG TARGETARCH
165 | ARG GIT_HTTPS_URL
166 | ARG GIT_HTTPS_USER
167 | ARG GIT_HTTPS_PASSWORD
168 | ENV TARGETARCH=${TARGETARCH}
169 | ENV DOCKER_ROS=1
170 |
171 | USER root
172 | SHELL ["/bin/bash", "-c"]
173 | ARG DEBIAN_FRONTEND=noninteractive
174 |
175 | # user setup
176 | ENV DOCKER_USER=dockeruser
177 | ENV DOCKER_UID=
178 | ENV DOCKER_GID=
179 |
180 | # ROS setup
181 | ENV RCUTILS_COLORIZED_OUTPUT=1
182 | ENV WORKSPACE=/docker-ros/ws
183 | ENV COLCON_HOME=$WORKSPACE/.colcon
184 | WORKDIR $WORKSPACE
185 |
186 | # setup keys and sources.list for ROS packages
187 | ARG ROS_DISTRO
188 | ENV ROS_DISTRO=${ROS_DISTRO}
189 | RUN test -n "$ROS_DISTRO" || (echo "missing build-arg: ROS_DISTRO" && false)
190 | RUN apt-get update && \
191 | apt-get install -y curl gnupg && \
192 | if ! [[ -n $(find /etc/apt/sources.list.d/ -name "*ros2*") ]]; then \
193 | ROS_APT_SOURCE_VERSION=$(curl -s https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | grep -F "tag_name" | awk -F\" '{print $4}') && \
194 | curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo $UBUNTU_CODENAME)_all.deb" && \
195 | apt-get install -y /tmp/ros2-apt-source.deb ; \
196 | fi && \
197 | rm -rf /var/lib/apt/lists/*
198 |
199 | # copy contents of files-folder into image
200 | ARG ADDITIONAL_FILES_DIR="docker/additional-files"
201 | ADD ${ADDITIONAL_FILES_DIR}* /docker-ros/additional-files/
202 |
203 | # install essential build tools and dependencies for install script
204 | ARG DISABLE_ROS_INSTALLATION="false"
205 | RUN apt-get update && \
206 | apt-get install -y \
207 | build-essential \
208 | gosu \
209 | python-is-python3 \
210 | python3-pip && \
211 | if [[ "$DISABLE_ROS_INSTALLATION" != "true" ]]; then \
212 | apt-get install -y ros-${ROS_DISTRO}-ros-core; \
213 | fi && \
214 | rm -rf /var/lib/apt/lists/*
215 |
216 | # configure pip
217 | RUN python -m pip config --global set global.break-system-packages true
218 |
219 | # copy install script from dependencies stage
220 | COPY --from=dependencies $WORKSPACE/.install-dependencies.sh $WORKSPACE/.install-dependencies.sh
221 |
222 | # install dependencies
223 | RUN apt-get update && \
224 | $WORKSPACE/.install-dependencies.sh && \
225 | rm -rf /var/lib/apt/lists/*
226 |
227 | # install ROS CLI tools
228 | RUN source /opt/ros/$ROS_DISTRO/setup.bash && \
229 | apt-get update && \
230 | apt-get install -y python3-colcon-common-extensions && \
231 | rm -rf /var/lib/apt/lists/*
232 |
233 | # source ROS
234 | RUN echo "source /opt/ros/$ROS_DISTRO/setup.bash" >> ~/.bashrc
235 |
236 | # install desired ROS 2 middleware
237 | ARG RMW_IMPLEMENTATION
238 | ENV RMW_IMPLEMENTATION=${RMW_IMPLEMENTATION:-rmw_fastrtps_cpp}
239 | RUN source /opt/ros/$ROS_DISTRO/setup.bash && \
240 | if [[ "$DISABLE_ROS_INSTALLATION" != "true" ]]; then \
241 | apt-get update && \
242 | RMW_PACKAGE=ros-$ROS_DISTRO-$(echo $RMW_IMPLEMENTATION | tr '_' '-') && \
243 | apt-get install -y $RMW_PACKAGE && \
244 | rm -rf /var/lib/apt/lists/* ; \
245 | fi
246 |
247 | # move existing install space from base image to make room for new one
248 | RUN if [[ -d $WORKSPACE/install ]]; then \
249 | mkdir -p /opt/ws_base_image && \
250 | mv $WORKSPACE/install /opt/ws_base_image/ && \
251 | echo "source /opt/ws_base_image/install/setup.bash" >> ~/.bashrc ; \
252 | fi
253 |
254 | # set entrypoint
255 | ENV TINI_VERSION=v0.19.0
256 | ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-${TARGETARCH} /tini
257 | RUN chmod +x /tini
258 | COPY docker/docker-ros/docker/entrypoint.sh /
259 | ENTRYPOINT ["/tini", "--", "/entrypoint.sh"]
260 |
261 | ############ dev ###############################################################
262 | FROM dependencies-install AS dev
263 |
264 | # copy contents of repository from dependencies stage
265 | COPY --from=dependencies $WORKSPACE/src $WORKSPACE/src
266 |
267 | CMD ["bash"]
268 |
269 | ############ build #############################################################
270 | FROM dev AS build
271 |
272 | ARG CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Release"
273 | # build ROS workspace
274 | RUN source /opt/ros/${ROS_DISTRO}/setup.bash && \
275 | [[ -f /opt/ws_base_image/install/setup.bash ]] && source /opt/ws_base_image/install/setup.bash ; \
276 | colcon build --cmake-args ${CMAKE_ARGS}
277 |
278 | ############ run ###############################################################
279 | FROM dependencies-install AS run
280 |
281 | # remove source code, if still existing from custom.sh modifications
282 | RUN rm -rf $WORKSPACE/src
283 |
284 | # copy ROS install space from build stage
285 | COPY --from=build $WORKSPACE/install install
286 | RUN ldconfig
287 | RUN echo "[[ -f $WORKSPACE/install/setup.bash ]] && source $WORKSPACE/install/setup.bash" >> ~/.bashrc
288 |
289 | # setup command
290 | ARG COMMAND
291 | ENV DEFAULT_CMD=${COMMAND}
292 | CMD bash -c "${DEFAULT_CMD}"
293 |
--------------------------------------------------------------------------------
/docker/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | # source ROS workspace
5 | source /opt/ros/$ROS_DISTRO/setup.bash
6 | [[ -f /opt/ws_base_image/install/setup.bash ]] && source /opt/ws_base_image/install/setup.bash
7 | [[ -f $WORKSPACE/install/setup.bash ]] && source $WORKSPACE/install/setup.bash
8 |
9 | # exec as dockeruser with configured UID/GID
10 | if [[ $DOCKER_UID && $DOCKER_GID ]]; then
11 | if ! getent group $DOCKER_GID > /dev/null 2>&1; then
12 | groupadd -g $DOCKER_GID $DOCKER_USER
13 | else
14 | echo -e "\e[33mWARNING | Cannot create group '$DOCKER_USER' with GID $DOCKER_GID, another group '$(getent group $DOCKER_GID | cut -d: -f1)' with same GID is already existing\e[0m"
15 | fi
16 | if ! getent passwd $DOCKER_UID > /dev/null 2>&1; then
17 | useradd -s /bin/bash \
18 | -u $DOCKER_UID \
19 | -g $DOCKER_GID \
20 | --create-home \
21 | --home-dir /home/$DOCKER_USER \
22 | --groups sudo,video \
23 | --password "$(openssl passwd -1 $DOCKER_USER)" \
24 | $DOCKER_USER && \
25 | touch /home/$DOCKER_USER/.sudo_as_admin_successful
26 | cp /root/.bashrc /home/$DOCKER_USER
27 | ln -s $WORKSPACE /home/$DOCKER_USER/ws
28 | chown -h $DOCKER_UID:$DOCKER_GID $WORKSPACE /home/$DOCKER_USER/ws /home/$DOCKER_USER/.sudo_as_admin_successful
29 | if [[ -d $WORKSPACE/src ]]; then
30 | chown -R $DOCKER_UID:$DOCKER_GID $WORKSPACE/src
31 | fi
32 | else
33 | echo -e "\e[33mWARNING | Cannot create user '$DOCKER_USER' with UID $DOCKER_UID, another user '$(getent passwd $DOCKER_UID | cut -d: -f1)' with same UID is already existing\e[0m"
34 | fi
35 | [[ $(pwd) == "$WORKSPACE" ]] && cd /home/$DOCKER_USER/ws
36 | exec gosu $DOCKER_USER "$@"
37 | else
38 | exec "$@"
39 | fi
40 |
--------------------------------------------------------------------------------
/docker/recursive_vcs_import.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import pathlib
4 | import subprocess
5 | import sys
6 | from typing import List, Optional
7 |
8 |
9 | def find_dot_repos(search_path: str, clone_path: Optional[str] = None) -> List[pathlib.Path]:
10 |
11 | repos = list(pathlib.Path(search_path).glob("**/*.repos"))
12 | if clone_path is not None:
13 | repos.extend(list(pathlib.Path(clone_path).glob("**/*.repos")))
14 | return repos
15 |
16 | def is_file_empty(filename):
17 | """Check if file is empty, allowing blank spaces."""
18 | try:
19 | with open(filename, 'r') as file:
20 | content = file.read().strip()
21 | return len(content) == 0
22 | except Exception as e:
23 | print(e)
24 | # Exception gets raised later
25 | return False
26 |
27 | def main():
28 |
29 | search_path = sys.argv[1] if len(sys.argv) > 1 else "."
30 | clone_path = sys.argv[2] if len(sys.argv) > 2 else "."
31 | cloned_repos = []
32 |
33 | while True:
34 |
35 | found_repos = find_dot_repos(search_path, clone_path)
36 | remaining_repos = set(found_repos) - set(cloned_repos)
37 |
38 | if not remaining_repos:
39 | break
40 |
41 | next_repo = list(remaining_repos)[0]
42 | with open(str(next_repo), "r") as f:
43 | proc = subprocess.run(["vcs", "import", clone_path, "--recursive"], stdin=f)
44 | if proc.returncode != 0:
45 | # Ignore empty .repos files
46 | if not is_file_empty(next_repo):
47 | raise RuntimeError("vcs import failed")
48 | else:
49 | print(f"Ignored empty .repos file: {next_repo}")
50 |
51 | cloned_repos.append(next_repo)
52 |
53 | print(" ".join([str(repo) for repo in set(found_repos)]))
54 |
55 |
56 | if __name__ == "__main__":
57 | main()
58 |
--------------------------------------------------------------------------------
/scripts/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | ROOT_PATH="$(realpath "$(cd -P "$(dirname "${0}")" && pwd)"/..)"
6 | source "${ROOT_PATH}/scripts/utils.sh"
7 |
8 |
9 | build_image() {
10 | echo "Building stage '${TARGET}' for platform '${PLATFORM}' as '${IMAGE}' ..."
11 |
12 | DOCKER_ARGS=(
13 | --file "$(dirname "$0")/../docker/Dockerfile"
14 | --target "${TARGET}"
15 | --platform "${PLATFORM}"
16 | --tag "${IMAGE}"
17 | )
18 |
19 | if [[ "${_ENABLE_IMAGE_PUSH}" == "true" ]]; then
20 | DOCKER_ARGS+=( "--push" )
21 | else
22 | DOCKER_ARGS+=( "--load" )
23 | fi
24 |
25 | # required build args
26 | DOCKER_ARGS+=( --build-arg "BASE_IMAGE=${BASE_IMAGE}" )
27 | DOCKER_ARGS+=( --build-arg "COMMAND=${COMMAND}" )
28 |
29 | # function to add "--build-arg NAME=VALUE" only if VALUE is non-empty
30 | add_arg_if_set() {
31 | local var_name="$1"
32 | local var_value="${!var_name}"
33 | if [[ -n "${var_value}" ]]; then
34 | DOCKER_ARGS+=( "--build-arg" "${var_name}=${var_value}" )
35 | fi
36 | }
37 |
38 | # optional build args
39 | add_arg_if_set "ADDITIONAL_DEBS_FILE"
40 | add_arg_if_set "ADDITIONAL_FILES_DIR"
41 | add_arg_if_set "ADDITIONAL_PIP_FILE"
42 | add_arg_if_set "AFTER_DEPENDENCY_INSTALLATION_SCRIPT"
43 | add_arg_if_set "BEFORE_DEPENDENCY_IDENTIFICATION_SCRIPT"
44 | add_arg_if_set "BEFORE_DEPENDENCY_INSTALLATION_SCRIPT"
45 | add_arg_if_set "BLACKLISTED_PACKAGES_FILE"
46 | add_arg_if_set "CMAKE_ARGS"
47 | add_arg_if_set "CUSTOM_SCRIPT_FILE"
48 | add_arg_if_set "DISABLE_ROS_INSTALLATION"
49 | add_arg_if_set "ENABLE_RECURSIVE_ADDITIONAL_DEBS"
50 | add_arg_if_set "ENABLE_RECURSIVE_ADDITIONAL_PIP"
51 | add_arg_if_set "ENABLE_RECURSIVE_AFTER_DEPENDENCY_INSTALLATION_SCRIPT"
52 | add_arg_if_set "ENABLE_RECURSIVE_BEFORE_DEPENDENCY_INSTALLATION_SCRIPT"
53 | add_arg_if_set "ENABLE_RECURSIVE_BLACKLISTED_PACKAGES"
54 | add_arg_if_set "ENABLE_RECURSIVE_VCS_IMPORT"
55 | add_arg_if_set "GIT_HTTPS_PASSWORD"
56 | add_arg_if_set "GIT_HTTPS_SERVER"
57 | add_arg_if_set "GIT_HTTPS_USER"
58 | add_arg_if_set "GIT_SSH_KNOWN_HOST_KEYS"
59 | add_arg_if_set "GIT_SSH_PRIVATE_KEY"
60 | add_arg_if_set "RMW_IMPLEMENTATION"
61 | add_arg_if_set "ROS_DISTRO"
62 | add_arg_if_set "VCS_IMPORT_FILE"
63 |
64 | DOCKER_ARGS+=( "." )
65 |
66 | docker buildx build "${DOCKER_ARGS[@]}"
67 | echo "Successfully built stage '${TARGET}' for platform '${PLATFORM}' as '${IMAGE}'"
68 | }
69 |
70 |
71 | # check if script is executed
72 | if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
73 | # load variables from .env
74 | [[ -f "$(pwd)/.env" ]] && source "$(pwd)/.env"
75 | # check for required variables and set defaults for optional variables
76 | TARGET="${TARGET:-run}"
77 | PLATFORM="${PLATFORM:-$(dpkg --print-architecture)}"
78 | require_var "BASE_IMAGE"
79 | require_var "IMAGE"
80 | [[ "${TARGET}" == *"run"* ]] && require_var "COMMAND"
81 | _ENABLE_IMAGE_PUSH="${_ENABLE_IMAGE_PUSH:-false}"
82 | build_image
83 | fi
84 |
--------------------------------------------------------------------------------
/scripts/ci.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | ROOT_PATH="$(realpath "$(cd -P "$(dirname "${0}")" && pwd)"/..)"
6 | source "${ROOT_PATH}/scripts/build.sh"
7 | source "${ROOT_PATH}/scripts/utils.sh"
8 |
9 |
10 | # check for required variables and set defaults for optional variables
11 | TARGET="${TARGET:-run}"
12 | PLATFORM="${PLATFORM:-$(dpkg --print-architecture)}"
13 | require_var "BASE_IMAGE"
14 | require_var "IMAGE_NAME"
15 | IMAGE_TAG="${IMAGE_TAG:-latest}"
16 | [[ "${TARGET}" == *"run"* ]] && require_var "COMMAND"
17 | DEV_IMAGE_NAME="${DEV_IMAGE_NAME:-${IMAGE_NAME}}"
18 | DEV_IMAGE_TAG="${DEV_IMAGE_TAG:-${IMAGE_TAG}-dev}"
19 | SLIM_IMAGE_NAME="${SLIM_IMAGE_NAME:-${IMAGE_NAME}}"
20 | SLIM_IMAGE_TAG="${SLIM_IMAGE_TAG:-${IMAGE_TAG}-slim}"
21 |
22 | ADDITIONAL_DEBS_FILE="${ADDITIONAL_DEBS_FILE:-}"
23 | ADDITIONAL_FILES_DIR="${ADDITIONAL_FILES_DIR:-}"
24 | ADDITIONAL_PIP_FILE="${ADDITIONAL_PIP_FILE:-}"
25 | AFTER_DEPENDENCY_INSTALLATION_SCRIPT="${AFTER_DEPENDENCY_INSTALLATION_SCRIPT:-}"
26 | BEFORE_DEPENDENCY_IDENTIFICATION_SCRIPT="${BEFORE_DEPENDENCY_IDENTIFICATION_SCRIPT:-}"
27 | BEFORE_DEPENDENCY_INSTALLATION_SCRIPT="${BEFORE_DEPENDENCY_INSTALLATION_SCRIPT:-}"
28 | BLACKLISTED_PACKAGES_FILE="${BLACKLISTED_PACKAGES_FILE:-}"
29 | CMAKE_ARGS="${CMAKE_ARGS:-}"
30 | DEV_IMAGE="${DEV_IMAGE_NAME}:${DEV_IMAGE_TAG}"
31 | DISABLE_ROS_INSTALLATION="${DISABLE_ROS_INSTALLATION:-}"
32 | ENABLE_RECURSIVE_ADDITIONAL_DEBS="${ENABLE_RECURSIVE_ADDITIONAL_DEBS:-}"
33 | ENABLE_RECURSIVE_ADDITIONAL_PIP="${ENABLE_RECURSIVE_ADDITIONAL_PIP:-}"
34 | ENABLE_RECURSIVE_AFTER_DEPENDENCY_INSTALLATION_SCRIPT="${ENABLE_RECURSIVE_AFTER_DEPENDENCY_INSTALLATION_SCRIPT:-}"
35 | ENABLE_RECURSIVE_BEFORE_DEPENDENCY_INSTALLATION_SCRIPT="${ENABLE_RECURSIVE_BEFORE_DEPENDENCY_INSTALLATION_SCRIPT:-}"
36 | ENABLE_RECURSIVE_BLACKLISTED_PACKAGES="${ENABLE_RECURSIVE_BLACKLISTED_PACKAGES:-}"
37 | ENABLE_RECURSIVE_VCS_IMPORT="${ENABLE_RECURSIVE_VCS_IMPORT:-}"
38 | ENABLE_SINGLEARCH_PUSH="${ENABLE_SINGLEARCH_PUSH:-false}"
39 | ENABLE_SLIM="${ENABLE_SLIM:-true}"
40 | GIT_HTTPS_PASSWORD="${GIT_HTTPS_PASSWORD:-}"
41 | GIT_HTTPS_SERVER="${GIT_HTTPS_SERVER:-}"
42 | GIT_HTTPS_USER="${GIT_HTTPS_USER:-}"
43 | GIT_SSH_KNOWN_HOST_KEYS="${GIT_SSH_KNOWN_HOST_KEYS:-}"
44 | GIT_SSH_PRIVATE_KEY="${GIT_SSH_PRIVATE_KEY:-}"
45 | IMAGE="${IMAGE_NAME}:${IMAGE_TAG}"
46 | RMW_IMPLEMENTATION="${RMW_IMPLEMENTATION:-}"
47 | ROS_DISTRO="${ROS_DISTRO:-}"
48 | SLIM_BUILD_ARGS="${SLIM_BUILD_ARGS:-'--sensor-ipc-mode proxy --continue-after=10 --show-clogs --http-probe=false --include-path /opt/ros --include-path /docker-ros/ws/install'}"
49 | SLIM_IMAGE="${SLIM_IMAGE_NAME}:${SLIM_IMAGE_TAG}"
50 | VCS_IMPORT_FILE="${VCS_IMPORT_FILE:-}"
51 | _ENABLE_IMAGE_PUSH="${_ENABLE_IMAGE_PUSH:-false}"
52 | _IMAGE_POSTFIX="${_IMAGE_POSTFIX:-""}"
53 |
54 | # write image name for industrial_ci to output (GitHub-only)
55 | if [[ -n "${GITHUB_ACTIONS}" ]]; then
56 | industrial_ci_image="${IMAGE}"
57 | [[ "${TARGET}" == *"dev"* ]] && industrial_ci_image="${DEV_IMAGE}"
58 | [[ -n "${_IMAGE_POSTFIX}" ]] && industrial_ci_image="${industrial_ci_image}${_IMAGE_POSTFIX}"
59 | if [[ "${PLATFORM}" != *","* ]]; then
60 | industrial_ci_image="${industrial_ci_image}-${PLATFORM}"
61 | else
62 | industrial_ci_image="${industrial_ci_image}-$(dpkg --print-architecture)"
63 | fi
64 | echo "INDUSTRIAL_CI_IMAGE=${industrial_ci_image}" >> "${GITHUB_OUTPUT}"
65 | fi
66 |
67 | # parse (potentially) comma-separated lists to arrays
68 | IFS="," read -ra TARGETS <<< "${TARGET}"
69 | if [[ "${_ENABLE_IMAGE_PUSH}" != "true" || "${ENABLE_SINGLEARCH_PUSH}" == "true" ]]; then
70 | IFS="," read -ra PLATFORMS <<< "${PLATFORM}"
71 | else
72 | PLATFORMS=( "${PLATFORM}" )
73 | fi
74 | unset TARGET
75 | unset PLATFORM
76 |
77 | # prepare slim
78 | if [[ "${ENABLE_SLIM}" == "true" ]]; then
79 | curl -L -o ds.tar.gz https://github.com/slimtoolkit/slim/releases/download/1.40.11/dist_linux.tar.gz
80 | tar -xvf ds.tar.gz
81 | fi
82 |
83 | # loop over targets and platforms to build images
84 | for PLATFORM in "${PLATFORMS[@]}"; do
85 | for TARGET in "${TARGETS[@]}"; do
86 | open_log_group "Build ${TARGET} image (${PLATFORM})"
87 | image="${IMAGE}"
88 | [[ "${TARGET}" == "dev" ]] && image="${DEV_IMAGE}"
89 | [[ -n "${_IMAGE_POSTFIX}" ]] && image="${image}${_IMAGE_POSTFIX}"
90 | [[ "${_ENABLE_IMAGE_PUSH}" != "true" || "${ENABLE_SINGLEARCH_PUSH}" == "true" ]] && image="${image}-${PLATFORM}"
91 | IMAGE="${image}" build_image
92 | close_log_group
93 | done
94 |
95 | # slim image
96 | if [[ "${ENABLE_SLIM}" == "true" && "${TARGET}" == "run" ]]; then
97 | open_log_group "Slim image (${PLATFORM})"
98 | image="${IMAGE}"
99 | slim_image="${SLIM_IMAGE}"
100 | [[ -n "${_IMAGE_POSTFIX}" ]] && image="${image}${_IMAGE_POSTFIX}"
101 | [[ -n "${_IMAGE_POSTFIX}" ]] && slim_image="${slim_image}${_IMAGE_POSTFIX}"
102 | [[ "${_ENABLE_IMAGE_PUSH}" != "true" || "${ENABLE_SINGLEARCH_PUSH}" == "true" ]] && image="${image}-${PLATFORM}"
103 | [[ "${_ENABLE_IMAGE_PUSH}" != "true" || "${ENABLE_SINGLEARCH_PUSH}" == "true" ]] && slim_image="${slim_image}-${PLATFORM}"
104 | cd dist_linux*
105 | ./slim build --target "${image}" --tag "${slim_image}" ${SLIM_BUILD_ARGS}
106 | if [[ "${_ENABLE_IMAGE_PUSH}" == "true" ]]; then
107 | docker push "${slim_image}"
108 | fi
109 | cd -
110 | close_log_group
111 | fi
112 | done
113 |
--------------------------------------------------------------------------------
/scripts/utils.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | require_var() {
4 | if [[ -z "${!1}" ]]; then
5 | echo "Environment variable '${1}' is required"
6 | exit 1
7 | fi
8 | }
9 |
10 | open_log_group() {
11 | if [[ -n "${GITLAB_CI}" ]]; then
12 | echo -e "section_start:`date +%s`:build_section[collapsed=true]\r\e[0K[docker-ros] ${1}"
13 | elif [[ -n "${GITHUB_ACTIONS}" ]]; then
14 | echo "::group::[docker-ros] ${1}"
15 | fi
16 | }
17 |
18 | close_log_group() {
19 | if [[ -n "${GITLAB_CI}" ]]; then
20 | echo -e "section_end:`date +%s`:build_section\r\e[0K"
21 | elif [[ -n "${GITHUB_ACTIONS}" ]]; then
22 | echo "::endgroup::"
23 | fi
24 | }
25 |
--------------------------------------------------------------------------------