Container Structure Tests
10K+
GitHub Action running Google Container Structure Tests against a container image
docker, tar, and host drivers{{VAR_NAME}} placeholders in test configs from the GitHub Actions job environment before executing CSTlinux/amd64 and linux/arm64Check also other actions from DevOps-Infraโ
This action supports three tag levels for flexible versioning:
vX: latest patch of the major version (e.g., v1).vX.Y: latest patch of the minor version (e.g., v1.0).vX.Y.Z: fixed to a specific release (e.g., v1.0.0). - name: Run the Action
uses: devops-infra/[email protected]
with:
image: my-image:latest
config: tests/structure-test.yaml
driver: docker
output: text
debug: false
| Input | Required | Default | Description |
|---|---|---|---|
image | * | Image to test. Required unless image_from_oci_layout is set. Mutually exclusive with it. | |
config | Yes | Path(s) to test config file(s). Space or newline-separated for multiple files. {{VAR_NAME}} placeholders are rendered from the job environment. | |
driver | No | docker | Driver to use when running tests: docker, tar, or host. |
platform | No | Platform to test, e.g. linux/amd64 or linux/arm64. Defaults to host arch. | |
pull | No | false | Force pull the image before running tests (docker driver only). |
save | No | false | Preserve created containers after the test run. |
quiet | No | false | Suppress test output. |
no_color | No | false | Disable colorized output. |
output | No | text | Output format: text, json, or junit. |
test_report | No | Write test results to this file path, then print it to logs. CST converts text to json automatically. | |
junit_suite_name | No | Name for the JUnit test suite (only used when output is junit). | |
metadata | No | Path to image metadata file. | |
runtime | No | Runtime to use with the docker driver (e.g. runsc for gVisor). | |
force | No | false | Force run of host driver without interactive prompt. |
image_from_oci_layout | No | Path to OCI image layout directory. Mutually exclusive with image. | |
default_image_tag | No | Default image tag when OCI layout lacks a ref annotation. Requires image_from_oci_layout. | |
ignore_ref_annotation | No | false | Ignore org.opencontainers.image.ref.name annotation when loading OCI layout. |
debug | No | false | Enable verbose debug logging in the action entrypoint. |
| Output | Description |
|---|---|
total | Total number of tests executed. |
passed | Number of tests that passed. |
failed | Number of tests that failed. |
exit_code | Exit code returned by container-structure-test. |
Run structure tests against a Docker image using a single config file.
name: Run structure tests on each commit
on: [push]
jobs:
container-structure-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Build image
run: docker build -t my-image:latest .
- uses: devops-infra/action-container-structure-test@v1
with:
image: my-image:latest
config: tests/structure-test.yaml
Run tests with multiple config files, JSON output, and a saved report.
name: Run structure tests on each commit
on: [push]
jobs:
container-structure-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Build image
run: docker build -t my-image:latest .
- name: Run structure tests
id: cst
uses: devops-infra/action-container-structure-test@v1
with:
image: my-image:latest
config: |
tests/command-tests.yaml
tests/file-tests.yaml
output: json
test_report: /tmp/cst-report.json
pull: 'false'
debug: 'false'
- name: Show test results
run: |
echo "Total: ${{ steps.cst.outputs.total }}"
echo "Passed: ${{ steps.cst.outputs.passed }}"
echo "Failed: ${{ steps.cst.outputs.failed }}"
echo "Exit: ${{ steps.cst.outputs.exit_code }}"
Pick the tag level based on your stability needs:
vX.Y.Z: exact immutable release (most predictable)vX.Y: latest patch within one minor linevX: latest patch within one major linename: Run structure tests on each commit
on: [push]
jobs:
container-structure-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: devops-infra/[email protected]
id: pin-patch-version
with:
image: my-image:latest
config: tests/structure-test.yaml
- uses: devops-infra/[email protected]
id: pin-minor-version
with:
image: my-image:latest
config: tests/structure-test.yaml
- uses: devops-infra/action-container-structure-test@v1
id: pin-major-version
with:
image: my-image:latest
config: tests/structure-test.yaml
Generate a JUnit report for test result publishing.
name: Run structure tests on each commit
on: [push]
jobs:
container-structure-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Build image
run: docker build -t my-image:latest .
- name: Run structure tests
uses: devops-infra/action-container-structure-test@v1
with:
image: my-image:latest
config: tests/structure-test.yaml
output: junit
junit_suite_name: container-structure-tests
test_report: /tmp/cst-results.xml
Test an exported image without a Docker daemon (file/metadata tests only).
name: Run structure tests on each commit
on: [push]
jobs:
container-structure-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Export image as tar
run: docker save my-image:latest -o my-image.tar
- uses: devops-infra/action-container-structure-test@v1
with:
image: my-image.tar
config: tests/file-tests.yaml
driver: tar
Container Structure Test configs are YAML or JSON files.
The current schema version is 2.0.0 and must be set in every config.
The action renders {{VAR_NAME}} placeholders in config files before calling CST.
Use this for workflow-controlled values such as versions exported into GITHUB_ENV.
Keep ${VAR} syntax for shell expansion that should happen inside the tested container.
Example:
commandTests:
- name: Azure CLI
command: bash
args:
- -lc
- test "$(az version --output json | jq -r '."azure-cli"')" = '{{AZ_VERSION}}'
schemaVersion: '2.0.0'
commandTests:
- name: "python version"
command: "python3"
args: ["--version"]
expectedOutput: ["Python 3\\..*"]
fileExistenceTests:
- name: "entrypoint exists"
path: "/entrypoint.sh"
shouldExist: true
permissions: "-rwxr-xr-x"
fileContentTests:
- name: "sources list"
path: "/etc/os-release"
expectedContents: [".*alpine.*"]
metadataTest:
workdir: "/app"
envVars:
- key: PATH
value: "/usr/local/bin:.*"
isRegex: true
Full documentation: GoogleContainerTools/container-structure-testโ
Workflows included:
.github/workflows/auto-pull-request-create.yml)
master and dependabot/**..github/workflows/auto-create-release.yml)
pull_request closed and push to release/** (runs only for merged PRs from release/)vX.Y.Z; update vX.Y and vX (fails if full tag exists on remote).github/workflows/cron-dependency-update.yml)
.github/workflows/manual-release-branch-prepare.yml)
workflow_dispatch with type (patch|minor|major|set) xor version when type=setrelease/vX.Y.Z, builds/pushes the -rc image, and opens the release PR.github/workflows/auto-release-create.yml, which tags and publishes the final releaseREL_VERSION-rc image, and inspect manifestPrerequisites:
brew install gnu-sed),Common tasks:
# Run all linters
task lint
# Build multi-arch images locally (no push)
task docker:build
# Build a local runnable image for your current architecture
task docker:build:local
# Run container-structure-test action locally (build is required and enforced)
task docker:test:local IMAGE=my-image:latest CONFIG=tests/structure-test.yaml
# Run with multiple config files
task docker:test:local IMAGE=my-image:latest CONFIG="tests/command-tests.yaml tests/file-tests.yaml"
# Run against OCI layout
task docker:test:local IMAGE_FROM_OCI_LAYOUT=./oci-layout CONFIG=tests/structure-test.yaml
# Run built-in smoke test against the locally built action image
task docker:test:smoke
# Push images (requires DOCKER_TOKEN and GITHUB_TOKEN)
DOCKER_TOKEN=... GITHUB_TOKEN=... task docker:push
Local run notes:
docker:test:local always builds the action image first via docker:build:local.docker:test:smoke uses tests/docker/local-image.yml to verify installed binaries,
metadata, and cache cleanup on the built image.DRIVER=docker (default), Docker socket access is required.OUTPUT=json, PULL=true, PLATFORM=linux/arm64, DEBUG=truePre-commit hooks:
brew install pre-commit
task pre-commit:install
task pre-commit
Contributions are welcome! See CONTRIBUTINGโ . This project is licensed under the MIT License - see the LICENSEโ file for details.
This project is licensed under the MIT License - see the LICENSEโ file for details.
If you have any questions or need help, please:
Use the manual workflow .github/workflows/manual-e2e-validate.yml to validate this action against the centralized E2E repository.
mode=image validates a published image tag (recommended for -test and -rc release checks).mode=ref validates ref-oriented E2E paths against stable pinned action refs.CI/CD automation also runs these E2E checks automatically:
-rc).Example trigger inputs:
mode=ref
mode=image
image_tag=v1.2.3-test
Content type
Image
Digest
sha256:7ea2c17ccโฆ
Size
15.9 MB
Last updated
2 months ago
docker pull devopsinfra/action-container-structure-testPulls:
251
Last week