Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci_orchestrator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,5 +99,5 @@ jobs:

pre-commit:
name: pre-commit
if: ${{ (inputs.ros_distro == '' || inputs.ros_distro == 'all') && !(github.event_name == 'pull_request' && github.event.pull_request.draft) }}
if: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.draft) }}
uses: ./.github/workflows/pre-commit.yml
12 changes: 9 additions & 3 deletions .github/workflows/reusable_ici.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,25 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Detect upstream workspace and extra apt dependencies
- name: Detect upstream workspace, extra apt dependencies, and pip requirements
id: detect
run: |
if [ -f repos.list ]; then
echo "upstream_workspace=repos.list" >> "$GITHUB_OUTPUT"
else
echo "upstream_workspace=" >> "$GITHUB_OUTPUT"
fi
if [ -f .github/ci/apt_dependencies ]; then
packages=$(grep -v '^\s*#' .github/ci/apt_dependencies | tr '\n' ' ')
if [ -f Aptfile ]; then
packages=$(grep -v '^\s*#' Aptfile | tr '\n' ' ')
echo "before_install=sudo apt-get install -y -qq ${packages}" >> "$GITHUB_OUTPUT"
else
echo "before_install=" >> "$GITHUB_OUTPUT"
fi
if [ -f requirements.txt ]; then
echo "pip_install=python3 -m pip install --break-system-packages --ignore-installed -r requirements.txt" >> "$GITHUB_OUTPUT"
else
echo "pip_install=" >> "$GITHUB_OUTPUT"
fi
- name: Cache ccache
uses: actions/cache@v6
with:
Expand All @@ -74,4 +79,5 @@ jobs:
ROS_DISTRO: ${{ inputs.ros_distro }}
ROS_REPO: ${{ inputs.ros_repo }}
BEFORE_INSTALL_UPSTREAM_DEPENDENCIES: ${{ steps.detect.outputs.before_install }}
AFTER_INSTALL_TARGET_DEPENDENCIES: ${{ steps.detect.outputs.pip_install }}
BEFORE_INIT: ${{ secrets.CI_PAT != '' && format('apt-get update -qq && apt-get install -y -qq git && git config --global url."https://{0}@github.com/".insteadOf "https://github.com/"', secrets.CI_PAT) || '' }}
12 changes: 12 additions & 0 deletions .github/workflows/self-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: Self-check
# Runs pre-commit against this repo's own files (workflow YAML, docs), dogfooding the
# pre-commit.yml leaf workflow that every consumer repo gets via ci_orchestrator.yml.

on:
pull_request:
push:
branches: [main]

jobs:
pre-commit:
uses: ./.github/workflows/pre-commit.yml
41 changes: 41 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# To use:
#
# pre-commit run -a
#
# Or:
#
# pre-commit install # (runs every time you commit in git)
#
# To update this file:
#
# pre-commit autoupdate
#
# See https://github.com/pre-commit/pre-commit

repos:
# Standard hooks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-merge-conflict
- id: check-symlinks
- id: check-yaml
- id: end-of-file-fixer
- id: mixed-line-ending
- id: trailing-whitespace
- id: check-byte-order-marker # Forbid UTF-8 byte-order markers

# GitHub Actions workflow linting
- repo: https://github.com/rhysd/actionlint
rev: v1.7.7
hooks:
- id: actionlint

# Spellcheck in comments and docs
- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
hooks:
- id: codespell
args: ["--write-changes"]
33 changes: 33 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Contributing

We encourage community contributions to this repository. Feel free to open an issue or provide a pull request.

# Licensing

Any contribution to this repository will be under the BSD License.

## Tooling

Please make sure for any pull request that it passes the `pre-commit` checks (see Tooling section).

### pre-commit

The [pre-commit](https://pre-commit.com/) tool is used for running certain checks and formatters every time before a commit is done.
Please use it on any pull requests for this repository. It also runs automatically in CI via `.github/workflows/self-check.yml`.

__Installation:__

`apt install pipx && pipx install pre-commit`. Open a new terminal afterwards.

__Usage:__
In the repository folder run: `pre-commit run --all`. This can be automated with `pre-commit install`, so all checks are run every time a commit is done.

## Testing workflow changes

Beyond `pre-commit` (which lints the YAML itself via `actionlint`), workflow *behavior* changes still
need to be validated against a real consumer, since this repo has no build/test of its own:

1. Actually dispatch a consumer workflow against your branch/commit and watch it run, e.g.
`gh run watch <id> --repo Duatic/<consumer-repo> --exit-status`.
2. When testing something version-sensitive, point the consumer's `uses:` at the exact commit SHA
first, confirm it works, then move a tag - don't debug against a moving tag.
11 changes: 11 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Copyright 2026 Duatic AG

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50 changes: 14 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
# ci-workflows

Central, versioned GitHub Actions reusable workflows for the [Duatic](https://github.com/Duatic)
organization's ROS 2 repositories. Consolidates what used to be copy-pasted, drift-prone
`reusable_ici.yml` / `build-<distro>.yml` / `pre-commit.yml` files across every product and
library repo into one entry point.
Central, versioned GitHub Actions reusable workflows.

## Versioning

Expand All @@ -24,41 +21,30 @@ GitHub Release with auto-generated notes.

## What consumers call

Repos only ever call **`ci_orchestrator.yml`** it owns the distro matrix, the `ROS_REPO` channel
Repos only ever call **`ci_orchestrator.yml`** - it owns the distro matrix, the `ROS_REPO` channel
per distro, gating, concurrency, and the draft-PR policy, and internally drives the two leaf
workflows (`reusable_ici.yml`, `pre-commit.yml`) that most repos never reference directly.

### `ci_orchestrator.yml` consumer-facing entry point
### `ci_orchestrator.yml` - consumer-facing entry point

| Input | Required | Default | Description |
|---|---|---|---|
| `ros_distro` | no | `all` | `all` runs the full gated matrix (jazzy, then kilted/lyrical/rolling once jazzy succeeds); a single distro name (e.g. `kilted`) builds only that one and skips `pre-commit`. |
| `ros_distro` | no | `all` | `all` runs the full gated matrix (jazzy, then kilted/lyrical/rolling once jazzy succeeds); a single distro name (e.g. `kilted`) builds only that one. |
| `runner` | no | `ubuntu-latest` | Runner label(s) for the build jobs, e.g. `self-hosted`. |

| Secret | Required | Description |
|---|---|---|
| `CI_PAT` | no | PAT used to clone private upstream dependencies from a `repos.list` file at the repo root. Pass via `secrets: inherit`; omit entirely for public-only repos. |

What's **not** an input, because it's derived or auto-detected rather than repo-specific config:
- **`ROS_REPO` channel** — `rolling` always builds against `testing`, every other distro against
`main`. Not overridable per repo.
- **`upstream_workspace`** — auto-detected: `reusable_ici.yml` uses `repos.list` if it exists at the
repo root, otherwise nothing.
- **Extra `apt` dependencies** (e.g. `libboost-regex-dev`) — drop a `.github/ci/apt_dependencies`
file in the consumer repo (one package per line, `#` comments allowed); picked up automatically.
Most repos don't need this file at all.
- **Concurrency** — every orchestrator run is keyed on `<caller repo>-<caller workflow>-<caller ref>`
with `cancel-in-progress: true`, so pushing a new commit to a branch/PR cancels the superseded run.
No consumer configuration needed.
- **Draft PRs** — the full matrix is too expensive to run on drafts. On a draft PR the orchestrator
runs a single job that fails immediately with a message to mark the PR ready for review, instead of
building anything. Marking the PR "Ready for review" (which the consumer's `pull_request` trigger
must include as a `ready_for_review` type) runs the real matrix.
- **`upstream_workspace`** - auto-detected: `reusable_ici.yml` uses `repos.list` if it exists at the repo root, otherwise nothing.
- **Extra `apt` dependencies** (e.g. `libboost-regex-dev`) - drop an `Aptfile` at the consumer repo root (one package per line, `#` comments allowed); picked up automatically. Most repos don't need this file at all.
- **Extra `pip` dependencies not resolvable via rosdep** (e.g. `open3d`) - drop a `requirements.txt` file at the consumer repo root; `pip install`ed (full dependency tree, `--break-system-packages`) after target dependencies are installed, before build/test. Most repos don't need this file at all.
- **Draft PRs** - the full matrix is too expensive to run on drafts. On a draft PR the orchestrator runs a single job that fails immediately with a message to mark the PR ready for review, instead of building anything. Marking the PR "Ready for review" runs the real matrix.

## Example: library repo, no private deps

This is the *entire* CI file a typical repo needs. The only things meant to be edited per repo are
the `cron`/`timezone` and, at rollout, whether it has a `CI_PAT` secret to inherit.
This is the *entire* CI file a typical repo needs. The only things meant to be edited per repo are the `cron`/`timezone` and, whether it has a secret to inherit.

```yaml
name: CI
Expand Down Expand Up @@ -88,8 +74,7 @@ jobs:

## Example: product repo with a private-dependency PAT, self-hosted runner

Same file, plus `runner: self-hosted`. The `repos.list` file at the repo root and the `CI_PAT`
secret (repo or org level) are all that's needed for private upstream deps — no extra workflow input.
Same file, plus `runner: self-hosted`. The `repos.list` file at the repo root and the secret (repo or org level) are all that's needed for private upstream deps - no extra workflow input.

```yaml
jobs:
Expand All @@ -98,20 +83,13 @@ jobs:
with:
ros_distro: ${{ inputs.ros_distro }}
runner: self-hosted
secrets: inherit # forwards CI_PAT
secrets: inherit
```

## Leaf workflows (internal, not called directly by product repos)

- **`reusable_ici.yml`** — the upstream `ros-industrial` industrial_ci template, builds one
distro/channel combination. Auto-detects `repos.list` and `.github/ci/apt_dependencies`.
- **`pre-commit.yml`** — runs `pre-commit` across all files.
- **`reusable_ici.yml`** - the upstream `ros-industrial` industrial_ci template, builds one distro/channel combination. Auto-detects `repos.list`, `Aptfile`, and `requirements.txt`.
- **`pre-commit.yml`** - runs `pre-commit` across all files.

## Requirements on consumer repos

- This repo is public, so no per-repo "Access" toggle is needed to resolve
`uses: Duatic/ci-workflows/...`. (A private version of this repo was tried first; cross-repo
reusable-workflow resolution was unreliable for a newly created private repo even with the
organization-access setting enabled, so it was made public instead — it holds no secrets.)
- Repos using a private-dependency PAT must have a `CI_PAT` secret available (repo or org level)
and pass `secrets: inherit`.
- Repos using a private-dependency PAT must have a secret available (repo or org level) and pass `secrets: inherit`.
Loading