Skip to content

DEP-91 (3/5): attach SBOM and bundled-tool manifest to releases - #1687

Open
racerxdl wants to merge 4 commits into
dep-91-gatesfrom
dep-91-sbom
Open

DEP-91 (3/5): attach SBOM and bundled-tool manifest to releases#1687
racerxdl wants to merge 4 commits into
dep-91-gatesfrom
dep-91-sbom

Conversation

@racerxdl

@racerxdl racerxdl commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Stack (DEP-91)

Split from #1662 for reviewability, then rescoped: main has since landed #1681 (dependency/toolchain CVEs) and #1693 (hoophq/hoopagent), which superseded two PRs of the original stack.

Merge in order, bottom-up. Each PR targets the one below it.

The remaining work is what main still lacks: the fat image's kernel-header CVEs, and any vulnerability gate, SBOM, or scheduled OS patching for hoophq/hoopagent.


What

Generates CycloneDX/SPDX SBOMs and a per-tag release manifest for the published images, recording each tag's index digest, per-platform child digests, and (for the fat agent) bundled CLI versions.

Covers hoophq/hoop, hoophq/hoopdev, and both hoophq/hoopagent flavours.

Why

Makes shipped image contents auditable, and prevents reported tool versions from drifting from what actually shipped.

Note on hoopagent tags

hoophq/hoopagent deliberately publishes no unsuffixed tag and no :latest — every deployment names its flavour. The image list therefore carries an explicit tag suffix per entry (("hoophq/hoopagent", "-minimal")) instead of assuming repo:tag resolves. Assuming a bare tag would have made the SBOM job silently skip both flavours.

hoopagent is intentionally not in REQUIRED_IMAGE_REPOS, so a lagging publish warns rather than failing the release.

How to test

python3 -m unittest scripts/ci/test_generate_sbom_manifest.py

Expected: tests pass. Registry visibility probes stay non-raising; Trivy failures propagate.

Part of DEP-91.

Automated by MisterMal

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Attach Trivy SBOMs and release manifest to GitHub releases

✨ Enhancement ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add a release job that generates per-platform CycloneDX/SPDX SBOMs and a JSON manifest.
• Upload SBOMs/manifest as GitHub release assets, without blocking publishing on failures.
• Harden tooling/version reporting (AWS CLI pin; non-blocking Trivy report steps).
Diagram

graph TD
  n1["GitHub Actions: release.yml"] --> n2["Job: sbom-and-manifest"] --> n3["generate-sbom-manifest.py"]
  n3 --> n5["buildx imagetools"] --> n4[("Docker Registry")]
  n3 --> n6["Trivy container"] --> n4
  n3 --> n7["dist/sbom artifacts"] --> n8(("GitHub Release assets"))

  subgraph Legend
    direction LR
    _wf["Workflow/Job"] ~~~ _ext[("External system")] ~~~ _out(("Published artifact"))
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use Trivy GitHub Action + built-in upload steps
  • ➕ Less custom scripting/maintenance
  • ➕ Standardized action inputs and caching patterns
  • ➖ Harder to do digest-pinned, per-platform scans with consistent manifest semantics
  • ➖ Less control over retry/skip behavior for lagging tags
2. Publish SBOMs as OCI artifacts (ORAS/Cosign attestations)
  • ➕ Co-locates SBOM/attestations with images in the registry
  • ➕ Better alignment with emerging supply-chain verification workflows
  • ➖ More tooling and trust-policy complexity than simple GitHub release assets
  • ➖ May be overkill if consumers primarily read GitHub releases

Recommendation: Current approach is a good fit for DEP-91’s goal: it’s release-artifact-only, digest-aware (so outputs match shipped bytes), and per-platform (so arm64 isn’t misrepresented). Keep the GitHub release attachments for accessibility; consider OCI-native attestations later if downstream verification workflows demand it.

Files changed (4) +349 / -3

Enhancement (1) +297 / -0
generate-sbom-manifest.pyGenerate per-platform SBOMs and release-manifest.json for published images +297/-0

Generate per-platform SBOMs and release-manifest.json for published images

• Adds a script that resolves multi-arch image digests (with retries), generates CycloneDX and SPDX SBOMs per platform via a digest-pinned Trivy container, and emits a compact manifest with index/child digests, base OS, component counts, and CRITICAL/HIGH vuln totals. Reads bundled-tool versions from Dockerfile.tools ARG defaults for the fat agent image and ensures outdir/cache paths are absolute for Docker bind mounts.

scripts/ci/generate-sbom-manifest.py

Bug fix (1) +1 / -1
Dockerfile.toolsDownload AWS CLI from versioned archive URL +1/-1

Download AWS CLI from versioned archive URL

• Updates the AWS CLI installer download URL to include the configured AWS_CLI_VERSION, preventing drift from the intended pinned version.

Dockerfile.tools

Other (2) +51 / -2
agent-tools-build.ymlPin AWS CLI version and make Trivy report steps truly non-blocking +5/-2

Pin AWS CLI version and make Trivy report steps truly non-blocking

• Adds an AWS_CLI_VERSION value to the tool-version outputs. Marks the report-only Trivy vuln steps continue-on-error and removes a pipefail hazard by slicing inside jq and guarding failures, preventing report JSON issues from blocking publishing.

.github/workflows/agent-tools-build.yml

release.ymlAdd non-blocking SBOM + manifest job and upload assets to release +46/-0

Add non-blocking SBOM + manifest job and upload assets to release

• Introduces a new sbom-and-manifest job gated behind publish-release (and minimal multiarch publishing) to generate SBOMs and a release-manifest.json. Logs into Docker Hub to avoid rate limits, runs the generator script, and uploads outputs to the GitHub release with clobber semantics.

.github/workflows/release.yml

@qodo-code-review

qodo-code-review Bot commented Aug 6, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📎 Requirement gaps (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. SBOM job skipped on failure ⊘ Outdated 🐞 Bug ☼ Reliability
Description
The new sbom-and-manifest job depends on docker-publish-hoopdev-minimal-multiarch; if that
upstream job fails, GitHub Actions can skip sbom-and-manifest, preventing SBOM/manifest upload for
all images even though the generator already tolerates missing tags.
Code

.github/workflows/release.yml[R1345-1348]

+    needs:
+      - publish-release
+      - docker-publish-hoopdev-minimal-multiarch
+    continue-on-error: true
Relevance

●●● Strong

Workflow reliability/robustness changes are typically accepted; always()-style gating is a small,
safe fix.

PR-#1031
PR-#1072
PR-#1595

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
sbom-and-manifest is configured with a hard needs dependency on the minimal multi-arch publish
job. That upstream job is marked continue-on-error: true (i.e., it may fail), and without an
explicit always()-style condition the SBOM job can be skipped when a needed job fails; this
bypasses the generator’s built-in “skip missing tags” behavior because the generator never runs.

.github/workflows/release.yml[1341-1377]
.github/workflows/release.yml[979-1016]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`sbom-and-manifest` is intended to be best-effort and should run after a release is published, even if optional image-publish jobs fail. Currently it has a hard `needs` dependency on `docker-publish-hoopdev-minimal-multiarch`, so if that job fails, `sbom-and-manifest` can be skipped and no SBOM/manifest artifacts are attached.

### Issue Context
- The SBOM generator already retries registry visibility and skips missing image tags; the job being skipped prevents that logic from running at all.
- `publish-release` should remain the real gate (only run SBOM job when the release actually shipped).

### Fix Focus Areas
- .github/workflows/release.yml[1345-1348]

### Suggested fix
Adjust the job condition so it always runs when `publish-release` succeeded, regardless of the minimal-image job result.

Example:
```yaml
sbom-and-manifest:
 needs:
   - publish-release
   - docker-publish-hoopdev-minimal-multiarch
 if: ${{ always() && needs.publish-release.result == 'success' }}
```

Alternative: remove the minimal-image job from `needs` and rely on the generator's retry/skip behavior.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. No tests for SBOM generator 📘 Rule violation ▣ Testability
Description
The PR adds a non-trivial SBOM/manifest generator with parsing and retry logic but does not
add/modify any unit tests covering common and edge cases. This increases the risk of silent
release-artifact failures (e.g., registry lag, non-index manifests, missing ARGs) going undetected.
Code

scripts/ci/generate-sbom-manifest.py[R172-175]

+def bundled_tools() -> dict:
+    """Read bundled-CLI versions from Dockerfile.tools' ARG defaults.
+
+    Best-effort: an ARG that is renamed or removed is simply omitted (the SBOMs
Relevance

●● Moderate

They sometimes push back on adding broader tests; no close precedent for requiring unit tests for
new CI scripts.

PR-#1515

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1807237 requires unit tests for new/changed helpers/parsers. The PR introduces new
parsing helpers (e.g., Dockerfile ARG parsing in bundled_tools()), but the diff includes no added
or updated test files for this logic.

Rule 1807237: For new or changed helpers/parsers, add unit tests covering common and edge formats
scripts/ci/generate-sbom-manifest.py[172-188]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A new helper/parsing script (`scripts/ci/generate-sbom-manifest.py`) was added without accompanying unit tests, despite containing parsing/normalization and retry/branching logic.

## Issue Context
This script impacts release artifact generation (SBOMs + `release-manifest.json`). Unit tests should cover at least: multi-arch index vs single-manifest behavior, registry visibility retry/skip logic, Dockerfile ARG parsing, and Trivy JSON summarization.

## Fix Focus Areas
- scripts/ci/generate-sbom-manifest.py[75-115]
- scripts/ci/generate-sbom-manifest.py[117-131]
- scripts/ci/generate-sbom-manifest.py[147-165]
- scripts/ci/generate-sbom-manifest.py[172-188]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context
✅ Compliance rules (platform): 39 rules
✅ Cross-repo context
  Not relevant to this PR: hoophq/libhoop
  Not relevant to this PR: hoophq/infra
  Not relevant to this PR: hoophq/documentation
  Not relevant to this PR: hoophq/changelog

Grey Divider

Tip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines +172 to +175
def bundled_tools() -> dict:
"""Read bundled-CLI versions from Dockerfile.tools' ARG defaults.

Best-effort: an ARG that is renamed or removed is simply omitted (the SBOMs

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

1. No tests for sbom generator 📘 Rule violation ▣ Testability

The PR adds a non-trivial SBOM/manifest generator with parsing and retry logic but does not
add/modify any unit tests covering common and edge cases. This increases the risk of silent
release-artifact failures (e.g., registry lag, non-index manifests, missing ARGs) going undetected.
Agent Prompt
## Issue description
A new helper/parsing script (`scripts/ci/generate-sbom-manifest.py`) was added without accompanying unit tests, despite containing parsing/normalization and retry/branching logic.

## Issue Context
This script impacts release artifact generation (SBOMs + `release-manifest.json`). Unit tests should cover at least: multi-arch index vs single-manifest behavior, registry visibility retry/skip logic, Dockerfile ARG parsing, and Trivy JSON summarization.

## Fix Focus Areas
- scripts/ci/generate-sbom-manifest.py[75-115]
- scripts/ci/generate-sbom-manifest.py[117-131]
- scripts/ci/generate-sbom-manifest.py[147-165]
- scripts/ci/generate-sbom-manifest.py[172-188]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread .github/workflows/release.yml
@racerxdl racerxdl changed the title DEP-91 (5/7): attach SBOM and bundled-tool manifest to releases DEP-91 (3/5): attach SBOM and bundled-tool manifest to releases Aug 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Migration Safety Analysis

No database migrations were changed in this PR. Safe to deploy to sandbox.

@sandromello

Copy link
Copy Markdown
Contributor

✅ Build Completed with Success, Version=1687.0.0-g5f1a276

racerxdl and others added 4 commits August 12, 2026 14:52
Part of DEP-91. New sbom-and-manifest release job (non-blocking, gated
behind publish-release) generates per-platform CycloneDX + SPDX SBOMs and a
release-manifest.json (index digest, per-platform child digest, base OS,
component count, Trivy CRITICAL/HIGH summary) for hoop, hoopdev and
hoopdev-minimal, then attaches them to the GitHub release so downstream
users can verify shipped components and fixes without re-scanning.

 🤖 Generated with Mister Maluco

Co-Authored-By: MisterMal <teskeslab@lucasteske.dev>
Part of DEP-91.

The SBOM job invoked the generator with a relative outdir that was then
bind-mounted into the Trivy container; Docker rejects a relative path as a
volume name, so the (continue-on-error) job never attached SBOMs. Resolve
outdir to an absolute path before mounting.

The agent-tools report-only vuln steps ended a jq summary with `| head`
under pipefail: a failed or empty Trivy scan yields invalid JSON, jq exits
nonzero, and pipefail failed the step -- blocking the push despite being
"non-blocking". Slice inside jq, guard it, and mark the steps
continue-on-error so a report hiccup can never block publishing.

 🤖 Generated with Mister Maluco

Co-Authored-By: MisterMal <teskeslab@lucasteske.dev>
Part of DEP-91.

- SBOM: scan each platform by its immutable child digest (repo@sha256:...)
  so the SBOM matches the digest recorded in the manifest; a non-index tag
  resolves its real platform and scans by digest (never assumes amd64).
- Manifest: record bundled CLI versions for the fat hoophq/hoopdev image
  (kubectl/sqlcmd/mongosh/mongodb-tools/node/aws-cli/gcloud/gke-plugin),
  read from Dockerfile.tools ARG defaults, per issue #1643.
- AWS CLI: install the pinned version via the versioned installer URL using
  the previously-unused AWS_CLI_VERSION ARG; the clean train pins 2.36.14,
  legacy keeps 2.28.6.

 🤖 Generated with Mister Maluco

Co-Authored-By: MisterMal <teskeslab@lucasteske.dev>
Part of DEP-91. Generate CycloneDX/SPDX SBOMs and a per-tag release manifest
for the published images, recording each tag's index digest, per-platform child
digests, and (for the fat agent) bundled CLI versions read from Dockerfile.tools
ARG defaults.

Covers hoophq/hoop, hoophq/hoopdev and both hoophq/hoopagent flavours. Because
hoopagent publishes no unsuffixed tag, the image list carries an explicit tag
suffix per entry rather than assuming a bare release tag exists.

Fixes #DEP-91

 🤖 Generated with Mister Maluco

Co-Authored-By: MisterMal <teskeslab@lucasteske.dev>
@sandromello

Copy link
Copy Markdown
Contributor

✅ Build Completed with Success, Version=1687.0.0-ge80324a

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-release Merges without publishing a release (docs, CI, refactors, etc.)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants