Note
At the moment, this workflow does not build for the ARM architecture. Please build these locally until Microsoft releases the feature for private repositories. source one, source two. For now, Jetson builds done online will simply be skipped.
- The user creates a new branch for their changes.
- The user commits their changes to the branch, with a relevant tag.
- The user pushes the branch to the remote repository.
- The CI/CD system detects the new tag and starts building the image.
- If the build is successful, the image is pushed to the GitHub Container Registry.
- The user can then pull the image from the registry and use it.
- Dependant images are built in the correct order.
- Images are built with the correct tags.
- The CI/CD system is triggered by tags, not pushes to branches or pull requests.
- Upon any failure, the system will exit gracefully and not proceed with downstream dependant builds.
This is a high-level understanding of the workflow described in build.yaml.
-
The workflow is triggered when a tag starting with
v(ordev) is pushed to the repository. -
Next, the workflow looks for the Git tag. If it finds a tag that starts with
v/dev, records this to a variable, so that it may be used later in the workflow. -
The remainder of the build is split into five sections, which run in three steps (some sections run in parallel):
Step Sections that run in parallel 1 - ROS2 base humble images (AMD64/x86)
- ROS2 base humble images (Jetson/ARM)2 - ROS2 humble_harmonic (AMD64/x86) 3 - ROS2 wheelchair2_base images (AMD64/x86)
- ROS2 wheelchair2_base_jetson (depends on humble_jetson)By default, all of these sections are configured as matrix strategies. Although some sections have only one entry, this allows for easy expansion in the future.
It quickly became evident that several steps in the workflow were repetitive, so the majority of the actual work was refactored into a reusable workflow. This is described in build-workflow.yaml, and is briefly explained below.
This workflow is triggered by the main workflow, and receives the following inputs:
| Input | Description | Default |
|---|---|---|
build_context |
The build context for the Dockerfile | - |
image_name |
The target name of the image to build | - |
image_version |
The version tag of the image to build | - |
runs_on |
The GitHub runner to use for the build | ubuntu-latest |
First, code is checked out, buildx is setup, and GHCR is authenticated.
Finally, the actual image is built and (if successful) pushed to the GitHub Container Registry, using the docker/build-push-action action.
Here, we discuss only the potentially confusing parts of the main workflow, which is described in build.yaml.
You are encouraged to read the GitHub Actions documentation for more information on how GitHub Actions work, how to write workflows, and reference for syntax. If you use VS Code to write workflows, the GitHub Actions extension is useful.
We only trigger the workflow on the push of certain tags:
on:
push:
tags:
- 'dev*'
- 'v*'This simply involves obtaining the Git tag and setting it as an environment variable for later use:
- name: Set image version
id: set_version
run: |
echo "image_version=${{ github.ref_name }}" >> $GITHUB_OUTPUTWhile there are several sections as described earlier, we shall dive deeper into one of these as exposition. Consider the "Stage 3: ROS2 wheelchair2_base images (AMD64/x86)" section.
# Stage 3: ROS2 wheelchair2_base images (AMD64/x86)
ros2-wheelchair-base:
needs: [changes, ros2-humble-base-amd64, ros2-humble-harmonic]
name: ROS2 Wheelchair2 Base Images
permissions:
packages: write
contents: read
strategy:
matrix:
config:
- { build_context: './ROS2/AMD64x86/wheelchair2_base', image_name: 'wheelchair2_base', image_version: "${{ needs.changes.outputs.image_version }}" }
- { build_context: './ROS2/AMD64x86/wheelchair2_base_gazebo', image_name: 'wheelchair2_base_gazebo', image_version: "${{ needs.changes.outputs.image_version }}" }
uses: ./.github/workflows/build-workflow.yaml
with:
build_context: ${{ matrix.config.build_context }}
image_name: ${{ matrix.config.image_name }}
image_version: ${{ matrix.config.image_version }}
runs_on: ubuntu-latestLet's break this down:
needs: This job depends on thechangesjob and theros2-humble-base-amd64andros2-humble-harmonicjobs. It will only run if these jobs are successful. This ensures that dependant images are built in the correct order.permissions: This job requires write access to packages (to push the built images) and read access to contents (to read the repository).strategy: This defines a matrix strategy for the job. In this case, we have two configurations:wheelchair2_base: The base image for the wheelchair2 project.wheelchair2_base_gazebo: The base image for the wheelchair2 project with Gazebo support.
uses: This specifies that the job will use the reusable workflow defined in build-workflow.yaml.with: This passes the necessary parameters to the reusable workflow, including the build context, image name, image version, and the runner to use.
Note the matrix system:
strategy:
matrix:
config:
- { build_context: './ROS2/AMD64x86/wheelchair2_base', image_name: 'wheelchair2_base', image_version: "${{ needs.changes.outputs.image_version }}" }
- { build_context: './ROS2/AMD64x86/wheelchair2_base_gazebo', image_name: 'wheelchair2_base_gazebo', image_version: "${{ needs.changes.outputs.image_version }}" }In this system, we specify one or more configurations for the job. Each configuration corresponds to one resulting image. The reusable workflow will be run for each configuration, allowing us to build multiple images in parallel by just adding one more entry to the config list.
Please consult the GitHub Actions documentation for more information on matrix strategies and syntax.