Files
Genarrative/scripts/build-gitea-rust-cache.sh
T
lhk229 292e42cf5e
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Successful in 1m22s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m51s
Project CI / Backend tests (push) Successful in 5m35s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 7m8s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 8m11s
Project CI / Native shell tests (push) Successful in 7m6s
Project CI / Frontend tests (push) Successful in 3m30s
Project CI / Repository checks (push) Successful in 3m28s
Project CI / AI game creator shell web tests (push) Successful in 3m7s
为全部 Rust 测试组启用隔离编译缓存
扩展缓存消费到 AGC 双 lane、crates、smoke、后端及桌面壳测试
补齐各组实际编译目标和工作目录的可信快照预热
保留 SpacetimeDB 独立编译边界并排除发布构建缓存
根据完整快照体积将对象缓存上限调整为 4 GiB
更新工作流契约测试、运维文档及项目共享记忆
2026-09-22 05:52:16 +00:00

126 lines
6.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
# 与 CI 的工作目录、features 和目标逐项对齐,只编译,不运行测试/应用。
echo '[rust-cache] warming backend tests and checks'
cargo test --locked --workspace --exclude spacetime-module --no-fail-fast \
--manifest-path server-rs/Cargo.toml --no-run
cargo test --locked -p spacetime-module --no-fail-fast \
--manifest-path server-rs/Cargo.toml --no-run
cargo check --locked -p api-server --all-targets --manifest-path server-rs/Cargo.toml
cargo check --locked -p spacetime-module --manifest-path server-rs/Cargo.toml
echo '[rust-cache] warming AGC standalone, shared and plugin crates'
for manifest in \
server-rs/crates/agent-runtime-core/Cargo.toml \
server-rs/crates/agent-runtime-orchestration/Cargo.toml \
plugins/agc-cocos-editor/native/cocos-editor-bridge/Cargo.toml; do
# 与 CI 一样,这些 crate 没有已提交的独立 Cargo.lock。
cargo test --manifest-path "${manifest}" --no-run
done
cargo test --locked -p platform-llm --manifest-path server-rs/Cargo.toml --no-run
cargo test --locked -p shared-contracts --manifest-path server-rs/Cargo.toml --no-run
for editor in unity godot; do
cargo test --locked --manifest-path "plugins/agc-${editor}-editor/native/${editor}-editor-bridge/Cargo.toml" --no-run
done
echo '[rust-cache] warming desktop tests and AGC prompt contracts'
cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml --no-run
cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml \
--test runtime_prompt_bundle_build --test prompt_source_boundaries --no-run
# Cargo 的 fresh 判断不保证不同 cwd 都经过 sccache;先清掉临时容器中的 target。
cargo clean --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml
echo '[rust-cache] warming AGC Rust lanes'
(
cd apps/ai-game-creator-shell/src-tauri
cargo test --locked --manifest-path Cargo.toml \
--bin genarrative-ai-game-creator-shell --no-run
)
cargo clean --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml
echo '[rust-cache] warming AGC agent-run smoke executable'
(
cd apps/ai-game-creator-shell
cargo build --manifest-path src-tauri/Cargo.toml
)
du -sh /opt/genarrative-ci/rust-cache/objects
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.'