-
Notifications
You must be signed in to change notification settings - Fork 0
335 lines (299 loc) · 15 KB
/
Copy pathrelease.yml
File metadata and controls
335 lines (299 loc) · 15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
name: Release
on:
push:
tags:
- "v*"
permissions:
contents: write
pull-requests: write
jobs:
regenerate-safety-artifacts:
name: Regenerate safety artifacts
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: main
- name: Install tools
run: |
sudo apt-get update -qq
sudo apt-get install -y cmake ninja-build lcov
- name: Build cpp-FuSa from source
run: |
git clone --depth=1 --branch v0.18.0 https://github.com/SoundMatt/cpp-FuSa /tmp/cpfusa
cmake -B /tmp/cpfusa/build -S /tmp/cpfusa \
-DCMAKE_BUILD_TYPE=Release -G Ninja
cmake --build /tmp/cpfusa/build --parallel
- name: Configure project (coverage build)
env:
CC: gcc-12
CXX: g++-12
run: |
cmake -B build-cov \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_FLAGS="--coverage -O0" \
-DCMAKE_EXE_LINKER_FLAGS="--coverage" \
-G Ninja
- name: Build project
run: cmake --build build-cov --parallel
# Kept in sync with ci.yml's `coverage` job (issues #67/#68): branch
# coverage collection and /usr/include system-header exclusion were
# both missing here too, since this step duplicates the same lcov
# invocation. Without --rc lcov_branch_coverage=1 on both the
# --capture and --remove calls, coverage-report.json's
# totalBranches/hitBranches are always 0/0, vacuously "passing" any
# branch-coverage threshold rather than genuinely meeting it.
- name: Run tests and collect coverage
run: |
ctest --test-dir build-cov --output-on-failure -j1
lcov --capture \
--directory build-cov \
--gcov-tool gcov-12 \
--rc lcov_branch_coverage=1 \
--output-file coverage.info
lcov --remove coverage.info \
'*/tests/*' '*/catch2/*' '*/FetchContent/*' \
'/usr/include/*' \
--rc lcov_branch_coverage=1 \
--output-file coverage.info
- name: Regenerate dFMEA (with cybersecurity analysis)
run: /tmp/cpfusa/build/cpfusa fmea --cyber
- name: Regenerate safety case (GSN)
run: /tmp/cpfusa/build/cpfusa safety-case
# issues #78/#79 (cpp-RCP-10/11): `cpfusa tara` analyzes cpp-FuSa's OWN
# tooling/CI/supply-chain surface (release binary, .fusa.json, CMake
# deps) — it is not a protocol-level TARA for RCP itself. The real
# product-level TARA is the hand-authored TARA-ANALYSIS.md. Having
# both an ISO-21434-labeled `tara.json`/`tara.md` and TARA-ANALYSIS.md
# at the repo root with no distinguishing signal made it ambiguous
# which a certification reviewer should trust, so `cpfusa tara`'s
# output is relocated under tooling/ immediately after generation
# (cpfusa itself has no --output flag for this command, so the file is
# moved rather than generated in place) to make its tooling-only scope
# self-evident and keep that distinction stable across every future
# regeneration on a tag, not just this one.
- name: Regenerate TARA (ISO 21434 Ch. 9, cpp-FuSa tooling-process TARA — not the product TARA)
run: |
/tmp/cpfusa/build/cpfusa tara
mv tara.json tooling/tara.json
mv tara.md tooling/tara.md
- name: Regenerate SBOM, provenance, and artifact manifest
run: /tmp/cpfusa/build/cpfusa release
# Gap/coverage reports below grade against certification targets
# (ASIL-B / SIL-2 / DAL-B). Stub bridge implementations cannot reach the
# 100% thresholds those targets demand, so these commands exit non-zero by
# design — but the workflow's job is to GENERATE the report artifact, not
# to gate on it (gating lives in ci.yml). `|| true` keeps the artifact
# while letting the report record the gaps.
#
# (issue #81 / cpp-RCP-13: the step names below spell out
# "artifact only — non-gating" explicitly, so this intent doesn't rely
# solely on the comment above surviving future edits.)
- name: Regenerate ISO 26262 gap report (ASIL-B, artifact only — non-gating)
run: |
/tmp/cpfusa/build/cpfusa iso26262 \
--asil ASIL-B \
--output iso26262-gap-report.json || true
- name: Regenerate IEC 61508 gap report (SIL-2, artifact only — non-gating)
run: |
/tmp/cpfusa/build/cpfusa iec61508 \
--sil SIL-2 \
--output iec61508-gap-report.json || true
- name: Regenerate DO-178C gap report (DAL-B, artifact only — non-gating)
run: |
/tmp/cpfusa/build/cpfusa do178 \
--dal DAL-B \
--output do178-gap-report.json || true
# .fusa-iec62443.json (target_sl/component_type/incident_resp_doc) is
# the hand-authored input `cpfusa iec62443` reads; already a required
# artifact per ci.yml's cpfusa-check job. Same non-gating rationale as
# the ISO 26262/IEC 61508/DO-178C gap reports above.
- name: Regenerate IEC 62443 gap report (SL-2, artifact only — non-gating)
run: |
/tmp/cpfusa/build/cpfusa iec62443 \
--sl SL-2 \
--output iec62443-gap-report.json || true
- name: Regenerate structural coverage report (DO-178C, artifact only — non-gating)
run: |
/tmp/cpfusa/build/cpfusa coverage \
--profile coverage.info \
--dal DAL-B \
--output coverage-report.json || true
- name: Regenerate Software Accomplishment Summary (DO-178C §11.20)
run: /tmp/cpfusa/build/cpfusa sas
- name: Regenerate Software Configuration Index (DO-178C §11.16)
run: /tmp/cpfusa/build/cpfusa sci
- name: Bundle audit evidence pack
run: /tmp/cpfusa/build/cpfusa audit-pack
- name: Generate safety badge (Shields.io SVG)
run: /tmp/cpfusa/build/cpfusa badge
# Artifact only — non-gating (issue #81): format conversion of already
# -generated findings, nothing pass/fail-shaped about it.
- name: Generate full compliance report (JSON + HTML, artifact only — non-gating)
run: |
/tmp/cpfusa/build/cpfusa report --format json --output report.json || true
/tmp/cpfusa/build/cpfusa report --format html --output report.html || true
# Release-gate check (mirrors c-RCP's own "Verify shipped artifacts
# declare the version being released" step), adapted to what cpp-FuSa
# v0.18.0's actual output carries for this project. c-RCP's tool
# stamps its generated sbom.json/provenance.json module field as
# "c-RCP@<version>", so its check reads that field directly. cpp-FuSa
# v0.18.0 does not do the equivalent for cpp-RCP: `cpfusa release`'s
# sbom.json/provenance.json/artifact-manifest.json and `cpfusa
# qualify`'s qualify-report.json all hardcode
# "module": "github.com/SoundMatt/cpp-FuSa" regardless of target
# project (verified directly against a local build of the pinned
# v0.18.0 binary) — a real limitation of that pinned tool release, not
# something this repo's own files can fix. report.html is the one
# artifact `cpfusa report` actually generates that renders the target
# project's own name+version ("<b>Project:</b> cpp-RCP v<version>"),
# sourced from .fusa.json's project.version — so it is what this check
# verifies instead, alongside re-confirming .fusa.json itself agrees
# with version.hpp. This re-checks what ci.yml's version-sources-agree
# job already checks on every push, because that job's trigger
# (push to main/rewrite branches) does not fire on a tag push, so nothing
# else guarantees the three sources still agreed at the exact ref this
# release job checked out.
- name: Verify shipped artifacts declare the version being released
run: |
header_version=$(grep -m1 -oE 'kVersion[[:space:]]*=[[:space:]]*"[0-9]+\.[0-9]+\.[0-9]+"' include/rcp/version.hpp | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
if [ -z "$header_version" ]; then
echo "::error file=include/rcp/version.hpp::Could not parse kVersion"
exit 1
fi
fail=0
fusa_version=$(grep -m1 -oE '"version": *"[0-9]+\.[0-9]+\.[0-9]+"' .fusa.json | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
if [ "$fusa_version" != "$header_version" ]; then
echo "::error file=.fusa.json::.fusa.json's project.version is '$fusa_version', but include/rcp/version.hpp says '$header_version' -- every artifact cpfusa generates from .fusa.json (report.html included) is stamped from the stale value."
fail=1
fi
if [ ! -f report.html ]; then
echo "::error::Expected report.html to have been generated by the previous step"
fail=1
elif ! grep -qE "cpp-RCP v${header_version}[[:space:]&<]" report.html; then
echo "::error file=report.html::Does not declare 'cpp-RCP v${header_version}' -- expected this release's compliance report to carry the version being released."
fail=1
fi
[ "$fail" -eq 0 ] || exit 1
- name: Tool qualification evidence
run: /tmp/cpfusa/build/cpfusa qualify
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# issue #82 / cpp-RCP-14: this used to `git push origin HEAD:refs/heads/main`
# directly as github-actions[bot] with `[skip ci]`, landing regenerated
# evidence on main with no review and no CI run. Several of this
# repo's own docs (AUDIT_PACK.md, sas.md, safety-case.md, tara.md) point
# at these files by their root-level path and ROADMAP.md's milestone-61
# entry explicitly relies on release.yml's regeneration commit landing
# on main on every tag (see e.g. commit e7a8178, the v2.17.0
# regeneration) — so these files are relied on being live in the tree
# at main HEAD, ruling out artifact-upload-only as a fix. Instead, the
# regenerated evidence now lands on a dedicated branch and goes through
# a normal reviewed PR (opened in the next step) rather than a direct
# unreviewed bot push, while still ending up on main once merged.
- name: Commit regenerated artifacts to a review branch
run: |
TAG="${{ github.ref_name }}"
# A single `git add a b c` call aborts with a fatal error and
# stages NOTHING (not just the missing pathspec -- the whole
# invocation) if even one listed file doesn't exist on disk. The
# gap/coverage/report artifacts below come from generator steps
# above that are themselves `|| true`-guarded (cpfusa
# iso26262/iec61508/do178/iec62443/coverage/report "exit non-zero
# by design" -- see the comments on those steps), so their output
# file may legitimately be absent here if the underlying cpfusa
# subcommand crashed before writing it. Those are added one at a
# time and skipped with a warning when missing, instead of
# aborting the whole staging operation.
OPTIONAL_ARTIFACTS="
iso26262-gap-report.json
iec61508-gap-report.json
do178-gap-report.json
iec62443-gap-report.json
coverage-report.json
report.json
report.html
"
for f in $OPTIONAL_ARTIFACTS; do
if [ -f "$f" ]; then
git add "$f"
else
echo "::warning::$f not present -- its generator step is || true-guarded (exits non-zero by design), treating as legitimately absent and skipping."
fi
done
# Every other artifact's generator step above is NOT `|| true`
# -guarded, so if one of these is missing here something already
# went more seriously wrong upstream. Add them with no swallowing
# `|| true` so a genuinely-missing one still fails this step
# loudly instead of silently reporting "nothing to commit".
git add \
fmea.csv fmea.json \
safety-case.json safety-case.md safety-case.mermaid \
tooling/tara.json tooling/tara.md \
sbom.json provenance.json artifact-manifest.json \
sas.json sas.md \
sci.json \
audit-pack.zip \
fusa-badge.svg \
qualify-report.json
if git diff --cached --quiet; then
echo "No artifact changes for $TAG — nothing to commit."
echo "ARTIFACTS_CHANGED=false" >> "$GITHUB_ENV"
else
BRANCH="safety-artifacts/${TAG}"
git checkout -B "$BRANCH"
git commit \
-m "chore: regenerate safety artifacts for $TAG" \
-m "Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
git push --force origin "$BRANCH"
echo "ARTIFACTS_CHANGED=true" >> "$GITHUB_ENV"
echo "ARTIFACTS_BRANCH=$BRANCH" >> "$GITHUB_ENV"
fi
- name: Open review PR for regenerated artifacts
if: env.ARTIFACTS_CHANGED == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
TAG="${{ github.ref_name }}"
BRANCH="${{ env.ARTIFACTS_BRANCH }}"
if gh pr view "$BRANCH" --json url >/dev/null 2>&1; then
echo "PR for $BRANCH already exists, skipping create."
else
{
echo "Automated regeneration of cpp-FuSa safety/compliance evidence artifacts"
echo "(FMEA, safety case, TARA, SBOM, gap reports, coverage report, SAS/SCI,"
echo "audit pack, badge, compliance report, qualification report) for release"
echo "${TAG}."
echo ""
echo "This PR is opened by \`release.yml\`'s \`regenerate-safety-artifacts\` job"
echo "instead of pushing directly to \`main\`, so a human reviews the diff (and"
echo "CI runs against it) before these files land -- see issue #82 / cpp-RCP-14."
} > "$RUNNER_TEMP/pr-body.md"
gh pr create \
--base main \
--head "$BRANCH" \
--title "chore: regenerate safety artifacts for $TAG" \
--body-file "$RUNNER_TEMP/pr-body.md"
fi
- name: Upload release artifacts
uses: actions/upload-artifact@v4
with:
name: release-safety-artifacts-${{ github.ref_name }}
path: |
fmea.json
safety-case.json
tooling/tara.json
sbom.json
provenance.json
artifact-manifest.json
iso26262-gap-report.json
iec61508-gap-report.json
do178-gap-report.json
iec62443-gap-report.json
coverage-report.json
audit-pack.zip
fusa-badge.svg
report.json