Files
MoShiro/ci/verify-backend.sh
T
justme b2b63c065d
ci/woodpecker/push/web-build Pipeline was successful
ci/woodpecker/push/verify Pipeline failed
ci/woodpecker/push/dev-image unknown status
ci: sync packaging and docs into the test cache; retry failed tests once [CI]
2026-10-09 14:02:42 +08:00

79 lines
3.8 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# 后端测试,带增量编译:把后端源码同步到一个固定目录,在那里 dotnet build / test。
#
# 为什么:每条流水线的工作区都是新克隆的(路径、文件时间都是新的),MSBuild 没法增量,每次从零编译整个解决方案要两分钟。
# 固定目录里 obj/bin 留着,同步时只更新内容真的变了的文件(按内容比较,没变的文件时间不动),MSBuild 就只重编译受影响的项目;
# 改动小的时候整个测试 20–30 秒。
#
# 同一时间只允许一条流水线用这份缓存(flock);增量编译失败(缓存坏了)就清掉缓存从头编译一次,所以最坏也只是回到原来的速度。
set -euo pipefail
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cache="${MOSHIRO_CI_CACHE:-/home/justme/woodpecker/cache/backend-tests}"
src="$cache/src"
mkdir -p "$cache"
# .NET 第一次运行的欢迎信息、遥测、开发证书,在每条流水线的临时 HOME 里都要白做一遍
export DOTNET_CLI_TELEMETRY_OPTOUT=1 DOTNET_NOLOGO=1 DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 DOTNET_GENERATE_ASPNET_CERTIFICATE=false
exec 9> "$cache/.lock"
flock -w 900 9 || { echo "could not get the backend test cache lock" >&2; exit 1; }
sync_sources() {
mkdir -p "$src"
local list
list="$(mktemp)"
# 构建和测试要用的:backend/、docs/(有的测试读协议文档)、packaging/(Backend.Api 把安装脚本、compose、Quadlet 单元和发布公钥当嵌入资源编进去)、根目录的几个文件(Directory.Packages.props、global.json、NuGet 配置)
(cd "$root" && git ls-files -z -- backend packaging docs Directory.Packages.props global.json 'NuGet.*' 'nuget.*') | tr '\0' '\n' | LC_ALL=C sort -u > "$list"
local changed=0 file
while IFS= read -r file; do
[[ -f "$root/$file" ]] || continue
if ! cmp -s "$root/$file" "$src/$file" 2>/dev/null; then
mkdir -p "$(dirname "$src/$file")"
cp "$root/$file" "$src/$file"
changed=$((changed + 1))
fi
done < "$list"
# 仓库里已经删掉的文件,缓存里也要删(构建产物 artifacts 不动)
local stale=0
while IFS= read -r file; do
[[ -n "$file" ]] || continue
rm -f "$src/$file"
stale=$((stale + 1))
done < <(cd "$src" && find backend packaging docs -type f -not -path 'backend/artifacts/*' -not -path '*/bin/*' -not -path '*/obj/*' 2>/dev/null | LC_ALL=C sort | LC_ALL=C comm -23 - <(grep -E '^(backend|packaging|docs)/' "$list"))
rm -f "$list"
echo "synced sources: $changed changed, $stale removed"
}
build() { (cd "$src/backend" && dotnet build MoShiro.slnx -c Release --nologo -v:q); }
sync_sources
if ! build; then
echo "incremental build failed; wiping the cache and building from scratch" >&2
rm -rf "$src"
sync_sources
build
fi
# 测试在缓存目录里跑完整的解决方案(不再重复编译)
log="$cache/last-test.log"
status=0
(cd "$src/backend" && dotnet test --solution MoShiro.slnx -c Release --no-build) 2>&1 | tee "$log" || status=${PIPESTATUS[0]}
[[ "$status" == 0 ]] && exit 0
# 个别端到端测试在机器忙的时候会偶发失败(比如探针 5 秒内没连上):失败的测试单独重跑一次,重跑通过就当通过(日志里留着第一次的失败);
# 失败太多说明是真的坏了,不重跑
mapfile -t failed < <(grep -E '^(failed|失败) ' "$log" | awk '{print $2}' | awk -F. '{print $NF}' | sort -u)
if ((${#failed[@]} == 0 || ${#failed[@]} > 3)); then
echo "${#failed[@]} failed test(s); not retrying" >&2
exit "$status"
fi
for name in "${failed[@]}"; do
echo "retrying $name once"
(cd "$src/backend" && dotnet test --solution MoShiro.slnx -c Release --no-build --filter-method "$name") || {
echo "$name failed again" >&2
exit 1
}
done
echo "the failed tests passed on retry"