TeamCity runs the container image through the Docker Wrapper build feature, which wraps a Command Line step so it executes inside a container.
Minimal setup
In the build configuration:
- Add a Command Line build step.
- Set Custom script to
sbomify-action. - Add the Docker Wrapper build feature to that step, with:
- Docker image:
ghcr.io/sbomify/sbomify-action - Additional docker run arguments:
-w /github/workspace
- Docker image:
- Add the configuration as environment variables under Parameters, prefixed
env.:
env.LOCK_FILE = requirements.txt
env.OUTPUT_FILE = sbom.cdx.json
env.ENRICH = true
env.UPLOAD = falseTeamCity mounts the checkout directory into the container automatically, so the lockfile path is relative to your repository root.
Kotlin DSL
If your configuration is versioned:
object GenerateSbom : BuildType({
name = "Generate SBOM"
params {
param("env.LOCK_FILE", "requirements.txt")
param("env.OUTPUT_FILE", "sbom.cdx.json")
param("env.ENRICH", "true")
param("env.UPLOAD", "false")
}
vcs { root(DslContext.settingsRoot) }
steps {
script {
name = "Generate SBOM"
scriptContent = "sbomify-action"
dockerImage = "ghcr.io/sbomify/sbomify-action"
dockerRunParameters = "-w /github/workspace"
}
}
artifactRules = "sbom.cdx.json"
})Uploading to sbomify
TeamCity does not support OIDC trusted publishing - that is currently GitHub-only. Use an API token stored as a password parameter so it is masked in the build log.
params {
password("env.TOKEN", "credentialsJSON:...", display = ParameterDisplay.HIDDEN)
param("env.COMPONENT_ID", "your-component-id")
param("env.LOCK_FILE", "requirements.txt")
param("env.AUGMENT", "true")
param("env.ENRICH", "true")
}Password parameters are the only kind TeamCity masks in logs. A plain param holding a token will be printed.
Versioning
TeamCity exposes the build number and VCS revision as parameters:
env.COMPONENT_NAME = my-app
env.COMPONENT_VERSION = %build.vcs.number%For tagged releases, use a VCS trigger on tags and read the branch name:
env.COMPONENT_VERSION = %teamcity.build.branch%
env.PRODUCT_RELEASE = ["your-product-id:%teamcity.build.branch%"]Caching
TeamCity agents keep their working directories between builds, so pointing the caches at a path outside the checkout directory persists them across runs on the same agent:
env.SBOMIFY_CACHE_DIR = %system.agent.home.dir%/cache/sbomify
env.SYFT_CACHE_DIR = %system.agent.home.dir%/cache/syft
env.SBOMIFY_TOOL_CACHE = %system.agent.home.dir%/cache/sbomify-runtimes
env.GITHUB_TOKEN = %github.token%Mount that directory into the container by adding it to the Docker Wrapper’s run arguments:
-w /github/workspace -v %system.agent.home.dir%/cache:/cacheTwo things are worth caching here. The tool runtimes are downloaded on first use, so without a cache every build re-fetches them. And GITHUB_TOKEN matters even though you are not on GitHub: license databases come from GitHub Releases, unauthenticated requests are capped at 60 per hour per IP, and a pool of agents behind one NAT address exhausts that quickly. When it happens, enrichment degrades silently. See license database rate limits.
VCS information
TeamCity does not expose repository details in the form the action auto-detects, so set them in sbomify.json. Generate it from build parameters in an earlier Command Line step:
cat > sbomify.json <<EOF
{
"vcs_url": "%vcsroot.url%",
"vcs_commit_sha": "%build.vcs.number%",
"vcs_ref": "%teamcity.build.branch%",
"supplier": {"name": "My Company"},
"lifecycle_phase": "build"
}
EOFThen set env.AUGMENT = true. See augmentation.
Container images
Scanning an image needs access to a Docker daemon. TeamCity agents that already run Docker builds have one; add the socket to the wrapper’s run arguments:
-w /github/workspace -v /var/run/docker.sock:/var/run/docker.sockenv.DOCKER_IMAGE = my-app:%build.number%
env.OUTPUT_FILE = container-sbom.cdx.json
env.ENRICH = true
env.UPLOAD = falseMounting the socket gives the container control of the host daemon. Prefer a rootless or remote daemon where your agents support it.
Monorepos
env.WORKING_DIR = packages/my-app
env.LOCK_FILE = package-lock.jsonFor several components, use a build configuration per component, each with its own COMPONENT_ID, or a single configuration with a matrix parameter.
Signing
Build provenance attestation is GitHub-specific. Sign with cosign instead, which runs anywhere.
Next steps
- Configuration reference - every option
- Augmentation - setting VCS details manually
- Advanced - tool runtimes, caching, troubleshooting