Files
Genarrative/scripts/build-gitea-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

86 lines
4.6 KiB
Bash

#!/usr/bin/env bash
# 由能管理 CI 镜像的维护者运行;不得在 PR job 内提供 Docker API/发布权限。
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)"
base_ref="${1:?usage: build-gitea-rust-cache.sh <verified-base-image> <candidate-tag>}"
candidate_tag="${2:?candidate image tag is required}"
# 与实际 Gitea checkout 路径一致;Rust 对象 key 包含编译 cwd,不能随意换临时根。
workspace=/workspace/GenarrativeAI/Genarrative
[[ "${CI:-}" != true ]] || { echo 'Run on the trusted image builder, outside CI jobs.' >&2; exit 1; }
base_id="$(docker image inspect --format '{{.Id}}' "${base_ref}")"
[[ "${base_id}" =~ ^sha256:[a-f0-9]{64}$ ]]
# 删除容器内旧对象不能释放镜像底层;每次必须从不含对象快照的基础镜像重建。
docker run --rm --network none --read-only --cap-drop=ALL \
--security-opt=no-new-privileges --entrypoint /bin/bash "${base_id}" -c '
if [[ -e /opt/genarrative-ci/rust-cache ]]; then
echo "基础镜像已包含 Rust 对象缓存;请使用不含对象快照的原始 CI 镜像,禁止叠层。" >&2
exit 1
fi
'
bash "${repo_root}/scripts/gitea-ci-job-image.sh" verify "${base_id}"
# 只归档远端 master 的确定提交;不复制当前工作区或本地凭据。
git -C "${repo_root}" fetch --no-tags origin refs/heads/master
source_commit="$(git -C "${repo_root}" rev-parse FETCH_HEAD^{commit})"
work_dir="$(mktemp -d "${TMPDIR:-/tmp}/gitea-rust-cache.XXXXXX")"
container_id=''
cleanup() {
if [[ -n "${container_id}" ]]; then docker rm -f "${container_id}" >/dev/null; fi
rm -rf -- "${work_dir}"
}
trap cleanup EXIT
archive=sccache-v0.18.0-x86_64-unknown-linux-musl.tar.gz
curl --fail --location --retry 3 --connect-timeout 15 --max-time 180 \
"https://github.com/mozilla/sccache/releases/download/v0.18.0/${archive}" \
--output "${work_dir}/${archive}"
printf '45f1447fbe231e3037bde351ef70677dd212216c8d62ae7ca409fecc4d6acc89 %s\n' "${work_dir}/${archive}" | sha256sum --check
tar -xzf "${work_dir}/${archive}" --directory "${work_dir}"
mkdir "${work_dir}/snapshot"
cp "${work_dir}/sccache-v0.18.0-x86_64-unknown-linux-musl/sccache" "${work_dir}/snapshot/sccache"
printf '%s\n' "${source_commit}" > "${work_dir}/snapshot/source-commit.txt"
printf '%s\n' "${base_id}" > "${work_dir}/snapshot/base-image.txt"
printf '%s\n' "${workspace}" > "${work_dir}/snapshot/workspace.txt"
# 临时容器不挂载宿主目录/socket,不携带 Git/OSS/Jenkins 凭据,限制资源占用。
container_id="$(docker run --detach --cpus=4 --memory=12g --pids-limit=1024 \
--cap-drop=ALL --security-opt=no-new-privileges \
--entrypoint /bin/bash "${base_id}" -c 'sleep infinity')"
docker exec "${container_id}" mkdir -p "${workspace}" /opt/genarrative-ci/rust-cache/objects
git -C "${repo_root}" archive "${source_commit}" | docker cp - "${container_id}:${workspace}"
docker cp "${work_dir}/snapshot/." "${container_id}:/opt/genarrative-ci/rust-cache/"
docker cp "${repo_root}/scripts/ci-rust-cache.sh" "${container_id}:/tmp/ci-rust-cache.sh"
docker exec --interactive --workdir "${workspace}" "${container_id}" bash -s <<'WARM'
set -euo pipefail
rustc -vV > /opt/genarrative-ci/rust-cache/rustc.txt
export GITHUB_ENV=/tmp/rust-cache.env CARGO_INCREMENTAL=0 CARGO_BUILD_JOBS=4 CI=true
# sccache 对 CARGO_*(除 jobserver/jobs 等特例)参与 hash,必须与 workflow 对齐。
export CARGO_HTTP_MULTIPLEXING=false CARGO_NET_RETRY=10 CARGO_TERM_COLOR=always
bash /tmp/ci-rust-cache.sh prepare
set -a
source "${GITHUB_ENV}"
set +a
test -n "${RUSTC_WRAPPER}"
trap 'bash /tmp/ci-rust-cache.sh report' EXIT
cd apps/ai-game-creator-shell/src-tauri
cargo test --locked --manifest-path Cargo.toml \
--bin genarrative-ai-game-creator-shell --no-run
WARM
docker cp "${container_id}:/opt/genarrative-ci/rust-cache/." "${work_dir}/snapshot/"
docker rm -f "${container_id}" >/dev/null
container_id=''
# 从原基础镜像重新组装,只 COPY 对象快照;不 commit 含源码/target 的预热容器。
cat > "${work_dir}/Dockerfile" <<EOF
FROM ${base_id}
COPY snapshot/ /opt/genarrative-ci/rust-cache/
LABEL world.genarrative.ci.rust-cache-source="${source_commit}"
LABEL world.genarrative.ci.rust-cache-base="${base_id}"
EOF
printf '**\n!Dockerfile\n!snapshot/\n!snapshot/**\n' > "${work_dir}/.dockerignore"
docker build --pull=false --tag "${candidate_tag}" "${work_dir}"
bash "${repo_root}/scripts/gitea-ci-job-image.sh" verify "${candidate_tag}"
printf 'snapshot_source=%s\ncandidate_image=%s\n' "${source_commit}" "$(docker image inspect --format '{{.Id}}' "${candidate_tag}")"
echo 'Candidate only: the runner configuration and running jobs have not been changed.'