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
9 changes: 8 additions & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,15 @@ jobs:
.github/workflows/validate.yml | linux/* | \
audio-toolchain.json | audio-toolchain.spdx.json | \
classic-packages.lock | classic-toolchain.json | \
classic-shader-toolchain.json | \
classic-shader-toolchain.spdx.json | \
tools/audio/* | tools/build-sdl3-mixer.sh | \
tools/require-image-checks.sh | \
tools/test-require-image-checks.sh | \
tools/validate-classic-check.sh | \
tools/validate-classic-image.sh)
tools/validate-classic-image.sh | \
tools/install_classic_shader_toolchain.py | \
tools/tests/test_install_classic_shader_toolchain.py)
classic=true
;;
esac
Expand Down Expand Up @@ -91,6 +95,9 @@ jobs:
- name: Test Classic dependency preflight
run: python3 -m unittest tools/tests/test_verify_classic_check_dependencies.py

- name: Test Classic shader toolchain installer
run: python3 -m unittest tools/tests/test_install_classic_shader_toolchain.py

linux:
name: Linux image
needs: changes
Expand Down
10 changes: 8 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@
changes, the required aggregate validation must still run.
- `classic-final` is the slim Classic Check target. Keep its Ubuntu snapshot,
direct package lock, tool inventory, non-root ccache mount, Classic validation
revision, smoke/SBOM checks, and published tags synchronized. Do not make it
inherit the broad replacement/development toolchain.
revision, shader-toolchain inventory, GPU runtime, smoke/SBOM checks, and
published tags synchronized. Do not make it inherit the broad
replacement/development toolchain.
- The public Classic image's shader contract is defined by
`classic-shader-toolchain.json`: retain the exact DXC/SPIRV-Cross archive and
source checksums, upstream licenses, and `/usr/local/bin` tool paths. Its
pinned Lavapipe/Xvfb packages provide the equivalent public environment for
fork-safe GPU coverage; consumers still pin a released image digest.
- Keep a stable numeric runner UID when restoring a Classic ccache directory;
the mode-1777 mount root supports non-root initialization but does not make
ccache's owner-writable nested directories reusable across different UIDs.
Expand Down
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ docker run --rm atrinik-linux-build pnpm --version
docker run --rm atrinik-classic-build gcc --version
docker run --rm atrinik-classic-build cmake --version
docker run --rm atrinik-classic-build ccache --version
docker run --rm atrinik-classic-build dxc --version
docker run --rm atrinik-classic-build spirv-cross --help
docker run --rm atrinik-classic-build \
sh -c 'test -f /usr/share/vulkan/icd.d/lvp_icd.json && \
VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json vulkaninfo --summary'
docker run --rm atrinik-linux-build \
atrinik-sdl3-mixer-probe \
/usr/local/share/atrinik/audio/opus-probe.opus
Expand Down Expand Up @@ -163,6 +168,17 @@ needed by the Classic client and server. SDL3_mixer and its codec closure retain
the checksum-pinned source and nested SPDX inventory used by the development
image.

The same public `classic-build` image now carries the qualified DXC release and
SPIRV-Cross source snapshot described by
[`classic-shader-toolchain.json`](classic-shader-toolchain.json). Their
checksum-verified executables are available as `dxc` and `spirv-cross`, with
the upstream license files under `/usr/local/share/licenses`. The direct
Lavapipe, Vulkan, and Xvfb runtime packages are locked in
[`classic-packages.lock`](classic-packages.lock), so a released immutable
Classic digest can serve both the shader-producing build and fork-safe GPU
coverage jobs. The Classic consumer must update its separate workflow to pin
that released digest; this image change does not rewrite consumer source.

Classic runs as the unprivileged `ubuntu` user by default. `/cache/ccache` is a
mode-1777 mount contract so CI can run with its own numeric UID and persist the
directory without granting root. Consumers must still select ccache explicitly
Expand All @@ -188,8 +204,9 @@ the digest, never a rolling tag. To update that pin:

1. Update the matching Ubuntu base digest and snapshot value in both
`linux/Dockerfile` and `classic-toolchain.json`, refresh the exact direct
versions in `classic-packages.lock`, and update the tool versions and pinned
Classic validation commit in `classic-toolchain.json`.
versions in `classic-packages.lock`, and update the tool versions, shader
coordinates in `classic-shader-toolchain.json`, and pinned Classic
validation commit in `classic-toolchain.json`.
2. Build `classic-validation` and `classic-final`, run the repository checks,
and compare compressed image size plus local client/server timings with the
prior digest.
Expand Down
5 changes: 5 additions & 0 deletions classic-packages.lock
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@ libsdl3-ttf-dev=3.2.2+ds-1build1
libssl-dev=3.5.5-1ubuntu3.3
libssl3t64=3.5.5-1ubuntu3.3
libsubunit-dev=1.4.4+1-1
libvulkan1=1.4.341.0-1
libxml2-dev=2.15.2+dfsg-0.1ubuntu0.1
mesa-vulkan-drivers=26.0.3-1ubuntu1
ninja-build=1.13.2-1
openssl=3.5.5-1ubuntu3.3
openssl-provider-legacy=3.5.5-1ubuntu3.3
pkgconf=2.5.1-4
python3=3.14.3-0ubuntu2
python3-dev=3.14.3-0ubuntu2
vulkan-tools=1.4.341.0+dfsg1-1
xauth=1:1.1.2-1.1build1
xvfb=2:21.1.22-1ubuntu1
zlib1g-dev=1:1.3.dfsg+really1.3.1-1ubuntu3
67 changes: 67 additions & 0 deletions classic-shader-toolchain.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"schema_version": 1,
"platform": "linux/amd64",
"target": "classic-final",
"image": "ghcr.io/atrinik/classic-build",
"dxc": {
"repository": "microsoft/DirectXShaderCompiler",
"tag": "v1.9.2607",
"commit": "0d3ee6b551b8fa768fbf825300ebab81047ef6a8",
"url": "https://github.com/microsoft/DirectXShaderCompiler/releases/download/v1.9.2607/linux_dxc_2026_07_29.x86_x64.tar.gz",
"sha256": "55665c87824051ed4774ff3280a79ccbbb7d39243b9736ca5e98222134112d54",
"archive_root": "linux_dxc_2026_07_29.x86_x64",
"files": {
"bin/dxc": "b1bfa493d5c780b94c20b8b5f5aed50d1c4d03339cd55f496bd223eedeec1734",
"lib/libdxcompiler.so": "be01593d3ff635fca6f20b044aa49c777f6409e50a28fb0242a0c87272620b62",
"lib/libdxil.so": "e74c02162cd553a0ceab041937d749cfd6ab9adf919142603ff5fc55af222c73",
"LICENCE-MIT.txt": "903df5512f7d02609fed0c780a9b704f5a3eeb6e4d84ebe42a29845c81899a3c",
"LICENSE-LLVM.txt": "729615317e28dd03907e46f0fc3b5e88f7853cee61d1a1471d2749335516b46f",
"LICENSE-MS.txt": "734f72f239fe7b07b4c7203f294c1a7ce27095687278bab7e56d630d7c672963"
}
},
"spirv_cross": {
"repository": "KhronosGroup/SPIRV-Cross",
"commit": "9c3c8e2cefdd8194b193bb8ed2fdff4d5527e382",
"url": "https://codeload.github.com/KhronosGroup/SPIRV-Cross/tar.gz/9c3c8e2cefdd8194b193bb8ed2fdff4d5527e382",
"sha256": "78939435d588998e5174a7865ddd36b6d9d7cd05eafac42d42ef537ea770b40a",
"license": "Apache-2.0",
"license_path": "LICENSE",
"license_sha256": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"
},
"install": {
"dxc": {
"executable": "bin/dxc",
"libraries": [
"lib/libdxcompiler.so",
"lib/libdxil.so"
],
"licenses": [
{
"archive_path": "LICENCE-MIT.txt",
"destination": "share/licenses/dxc/LICENCE-MIT.txt"
},
{
"archive_path": "LICENSE-LLVM.txt",
"destination": "share/licenses/dxc/LICENSE-LLVM.txt"
},
{
"archive_path": "LICENSE-MS.txt",
"destination": "share/licenses/dxc/LICENSE-MS.txt"
}
]
},
"spirv_cross": {
"executable": "bin/spirv-cross",
"license": {
"archive_path": "LICENSE",
"destination": "share/licenses/spirv-cross/LICENSE"
}
}
},
"runtime": {
"vulkan_icd": "/usr/share/vulkan/icd.d/lvp_icd.json",
"display_server": "Xvfb",
"commands": ["vulkaninfo", "Xvfb", "xvfb-run"]
}
}
57 changes: 57 additions & 0 deletions classic-shader-toolchain.spdx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "Atrinik Classic GPU shader toolchain",
"documentNamespace": "https://github.com/atrinik/devcontainer/sbom/classic-shader-toolchain/9c3c8e2cefdd8194b193bb8ed2fdff4d5527e382",
"creationInfo": {
"created": "2026-08-30T00:00:00Z",
"creators": ["Tool: Atrinik devcontainer shader-toolchain inventory"]
},
"packages": [
{
"SPDXID": "SPDXRef-Package-DirectXShaderCompiler",
"name": "DirectXShaderCompiler",
"versionInfo": "v1.9.2607",
"downloadLocation": "https://github.com/microsoft/DirectXShaderCompiler/releases/download/v1.9.2607/linux_dxc_2026_07_29.x86_x64.tar.gz",
"filesAnalyzed": false,
"checksums": [
{
"algorithm": "SHA256",
"checksumValue": "55665c87824051ed4774ff3280a79ccbbb7d39243b9736ca5e98222134112d54"
}
],
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"copyrightText": "NOASSERTION"
},
{
"SPDXID": "SPDXRef-Package-SPIRV-Cross",
"name": "SPIRV-Cross",
"versionInfo": "9c3c8e2cefdd8194b193bb8ed2fdff4d5527e382",
"downloadLocation": "https://codeload.github.com/KhronosGroup/SPIRV-Cross/tar.gz/9c3c8e2cefdd8194b193bb8ed2fdff4d5527e382",
"filesAnalyzed": false,
"checksums": [
{
"algorithm": "SHA256",
"checksumValue": "78939435d588998e5174a7865ddd36b6d9d7cd05eafac42d42ef537ea770b40a"
}
],
"licenseConcluded": "Apache-2.0",
"licenseDeclared": "Apache-2.0",
"copyrightText": "NOASSERTION"
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-DOCUMENT",
"relationshipType": "DESCRIBES",
"relatedSpdxElement": "SPDXRef-Package-DirectXShaderCompiler"
},
{
"spdxElementId": "SPDXRef-DOCUMENT",
"relationshipType": "DESCRIBES",
"relatedSpdxElement": "SPDXRef-Package-SPIRV-Cross"
}
]
}
33 changes: 32 additions & 1 deletion linux/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -405,11 +405,36 @@ RUN test "$(sha256sum /usr/local/share/atrinik/audio/opus-probe.opus \
/usr/local/share/atrinik/audio/opus-probe.opus \
&& rm /tmp/sdl3-mixer-probe.c

# Classic's GPU shader pipeline uses a qualified DXC release and a source
# snapshot of SPIRV-Cross. Build the latter from its locked archive and retain
# every upstream license beside the installed tools. The same final image also
# carries the pinned Lavapipe/Xvfb runtime below for fork-safe GPU coverage.
COPY classic-shader-toolchain.json /tmp/classic-shader-toolchain.json
COPY tools/install_classic_shader_toolchain.py \
/tmp/install_classic_shader_toolchain.py
RUN python3 /tmp/install_classic_shader_toolchain.py \
--manifest /tmp/classic-shader-toolchain.json \
--cache /tmp/classic-shader-downloads \
--prefix /usr/local \
--jobs "$(nproc)" \
&& ldconfig \
&& test -x /usr/local/bin/dxc \
&& test -x /usr/local/bin/spirv-cross \
&& dxc --version >/dev/null \
&& spirv-cross --help >/dev/null 2>&1 \
&& rm -rf /tmp/classic-shader-downloads \
/tmp/classic-shader-toolchain.json \
/tmp/install_classic_shader_toolchain.py

ENV CCACHE_DIR=/cache/ccache
RUN install -d -m 1777 "${CCACHE_DIR}"

COPY classic-toolchain.json /usr/local/share/atrinik/classic-toolchain.json
COPY classic-packages.lock /usr/local/share/atrinik/classic-packages.lock
COPY classic-shader-toolchain.json \
/usr/local/share/atrinik/classic-shader-toolchain.json
COPY classic-shader-toolchain.spdx.json \
/usr/local/share/atrinik/classic-shader-toolchain.spdx.json
COPY audio-toolchain.json /usr/local/share/atrinik/audio-toolchain.json
COPY audio-toolchain.spdx.json /usr/local/share/atrinik/audio-toolchain.spdx.json

Expand All @@ -430,6 +455,8 @@ RUN curl --fail --location --silent --show-error \

COPY classic-packages.lock /workspace/classic-packages.lock
COPY classic-toolchain.json /workspace/classic-toolchain.json
COPY classic-shader-toolchain.json /workspace/classic-shader-toolchain.json
COPY classic-shader-toolchain.spdx.json /workspace/classic-shader-toolchain.spdx.json
COPY audio-toolchain.json /workspace/audio-toolchain.json
COPY linux/Dockerfile /workspace/linux/Dockerfile
COPY tools/validate-classic-image.sh /workspace/tools/validate-classic-image.sh
Expand All @@ -440,7 +467,11 @@ RUN /workspace/tools/validate-classic-image.sh \
/workspace/classic-toolchain.json \
/usr/local/share/atrinik/classic-toolchain.json \
/workspace/audio-toolchain.json \
/workspace/linux/Dockerfile
/workspace/linux/Dockerfile \
/workspace/classic-shader-toolchain.json \
/usr/local/share/atrinik/classic-shader-toolchain.json \
/workspace/classic-shader-toolchain.spdx.json \
/usr/local/share/atrinik/classic-shader-toolchain.spdx.json

USER root
RUN syft dir:/ --select-catalogers +sbom-cataloger -o spdx-json \
Expand Down
Loading