From ccbf26cd24aefabd4fa6eccfb096a598dc0405c9 Mon Sep 17 00:00:00 2001 From: Tianyu Zhou Date: Thu, 10 Sep 2026 16:03:34 +0800 Subject: [PATCH 1/2] build: consume the distill-fs v0.1.1 static release Download the published Linux/amd64 distill-fs binary instead of compiling its source checkout for each all-in-one image. Pin the release URL and verified archive checksum in an AKernel-owned manifest, and check package provenance, binary checksum, CLI version, and static ELF linkage before installation. Retain its licenses and source manifest in the image. Remove distill-fs source initialization from CI and build prerequisites, report the release version and digest through make versions, and document the release update workflow. Keep the optional source submodule as a reference and leave the sandboxd source revision unchanged. Add installer checks for the real release and invalid inputs so corrupted or incompatible artifacts fail before they can enter the image. Signed-off-by: Tianyu Zhou --- .dockerignore | 10 +-- .github/workflows/ci.yml | 2 +- AGENTS.md | 40 ++++++++---- README.md | 2 +- builder/distill-fs-versions.env | 8 +++ builder/node.Dockerfile | 28 ++++---- builder/scripts/install-distill-fs.sh | 56 ++++++++++++++++ builder/scripts/test-install-distill-fs.py | 76 ++++++++++++++++++++++ deploy/README.md | 8 +++ deploy/scripts/build-image.sh | 39 +++++------ deploy/scripts/check-prereqs.sh | 14 +++- 11 files changed, 222 insertions(+), 61 deletions(-) create mode 100644 builder/distill-fs-versions.env create mode 100644 builder/scripts/install-distill-fs.sh create mode 100644 builder/scripts/test-install-distill-fs.py diff --git a/.dockerignore b/.dockerignore index 9550d54..9ecc731 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,6 @@ # Git metadata is not part of the image build. Component versions come from -# their source manifests, and exact revisions remain traceable through the -# parent repository's submodule gitlinks. +# source manifests or pinned releases; revisions remain traceable through +# submodule gitlinks and packaged release provenance. .git **/.git @@ -20,9 +20,9 @@ deploy/standalone/data/ deploy/standalone/logs/ deploy/standalone/output/ -# Local component build outputs. The image build compiles these components in -# dedicated Docker stages. -src/distill-fs/target/ +# Local source build outputs. +# distill-fs is consumed as a release artifact, not from this checkout. +src/distill-fs/ src/sandboxd/output/ src/yuanrong/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8c64cba..e748c1a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -102,7 +102,7 @@ jobs: - name: Initialize build submodules run: | - git submodule update --init src/sandboxd src/distill-fs + git submodule update --init src/sandboxd - name: Set up Python uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 diff --git a/AGENTS.md b/AGENTS.md index 2c72a0e..1de3944 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,8 +48,11 @@ tunnels. The project overview and deployment quick start are in The open-source AKernel repository contains the SDK, deployment configuration, build tooling, and examples. Node runtime components such as `sandboxd` and `distill-fs` are maintained in their own upstream repositories and pinned as -Git submodules. The all-in-one build compiles those revisions and packages the -runtime payloads described in the Build section below. +Git submodules. The all-in-one build compiles sandboxd and downloads the +checksum-pinned static distill-fs release recorded in +`builder/distill-fs-versions.env`. The distill-fs submodule is an optional +source reference, not a build input. See the Build section below for runtime +payloads. ## Common Commands @@ -129,9 +132,9 @@ actor backend is deprecated and retained only for compatibility with existing applications. Keep it on its explicitly pinned legacy version; do not advance it with the default `openyuanrong-sandbox` backend or use it for new features. -Initialize submodules with `git submodule update --init --recursive` before +Initialize sandboxd with `git submodule update --init src/sandboxd` before building. The all-in-one image builds the sandboxd binaries, including -`firecracker-agent`, and `distill_fs`; installs checksum-pinned gVisor and Kata +`firecracker-agent`; installs checksum-pinned static distill-fs, gVisor, and Kata artifacts; installs the Firecracker VMM and guest kernel; and constructs the matching guest-agent initrd. Runc remains build-time optional, and `AKERNEL_ENABLE_FIRECRACKER=false` excludes the Firecracker payload. @@ -149,14 +152,24 @@ pins it rather than overriding manifest fields from the AKernel build. Keep sandboxd's pooled-TAP contract and the matching gVisor compatibility patches validated together when upgrading. -The submodule gitlinks are the single source of truth for the sandboxd and -distill-fs revisions included in a clean release. `make build` always compiles -the local submodule worktrees, so developers may check out a different commit -or edit either directory and rebuild without pushing first. Each component -maintains and embeds its own semantic version: sandboxd uses -`version/VERSION`, while distill-fs uses the package version in `Cargo.toml`. -AKernel does not inject parent-repository version metadata into component -compilation. +The sandboxd gitlink fixes the source revision compiled by `make build`. +AKernel's `builder/distill-fs-versions.env` fixes the distill-fs release URL +and SHA-256. `make build` compiles the local sandboxd worktree and consumes the +static distill-fs release through `builder/scripts/install-distill-fs.sh`; editing +`src/distill-fs` no longer affects the image. The installer verifies the archive, +provenance, version, binary hash, and static ELF contract, and packages its +licenses and manifest under `/usr/local/share/distill-fs`. + +Publish and verify a distill-fs release before updating the AKernel manifest +pin. This dependency does not require a sandboxd source or gitlink change. +Never use a guessed checksum or silently fall back to a source build. +Missing or invalid release pins prevent builds. `make versions` reports the +release tag and archive digest without requiring the distill-fs submodule. + +Each component embeds its own semantic version: sandboxd uses +`version/VERSION`, while distill-fs uses its release package version in +`Cargo.toml`. AKernel does not inject parent-repository version metadata into +component compilation. To test an unreleased openYuanRong core wheel without rebuilding YuanRong, provide both `OPEN_YR_CORE_WHEEL_URL` and `OPEN_YR_CORE_WHEEL_SHA256` to @@ -175,7 +188,8 @@ make versions The final image uses standard OCI labels for the AKernel version and revision. Component semantic versions are reported by their binaries, and their exact -source revisions are traceable through the AKernel commit's submodule gitlinks. +source revisions are traceable through the sandboxd gitlink and the pinned +distill-fs release's packaged manifest. ## Deploy diff --git a/README.md b/README.md index 2f54892..314e5e3 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ See the complete [basic usage example](./sdk/python/examples/basic_usage.py), th - **Sandbox runtimes**: gVisor by default; Kata Containers and Firecracker on KVM-capable nodes; and an explicitly enabled native Linux runc backend - **sandboxd**: Sandbox lifecycle daemon with pluggable sandbox runtime integration -- **distill-fs**: Rust-based FUSE filesystem for lazy rootfs access, chunk caching, and deduplication +- **distill-fs**: Rust-based FUSE filesystem for lazy rootfs access, chunk caching, and deduplication; packaged from a static GitHub Release with its version and checksum pinned in AKernel **Cluster-Wide Services** - **Distributed Scheduler**: Workload-aware placement and scaling diff --git a/builder/distill-fs-versions.env b/builder/distill-fs-versions.env new file mode 100644 index 0000000..2ef24c2 --- /dev/null +++ b/builder/distill-fs-versions.env @@ -0,0 +1,8 @@ +# Copyright (c) 2026 Ant Group Corporation. +# SPDX-License-Identifier: Apache-2.0 + +# AKernel's distill-fs release dependency. Verify the published release archive +# before updating its version, URL, and SHA-256 together. +DISTILL_FS_RELEASE=v0.1.1 +DISTILL_FS_AMD64_URL=https://github.com/inclusionAI/distill-fs/releases/download/v0.1.1/distill-fs-v0.1.1-linux-amd64.tar.gz +DISTILL_FS_AMD64_SHA256=b16e225e5b777f673bd98dd0968a15c190e333690356552b69a2b2624c3d5dcb diff --git a/builder/node.Dockerfile b/builder/node.Dockerfile index 77ba145..f7b089f 100644 --- a/builder/node.Dockerfile +++ b/builder/node.Dockerfile @@ -9,7 +9,6 @@ ARG AKERNEL_ENABLE_KATA=true ARG AKERNEL_ENABLE_RUNC=false ARG AKERNEL_ENABLE_FIRECRACKER=true ARG SANDBOXD_BUILD_IMAGE=golang:1.25.5-bookworm -ARG DISTILL_FS_BUILD_IMAGE=rust:1.85.0-bookworm ARG OPEN_YR_VERSION=0.10.2rc2 ARG OPEN_YR_CORE_WHEEL_URL= ARG OPEN_YR_CORE_WHEEL_SHA256= @@ -214,23 +213,17 @@ RUN mkdir -p /runc/usr/local/bin FROM runc-runtime-${AKERNEL_ENABLE_RUNC} AS runc-runtime -FROM ${DISTILL_FS_BUILD_IMAGE} AS distill-fs-builder -ENV DEBIAN_FRONTEND=noninteractive \ - CARGO_NET_GIT_FETCH_WITH_CLI=true +FROM ubuntu:24.04 AS distill-fs-runtime +ARG TARGETARCH +ARG DISTILL_FS_RELEASE +ARG DISTILL_FS_AMD64_URL +ARG DISTILL_FS_AMD64_SHA256 RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - ca-certificates \ - cmake \ - g++ \ - gcc \ - git \ - make \ - perl \ - pkg-config && \ + apt-get install -y --no-install-recommends ca-certificates curl jq binutils && \ rm -rf /var/lib/apt/lists/* -WORKDIR /src/distill-fs -COPY ./src/distill-fs/ ./ -RUN cargo build --locked --release --bin distill_fs +COPY ./builder/scripts/install-distill-fs.sh /install-distill-fs.sh +RUN sh /install-distill-fs.sh "$DISTILL_FS_RELEASE" \ + "$DISTILL_FS_AMD64_URL" "$DISTILL_FS_AMD64_SHA256" /distill-fs FROM ${AKERNEL_NODE_BASE_IMAGE} # Let PID 1 systemd avoid remounting shared host filesystems during shutdown. @@ -371,7 +364,8 @@ COPY --from=gvisor-runtime /gvisor/runsc /usr/local/bin/runsc COPY --from=sandboxd-builder /src/sandboxd/output/sandboxd /usr/local/bin/sandboxd COPY --from=sandboxd-builder /src/sandboxd/output/sbox /usr/local/bin/sbox COPY --from=sandboxd-builder /src/sandboxd/output/sandbox-logger /usr/local/bin/sandbox-logger -COPY --from=distill-fs-builder /src/distill-fs/target/release/distill_fs /usr/local/bin/distill_fs +COPY --from=distill-fs-runtime /distill-fs/bin/distill_fs /usr/local/bin/distill_fs +COPY --from=distill-fs-runtime /distill-fs/share/distill-fs/ /usr/local/share/distill-fs/ COPY --from=kata-runtime /kata/opt/kata /opt/kata COPY --from=runc-runtime /runc/usr/local/bin/ /usr/local/bin/ COPY --from=firecracker-runtime /firecracker/ / diff --git a/builder/scripts/install-distill-fs.sh b/builder/scripts/install-distill-fs.sh new file mode 100644 index 0000000..4581d3c --- /dev/null +++ b/builder/scripts/install-distill-fs.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# Copyright (c) 2026 Ant Group Corporation. +# SPDX-License-Identifier: Apache-2.0 + +# Install the distill-fs release for AKernel. Callers pass pins from +# builder/distill-fs-versions.env, never checksums downloaded with the archive. +set -eu + +DISTILL_FS_RELEASE=$1 +DISTILL_FS_AMD64_URL=$2 +DISTILL_FS_AMD64_SHA256=$3 +destination=$4 + +case "${TARGETARCH:-$(uname -m)}" in + amd64|x86_64) ;; + *) echo "distill-fs release supports linux/amd64 only" >&2; exit 1 ;; +esac +: "${DISTILL_FS_RELEASE:?distill-fs release is not pinned}" +: "${DISTILL_FS_AMD64_URL:?distill-fs release URL is not pinned}" +: "${DISTILL_FS_AMD64_SHA256:?publish and pin the distill-fs release before building}" +printf '%s\n' "$DISTILL_FS_AMD64_SHA256" | grep -Eq '^[0-9a-f]{64}$' || { + echo "invalid distill-fs SHA-256 pin" >&2 + exit 1 +} + +# Keep the download separate from installed files. Image consumers copy only +# bin/ and share/ from this staging directory. +mkdir -p "$destination/download" "$destination/bin" "$destination/share/distill-fs" +archive="$destination/download/distill-fs.tar.gz" +curl -fSL --retry 5 --retry-delay 2 --retry-all-errors \ + "$DISTILL_FS_AMD64_URL" -o "$archive" +printf '%s %s\n' "$DISTILL_FS_AMD64_SHA256" "$archive" | sha256sum -c - +tar -xzf "$archive" -C "$destination/download" \ + distill_fs manifest.json LICENSE NOTICE Cargo.lock +bundle="$destination/download" +jq -e --arg release "$DISTILL_FS_RELEASE" \ + '.component == "distill-fs" and .release_tag == $release and + .version == ($release | ltrimstr("v")) and + .repository == "inclusionAI/distill-fs" and + .target == "x86_64-unknown-linux-musl" and + (.source_revision | test("^[0-9a-f]{40}$")) and + (.binary_sha256 | test("^[0-9a-f]{64}$"))' \ + "$bundle/manifest.json" >/dev/null +printf '%s %s\n' "$(jq -r .binary_sha256 "$bundle/manifest.json")" \ + "$bundle/distill_fs" | sha256sum -c - +readelf -h "$bundle/distill_fs" >/dev/null +if readelf -l "$bundle/distill_fs" | grep -q INTERP || + readelf -d "$bundle/distill_fs" | grep -q NEEDED; then + echo "distill-fs release must be a static executable" >&2 + exit 1 +fi +chmod 0755 "$bundle/distill_fs" +test "$("$bundle/distill_fs" --version)" = "distill_fs ${DISTILL_FS_RELEASE#v}" +install -m 0755 "$bundle/distill_fs" "$destination/bin/distill_fs" +install -m 0644 "$bundle/manifest.json" "$bundle/LICENSE" \ + "$bundle/NOTICE" "$bundle/Cargo.lock" "$destination/share/distill-fs/" diff --git a/builder/scripts/test-install-distill-fs.py b/builder/scripts/test-install-distill-fs.py new file mode 100644 index 0000000..c8c3aab --- /dev/null +++ b/builder/scripts/test-install-distill-fs.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 +"""Exercise the AKernel installer with a real candidate/release archive. + +Usage: python3 builder/scripts/test-install-distill-fs.py /path/to/release.tar.gz +Requires curl, jq, binutils, and a Linux/amd64 host. No network is needed. +""" + +import hashlib +import io +import json +import os +from pathlib import Path +import subprocess +import sys +import tarfile +import tempfile + + +archive = Path(sys.argv[1]).resolve() +installer = Path(__file__).with_name("install-distill-fs.sh") +with tarfile.open(archive) as bundle: + files = {name: bundle.extractfile(name).read() for name in ( + "distill_fs", "manifest.json", "LICENSE", "NOTICE", "Cargo.lock" + )} +manifest = json.loads(files["manifest.json"]) +release = manifest["release_tag"] +digest = hashlib.sha256(archive.read_bytes()).hexdigest() + +with tempfile.TemporaryDirectory(prefix="distill-fs-installer-") as work: + work = Path(work) + + def check(name, asset=archive, checksum=digest, tag=release, arch="amd64", error=None): + destination = work / name + result = subprocess.run( + ["sh", str(installer), tag, asset.as_uri(), checksum, str(destination)], + env={**os.environ, "TARGETARCH": arch}, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, + ) + binary = destination / "bin/distill_fs" + if error is None: + assert result.returncode == 0, result.stdout + assert binary.read_bytes() == files["distill_fs"] + for filename in ("manifest.json", "LICENSE", "NOTICE", "Cargo.lock"): + assert (destination / "share/distill-fs" / filename).read_bytes() == files[filename] + else: + assert result.returncode != 0, name + assert error in result.stdout, result.stdout + assert not binary.exists(), "a rejected artifact must not be installed" + print(f"PASS {name}") + + def altered_archive(name, binary, binary_hash): + data = {**files, "distill_fs": binary} + metadata = {**manifest, "binary_sha256": binary_hash} + data["manifest.json"] = json.dumps(metadata).encode() + path = work / f"{name}.tar.gz" + with tarfile.open(path, "w:gz") as tar: + for filename, content in data.items(): + entry = tarfile.TarInfo(filename) + entry.size = len(content) + tar.addfile(entry, io.BytesIO(content)) + return path, hashlib.sha256(path.read_bytes()).hexdigest() + + check("valid") + check("missing-pin", checksum="", error="publish and pin") + check("bad-pin", checksum="not-a-digest", error="invalid distill-fs SHA-256") + check("corrupt-archive", checksum="0" * 64, error="FAILED") + check("wrong-version", tag="v999.0.0", error="") + check("unsupported-arch", arch="arm64", error="linux/amd64 only") + bad, sha = altered_archive("bad-binary-hash", files["distill_fs"], "0" * 64) + check("bad-binary-hash", asset=bad, checksum=sha, error="FAILED") + # A correctly checksummed bundle must still reject a dynamic executable. + dynamic = Path("/bin/true").read_bytes() + headers = subprocess.check_output(["readelf", "-l", "/bin/true"], text=True) + assert "INTERP" in headers, "this negative fixture needs a dynamic /bin/true" + bad, sha = altered_archive("dynamic-binary", dynamic, hashlib.sha256(dynamic).hexdigest()) + check("dynamic-binary", asset=bad, checksum=sha, error="must be a static executable") diff --git a/deploy/README.md b/deploy/README.md index 6788897..c4457f0 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -367,3 +367,11 @@ deploy/ ├── terraform/ # multi-cloud provisioning (aliyun, huaweicloud, shared) └── scripts/ # deployment and image helper scripts ``` + +### distill-fs release dependency + +The all-in-one image downloads the static Linux/amd64 distill-fs release pinned in `builder/distill-fs-versions.env`. It does not compile `src/distill-fs`; that checkout is optional source reference. `make versions` reports the release tag and archive SHA-256. The AKernel installer at `builder/scripts/install-distill-fs.sh` checks the archive, package provenance, CLI version, and static ELF linkage, and retains licenses and provenance in `/usr/local/share/distill-fs`. + +Publish and verify the distill-fs release before updating the AKernel version, URL, and checksum pin together. Missing or invalid pins stop `make build` before either image is built. There is no source-build fallback. + +Validate installation against a downloaded candidate or release with `python3 builder/scripts/test-install-distill-fs.py /path/to/distill-fs-vX.Y.Z-linux-amd64.tar.gz` on Linux/amd64 with curl, jq, and binutils. This checks normal installation and rejects missing/invalid pins, corrupted archives, version/architecture mismatch, binary hash mismatch, and dynamically linked executables. The sandboxd pipeline and gitlink are independent of this dependency. diff --git a/deploy/scripts/build-image.sh b/deploy/scripts/build-image.sh index 53acd8d..0912c23 100755 --- a/deploy/scripts/build-image.sh +++ b/deploy/scripts/build-image.sh @@ -21,6 +21,12 @@ if [[ ! -f "${runtime_versions_file}" ]]; then fi # shellcheck source=/dev/null source "${runtime_versions_file}" +distill_fs_versions_file="${ROOT}/builder/distill-fs-versions.env" +if [[ ! -f "${distill_fs_versions_file}" ]]; then + die "missing distill-fs version manifest: ${distill_fs_versions_file}" +fi +# shellcheck source=/dev/null +source "${distill_fs_versions_file}" gvisor_release="${GVISOR_RELEASE:-}" gvisor_amd64_sha512="${GVISOR_AMD64_SHA512:-}" gvisor_amd64_url="${GVISOR_AMD64_URL:-}" @@ -59,20 +65,6 @@ component_version() { printf '%s\n' "${version}" } -package_version() { - local manifest="$1" - awk ' - /^\[package\]$/ { in_package = 1; next } - /^\[/ { in_package = 0 } - in_package && $1 == "version" { - value = $3 - gsub(/^"|"$/, "", value) - print value - exit - } - ' "${manifest}" -} - while [[ $# -gt 0 ]]; do case "$1" in --env) @@ -158,31 +150,33 @@ all_in_one_image="${repository}:${tag}" cd "${AKERNEL_REPO_ROOT}" sandboxd_source="${AKERNEL_REPO_ROOT}/src/sandboxd" -distill_fs_source="${AKERNEL_REPO_ROOT}/src/distill-fs" akernel_version="$(component_version "${AKERNEL_REPO_ROOT}")" akernel_revision="$(component_revision "${AKERNEL_REPO_ROOT}" akernel)" sandboxd_version="$(sed -n '1p' "${sandboxd_source}/version/VERSION")" sandboxd_revision="$(component_revision "${sandboxd_source}" sandboxd)" -distill_fs_version="$(package_version "${distill_fs_source}/Cargo.toml")" -distill_fs_revision="$(component_revision "${distill_fs_source}" distill-fs)" +distill_fs_version="${DISTILL_FS_RELEASE:-unpublished}" +distill_fs_revision="sha256:${DISTILL_FS_AMD64_SHA256:-pending-publication}" if [[ -z "${sandboxd_version}" ]]; then die "failed to read sandboxd version from ${sandboxd_source}/version/VERSION" fi -if [[ -z "${distill_fs_version}" ]]; then - die "failed to read distill-fs package version from ${distill_fs_source}/Cargo.toml" -fi info "component versions: akernel=${akernel_version} sandboxd=${sandboxd_version} distill-fs=${distill_fs_version}" if [[ "${print_component_versions}" == "1" ]]; then - printf '%-12s %-24s %s\n' COMPONENT VERSION REVISION + printf '%-12s %-24s %s\n' COMPONENT VERSION REVISION_OR_DIGEST printf '%-12s %-24s %s\n' akernel "${akernel_version}" "${akernel_revision}" printf '%-12s %-24s %s\n' sandboxd "${sandboxd_version}" "${sandboxd_revision}" printf '%-12s %-24s %s\n' distill-fs "${distill_fs_version}" "${distill_fs_revision}" exit 0 fi +# Fail before building either image if the release has not been published/pinned. +if [[ -z "${DISTILL_FS_RELEASE:-}" || -z "${DISTILL_FS_AMD64_URL:-}" || + ! "${DISTILL_FS_AMD64_SHA256:-}" =~ ^[0-9a-f]{64}$ ]]; then + die "publish and pin DISTILL_FS_RELEASE, DISTILL_FS_AMD64_URL, and DISTILL_FS_AMD64_SHA256 in ${distill_fs_versions_file} before building" +fi + runtime_build_args=() if [[ -n "${rrt_runtime_url}" || -n "${rrt_runtime_sha256}" ]]; then if [[ -z "${rrt_runtime_url}" || -z "${rrt_runtime_sha256}" ]]; then @@ -204,6 +198,9 @@ docker build \ info "building ${all_in_one_image}" node_build_args=( + --build-arg "DISTILL_FS_RELEASE=${DISTILL_FS_RELEASE}" + --build-arg "DISTILL_FS_AMD64_URL=${DISTILL_FS_AMD64_URL}" + --build-arg "DISTILL_FS_AMD64_SHA256=${DISTILL_FS_AMD64_SHA256}" --build-arg "AKERNEL_RUNTIME_IMAGE=${runtime_image}" --build-arg "AKERNEL_RUNTIME_PROFILE=${runtime_profile}" --build-arg "AKERNEL_ENABLE_KATA=${AKERNEL_ENABLE_KATA:-true}" diff --git a/deploy/scripts/check-prereqs.sh b/deploy/scripts/check-prereqs.sh index 9a11496..b3e0250 100755 --- a/deploy/scripts/check-prereqs.sh +++ b/deploy/scripts/check-prereqs.sh @@ -32,10 +32,18 @@ info "required tools are available" for source_file in \ "${AKERNEL_REPO_ROOT}/src/sandboxd/go.mod" \ "${AKERNEL_REPO_ROOT}/src/sandboxd/version/VERSION" \ - "${AKERNEL_REPO_ROOT}/src/distill-fs/Cargo.toml"; do + "${AKERNEL_REPO_ROOT}/src/sandboxd/third_party/runtime-versions.env"; do if [[ ! -f "${source_file}" ]]; then - die "missing submodule source ${source_file}; run git submodule update --init --recursive" + die "missing submodule source ${source_file}; run git submodule update --init src/sandboxd" fi done -info "runtime source submodules are available" +for source_file in \ + "${AKERNEL_REPO_ROOT}/builder/distill-fs-versions.env" \ + "${AKERNEL_REPO_ROOT}/builder/scripts/install-distill-fs.sh"; do + if [[ ! -f "${source_file}" ]]; then + die "missing distill-fs release build input ${source_file}; restore it from the AKernel checkout" + fi +done + +info "runtime sources and release build inputs are available" From e4aa0a40ed03b3cb70eb548b6041893a360ee4f8 Mon Sep 17 00:00:00 2001 From: Tianyu Zhou Date: Thu, 10 Sep 2026 16:24:33 +0800 Subject: [PATCH 2/2] ci: capture runtime file logs when standalone tests fail Include sandboxd, YuanRong scheduling components, and distill-fs daemon logs in failure diagnostics. These services write their detailed errors to files, so the systemd journal alone cannot explain why a node remains in RECOVERING while sandbox creation times out. Signed-off-by: Tianyu Zhou --- .github/workflows/ci.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e748c1a..c88c17a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -210,6 +210,18 @@ jobs: echo "=== akernel-node: service journal ===" docker exec akernel-node journalctl --no-pager -n 500 \ -u sandboxd.service -u yuanrong.service || true + docker exec akernel-node bash -c ' + shopt -s nullglob + for logfile in \ + /home/akernel/logs/sandboxd/sandboxd*.log \ + /home/yuanrong/logs/*function_master*.log \ + /home/yuanrong/logs/*function_proxy*.log \ + /home/yuanrong/logs/*function_agent*.log \ + /home/akernel/sandboxd/image_manager/daemons/*/daemon.log; do + echo "=== ${logfile} ===" + tail -n 300 "${logfile}" + done + ' || true fi - name: Stop standalone AKernel