Skip to content

Commit 788a1bf

Browse files
authored
Merge pull request #68 from linuxserver/use-build-cache-for-sbom-generation
Add commit sha, build number to create a build cache url for buildx s…
2 parents 15e9dee + 6ec608e commit 788a1bf

File tree

3 files changed

+63
-3
lines changed

3 files changed

+63
-3
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ This container is an automated testing tool for Docker images. It's designed to
2323

2424
1. **Spins up the container:** It runs the target Docker image with a specified tag.
2525
2. **Checks for successful startup:** It tails the container's logs, waiting for the `[services.d] done.` message, which confirms the init system has finished and the services are running.
26-
3. **Generates an SBOM:** It uses `syft` to create a Software Bill of Materials, providing a complete list of all packages inside the image.
26+
3. **Generates an SBOM:** It uses `buildx imagetools inspect` or `syft`(fallback) to create a Software Bill of Materials, providing a complete list of all packages inside the image.
2727
4. **Tests the Web UI (optional):** If the container runs a web service, it attempts to connect to the UI and take a screenshot to verify it's accessible and renders correctly.
2828
5. **Generates a report:** It gathers all the results—container logs, build info, SBOM, screenshots, and test statuses—into a comprehensive HTML report.
2929
6. **Uploads the report (CI only):** In a CI environment, it uploads the final report to an S3 bucket for review.
@@ -115,6 +115,8 @@ sudo docker run --rm -i \
115115
-e NODE_NAME=<optional, Name of the builder that runs the CI test.> \
116116
-e RELEASE_TAG=<optional, The release tag of the docker image. Used for upload location. Defaults to 'latest'> \
117117
-e SYFT_IMAGE_TAG=<optional, The image tag of the syft docker image. Used for generating SBOM. Defaults to '1.26.1'> \
118+
-e COMMIT_SHA=<commit sha, used for creating the sbom with buildx imagetool inspect> \
119+
-e BUILD_NUMBER=<jenkins build number, used for creating the sbom with buildx imagetool inspect>
118120
-t lsiodev/ci:latest \
119121
python3 test_build.py
120122
```

ci/ci.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,13 @@ class Platform(enum.Enum):
9393
RISCV64 = "riscv64"
9494
UNKNOWN = "amd64"
9595

96+
class BuildCacheTag(enum.Enum):
97+
"""Enum for the different build cache tags"""
98+
AMD64 = "amd64"
99+
ARM64 = "arm64v8"
100+
RISCV64 = "riscv64"
101+
UNKNOWN = "amd64"
102+
96103
class SetEnvs():
97104
"""Simple helper class that sets up the ENVs"""
98105
def __init__(self) -> None:
@@ -119,6 +126,9 @@ def __init__(self) -> None:
119126
self.bucket: str = os.environ.get("S3_BUCKET", "ci-tests.linuxserver.io")
120127
self.release_tag: str = os.environ.get("RELEASE_TAG", "latest")
121128
self.syft_image_tag: str = os.environ.get("SYFT_IMAGE_TAG", "v1.26.1")
129+
self.commit_sha: str = os.environ.get("COMMIT_SHA", "")
130+
self.build_number: str = os.environ.get("BUILD_NUMBER", "")
131+
self.build_cache_registry: str = os.environ.get("BUILD_CACHE_REGISTRY", "ghcr.io/linuxserver/lsiodev-buildcache")
122132

123133
if os.environ.get("DELAY_START"):
124134
self.logger.warning("DELAY_START env is obsolete, and not in use anymore")
@@ -169,6 +179,9 @@ def __init__(self) -> None:
169179
S3_REGION: '{os.environ.get("S3_REGION")}'
170180
S3_BUCKET: '{os.environ.get("S3_BUCKET")}'
171181
SYFT_IMAGE_TAG: '{os.environ.get("SYFT_IMAGE_TAG")}'
182+
COMMIT_SHA: '{os.environ.get("COMMIT_SHA")}'
183+
BUILD_NUMBER: '{os.environ.get("BUILD_NUMBER")}'
184+
BUILD_CACHE_REGISTRY: '{os.environ.get("BUILD_CACHE_REGISTRY")}'
172185
Docker Engine Version: '{self.get_docker_engine_version()}'
173186
""")
174187
self.logger.info(env_data)
@@ -630,6 +643,39 @@ def get_sbom_syft(self, tag: str) -> str | CITestResult:
630643
self.logger.exception("Failed to remove the Syft container, %s",tag)
631644
return CITestResult.ERROR
632645

646+
def get_build_cache_url(self, tag: str) -> str:
647+
"""Get the build cache URL for the given tag.
648+
649+
Args:
650+
tag (str): The tag we are testing
651+
Returns:
652+
str: The build cache URL ex `ghcr.io/linuxserver/lsiodev-buildcache:arm64v8-<COMMIT_SHA>-<BUILD_NUMBER>`
653+
"""
654+
platform: str = self.get_build_cache_platform(tag)
655+
build_cache_url: str = f"{self.build_cache_registry}:{platform}-{self.commit_sha}-{self.build_number}"
656+
return build_cache_url
657+
658+
def get_build_cache_platform(self, tag: str) -> str:
659+
"""Get the build cache platform for the given tag.
660+
661+
Args:
662+
tag (str): The tag we are testing
663+
664+
Returns:
665+
str: The build cache platform
666+
"""
667+
668+
platform = self.get_platform(tag)
669+
match platform:
670+
case Platform.AMD64.value:
671+
return BuildCacheTag.AMD64.value
672+
case Platform.ARM64.value:
673+
return BuildCacheTag.ARM64.value
674+
case Platform.RISCV64.value:
675+
return BuildCacheTag.RISCV64.value
676+
case _:
677+
return BuildCacheTag.UNKNOWN.value
678+
633679
def get_sbom_buildx_blob(self, tag: str) -> str | CITestResult:
634680
"""Get the SBOM for the image tag using docker buildx imagetools inspect.
635681
@@ -640,7 +686,7 @@ def get_sbom_buildx_blob(self, tag: str) -> str | CITestResult:
640686
str: SBOM output if successful, otherwise "CITestResult.ERROR".
641687
"""
642688
try:
643-
image_ref = f"{self.image}:{tag}"
689+
image_ref:str = self.get_build_cache_url(tag)
644690
self.logger.info("Generating SBOM for %s using buildx imagetools inspect", image_ref)
645691
cmd = f'docker buildx imagetools inspect {image_ref} --format "{{{{ json (index .SBOM).SPDX.packages }}}}"'
646692
result: subprocess.CompletedProcess[str] = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=self.sbom_timeout, check=False)

tests/test_ci.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from docker import DockerClient
99
from moto import mock_aws
1010

11-
from ci.ci import CI, SetEnvs, CITestResult, CITests, Platform
11+
from ci.ci import CI, SetEnvs, CITestResult, CITests, Platform, BuildCacheTag
1212

1313
os.environ["DRY_RUN"] = "false"
1414
os.environ["IMAGE"] = "linuxserver/test"
@@ -23,6 +23,8 @@
2323
os.environ["PORT"] = "443"
2424
os.environ["WEB_SCREENSHOT"] = "true"
2525
os.environ["WEB_AUTH"] = ""
26+
os.environ["COMMIT_SHA"] = "test-commit-sha"
27+
os.environ["BUILD_NUMBER"] = "1234"
2628

2729
@pytest.fixture
2830
def sbom_blob() -> bytes:
@@ -241,3 +243,13 @@ def test_get_image_name(ci: CI) -> None:
241243
assert ci.get_image_name() == "linuxserver/lspipepr-plex"
242244
ci.image = "lsiobase/ubuntu"
243245
assert ci.get_image_name() == "linuxserver/docker-baseimage-ubuntu"
246+
247+
def test_get_build_cache_url(ci: CI) -> None:
248+
for tag in ci.tags:
249+
cache_tag = ci.get_build_cache_platform(tag)
250+
expected_url = f"{ci.build_cache_registry}:{cache_tag}-{ci.commit_sha}-{ci.build_number}"
251+
assert ci.get_build_cache_url(tag) == expected_url
252+
253+
def test_get_build_cache_platform(ci: CI) -> None:
254+
assert ci.get_build_cache_platform(ci.tags[0]) == BuildCacheTag.AMD64.value
255+
assert ci.get_build_cache_platform(ci.tags[1]) == BuildCacheTag.ARM64.value

0 commit comments

Comments
 (0)