705bb1e6b3
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled
Project CI / AI game creator shell Rust crates (push) Has been cancelled
Project CI / Backend tests (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/462
145 lines
7.5 KiB
Bash
145 lines
7.5 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}"
|
|
requested_source_commit="${3:-}"
|
|
if [[ "$#" -gt 3 ]]; then
|
|
echo 'usage: build-gitea-rust-cache.sh <verified-base-image> <candidate-tag> [master-commit-sha]' >&2
|
|
exit 2
|
|
fi
|
|
if [[ -n "${requested_source_commit}" && ! "${requested_source_commit}" =~ ^[0-9a-fA-F]{40}$ ]]; then
|
|
echo 'master-commit-sha must be a complete 40-character SHA.' >&2
|
|
exit 2
|
|
fi
|
|
# 与实际 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
|
|
master_commit="$(git -C "${repo_root}" rev-parse FETCH_HEAD^{commit})"
|
|
if [[ -n "${requested_source_commit}" ]]; then
|
|
git -C "${repo_root}" cat-file -e "${requested_source_commit}^{commit}"
|
|
if ! git -C "${repo_root}" merge-base --is-ancestor "${requested_source_commit}" "${master_commit}"; then
|
|
echo "master-commit-sha is not contained in fetched master: ${requested_source_commit}" >&2
|
|
exit 1
|
|
fi
|
|
source_commit="$(git -C "${repo_root}" rev-parse "${requested_source_commit}^{commit}")"
|
|
else
|
|
source_commit="${master_commit}"
|
|
fi
|
|
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 exec "${container_id}" cp "${workspace}/scripts/ci-rust-cache.sh" /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.'
|