Skip to content

Commit 6410acb

Browse files
committed
feat(workflows): add dotnet-ci + cpp-ci reusables; extend required.yml dispatch
Adds the two deferred reusable workflows: - dotnet-ci.yml: restore, build, format check, test. Configurable dotnet-version (default 9.0.x), solution path, working-directory. - cpp-ci.yml: CMake configure + build + test matrix across ubuntu/macos/windows (configurable). Harden-runner on Linux only (step-security action is Linux-native). Extends required.yml to dispatch the two new languages, and adds proto handling (security-only, no language build dispatched). New inputs: dotnet-version, dotnet-solution, cpp-os-list, cpp-source-dir, cpp-cmake-flags. After this lands, these placeholder consumer PRs can be hardened to call real CI: - resq-software/dotnet-sdk#36 (dotnet) - resq-software/viz#4 (dotnet) - resq-software/vcpkg#6 (cpp) - resq-software/ardupilot#1 (cpp)
1 parent 5a72372 commit 6410acb

3 files changed

Lines changed: 228 additions & 11 deletions

File tree

.github/workflows/cpp-ci.yml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Copyright 2026 ResQ Software
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Reusable C++ CI — CMake configure + build + test. Matrix over OS.
5+
# Callable from any resq-software/* repo. Pairs with security-scan.yml.
6+
# SHA-pinned actions with trailing `# <tag>`.
7+
8+
name: cpp-ci
9+
10+
on:
11+
workflow_call:
12+
inputs:
13+
os-list:
14+
description: JSON array of runner OS labels.
15+
type: string
16+
required: false
17+
default: '["ubuntu-latest","macos-latest","windows-latest"]'
18+
source-dir:
19+
description: Path containing CMakeLists.txt (or subproject path).
20+
type: string
21+
required: false
22+
default: "."
23+
build-dir:
24+
type: string
25+
required: false
26+
default: "build"
27+
cmake-flags:
28+
description: Extra flags passed to `cmake -B <build-dir>`.
29+
type: string
30+
required: false
31+
default: ""
32+
build-config:
33+
type: string
34+
required: false
35+
default: "Debug"
36+
run-test:
37+
type: boolean
38+
required: false
39+
default: true
40+
timeout-minutes:
41+
type: number
42+
required: false
43+
default: 30
44+
45+
permissions:
46+
contents: read
47+
48+
jobs:
49+
build-test:
50+
name: ${{ matrix.os }}
51+
runs-on: ${{ matrix.os }}
52+
timeout-minutes: ${{ inputs.timeout-minutes }}
53+
strategy:
54+
fail-fast: false
55+
matrix:
56+
os: ${{ fromJSON(inputs.os-list) }}
57+
steps:
58+
- name: Harden Runner
59+
if: runner.os == 'Linux'
60+
uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2
61+
with:
62+
egress-policy: audit
63+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
64+
- name: Configure
65+
run: cmake -B ${{ inputs.build-dir }} ${{ inputs.cmake-flags }} ${{ inputs.source-dir }}
66+
- name: Build
67+
run: cmake --build ${{ inputs.build-dir }} --config ${{ inputs.build-config }}
68+
- if: ${{ inputs.run-test }}
69+
name: Test
70+
run: ctest --test-dir ${{ inputs.build-dir }} --output-on-failure -C ${{ inputs.build-config }}

.github/workflows/dotnet-ci.yml

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Copyright 2026 ResQ Software
2+
# SPDX-License-Identifier: Apache-2.0
3+
#
4+
# Reusable .NET CI — restore, build, test, format check. Callable from
5+
# any resq-software/* repo with a .sln or csproj tree. Pairs with
6+
# security-scan.yml. SHA-pinned actions with trailing `# <tag>`.
7+
8+
name: dotnet-ci
9+
10+
on:
11+
workflow_call:
12+
inputs:
13+
dotnet-version:
14+
description: .NET SDK version (e.g. "9.0.x", or "global.json").
15+
type: string
16+
required: false
17+
default: "9.0.x"
18+
solution:
19+
description: Path to .sln or csproj (defaults to repo root auto-detect).
20+
type: string
21+
required: false
22+
default: ""
23+
working-directory:
24+
type: string
25+
required: false
26+
default: "."
27+
configuration:
28+
type: string
29+
required: false
30+
default: "Release"
31+
run-format-check:
32+
type: boolean
33+
required: false
34+
default: true
35+
run-test:
36+
type: boolean
37+
required: false
38+
default: true
39+
timeout-minutes:
40+
type: number
41+
required: false
42+
default: 20
43+
44+
permissions:
45+
contents: read
46+
47+
jobs:
48+
build:
49+
name: Build
50+
runs-on: ubuntu-latest
51+
timeout-minutes: ${{ inputs.timeout-minutes }}
52+
defaults:
53+
run:
54+
working-directory: ${{ inputs.working-directory }}
55+
steps:
56+
- name: Harden Runner
57+
uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2
58+
with:
59+
egress-policy: audit
60+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
61+
- uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 # v4
62+
with:
63+
dotnet-version: ${{ inputs.dotnet-version }}
64+
- name: Restore
65+
run: dotnet restore ${{ inputs.solution }}
66+
- name: Build
67+
run: dotnet build ${{ inputs.solution }} -c ${{ inputs.configuration }} --no-restore
68+
69+
format:
70+
if: ${{ inputs.run-format-check }}
71+
name: Format check
72+
runs-on: ubuntu-latest
73+
timeout-minutes: 10
74+
defaults:
75+
run:
76+
working-directory: ${{ inputs.working-directory }}
77+
steps:
78+
- name: Harden Runner
79+
uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2
80+
with:
81+
egress-policy: audit
82+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
83+
- uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 # v4
84+
with:
85+
dotnet-version: ${{ inputs.dotnet-version }}
86+
- run: dotnet format ${{ inputs.solution }} --verify-no-changes --severity warn
87+
88+
test:
89+
if: ${{ inputs.run-test }}
90+
name: Test
91+
needs: build
92+
runs-on: ubuntu-latest
93+
timeout-minutes: ${{ inputs.timeout-minutes }}
94+
defaults:
95+
run:
96+
working-directory: ${{ inputs.working-directory }}
97+
steps:
98+
- name: Harden Runner
99+
uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2
100+
with:
101+
egress-policy: audit
102+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
103+
- uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25 # v4
104+
with:
105+
dotnet-version: ${{ inputs.dotnet-version }}
106+
- run: dotnet test ${{ inputs.solution }} -c ${{ inputs.configuration }} --verbosity normal

.github/workflows/required.yml

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,29 @@
33
#
44
# Reusable CI aggregator. Each consumer repo calls this with a `lang`
55
# input matching its `lang` custom-repo-property value. The workflow
6-
# dispatches to the matching language CI (rust|python|node|polyglot)
7-
# and always runs the org-wide security scan in parallel. The final
8-
# `required` job needs all upstream jobs — Ruleset A requires the
9-
# `required` status-check context.
6+
# dispatches to the matching language CI (rust|python|node|dotnet|cpp|
7+
# proto|polyglot) and always runs the org-wide security scan in
8+
# parallel. The final `required` job needs all upstream jobs — Ruleset
9+
# A requires the `required` status-check context.
1010
#
11-
# SHA-pinned actions with trailing `# <tag>` for Dependabot.
11+
# For `lang: proto` repos (e.g. resq-proto), only security scanning
12+
# runs — no language build is dispatched. Add a buf lint / breaking-
13+
# change workflow locally if you want extra checks.
1214

1315
name: required
1416

1517
on:
1618
workflow_call:
1719
inputs:
1820
lang:
19-
description: One of rust|python|node|polyglot.
21+
description: One of rust|python|node|dotnet|cpp|proto|polyglot.
2022
type: string
2123
required: true
2224
working-directory:
2325
type: string
2426
required: false
2527
default: "."
2628
codeql-languages:
27-
description: JSON array for security-scan CodeQL input.
2829
type: string
2930
required: false
3031
default: "[]"
@@ -68,6 +69,26 @@ on:
6869
type: string
6970
required: false
7071
default: '["3.11","3.12","3.13"]'
72+
dotnet-version:
73+
type: string
74+
required: false
75+
default: "9.0.x"
76+
dotnet-solution:
77+
type: string
78+
required: false
79+
default: ""
80+
cpp-os-list:
81+
type: string
82+
required: false
83+
default: '["ubuntu-latest"]'
84+
cpp-source-dir:
85+
type: string
86+
required: false
87+
default: "."
88+
cpp-cmake-flags:
89+
type: string
90+
required: false
91+
default: ""
7192

7293
permissions:
7394
contents: read
@@ -113,12 +134,30 @@ jobs:
113134
test-cmd: ${{ inputs.node-test-cmd }}
114135
build-cmd: ${{ inputs.node-build-cmd }}
115136

137+
dotnet:
138+
if: ${{ inputs.lang == 'dotnet' || inputs.lang == 'polyglot' }}
139+
name: .NET CI
140+
uses: ./.github/workflows/dotnet-ci.yml
141+
with:
142+
dotnet-version: ${{ inputs.dotnet-version }}
143+
solution: ${{ inputs.dotnet-solution }}
144+
working-directory: ${{ inputs.working-directory }}
145+
146+
cpp:
147+
if: ${{ inputs.lang == 'cpp' || inputs.lang == 'polyglot' }}
148+
name: C++ CI
149+
uses: ./.github/workflows/cpp-ci.yml
150+
with:
151+
os-list: ${{ inputs.cpp-os-list }}
152+
source-dir: ${{ inputs.cpp-source-dir }}
153+
cmake-flags: ${{ inputs.cpp-cmake-flags }}
154+
155+
# proto repos get security-only; nothing to dispatch here beyond that.
156+
116157
# `required` is the single status-check context consumed by Ruleset A.
117-
# Passes iff every dispatched language job AND security passed.
118-
# Skipped jobs (lang-mismatch `if:` gates) are treated as success.
119158
required:
120159
name: required
121-
needs: [security, rust, python, node]
160+
needs: [security, rust, python, node, dotnet, cpp]
122161
if: always()
123162
runs-on: ubuntu-latest
124163
steps:
@@ -128,10 +167,12 @@ jobs:
128167
RUST_RESULT: ${{ needs.rust.result }}
129168
PYTHON_RESULT: ${{ needs.python.result }}
130169
NODE_RESULT: ${{ needs.node.result }}
170+
DOTNET_RESULT: ${{ needs.dotnet.result }}
171+
CPP_RESULT: ${{ needs.cpp.result }}
131172
run: |
132173
set -eu
133174
fail=0
134-
for r in "$SECURITY_RESULT" "$RUST_RESULT" "$PYTHON_RESULT" "$NODE_RESULT"; do
175+
for r in "$SECURITY_RESULT" "$RUST_RESULT" "$PYTHON_RESULT" "$NODE_RESULT" "$DOTNET_RESULT" "$CPP_RESULT"; do
135176
case "$r" in
136177
success|skipped|"") ;;
137178
*) echo "::error::Upstream job returned: $r"; fail=1 ;;

0 commit comments

Comments
 (0)