2566ae705f
Project CI / AI game creator shell Rust shard 2/4 (push) Successful in 3m53s
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 4m11s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 4m6s
Project CI / AI game creator shell Rust shard 4/4 (push) Successful in 4m2s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m26s
Project CI / AI game creator shell Rust crates (push) Successful in 2m21s
Project CI / Backend tests (push) Successful in 6m22s
Project CI / Native shell tests (push) Successful in 5m25s
Project CI / Repository checks (push) Successful in 2m17s
Project CI / Frontend tests (push) Successful in 2m58s
Project CI / AI game creator shell web tests (push) Successful in 2m6s
上一轮把 2466 条 bin target 单测切成 4 片放在同一个 job 内多进程并行,run 2102 证明这条路 不通:门禁步骤跑满 18 分钟仍未结束,比整套串行的 507 秒还慢——同一容器内这几片共享 HOME、target 目录与固定临时路径,会互相拖慢。这一轮改成按 job 拆分。 - run-rust-shell-test-shards.mjs 新增 --shard-index=<i>:只跑第 i 片,供 CI 的每个分片 job 使用;覆盖自校验(片并集等于 --list 全集且互斥)仍针对全集执行,所以每个 job 都会 发现分片规则改动,不会被"只跑一片"绕过。负例 --shard-index 超出 --shards 立即失败。 不传 --shard-index 时行为不变(一条命令把 N 片放进程里并行),留给本地全量自测。 - project-ci.yml 由 7 个 job 变 11 个:原 AGC Rust job 拆成 AI game creator shell Rust shard 1/4 ~ 4/4 与 AI game creator shell Rust smoke,加上已有的 Rust crates,AGC 相关 门禁共 6 个 job。4 个片 job 只预热 AGC 壳自己那份锁定依赖,且与 smoke、crates 一样都是 纯 cargo 门禁,因此这 6 个 job 都不再执行 npm ci(每个省 1~3 分钟)。 - check-native-shells.mjs 分组由五个变十个:agc-rust-shard-1 ~ agc-rust-shard-4 与 agc-rust-smoke 取代 agc-rust-shell;每个分片分组的命令是 `npm run ai-game-creator-shell:check:rust:shell -- --shard-index=<i>`。 - project-ci-workflow.test.ts 相应更新:11 个 job、6 个 job 免 npm ci、10 个分组各被一个 job 恰好调用一次,并新增「每个分片 job 都落到 --shard-index」「4 个 index 各一次」 「分片运行器保留覆盖自校验」等断言。 - 同步运维文档、development-workflow、decision-log、pitfalls 与 gitea-ci-triage 技能到 job 级分片口径,并记录 run 2102 的反面实验,避免以后又改回单 job 内并行。 验证:npx vitest run scripts/project-ci-workflow.test.ts(12 passed)、eslint、prettier、 check:encoding(13330 文件)、check:doc-index、--groups=contract、mobile check-config 全绿; 分片运行器本地以 agent-runtime-core(7 条 → 2/2/2/1)验证 --shard-index 四个 index 各自 只跑一片、片 TMPDIR 隔离、无 index 时全量模式不变。Gitea master 分支保护需补 6 个新 context(共十一个),其中含 4 个分片 job、smoke job 与 crates job。 Co-authored-by: DotCraft <273930855+dotcraft-ai@users.noreply.github.com>
522 lines
20 KiB
YAML
522 lines
20 KiB
YAML
name: Project CI
|
||
|
||
on:
|
||
push:
|
||
branches:
|
||
- master
|
||
pull_request:
|
||
workflow_dispatch:
|
||
|
||
permissions:
|
||
contents: read
|
||
|
||
env:
|
||
CI: 'true'
|
||
CARGO_INCREMENTAL: '0'
|
||
CARGO_HTTP_MULTIPLEXING: 'false'
|
||
CARGO_NET_RETRY: '10'
|
||
CARGO_TERM_COLOR: always
|
||
NPM_CONFIG_AUDIT: 'false'
|
||
NPM_CONFIG_FETCH_RETRIES: '10'
|
||
NPM_CONFIG_FETCH_RETRY_FACTOR: '2'
|
||
NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT: '60000'
|
||
NPM_CONFIG_FETCH_RETRY_MINTIMEOUT: '2000'
|
||
NPM_CONFIG_FUND: 'false'
|
||
NPM_CONFIG_PREFER_OFFLINE: 'true'
|
||
RUSTUP_AUTO_INSTALL: '0'
|
||
RUSTC_WRAPPER: ''
|
||
CARGO_BUILD_RUSTC_WRAPPER: ''
|
||
|
||
# job 声明顺序就是 runner 领取顺序,因此把最长尾的客户端 Rust 门禁排在前面,
|
||
# 让它在最少的等待下占用并发槽位;其余 job 按时长递减排列。
|
||
#
|
||
# 客户端(微信壳 / Expo 移动壳 / Tauri 桌面壳 / AI 游戏创作壳)门禁原先全部串在
|
||
# `Native shell tests` 一个 job 里,实测 18 分 37 秒。现在按门禁组拆成一个分组一个 job:
|
||
# `Native shell tests`(契约 + H5 / 微信 / 移动 / 桌面壳门禁 + 发布构建 smoke)、
|
||
# `AI game creator shell web tests`(typecheck + 壳内测试)、`AI game creator shell
|
||
# Rust shard 1/4` 到 `4/4`(AGC 壳 bin 单测按名单分 4 片)、`AI game creator shell
|
||
# Rust smoke`(agent-run smoke)与 `AI game creator shell Rust crates`(AGC 壳依赖的
|
||
# 共享 / 平台 crate 测试)。各自的命令与拆分前逐一对应,本地
|
||
# `npm run check:native-shells` 仍是同一条串行序列。
|
||
#
|
||
# AGC 壳的 bin 单测(2466 条)按名单分 4 片、一片一个 job:片内保持 `--test-threads=1`
|
||
# (当年线程并行会互相干扰的是进程内后台锁与异步终态),片与片之间靠 job 级并发摊开。
|
||
# 不要改回「一个 job 里多进程并行这几片」:同一容器内它们会争抢共享 HOME、target 与固定
|
||
# 临时路径,实测比整套串行还慢。每个分片 job 都会自校验「片并集等于全集且互斥」。
|
||
jobs:
|
||
# AGC 壳自身的 Rust bin 单测分片,4 片各自独立 job 并发执行、片内仍保持
|
||
# `--test-threads=1`。这里不装 npm 依赖:壳 Rust 门禁只用 cargo 与 node 内建模块,
|
||
# 也只需要 AGC 壳自己那份锁定依赖。
|
||
ai-game-creator-shell-rust-shard-1:
|
||
name: AI game creator shell Rust shard 1/4
|
||
runs-on: genarrative-ci
|
||
steps:
|
||
- name: Checkout full history from Gitea
|
||
env:
|
||
GENARRATIVE_GITEA_FETCH_DEPTH: '0'
|
||
GENARRATIVE_GITEA_TOKEN: ${{ github.token }}
|
||
run: genarrative-gitea-checkout
|
||
|
||
- name: Validate preinstalled CI job image and sandbox
|
||
run: GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1 bash scripts/check-gitea-ci-job-image.sh
|
||
|
||
- name: Prepare AI game creator shell Rust dependencies
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
# AGC 壳有独立 Cargo.lock,其 path 依赖已含 platform-llm、platform-agent、
|
||
# agent-runtime-core 与 shared-contracts,因此只锁这一份 manifest 就能覆盖壳测试
|
||
# 与 smoke 的全部第三方依赖;server-rs 那次预热归 crate 级 job,不在这里重复。
|
||
for attempt in $(seq 1 5); do
|
||
if cargo fetch --locked \
|
||
--target x86_64-unknown-linux-gnu \
|
||
--manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml; then
|
||
break
|
||
fi
|
||
if [[ "${attempt}" -eq 5 ]]; then
|
||
echo 'AI game creator shell Cargo dependency fetch failed after 5 attempts.' >&2
|
||
exit 1
|
||
fi
|
||
sleep $((attempt * 2))
|
||
done
|
||
|
||
- name: Run AI game creator shell Rust shard 1/4
|
||
run: npm run check:native-shells:agc-rust-shard-1
|
||
|
||
ai-game-creator-shell-rust-shard-2:
|
||
name: AI game creator shell Rust shard 2/4
|
||
runs-on: genarrative-ci
|
||
steps:
|
||
- name: Checkout full history from Gitea
|
||
env:
|
||
GENARRATIVE_GITEA_FETCH_DEPTH: '0'
|
||
GENARRATIVE_GITEA_TOKEN: ${{ github.token }}
|
||
run: genarrative-gitea-checkout
|
||
|
||
- name: Validate preinstalled CI job image and sandbox
|
||
run: GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1 bash scripts/check-gitea-ci-job-image.sh
|
||
|
||
- name: Prepare AI game creator shell Rust dependencies
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
for attempt in $(seq 1 5); do
|
||
if cargo fetch --locked \
|
||
--target x86_64-unknown-linux-gnu \
|
||
--manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml; then
|
||
break
|
||
fi
|
||
if [[ "${attempt}" -eq 5 ]]; then
|
||
echo 'AI game creator shell Cargo dependency fetch failed after 5 attempts.' >&2
|
||
exit 1
|
||
fi
|
||
sleep $((attempt * 2))
|
||
done
|
||
|
||
- name: Run AI game creator shell Rust shard 2/4
|
||
run: npm run check:native-shells:agc-rust-shard-2
|
||
|
||
ai-game-creator-shell-rust-shard-3:
|
||
name: AI game creator shell Rust shard 3/4
|
||
runs-on: genarrative-ci
|
||
steps:
|
||
- name: Checkout full history from Gitea
|
||
env:
|
||
GENARRATIVE_GITEA_FETCH_DEPTH: '0'
|
||
GENARRATIVE_GITEA_TOKEN: ${{ github.token }}
|
||
run: genarrative-gitea-checkout
|
||
|
||
- name: Validate preinstalled CI job image and sandbox
|
||
run: GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1 bash scripts/check-gitea-ci-job-image.sh
|
||
|
||
- name: Prepare AI game creator shell Rust dependencies
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
for attempt in $(seq 1 5); do
|
||
if cargo fetch --locked \
|
||
--target x86_64-unknown-linux-gnu \
|
||
--manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml; then
|
||
break
|
||
fi
|
||
if [[ "${attempt}" -eq 5 ]]; then
|
||
echo 'AI game creator shell Cargo dependency fetch failed after 5 attempts.' >&2
|
||
exit 1
|
||
fi
|
||
sleep $((attempt * 2))
|
||
done
|
||
|
||
- name: Run AI game creator shell Rust shard 3/4
|
||
run: npm run check:native-shells:agc-rust-shard-3
|
||
|
||
ai-game-creator-shell-rust-shard-4:
|
||
name: AI game creator shell Rust shard 4/4
|
||
runs-on: genarrative-ci
|
||
steps:
|
||
- name: Checkout full history from Gitea
|
||
env:
|
||
GENARRATIVE_GITEA_FETCH_DEPTH: '0'
|
||
GENARRATIVE_GITEA_TOKEN: ${{ github.token }}
|
||
run: genarrative-gitea-checkout
|
||
|
||
- name: Validate preinstalled CI job image and sandbox
|
||
run: GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1 bash scripts/check-gitea-ci-job-image.sh
|
||
|
||
- name: Prepare AI game creator shell Rust dependencies
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
for attempt in $(seq 1 5); do
|
||
if cargo fetch --locked \
|
||
--target x86_64-unknown-linux-gnu \
|
||
--manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml; then
|
||
break
|
||
fi
|
||
if [[ "${attempt}" -eq 5 ]]; then
|
||
echo 'AI game creator shell Cargo dependency fetch failed after 5 attempts.' >&2
|
||
exit 1
|
||
fi
|
||
sleep $((attempt * 2))
|
||
done
|
||
|
||
- name: Run AI game creator shell Rust shard 4/4
|
||
run: npm run check:native-shells:agc-rust-shard-4
|
||
|
||
# agent-run smoke 会 spawn `cargo run`(走壳自己的 manifest),同样不装 npm 依赖,
|
||
# 单独一个 job,免得把已经压到 4 分钟级的片 job 拖长。
|
||
ai-game-creator-shell-rust-smoke:
|
||
name: AI game creator shell Rust smoke
|
||
runs-on: genarrative-ci
|
||
steps:
|
||
- name: Checkout full history from Gitea
|
||
env:
|
||
GENARRATIVE_GITEA_FETCH_DEPTH: '0'
|
||
GENARRATIVE_GITEA_TOKEN: ${{ github.token }}
|
||
run: genarrative-gitea-checkout
|
||
|
||
- name: Validate preinstalled CI job image and sandbox
|
||
run: GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1 bash scripts/check-gitea-ci-job-image.sh
|
||
|
||
- name: Prepare AI game creator shell Rust dependencies
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
for attempt in $(seq 1 5); do
|
||
if cargo fetch --locked \
|
||
--target x86_64-unknown-linux-gnu \
|
||
--manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml; then
|
||
break
|
||
fi
|
||
if [[ "${attempt}" -eq 5 ]]; then
|
||
echo 'AI game creator shell Cargo dependency fetch failed after 5 attempts.' >&2
|
||
exit 1
|
||
fi
|
||
sleep $((attempt * 2))
|
||
done
|
||
|
||
- name: Run AI game creator shell agent-run smoke
|
||
run: npm run check:native-shells:agc-rust-smoke
|
||
|
||
# AGC 壳依赖的共享 / 平台 crate 测试用的是 server-rs workspace 与两个无锁独立 crate
|
||
# 的 manifest,属另一套依赖图,因此单独一个 job 预热、单独跑。
|
||
ai-game-creator-shell-rust-crates:
|
||
name: AI game creator shell Rust crates
|
||
runs-on: genarrative-ci
|
||
steps:
|
||
- name: Checkout full history from Gitea
|
||
env:
|
||
GENARRATIVE_GITEA_FETCH_DEPTH: '0'
|
||
GENARRATIVE_GITEA_TOKEN: ${{ github.token }}
|
||
run: genarrative-gitea-checkout
|
||
|
||
- name: Validate preinstalled CI job image and sandbox
|
||
run: GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1 bash scripts/check-gitea-ci-job-image.sh
|
||
|
||
- name: Prepare server-rs Rust dependencies
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
for attempt in $(seq 1 5); do
|
||
if cargo fetch --locked \
|
||
--target x86_64-unknown-linux-gnu \
|
||
--manifest-path server-rs/Cargo.toml; then
|
||
break
|
||
fi
|
||
if [[ "${attempt}" -eq 5 ]]; then
|
||
echo 'server-rs Cargo dependency fetch failed after 5 attempts.' >&2
|
||
exit 1
|
||
fi
|
||
sleep $((attempt * 2))
|
||
done
|
||
|
||
- name: Prepare standalone Rust crate dependencies
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
# agent-runtime-core / agent-runtime-orchestration 被 server-rs/Cargo.toml 的
|
||
# exclude 排除,不参与上面的 workspace 锁文件,因此上面那次锁定 fetch 覆盖不到它们;
|
||
# 而 `npm run ai-game-creator-shell:check:rust:crates` 会用
|
||
# `cargo test --manifest-path` 单独跑这两个 crate。不在这里预热的话,这两条测试
|
||
# 会在测试阶段自己 `Updating crates.io index`,crates.io 一抖动整条 job 就红
|
||
# (见 #327 / PR #316 run 1950)。
|
||
# 两个 crate 都没有提交 Cargo.lock,所以这里只能做不带锁标志的 fetch:
|
||
# 加锁标志会因为缺少锁文件直接失败。生成的 Cargo.lock 落在两个 crate 目录内,
|
||
# 已被各自的 .gitignore 忽略,只留在容器里;随后的测试阶段因此能用锁定版本
|
||
# 解析,不再触碰 registry index。
|
||
for manifest_path in \
|
||
server-rs/crates/agent-runtime-core/Cargo.toml \
|
||
server-rs/crates/agent-runtime-orchestration/Cargo.toml; do
|
||
for attempt in $(seq 1 5); do
|
||
if cargo fetch \
|
||
--target x86_64-unknown-linux-gnu \
|
||
--manifest-path "${manifest_path}"; then
|
||
break
|
||
fi
|
||
if [[ "${attempt}" -eq 5 ]]; then
|
||
echo "standalone crate dependency fetch failed after 5 attempts: ${manifest_path}" >&2
|
||
exit 1
|
||
fi
|
||
sleep $((attempt * 2))
|
||
done
|
||
done
|
||
|
||
- name: Run AI game creator shell shared crate gates
|
||
run: npm run check:native-shells:agc-rust-crates
|
||
|
||
backend-tests:
|
||
name: Backend tests
|
||
runs-on: genarrative-ci
|
||
steps:
|
||
- name: Checkout full history from Gitea
|
||
env:
|
||
GENARRATIVE_GITEA_FETCH_DEPTH: '0'
|
||
GENARRATIVE_GITEA_TOKEN: ${{ github.token }}
|
||
run: genarrative-gitea-checkout
|
||
|
||
- name: Validate preinstalled CI job image and sandbox
|
||
run: GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1 bash scripts/check-gitea-ci-job-image.sh
|
||
|
||
- name: Resolve comparison base
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
base_ref="$(node -e '
|
||
const fs = require("node:fs");
|
||
const event = JSON.parse(fs.readFileSync(process.env.GITHUB_EVENT_PATH, "utf8"));
|
||
process.stdout.write(event.pull_request?.base?.sha ?? event.before ?? "");
|
||
')"
|
||
if [[ -n "${base_ref}" && ! "${base_ref}" =~ ^0+$ ]]; then
|
||
git cat-file -e "${base_ref}^{commit}" 2>/dev/null || {
|
||
echo "comparison base commit is unavailable: ${base_ref}" >&2
|
||
exit 1
|
||
}
|
||
else
|
||
base_ref="$(git merge-base HEAD origin/master 2>/dev/null || git rev-parse HEAD)"
|
||
fi
|
||
resolved_base_ref="$(git rev-parse --verify "${base_ref}^{commit}" 2>/dev/null || true)"
|
||
head_ref="$(git rev-parse HEAD)"
|
||
if [[ "${resolved_base_ref}" == "${head_ref}" ]]; then
|
||
resolved_base_ref="$(git rev-parse --verify HEAD^ 2>/dev/null || true)"
|
||
fi
|
||
if [[ -z "${resolved_base_ref}" ]]; then
|
||
echo 'comparison base must resolve to a commit distinct from HEAD.' >&2
|
||
exit 1
|
||
fi
|
||
base_ref="${resolved_base_ref}"
|
||
if [[ "${GITHUB_EVENT_NAME:-}" == 'pull_request' ]] \
|
||
&& ! git merge-base --is-ancestor "${base_ref}" HEAD; then
|
||
echo 'pull request head does not contain the latest base commit; update the branch and rerun CI.' >&2
|
||
exit 1
|
||
fi
|
||
echo "SPACETIME_SCHEMA_BASE_REF=${base_ref}" >> "${GITHUB_ENV}"
|
||
|
||
- name: Install npm dependencies
|
||
run: bash scripts/ci-npm-ci-with-retry.sh
|
||
|
||
- name: Prepare server-rs Rust dependencies
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
for attempt in $(seq 1 5); do
|
||
if cargo fetch --locked \
|
||
--target x86_64-unknown-linux-gnu \
|
||
--manifest-path server-rs/Cargo.toml; then
|
||
break
|
||
fi
|
||
if [[ "${attempt}" -eq 5 ]]; then
|
||
echo 'server-rs Cargo dependency fetch failed after 5 attempts.' >&2
|
||
exit 1
|
||
fi
|
||
sleep $((attempt * 2))
|
||
done
|
||
|
||
- name: Check server-rs boundaries
|
||
run: npm run check:server-rs-ddd
|
||
|
||
- name: Run server-rs workspace tests
|
||
run: cargo test --locked --workspace --exclude spacetime-module --no-fail-fast --manifest-path server-rs/Cargo.toml
|
||
|
||
- name: Run SpacetimeDB module unit tests
|
||
run: cargo test --locked -p spacetime-module --no-fail-fast --manifest-path server-rs/Cargo.toml
|
||
|
||
- name: Check api-server targets
|
||
run: cargo check --locked -p api-server --all-targets --manifest-path server-rs/Cargo.toml
|
||
|
||
- name: Check SpacetimeDB module
|
||
run: cargo check --locked -p spacetime-module --manifest-path server-rs/Cargo.toml
|
||
|
||
# 客户端的壳级与契约门禁:静态契约断言、H5 / 微信 / 移动 / 桌面壳运行时门禁,
|
||
# 以及依赖发布产物的构建 smoke。
|
||
native-shell-tests:
|
||
name: Native shell tests
|
||
runs-on: genarrative-ci
|
||
steps:
|
||
- name: Checkout full history from Gitea
|
||
env:
|
||
GENARRATIVE_GITEA_FETCH_DEPTH: '0'
|
||
GENARRATIVE_GITEA_TOKEN: ${{ github.token }}
|
||
run: genarrative-gitea-checkout
|
||
|
||
- name: Validate preinstalled CI job image and sandbox
|
||
run: GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1 bash scripts/check-gitea-ci-job-image.sh
|
||
|
||
- name: Install npm dependencies
|
||
run: bash scripts/ci-npm-ci-with-retry.sh
|
||
|
||
- name: Prepare native Rust dependencies
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
for manifest_path in \
|
||
apps/desktop-shell/src-tauri/Cargo.toml \
|
||
apps/ai-game-creator-shell/src-tauri/Cargo.toml; do
|
||
for attempt in $(seq 1 5); do
|
||
if cargo fetch --locked \
|
||
--target x86_64-unknown-linux-gnu \
|
||
--manifest-path "${manifest_path}"; then
|
||
break
|
||
fi
|
||
if [[ "${attempt}" -eq 5 ]]; then
|
||
echo "Cargo dependency fetch failed after 5 attempts: ${manifest_path}" >&2
|
||
exit 1
|
||
fi
|
||
sleep $((attempt * 2))
|
||
done
|
||
done
|
||
|
||
- name: Run native shell contract gates
|
||
run: npm run check:native-shells:contract
|
||
|
||
- name: Run native shell gates
|
||
run: npm run check:native-shells:shells
|
||
|
||
- name: Run native shell release build smoke
|
||
run: npm run check:native-shells:release
|
||
|
||
- name: Ensure native lockfiles are unchanged
|
||
run: git diff --exit-code -- apps/desktop-shell/src-tauri/Cargo.lock apps/ai-game-creator-shell/src-tauri/Cargo.lock
|
||
|
||
frontend-tests:
|
||
name: Frontend tests
|
||
runs-on: genarrative-ci
|
||
steps:
|
||
- name: Checkout source from Gitea
|
||
env:
|
||
GENARRATIVE_GITEA_FETCH_DEPTH: '1'
|
||
GENARRATIVE_GITEA_TOKEN: ${{ github.token }}
|
||
run: genarrative-gitea-checkout
|
||
|
||
- name: Validate preinstalled CI job image and sandbox
|
||
run: GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1 bash scripts/check-gitea-ci-job-image.sh
|
||
|
||
- name: Install npm dependencies
|
||
run: bash scripts/ci-npm-ci-with-retry.sh
|
||
|
||
- name: Run frontend and script tests
|
||
run: npm run test
|
||
|
||
- name: Run BgFilter worker smoke harness tests
|
||
run: npm run bgfilter-worker:smoke-test
|
||
|
||
- name: Validate production health patrol behavior
|
||
run: npm run check:production-health-patrol
|
||
|
||
- name: Validate production API release behavior
|
||
run: npm run check:production-api-release
|
||
|
||
- name: Validate production API deploy behavior
|
||
run: npm run check:production-api-deploy
|
||
|
||
repository-checks:
|
||
name: Repository checks
|
||
runs-on: genarrative-ci
|
||
steps:
|
||
- name: Checkout full history from Gitea
|
||
env:
|
||
GENARRATIVE_GITEA_FETCH_DEPTH: '0'
|
||
GENARRATIVE_GITEA_TOKEN: ${{ github.token }}
|
||
run: genarrative-gitea-checkout
|
||
|
||
- name: Validate preinstalled CI job image and sandbox
|
||
run: GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1 bash scripts/check-gitea-ci-job-image.sh
|
||
|
||
- name: Resolve comparison base
|
||
shell: bash
|
||
run: |
|
||
set -euo pipefail
|
||
base_ref="$(node -e '
|
||
const fs = require("node:fs");
|
||
const event = JSON.parse(fs.readFileSync(process.env.GITHUB_EVENT_PATH, "utf8"));
|
||
process.stdout.write(event.pull_request?.base?.sha ?? event.before ?? "");
|
||
')"
|
||
if [[ -n "${base_ref}" && ! "${base_ref}" =~ ^0+$ ]]; then
|
||
git cat-file -e "${base_ref}^{commit}" 2>/dev/null || {
|
||
echo "comparison base commit is unavailable: ${base_ref}" >&2
|
||
exit 1
|
||
}
|
||
else
|
||
base_ref="$(git merge-base HEAD origin/master 2>/dev/null || git rev-parse HEAD)"
|
||
fi
|
||
resolved_base_ref="$(git rev-parse --verify "${base_ref}^{commit}" 2>/dev/null || true)"
|
||
head_ref="$(git rev-parse HEAD)"
|
||
if [[ "${resolved_base_ref}" == "${head_ref}" ]]; then
|
||
resolved_base_ref="$(git rev-parse --verify HEAD^ 2>/dev/null || true)"
|
||
fi
|
||
if [[ -z "${resolved_base_ref}" ]]; then
|
||
echo 'comparison base must resolve to a commit distinct from HEAD.' >&2
|
||
exit 1
|
||
fi
|
||
base_ref="${resolved_base_ref}"
|
||
if [[ "${GITHUB_EVENT_NAME:-}" == 'pull_request' ]] \
|
||
&& ! git merge-base --is-ancestor "${base_ref}" HEAD; then
|
||
echo 'pull request head does not contain the latest base commit; update the branch and rerun CI.' >&2
|
||
exit 1
|
||
fi
|
||
echo "SPACETIME_SCHEMA_BASE_REF=${base_ref}" >> "${GITHUB_ENV}"
|
||
|
||
- name: Install npm dependencies
|
||
run: bash scripts/ci-npm-ci-with-retry.sh
|
||
|
||
- name: Run repository checks
|
||
run: npm run check:repository-ci
|
||
|
||
# 客户端的 AI 游戏创作壳前端门禁:typecheck 与壳内测试,不触碰 Cargo。
|
||
ai-game-creator-shell-web-tests:
|
||
name: AI game creator shell web tests
|
||
runs-on: genarrative-ci
|
||
steps:
|
||
- name: Checkout full history from Gitea
|
||
env:
|
||
GENARRATIVE_GITEA_FETCH_DEPTH: '0'
|
||
GENARRATIVE_GITEA_TOKEN: ${{ github.token }}
|
||
run: genarrative-gitea-checkout
|
||
|
||
- name: Validate preinstalled CI job image and sandbox
|
||
run: GENARRATIVE_GITEA_CI_CHECK_RUNTIME=1 bash scripts/check-gitea-ci-job-image.sh
|
||
|
||
- name: Install npm dependencies
|
||
run: bash scripts/ci-npm-ci-with-retry.sh
|
||
|
||
- name: Run AI game creator shell web gates
|
||
run: npm run check:native-shells:agc-web
|