Repository navigation
261 lines (235 loc) · 10.3 KB
/
Copy pathsbom.yml
File metadata and controls
261 lines (235 loc) · 10.3 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
# SBOM: CycloneDX JSON from lockfile-aligned export (Phase A), then Syft on built image (Phase B).
# ADR 0003 — workflow artifacts; attach to GitHub Release when a release exists for the tag.
# #1891: on tag/release, Sigstore OIDC provenance (`actions/attest-build-provenance`) and
# upload `data-boar.intoto.jsonl` as a GitHub Release asset (Scorecard Signed-Releases).
# No Cosign private key. Does not change publish-pypi.yml (PyPI Trusted Publishing).
#
# Syft runs in the official `anchore/syft` image (version tag pinned below; bump with care).
# Third-party Actions use full commit SHAs (see ADR 0005).
name: SBOM
permissions:
contents: read
'on':
push:
tags: ["v*"]
release:
types: [published, created]
pull_request:
branches: [main, master]
paths:
- "uv.lock"
- "pyproject.toml"
- "requirements.txt"
- "Dockerfile"
- ".github/workflows/sbom.yml"
- "scripts/generate-sbom.ps1"
- "scripts/generate-sbom.sh"
- "scripts/check-sbom.sh"
- "scripts/check-sbom.ps1"
- "scripts/emit-provenance.sh"
- "scripts/emit-provenance.ps1"
- "scripts/emit_provenance.py"
- "scripts/application_sbom.py"
- "rust/boar_fast_filter/Cargo.lock"
- "rust/boar_fast_filter/Cargo.toml"
- "scripts/generate_build_digest.py"
- "scripts/generate_release_manifest.py"
- "core/**/*.py"
- "main.py"
workflow_dispatch:
concurrency:
group: sbom-${{ github.workflow }}-${{ github.event.release.tag_name || github.ref }}
cancel-in-progress: true
jobs:
generate:
name: Generate SBOMs (CycloneDX + Syft)
runs-on: ubuntu-latest
timeout-minutes: 45
# Fork PRs: token cannot upload artifacts / release assets; same-repo PRs still validate SBOM generation.
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
- name: Install uv
uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0
with:
version: "0.11.2"
enable-cache: false
# Same apt mirror pin + timed install as ci.yml (#1646 / #1648). Do not
# inline a bare apt-get update here — azure.archive.ubuntu.com stalls hang
# the 45m SBOM job (PRs #1691 / #1695).
- name: Install libmariadb-dev (mariadb PyPI connector builds from source on Linux)
uses: $/.github/actions/install-libmariadb-dev
- name: Install build-essential
run: timeout 240 sudo apt-get install -y build-essential
- name: Install dependencies
run: uv sync --group dev
- name: Export requirements from lockfile
run: uv export --no-emit-package pyproject.toml -o requirements-sbom.txt
- name: Generate CycloneDX JSON (Python / lockfile-aligned)
run: >-
uv run cyclonedx-py requirements requirements-sbom.txt
--pyproject pyproject.toml
--sv 1.6
--of JSON
--output-reproducible
-o sbom-python.cdx.json
- name: Merge Cargo.lock into application CycloneDX (issue 1950)
run: |
uv run python scripts/application_sbom.py merge \
--python-bom sbom-python.cdx.json \
--out sbom-python.cdx.json \
--canonical
uv run python scripts/application_sbom.py check \
--application sbom-python.cdx.json
- name: Generate build digest (licensing tamper check)
run: uv run python scripts/generate_build_digest.py --quiet
- name: Stage build digest for release verification
run: cp core/licensing/_build_digest.txt build-digest.txt
- name: Build image for SBOM (same Dockerfile as publish)
run: docker build -t data_boar:sbom -f Dockerfile .
- name: Generate release manifest inside image
run: |
mkdir -p dist
chmod a+w dist
docker run --rm -v "$PWD/dist:/out" -w /app data_boar:sbom \
python scripts/generate_release_manifest.py --out /out/release-manifest.json --quiet
cp dist/release-manifest.json release-manifest.json
- name: Verify release manifest
run: |
docker run --rm -v "$PWD:/work" -w /app data_boar:sbom \
python scripts/generate_release_manifest.py --check /work/release-manifest.json --quiet
- name: Generate image SBOM (Syft → CycloneDX JSON)
run: |
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v "$PWD:/out" \
anchore/syft:v1.28.0 \
scan docker:data_boar:sbom \
-o cyclonedx-json=/out/sbom-docker-image.cdx.json
- name: Canonical runtime copy and SBOM check (issue 1950)
run: |
mkdir -p sbom
cp -f sbom-docker-image.cdx.json sbom/sbom-runtime.cdx.json
uv run python scripts/application_sbom.py check \
--application sbom-python.cdx.json \
--runtime sbom-docker-image.cdx.json
- name: Emit local unsigned provenance record (issue 1950)
run: |
uv run python scripts/emit_provenance.py emit --require-sboms
uv run python scripts/emit_provenance.py verify --require-sboms
- name: Upload workflow artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: sbom-cyclonedx-${{ github.sha }}
path: |
sbom-python.cdx.json
sbom-docker-image.cdx.json
sbom/sbom-application.cdx.json
sbom/sbom-runtime.cdx.json
sbom/provenance-local.json
build-digest.txt
release-manifest.json
if-no-files-found: error
attest-and-attach:
name: Attest and attach SBOMs to GitHub Release
needs: [generate]
if: >-
${{
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')) ||
github.event_name == 'release'
}}
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
id-token: write
attestations: write
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Download SBOM artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: sbom-cyclonedx-${{ github.sha }}
path: .
- name: Attest release artifacts (Sigstore OIDC, issue 1891)
id: provenance
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
with:
subject-path: |
sbom-python.cdx.json
sbom-docker-image.cdx.json
build-digest.txt
release-manifest.json
- name: Attach SBOMs to GitHub Release (when applicable)
env:
GH_TOKEN: ${{ github.token }}
GITHUB_EVENT_RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
PROVENANCE_BUNDLE: ${{ steps.provenance.outputs.bundle-path }}
run: |
set -euo pipefail
if [ "${{ github.event_name }}" = "release" ]; then
TAG="${GITHUB_EVENT_RELEASE_TAG_NAME}"
else
TAG="${GITHUB_REF#refs/tags/}"
fi
if ! gh release view "$TAG" --repo "${{ github.repository }}" >/dev/null 2>&1; then
echo "::warning title=SBOM nao anexado::Release $TAG nao existe ainda; assets ficaram apenas no run. Crie a Release para disparar o upload."
{
echo "### SBOM nao anexado"
echo "Release \`$TAG\` inexistente no momento do push da tag."
} >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
# #1408: never --clobber release-manifest.json with a wipe.
# If the asset already exists, download MUST succeed and native_packages[]
# must be patched onto the Docker-built files[] before upload.
rm -rf /tmp/relman
mkdir -p /tmp/relman
if gh release view "$TAG" --repo "${{ github.repository }}" --json assets \
--jq '.assets[].name' | grep -Fxq 'release-manifest.json'; then
if ! gh release download "$TAG" --repo "${{ github.repository }}" \
--pattern 'release-manifest.json' --dir /tmp/relman; then
echo "::error title=release-manifest download failed::Asset exists on $TAG but download failed; refusing to clobber native_packages[] / files[]."
exit 1
fi
test -s /tmp/relman/release-manifest.json
python3 scripts/generate_release_manifest.py \
--patch-native-into release-manifest.json \
--preserve-native-from /tmp/relman/release-manifest.json \
--quiet
fi
if [ -z "${PROVENANCE_BUNDLE:-}" ] || [ ! -f "$PROVENANCE_BUNDLE" ]; then
echo "::error title=missing provenance::attest-build-provenance produced no bundle-path (#1891)"
exit 1
fi
cp "$PROVENANCE_BUNDLE" data-boar.intoto.jsonl
python3 scripts/emit_provenance.py emit --require-sboms --require-signed-attestation \
--attestation data-boar.intoto.jsonl
python3 scripts/emit_provenance.py verify --require-sboms --require-signed-attestation
gh release upload "$TAG" sbom-python.cdx.json sbom-docker-image.cdx.json build-digest.txt release-manifest.json data-boar.intoto.jsonl \
--clobber --repo "${{ github.repository }}"
slack-notify-on-failure:
name: Slack CI failure notify
if: ${{ always() && contains(needs.*.result, 'failure') }}
needs: [generate, attest-and-attach]
uses: $/.github/workflows/slack-ci-failure-notify.yml
with:
run_name: SBOM
head_branch: ${{ github.head_ref || github.ref_name }}
event: ${{ github.event_name }}
html_url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
secrets:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}