No description
Find a file
Matthias Leinweber 9369e2d006
feat: fs scans + client/server mode in trivy-sbom; add sbom-merge
Make the SBOM actions cover opsroom's flow (image scan + webapp filesystem
scan, merged into one BOM, uploaded to DT), not just rahla's single-image
case:

trivy-sbom:
- scan-type input (image|fs) + path input -> supports `trivy fs <path>`.
- server-url input -> client/server mode (--server) for a remote Trivy.
- image is now required only when scan-type=image.

sbom-merge (new): thin cyclonedx-cli wrapper that merges N CycloneDX BOMs.
- Passes each file as its own --input-files flag (cyclonedx-cli treats a
  space-joined value as a single filename).
- Sets the .NET CLI opt-out env + a writable DOTNET_CLI_HOME to avoid the
  first-run dotnet-suggest exception that can abort a cold merge, and
  guards that the merge actually produced a file.
- Verified live end-to-end (fs scan + merge of two real CycloneDX 1.6
  BOMs -> 453 components).

dtrack-upload already covers opsroom's upload (and fixes its manual
Content-Type: multipart/form-data header, which clobbers curl's boundary).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:50:18 +02:00
dtrack-upload refactor: split action scripts into named steps + fix upload error handling 2026-07-15 11:24:49 +02:00
notify-talk fix(notify-talk): Markdown-Umbrueche (Talk soft-break) + Default-Titel = Workflow-Name 2026-06-08 16:21:42 +00:00
sbom-merge feat: fs scans + client/server mode in trivy-sbom; add sbom-merge 2026-07-15 13:50:18 +02:00
trivy-sbom feat: fs scans + client/server mode in trivy-sbom; add sbom-merge 2026-07-15 13:50:18 +02:00
README.md feat: fs scans + client/server mode in trivy-sbom; add sbom-merge 2026-07-15 13:50:18 +02:00

forgejo-actions

Shared composite actions for Forgejo / Codeberg CI.

  • notify-talk — post a build/run status into a Nextcloud Talk channel
  • trivy-sbom — CycloneDX SBOM of a container image or filesystem
  • sbom-merge — merge several CycloneDX SBOMs into one
  • dtrack-upload — publish a BOM to Dependency-Track

notify-talk — Nextcloud Talk build/run notification

Posts a Markdown status message into a Nextcloud Talk channel. Use it as a final if: always() step so it reports both success and failure.

      - name: Notify Nextcloud Talk
        if: always()
        uses: https://codeberg.org/datatactics/forgejo-actions/notify-talk@main
        with:
          status: ${{ job.status }}
        env:
          TALK_BOT_PASSWORD: ${{ secrets.TALK_BOT_PASSWORD }}
          TALK_ROOM: ${{ vars.TALK_ROOM }}
          TALK_USER: ${{ vars.TALK_USER }}

Inputs

Input Required Default Description
status yes Job result; pass ${{ job.status }} → renders ✅ / ❌ / ⚪ / ❓
title no Build Header text → ### <icon> <title>
message no commit message Body line; falls back to the push commit message when empty

Required environment (provided by the caller)

The action embeds no credentials and no instance config — the calling job supplies everything via env::

Env Suggested source Value
TALK_BOT_PASSWORD repository/org secret Nextcloud app-password of the bot user
TALK_ROOM variable Talk conversation token (the last path segment of the chat URL)
TALK_USER variable Nextcloud bot username

The action fails fast (set -eu) if any of the three is unset. TALK_ROOM accepts either the bare token or a full chat URL (the last path segment is used).

Custom message example

      - name: Notify Nextcloud Talk
        if: always()
        uses: https://codeberg.org/datatactics/forgejo-actions/notify-talk@main
        with:
          status: ${{ needs.deploy.result }}
          title: "Deploy"
          message: "Release ${{ github.ref_name }} deployed (${{ needs.deploy.result }})"
        env:
          TALK_BOT_PASSWORD: ${{ secrets.TALK_BOT_PASSWORD }}
          TALK_ROOM: ${{ vars.TALK_ROOM }}
          TALK_USER: ${{ vars.TALK_USER }}

Rendered into Talk:

### ✅ Deploy
**Repo:** <owner>/<repo>  **Branch:** <branch>  **Author:** <actor>
Release v1.4.0 deployed (success)
[View run](<server>/<owner>/<repo>/actions/runs/<n>)

trivy-sbom — CycloneDX SBOM of a container image or filesystem

Scans a container image or a filesystem path with Trivy (standalone or client/server mode), writes a CycloneDX SBOM, optionally strips licenses, and fails the job if the BOM has no components (guards against silently emitting an empty BOM after a transient scanner/registry hiccup). Pairs with sbom-merge and dtrack-upload.

      - name: Trivy SBOM
        id: sbom
        uses: https://codeberg.org/datatactics/forgejo-actions/trivy-sbom@main
        with:
          image: docker.io/datatactics/rahla:${{ steps.meta.outputs.version }}
          output: sbom_rahla_${{ steps.meta.outputs.version }}.json
        env:                                   # optional — only for a private registry
          TRIVY_USERNAME: ${{ vars.DOCKERHUB_USER }}
          TRIVY_PASSWORD: ${{ secrets.DOCKERHUB_PASS }}

Filesystem scan against a remote Trivy server:

      - uses: https://codeberg.org/datatactics/forgejo-actions/trivy-sbom@main
        with:
          scan-type: fs
          path: webapp/
          server-url: http://trivy.dependencytrack.svc.cluster.local
          output: sbom_webapp.json

Inputs

Input Required Default Description
scan-type no image image (a container image) or fs (a filesystem path)
image when scan-type=image "" Image ref to scan
path no . Filesystem path to scan (used when scan-type=fs)
server-url no "" Trivy client/server URL — when set, scans in client mode (--server)
output no sbom.json SBOM filename
format no cyclonedx Trivy output format
scanners no vuln Trivy scanners
severity no MEDIUM,HIGH,CRITICAL Trivy severities
trivy-version no v0.58.1 Trivy release tag to install (only when trivy is not already on PATH)
strip-licenses no false Delete .licenses from components + metadata.component. Dependency-Track v4 rejected Trivy's license format; v5.0.2 ingests it fine (verified 2026-07) — set true only if you still upload to a DT v4 instance
min-components no 1 Fail if the SBOM has fewer than this many components

Outputs

Output Description
sbom-file Path to the generated SBOM
component-count Number of components in the SBOM

Registry credentials are not action inputs — Trivy reads TRIVY_USERNAME / TRIVY_PASSWORD (and other TRIVY_*) natively, so pass them via the caller's env:.


sbom-merge — merge CycloneDX SBOMs

Merges two or more CycloneDX SBOMs into one with the CycloneDX CLI — e.g. combine a container-image scan with a filesystem scan before uploading a single project to Dependency-Track.

      - name: Scan image
        uses: https://codeberg.org/datatactics/forgejo-actions/trivy-sbom@main
        with: { image: "${{ env.IMAGE_REF }}", server-url: "${{ env.TRIVY_SERVER }}", output: sbom_image.json }
      - name: Scan webapp/
        uses: https://codeberg.org/datatactics/forgejo-actions/trivy-sbom@main
        with: { scan-type: fs, path: webapp/, server-url: "${{ env.TRIVY_SERVER }}", output: sbom_webapp.json }
      - name: Merge
        id: sbom
        uses: https://codeberg.org/datatactics/forgejo-actions/sbom-merge@main
        with:
          input-files: sbom_image.json sbom_webapp.json
          output: sbom_merged.json
      - name: Upload
        uses: https://codeberg.org/datatactics/forgejo-actions/dtrack-upload@main
        with: { bom: "${{ steps.sbom.outputs.sbom-file }}", project-name: opsroom, project-version: "${{ github.ref_name }}" }
        env:  { DTRACK_URL: "${{ vars.DTRACK_URL }}", DTRACK_API_KEY: "${{ secrets.DTRACK_API_KEY }}" }

Inputs

Input Required Default Description
input-files yes Space-separated list of BOM files to merge
output no sbom_merged.json Merged SBOM filename
output-version no v1_6 CycloneDX output spec version
cyclonedx-version no v0.32.0 CycloneDX CLI release tag to install (only when cyclonedx is not on PATH)

Outputs

Output Description
sbom-file Path to the merged SBOM
component-count Number of components in the merged SBOM

dtrack-upload — publish a BOM to Dependency-Track

POSTs a BOM to a Dependency-Track instance (multipart bom upload, autoCreate), then verifies DT returned a processing token — a rejected upload fails the job instead of going green. Optionally polls until DT finishes processing.

      - name: Upload SBOM to Dependency-Track
        uses: https://codeberg.org/datatactics/forgejo-actions/dtrack-upload@main
        with:
          bom: ${{ steps.sbom.outputs.sbom-file }}
          project-name: datatactics/rahla
          project-version: ${{ steps.meta.outputs.version }}
          parent-uuid: cff07b40-c95c-47e9-9367-cb9e8df75dcd
        env:
          DTRACK_URL: ${{ vars.DTRACK_URL }}          # the .../api/v1/bom endpoint
          DTRACK_API_KEY: ${{ secrets.DTRACK_API_KEY }}

Inputs

Input Required Default Description
bom yes Path to the BOM file to upload
project-name yes Dependency-Track projectName
project-version yes Dependency-Track projectVersion
parent-uuid no "" Parent project UUID (field omitted when empty)
auto-create no true Create the project if it does not exist
verify-non-empty no true Refuse to upload a BOM with 0 components
wait-for-processing no false Poll DT until the BOM finishes processing
timeout-seconds no 120 Max wait when wait-for-processing: true

Outputs

Output Description
token Dependency-Track processing token

Required environment (provided by the caller)

Env Suggested source Value
DTRACK_URL variable The .../api/v1/bom endpoint
DTRACK_API_KEY secret DT API key with BOM_UPLOAD (+ PROJECT_CREATION_UPLOAD when auto-create: true) permission

Setup (notify-talk)

The credentials and channel config live outside the action:

  • A secret TALK_BOT_PASSWORD — the Nextcloud app-password of the bot user (repository- or organization-level).
  • Variables TALK_ROOM (channel token) and TALK_USER (bot username) — repository-, organization-, or instance-level so they can be shared.

The Nextcloud base URL is configured inside the action.


Conventions

  • Use the full action URL. A bare owner/repo@ref resolves against the default actions registry, not the repo you expect — always reference the full https://codeberg.org/... URL.
  • Pin the ref. @main tracks latest; pin a tag (e.g. @v1) once releases are tagged for reproducible builds.