Files
Genarrative/scripts/ci-rust-cache.sh
T
lhk229 5e57fdff62
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m23s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m34s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 9m23s
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
试点启用隔离 Rust 编译缓存并防止镜像叠层
仅在 AGC Rust lane 1 消费可信缓存快照,保持独立 target 和关闭增量编译
从远端 master 的确定提交构建快照,拒绝以已有对象缓存的镜像继续叠层
隔离缓存配置与进程状态,缓存故障回退真实编译并保留失败状态
增加命中统计、编译耗时和缓存行为验证
同步缓存来源、镜像保留及不中断运行中 CI 的运维约定
2026-09-22 04:24:58 +00:00

87 lines
4.0 KiB
Bash

#!/usr/bin/env bash
# 仅消费镜像内的可信快照;所有写入留在当前容器的可写层。
set -euo pipefail
cache_root="${GENARRATIVE_CI_RUST_CACHE_ROOT:-/opt/genarrative-ci/rust-cache}"
cache_binary="${cache_root}/sccache"
state="${GENARRATIVE_CI_RUST_CACHE_STATE:-}"
configure_local_cache() {
# 不继承开发机/其它流水线的 OSS、S3、GHA 或 daemon 配置。
local variable
for variable in ${!SCCACHE_@}; do unset "${variable}"; done
export SCCACHE_CONF="${state}/config"
export SCCACHE_DIR="${cache_root}/objects"
export SCCACHE_CACHE_SIZE=2G
export SCCACHE_SERVER_UDS="${state}/server.sock"
# 单个不可缓存的链接/测试阶段可能超过一分钟;保持统计直到 report 显式停止。
export SCCACHE_IDLE_TIMEOUT=0
export SCCACHE_IGNORE_SERVER_IO_ERROR=1
}
case "${1:-}" in
prepare)
: "${GITHUB_ENV:?GITHUB_ENV is required}"
printf 'RUSTC_WRAPPER=\nCARGO_BUILD_RUSTC_WRAPPER=\nGENARRATIVE_CI_RUST_CACHE_STATE=\n' >> "${GITHUB_ENV}"
fallback() { printf '[rust-cache] mode=direct reason=%s\n' "$1"; exit 0; }
[[ -x "${cache_binary}" && -d "${cache_root}/objects" && -f "${cache_root}/rustc.txt" && -f "${cache_root}/source-commit.txt" && -f "${cache_root}/workspace.txt" ]] \
|| fallback snapshot-unavailable
if ! rustc -vV | cmp -s - "${cache_root}/rustc.txt"; then
fallback toolchain-mismatch
fi
if [[ "$(pwd -P)" != "$(cat "${cache_root}/workspace.txt")" ]]; then
fallback workspace-mismatch
fi
# 使用短 Unix socket 路径,避免共享固定端口或超出 sockaddr_un 长度。
state="$(mktemp -d /tmp/ci-rust-cache.XXXXXX)"
printf 'server_startup_timeout_ms = 5000\n' > "${state}/config"
configure_local_cache
# 探测必须暴露 daemon 故障;只有正式编译允许 sccache 的 IO 回退。
unset SCCACHE_IGNORE_SERVER_IO_ERROR
if ! timeout --kill-after=2 15 "${cache_binary}" "$(command -v rustc)" -vV > "${state}/probe.log" 2>&1; then
timeout --kill-after=2 5 "${cache_binary}" --stop-server >/dev/null 2>&1 || true
rm -rf -- "${state}"
fallback wrapper-probe-failed
fi
script_path="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)/$(basename "${BASH_SOURCE[0]}")"
# wrapper 路径也参与 Rust cache key;固定容器内路径,隔离由 job 容器保证。
wrapper_path="${cache_root}/rustc-wrapper"
printf '#!/usr/bin/env bash\nexec bash %q "$@"\n' "${script_path}" > "${wrapper_path}"
chmod 700 "${wrapper_path}"
{
printf 'GENARRATIVE_CI_RUST_CACHE_ROOT=%s\n' "${cache_root}"
printf 'GENARRATIVE_CI_RUST_CACHE_STATE=%s\n' "${state}"
printf 'RUSTC_WRAPPER=%s\nCARGO_BUILD_RUSTC_WRAPPER=%s\n' "${wrapper_path}" "${wrapper_path}"
} >> "${GITHUB_ENV}"
printf '[rust-cache] mode=sccache snapshot=%s\n' "$(cat "${cache_root}/source-commit.txt")"
;;
report)
if [[ -n "${state}" && -d "${state}" ]]; then
configure_local_cache
timeout --kill-after=2 5 "${cache_binary}" --show-stats || true
timeout --kill-after=2 5 "${cache_binary}" --stop-server >/dev/null 2>&1 || true
rm -rf -- "${state}"
else
printf '[rust-cache] mode=direct\n'
fi
;;
*)
# Cargo wrapper 协议:第一个参数是真实 rustc。失去缓存状态时仍执行原编译。
if [[ -z "${state}" || ! -f "${state}/config" || -f "${state}/disabled" || ! -x "${cache_binary}" ]]; then
exec "$@"
fi
configure_local_cache
status=0
"${cache_binary}" "$@" || status=$?
if [[ "${status}" == 2 ]]; then
# 固定 sccache 版本的自身错误码为 2;实际 rustc 失败通常为 1/101。
# 只重试这次编译,不重跑 Cargo 或测试,真实编译错误仍按 rustc 状态返回。
echo '[rust-cache] cache infrastructure failed; compiling directly' >&2
# 后续 crate 直接编译,避免坏 daemon 让每个 crate 都支付一次启动超时。
: > "${state}/disabled"
exec "$@"
fi
exit "${status}"
;;
esac