images: shared task base, code-last layers, no per-push rebuilds [skip ci]

One base (Temurin 21 JDK, node/npm/tsc, php/composer, maven, sqlite, bwrap)
built only when its Dockerfile or dependencies change, plus a browser variant;
engine/search/VM images FROM base@sha256 with the code in the last layer; the
image workflow runs on dispatch, tags, Dockerfile or pin changes, with a build
cache. Executor NPROC bound relative to the current process count.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Marius Ciepluch
2026-10-05 20:40:13 +02:00
co-authored by Claude Fable 5.1
parent 7fb061fb69
commit 1b9d1ca85c
12 changed files with 671 additions and 127 deletions
+41
View File
@@ -0,0 +1,41 @@
# syntax=docker/dockerfile:1
# base-browser is built against the published base digest in CI; local builds
# default to the base stage. Never independently rebuild its shared ancestry.
ARG TASK_BASE_REF=base
FROM eclipse-temurin:21-jdk-jammy AS jdk
FROM ghcr.io/norandom/ax-task-runner:v0.3.1 AS base
LABEL org.opencontainers.image.licenses="Apache-2.0" \
org.opencontainers.image.source="https://github.com/norandom/OpenUltraSAST"
USER root
COPY --from=jdk /opt/java/openjdk /opt/java/openjdk
ENV JAVA_HOME=/opt/java/openjdk
ENV PATH="/venv/bin:/opt/java/openjdk/bin:${PATH}"
# Use upstream /usr/local Python 3.12 (also required by frontend retention).
# Its venv/pip and the toolchains below support every executor build recipe.
# Gradle projects use their wrapper; no system Gradle is installed.
RUN printf 'path-exclude=/usr/share/doc/*\npath-exclude=/usr/share/man/*\npath-exclude=/usr/share/info/*\n' \
> /etc/dpkg/dpkg.cfg.d/01-no-docs \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
nodejs npm node-typescript php-cli composer maven sqlite3 bubblewrap util-linux git ca-certificates libstdc++6 zlib1g \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /usr/share/doc/* /usr/share/man/* /usr/share/info/* \
/opt/java/openjdk/lib/src.zip /opt/java/openjdk/man /root/.cache
# Metadata only: install runtime dependencies, including the VM lane's extras.
# No project package or packaging backend enters this shared environment.
COPY pyproject.toml /tmp/dependencies/pyproject.toml
RUN python3 -c 'import pathlib,tomllib; p=tomllib.loads(pathlib.Path("/tmp/dependencies/pyproject.toml").read_text())["project"]; print("\n".join(p["dependencies"] + p["optional-dependencies"]["semantic"] + p["optional-dependencies"]["s3"]))' > /tmp/requirements.txt \
&& python3 -m venv /venv \
&& /venv/bin/pip install --no-cache-dir -r /tmp/requirements.txt \
&& rm -rf /tmp/dependencies /tmp/requirements.txt /root/.cache \
&& python3 --version && java -version && javac -version && node --version && npm --version && tsc --version \
&& php --version && composer --version && mvn --version && sqlite3 --version && bwrap --version \
&& setpriv --version && prlimit --version && nsenter --version
FROM ${TASK_BASE_REF} AS base-browser
RUN apt-get update \
&& apt-get install -y --no-install-recommends chromium \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /usr/share/doc/* /usr/share/man/* /usr/share/info/* /root/.cache \
&& chromium --version
+17 -29
View File
@@ -1,23 +1,15 @@
# Build from the repository root. AX v0.3.1 starts spec.command immediately;
# preserve its runner as PID 1 and put the engine command in the Task manifest.
FROM eclipse-temurin:21-jre-jammy AS jre
FROM ghcr.io/norandom/ax-task-runner:v0.3.1 AS engine
LABEL org.opencontainers.image.licenses="Apache-2.0" \
org.opencontainers.image.source="https://github.com/norandom/OpenUltraSAST"
USER root
# syntax=docker/dockerfile:1
# Supply digests from plane/task-base.digest; deliberately no floating fallback.
ARG TASK_BASE_TAG=base-unpublished
ARG TASK_BASE_DIGEST
ARG ENGINE_REF=engine
FROM ghcr.io/norandom/ousast-task-base:${TASK_BASE_TAG}@${TASK_BASE_DIGEST} AS joern-build
ENV PATH="/opt/joern-cli:${PATH}"
ARG JOERN_VERSION=v4.0.625
ARG ARCHIVE=joern-cli-linux-x86_64.zip
# The AX runner is Python 3.12 / Debian slim. Copy the Java 21 runtime from
# Jammy (older glibc than Debian bookworm) instead of relying on distro JRE versions.
COPY --from=jre /opt/java/openjdk /opt/java/openjdk
ENV JAVA_HOME=/opt/java/openjdk
ENV PATH="/venv/bin:/opt/java/openjdk/bin:/opt/joern-cli:${PATH}"
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
php-cli unzip curl ca-certificates libstdc++6 zlib1g \
&& apt-get install -y --no-install-recommends curl unzip \
&& rm -rf /var/lib/apt/lists/*
RUN set -eux; \
@@ -37,24 +29,20 @@ COPY ops/frontend-retention/install.py /tmp/frontend-retention-install.py
RUN python3 /tmp/frontend-retention-install.py /opt/joern-cli \
&& rm /tmp/frontend-retention-install.py
FROM ghcr.io/norandom/ousast-task-base:${TASK_BASE_TAG}@${TASK_BASE_DIGEST} AS engine
ENV PATH="/opt/joern-cli:${PATH}"
COPY --from=joern-build /opt/joern-cli /opt/joern-cli
WORKDIR /app
COPY pyproject.toml README.md LICENSE ./
COPY src ./src
RUN --mount=type=cache,target=/root/.cache/pip \
/venv/bin/pip install --no-deps --no-cache-dir .
COPY benchmarks/learn/engine_task_entry.py \
benchmarks/learn/engine_trace_worker.py \
benchmarks/learn/engine_trace_parse.py ./benchmarks/learn/
# Worker imports are standard-library plus engine modules. Isolate them from
# the AX runner's Python environment and omit PyYAML, boto3 and all extras.
RUN python3 -m venv /venv \
&& /venv/bin/pip install --no-cache-dir --no-deps . \
&& /venv/bin/python -I -c 'import pathlib, runpy, sys; sys.modules.update(yaml=None, boto3=None); sys.path.insert(0, "/app/benchmarks/learn"); p = pathlib.Path("/app/benchmarks/learn/engine_trace_worker.py"); print("Worker import smoke bytes:", len(p.read_bytes())); runpy.run_path(str(p), run_name="engine_worker_import_smoke")'
RUN /venv/bin/python -I -c 'import pathlib, runpy, sys; sys.modules.update(yaml=None, boto3=None); sys.path.insert(0, "/app/benchmarks/learn"); p = pathlib.Path("/app/benchmarks/learn/engine_trace_worker.py"); print("Worker import smoke bytes:", len(p.read_bytes())); runpy.run_path(str(p), run_name="engine_worker_import_smoke")'
# ENTRYPOINT and CMD belong to the upstream AX runner.
# Do not set ENTRYPOINT or CMD: both belong to the upstream AX runner.
FROM engine AS replay
RUN apt-get update \
&& apt-get install -y --no-install-recommends git \
&& rm -rf /var/lib/apt/lists/*
FROM ${ENGINE_REF} AS replay
COPY benchmarks/unseen/replay_entry.py ./benchmarks/unseen/replay_entry.py
RUN /venv/bin/pip install --no-cache-dir . \
&& /venv/bin/ousast pre-push --help > /dev/null
RUN /venv/bin/ousast pre-push --help > /dev/null
+13 -29
View File
@@ -1,35 +1,19 @@
# Build from the repository root; retain the AX runner entrypoint and command.
FROM eclipse-temurin:17-jdk-jammy AS jdk
FROM ghcr.io/norandom/ax-task-runner:v0.3.1
LABEL org.opencontainers.image.licenses="Apache-2.0" \
org.opencontainers.image.source="https://github.com/norandom/OpenUltraSAST"
# syntax=docker/dockerfile:1
# Supply digests from plane/task-base.digest; deliberately no floating fallback.
ARG TASK_BROWSER_TAG=base-unpublished-browser
ARG TASK_BROWSER_DIGEST
FROM ghcr.io/norandom/ousast-task-base:${TASK_BROWSER_TAG}@${TASK_BROWSER_DIGEST}
USER root
COPY --from=jdk /opt/java/openjdk /opt/java/openjdk
ENV JAVA_HOME=/opt/java/openjdk
ENV PATH="/venv/bin:/opt/java/openjdk/bin:${PATH}"
# dpkg exclusions prevent docs/manpages entering layers, including transitive deps.
RUN printf 'path-exclude=/usr/share/doc/*\npath-exclude=/usr/share/man/*\npath-exclude=/usr/share/info/*\n' \
> /etc/dpkg/dpkg.cfg.d/01-no-docs \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
python3 python3-venv python3-pip nodejs npm node-typescript php-cli composer maven gradle \
sqlite3 bubblewrap util-linux chromium git ca-certificates \
&& rm -rf /var/lib/apt/lists/* /opt/java/openjdk/lib/src.zip /opt/java/openjdk/man
WORKDIR /app
COPY pyproject.toml README.md LICENSE ./
COPY src ./src
# Includes the verifier-owned recipes, oracles, and key-free executor entrypoints.
RUN python3 -m venv /venv \
&& /venv/bin/pip install --no-cache-dir . \
&& python3 --version && node --version && tsc --version && php --version \
&& javac -version && sqlite3 --version && bwrap --version \
&& setpriv --version && prlimit --version && nsenter --version && chromium --version
# Dedicated identity for artefacts, apps and Chromium; the verifier keeps root.
RUN groupadd --gid 10001 search \
&& useradd --create-home --uid 10001 --gid 10001 search
# gVisor is the outer boundary; retain inner bwrap when its capability probe succeeds.
# The Python base image sets GPG_KEY to the public CPython release-signing fingerprint (not a secret).
# Clear it so the executor's guard against inherited credentials stays strict and still passes.
# Upstream's public CPython signing fingerprint trips the credential guard.
ENV GPG_KEY=
WORKDIR /app
COPY pyproject.toml README.md LICENSE ./
COPY src ./src
RUN --mount=type=cache,target=/root/.cache/pip \
/venv/bin/pip install --no-deps --no-cache-dir .
# Entrypoints and verifier-owned recipes/oracles are included in src.
USER root
# Do not replace the AX runner's ENTRYPOINT or CMD.
# Preserve the AX runner ENTRYPOINT and CMD.
+5
View File
@@ -0,0 +1,5 @@
{
"base": null,
"base-browser": null,
"note": "UNPUBLISHED: run task-base-image, then replace with the two full tag@sha256 references from its summary. Never invent digests."
}