Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions .github/actions/docker-init/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Copyright (C) 2025 Siemens
#
# SPDX-License-Identifier: MIT
#
# Derived from the https://github.com/siemens/kas docker-init action

name: docker-init

inputs:
deploy-user:
required: true
deploy-token:
required: true

runs:
using: composite
steps:
- name: Set up QEMU
shell: bash
env:
QEMU_USER_STATIC_PACKAGE: qemu-user-static_7.2+dfsg-7+deb12u12_amd64.deb
REPO_DATE: 20250130T084806Z
PACKAGE_SHA256: 1a2696081c1f30d464f79fd300196822397c77f05440ea9ce6dc8e9658b595ec
run: |
# temporarily use Debian qemu-user-static until Ubuntu fixes theirs
wget -q http://snapshot.debian.org/archive/debian/${REPO_DATE}/pool/main/q/qemu/${QEMU_USER_STATIC_PACKAGE}
echo "${PACKAGE_SHA256} ${QEMU_USER_STATIC_PACKAGE}" | sha256sum -c
sudo dpkg -i ${QEMU_USER_STATIC_PACKAGE}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver-opts: image=moby/buildkit:v0.16.0

- name: Login to ghcr.io
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ inputs.deploy-user }}
password: ${{ inputs.deploy-token }}

- name: Set SOURCE_DATE_EPOCH
run: |
echo "SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)" >> $GITHUB_ENV
shell: bash

- name: Determine Debian tag
run: |
COMMIT_DATE=$(date -d @$(git log -1 --pretty=%ct) +%Y%m%d)
DEBIAN_RELEASE=$(grep -m 1 'ARG DEBIAN_TAG=' Dockerfile | sed 's/.*DEBIAN_TAG=\(.*\)-.*/\1/')
echo "DEBIAN_TAG=$(podman search --list-tags docker.io/debian --limit 1000000000 | \
grep "$DEBIAN_RELEASE-.*-slim" | sort -r | sed 's/.*[ ]\+//' | \
./scripts/lower-bound.py $DEBIAN_RELEASE-$COMMIT_DATE-slim )" \
>> $GITHUB_ENV
shell: bash

- name: Prepare repository for COPY-in
run: |
git clone . /home/runner/debsbom-clone
shell: bash

- name: Define image metadata
run: |
echo "IMAGE_DESCRIPTION=debsbom generates (Software Bill of Materials) for distributions based on Debian" >> $GITHUB_ENV
# make image metadata reproducible (also for image re-builders)
echo "IMAGE_COMMIT_DATE=$(date -d @$(git log -1 --pretty=%ct) --iso-8601=seconds)" >> $GITHUB_ENV
echo "IMAGE_OFFICIAL_URL=https://github.com/siemens/debsbom" >> $GITHUB_ENV
shell: bash

- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
annotations: |
org.opencontainers.image.description=${{ env.IMAGE_DESCRIPTION }}
org.opencontainers.image.licenses=MIT
org.opencontainers.image.created=${{ env.IMAGE_COMMIT_DATE }}
org.opencontainers.image.source=${{ env.IMAGE_OFFICIAL_URL }}
org.opencontainers.image.url=${{ env.IMAGE_OFFICIAL_URL }}
env:
DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index

- name: Cache apt
id: cache-apt
uses: actions/cache@v4
with:
path: |
var-cache-apt
var-lib-apt
key: cache-apt-${{ env.DEBIAN_TAG }}-${{ inputs.image-name }}

- name: Inject cache into docker
uses: reproducible-containers/buildkit-cache-dance@5b81f4d29dc8397a7d341dba3aeecc7ec54d6361 #v3.3.0
with:
cache-map: |
{
"var-cache-apt": "/var/cache/apt",
"var-lib-apt": "/var/lib/apt"
}
skip-extraction: ${{ steps.cache.outputs.cache-hit }}
81 changes: 81 additions & 0 deletions .github/workflows/containers.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Copyright (C) 2025 Siemens
#
# SPDX-License-Identifier: MIT
name: Containers

on:
push:

jobs:
build_containers:
name: Build, test and deploy container images
runs-on: ubuntu-24.04
permissions:
id-token: write
packages: write
contents: read
attestations: write
steps:
- name: Check out repo
uses: actions/checkout@v4
- name: Set up docker build
uses: ./.github/actions/docker-init
with:
deploy-user: ${{ github.actor }}
deploy-token: ${{ secrets.GITHUB_TOKEN }}

- name: Build debsbom image
uses: docker/build-push-action@v6
with:
context: /home/runner/debsbom-clone
target: debsbom
platforms: linux/amd64
build-args: |
SOURCE_DATE_EPOCH=${{ env.SOURCE_DATE_EPOCH }}
DEBIAN_TAG=${{ env.DEBIAN_TAG }}
outputs: type=docker,rewrite-timestamp=true
tags: ghcr.io/${{ github.repository }}:test
- name: Test debsbom image
run: |
docker run ghcr.io/${{ github.repository }}:test debsbom --version
mkdir downloads
echo "guestfs-tools 1.52.3-1 source" | \
docker run -v$(pwd)/downloads:/mnt/downloads -i ghcr.io/${{ github.repository }}:test \
debsbom download --outdir /mnt/downloads --sources
find downloads/sources -name "guestfs-tools*" | grep .
- name: Complete build and deploy image
if: github.ref == 'refs/heads/main'
uses: docker/build-push-action@v6
id: push
with:
context: /home/runner/debsbom-clone
target: debsbom
platforms: linux/amd64,linux/arm64
build-args: |
SOURCE_DATE_EPOCH=${{ env.SOURCE_DATE_EPOCH }}
DEBIAN_TAG=${{ env.DEBIAN_TAG }}
provenance: false
outputs: type=registry,rewrite-timestamp=true
tags: ghcr.io/${{ github.repository }}:latest
annotations: ${{ env.DOCKER_METADATA_OUTPUT_ANNOTATIONS }}
- name: Attest image
if: github.ref == 'refs/heads/main'
uses: actions/attest-build-provenance@v1
with:
subject-name: ghcr.io/${{ github.repository }}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true

cleanup_ghcr_containers:
name: cleanup untagged containers
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-24.04
needs: build_containers
permissions:
packages: write
steps:
- uses: dataaxiom/ghcr-cleanup-action@cd0cdb900b5dbf3a6f2cc869f0dbb0b8211f50c4 #v1.0.16
with:
dry-run: false
validate: true
token: ${{ secrets.GITHUB_TOKEN }}
43 changes: 43 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,46 @@ jobs:
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e
with:
packages-dir: dist/

deploy_containers:
name: Build and deploy container images
runs-on: ubuntu-24.04
permissions:
id-token: write
packages: write
contents: read
attestations: write
steps:
- name: Check out repo
uses: actions/checkout@v4
- name: Get release
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
- name: Set up docker build
uses: ./.github/actions/docker-init
with:
deploy-user: ${{ github.actor }}
deploy-token: ${{ secrets.GITHUB_TOKEN }}
- name: Find latest tag
run: echo "LATEST_TAG=$(git tag | sort --version-sort | tail -n1)" >> $GITHUB_ENV
- name: Build image
uses: docker/build-push-action@v6
id: push
with:
context: /home/runner/debsbom-clone
target: debsbom
platforms: linux/amd64,linux/arm64
build-args: |
SOURCE_DATE_EPOCH=${{ env.SOURCE_DATE_EPOCH }}
DEBIAN_TAG=${{ env.DEBIAN_TAG }}
provenance: false
outputs: type=registry,rewrite-timestamp=true
tags: |
ghcr.io/${{ github.repository }}:${{ env.RELEASE_VERSION }}
${{ github.ref_name == env.LATEST_TAG && format('ghcr.io/{0}:latest-release', github.repository) || '' }}
annotations: ${{ env.DOCKER_METADATA_OUTPUT_ANNOTATIONS }}
- name: Attest image
uses: actions/attest-build-provenance@v1
with:
subject-name: ghcr.io/${{ github.repository }}
subject-digest: ${{ steps.push.outputs.digest }}
push-to-registry: true
56 changes: 56 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Copyright (C) 2025 Siemens
#
# SPDX-License-Identifier: MIT

ARG DEBIAN_TAG=trixie-slim

FROM debian:${DEBIAN_TAG} AS debsbom

ARG SOURCE_DATE_EPOCH

ARG DEBIAN_TAG=trixie-slim

ARG TARGETPLATFORM
ARG DEBIAN_FRONTEND=noninteractive
ENV LANG=en_US.utf8
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
rm -f /etc/apt/apt.conf.d/docker-clean && \
echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-packages.conf && \
if echo "${DEBIAN_TAG}" | grep -q "[0-9]"; then \
sed -i -e '/^URIs:/d' -e 's|^# http://snapshot\.|URIs: http://snapshot.|' \
/etc/apt/sources.list.d/debian.sources; \
echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/use-snapshot.conf; \
echo 'Acquire::Retries "10";' >> /etc/apt/apt.conf.d/use-snapshot.conf; \
echo 'Acquire::Retries::Delay::Maximum "600";' >> /etc/apt/apt.conf.d/use-snapshot.conf; \
fi && \
apt-get update && \
apt-get install -y locales && \
localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 && \
apt-get install --no-install-recommends -y \
python3-apt python3-cyclonedx-lib python3-debian python3-packageurl \
python3-beartype python3-click python3-license-expression python3-ply \
python3-rdflib python3-semantic-version python3-uritools python3-xmltodict \
python3-yaml python3-zstandard python3-requests && \
rm -rf /var/log/* /tmp/* /var/tmp/* /var/cache/ldconfig/aux-cache

# install debsbom in a reproducible way
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
--mount=type=bind,target=/debsbom,rw \
apt-get update && \
apt-get install --no-install-recommends -y \
python3-pip python3-setuptools && \
pip3 --proxy=$https_proxy install \
--no-deps \
--no-build-isolation \
--break-system-packages \
--root-user-action=ignore \
spdx-tools==0.8.3 \
/debsbom && \
rm -rf $(pip3 cache dir) && \
apt-get autopurge -y python3-pip python3-setuptools && \
rm -rf /root/.cache /var/log/* /tmp/* /var/tmp/* /var/cache/ldconfig/aux-cache && \
debsbom --version

WORKDIR /var/lib/debsbom
8 changes: 8 additions & 0 deletions docs/source/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ It is also possible to download multiple packages by name, version and architect
guestfs-tools 1.52.3-1 source
EOF

Alternatively, the download can be executed from the container image:

.. code-block:: bash

echo "guestfs-tools 1.52.3-1 source" | \
docker run -v$(pwd)/downloads:/mnt/downloads -i ghcr.io/siemens/debsbom:latest \
debsbom download --outdir /mnt/downloads --sources

Merge Source Packages
~~~~~~~~~~~~~~~~~~~~~

Expand Down
8 changes: 8 additions & 0 deletions docs/source/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ Their relationship is expressed with the ``GENERATES`` relation.
For packages that are marked as ``Built-Using`` in the dpkg status file, we use the ``GENERATED_FROM`` relation.
This expresses the same semantic in SPDX, but this way it can still be identified if it is a proper source/binary relationship or a built-using one.

Container Image
---------------

The ``debsbom`` tool is available as a container image at ``ghcr.io/siemens/debsbom:<latest|tag>``.
It runs as root inside the container, allowing mounted directories (e.g., the download directory) to be owned by the invoking user in rootless environments, simplifying CI usage.

The container image is built in a bit‑for‑bit reproducible manner.
This can be verified by forking the repository, executing the CI pipeline, and comparing the hashes of the resulting container manifest.

Limitations
-----------
Expand Down
16 changes: 16 additions & 0 deletions scripts/lower-bound.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env python3
#
# Copyright (C) 2025 Siemens
#
# SPDX-License-Identifier: MIT
#
# Takes a reverse-sorted, line separated list and
# returns the first element that is equal or smaller
# than the first argument.

import sys

for line in sys.stdin:
if line.rstrip() <= sys.argv[1]:
print(line.rstrip())
break