修复 Mac release 构建的 Git 心跳
Project CI / AI game creator shell Rust crates (pull_request) Successful in 5m49s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m35s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m19s
Project CI / Backend tests (pull_request) Successful in 8m31s
Project CI / Frontend tests (pull_request) Successful in 3m30s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m6s
Project CI / Repository checks (pull_request) Successful in 5m21s
Project CI / Native shell tests (pull_request) Successful in 7m3s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 5m49s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m35s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m19s
Project CI / Backend tests (pull_request) Successful in 8m31s
Project CI / Frontend tests (pull_request) Successful in 3m30s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m6s
Project CI / Repository checks (pull_request) Successful in 5m21s
Project CI / Native shell tests (pull_request) Successful in 7m3s
为 macOS Jenkins Checkout 增加 SSH 保活与 fetch 心跳。\n增加 fetch 超时诊断并补充生产运维门禁。\n更新 Mac 构建运维说明。
This commit is contained in:
@@ -908,6 +908,8 @@ Mac 单架构(arm64)构建脚本位于 `jenkins/Jenkinsfile.ai-game-creator-
|
||||
|
||||
Job 名为 `Genarrative-Agc-MacOS-Build`,SCM 直接读取仓库内上述 Jenkinsfile,参数为 `SOURCE_BRANCH`、`COMMIT_HASH`、`AGC_UPDATE_CHANNEL`、`AGC_RELEASE_VERSION`、`AGC_RELEASE_DRY_RUN`、`AGC_UPDATE_RELEASE_NOTES`、`OSSUTIL_BIN`、`CARGO_BUILD_JOBS`、`NOTIFICATION_EMAILS`。渠道参数是基础名(不含系统,默认 `dev`),脚本不接受 `dev-mac` 这类系统后缀,写入分区固定推导为 `<channel>-mac`:这与 Windows Job 的 `<channel>-win` 对称,也延续已发布客户端的端点。
|
||||
|
||||
Checkout 阶段的远端 `git fetch` 必须带 SSH `ConnectTimeout`、`ServerAliveInterval`/`ServerAliveCountMax`,并在前台以 20 秒间隔输出心跳;Mac agent 通过 WebSocket 接入 Jenkins,长时间没有 stdout 会被 Jetty idle timeout 断开,日志通常表现为 `ClosedChannelException`,不能误判成 Rust 或签名构建失败。心跳包装还设置 15 分钟可配置上限 `AGC_GIT_FETCH_TIMEOUT_SECONDS`,超时会输出 fetch 诊断并失败关闭。
|
||||
|
||||
该 Job 的职责是构建并发布 `<channel>-mac` 分区更新:执行 `npm ci` 后使用锁文件校验并补齐两种 macOS Codex 原生依赖,再调用 `scripts/build-macos-ci.mjs`(AGC 应用目录下)生成 arm64 单架构 app、arm64 隔离 smoke、arm64 DMG(`<产品名>_<版本>_aarch64.dmg`)与分区清单 `latest.json`,用产物内烘焙的公钥复核更新包签名(`verify-updater-signature.mjs`),最后按 `AGC_RELEASE_DRY_RUN` 决定是否上传 OSS。产物名(`*.app`、updater 归档、DMG、卷名)一律从渠道安装身份派生的产品名推导(`dev` 为开发版,`release` 复用正式产品名,自定义渠道带渠道后缀),校验脚本从包内 `Info.plist` 读取可执行名,改产品名或换渠道都不会让入口静默找错对象;隔离 smoke 用 `ditto --clone` 复制副本(实测整轮 6.8 秒,此前整包复制约 1 分钟),并在构建前删除本次将写出的 DMG/更新包/签名,保证归档产物一定来自本次构建。归档限 `artifacts/` 下的 DMG、SHA-256、`latest.json`、更新包签名、更新摘要、非敏感构建清单和源码 commit;不归档用户 HOME、Jenkins secret、原始工作目录或全量日志。
|
||||
|
||||
发布凭据全部走 Jenkins 全局凭据,并在 `withCredentials` 内注入当前进程:`AgcUpdaterSigningKey`(与 `AgcUpdaterSigningKeyPassword`)映射为 `TAURI_SIGNING_PRIVATE_KEY` / `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`,`AliyunAccessKeyId` / `AliyunaccessKeySecret` 映射为 `AGC_OSS_ACCESS_KEY_ID` / `AGC_OSS_ACCESS_KEY_SECRET`;私钥与凭据不写入 workspace、日志或归档产物。上传顺序为更新包、签名、首装包,三者全部成功后才覆盖 `agc/<channel>-mac/latest.json` 指针;`AGC_RELEASE_DRY_RUN` 与 Windows 渠道对称:默认关闭即真发布,勾选后才退化为演练(只打印将上传的对象、不写任何 OSS 对象)。本 Job 是正式发布入口,调度器在发号后与 Windows 一起触发它,并额外传 `SKIP_IF_SUPERSEDED=true`——Mac 节点是日常办公机,离线期间排队的旧构建在节点回来后若已被源码分支推进,直接跳过而不发布过期版本。Mac 节点需要 `ossutil`(实测 1.7.19 原生 arm64 可用,装在 `~/.local/bin`,已在 Job 的 PATH 内),可用 `OSSUTIL_BIN` 指定命令名或绝对路径。首次发布建议显式指定 `AGC_RELEASE_VERSION`,避免按渠道高水位递增时出现版本链回退。
|
||||
|
||||
@@ -62,13 +62,47 @@ pipeline {
|
||||
SOURCE_BRANCH="${SOURCE_BRANCH:-master}"
|
||||
COMMIT_HASH="${COMMIT_HASH:-}"
|
||||
SKIP_IF_SUPERSEDED="${SKIP_IF_SUPERSEDED:-false}"
|
||||
export GIT_SSH_COMMAND="ssh -i \\"$GIT_SSH_KEY\\" -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes"
|
||||
# Mac 节点通过 WebSocket 接入 Jenkins;远端 Git fetch 长时间没有 stdout 时,
|
||||
# Jenkins/Jetty 的 idle timeout 会先断开 agent channel,留下 ClosedChannelException。
|
||||
# SSH 自身开启保活,并由前台心跳包装 fetch,避免把网络等待误判成 agent 失联。
|
||||
export GIT_SSH_COMMAND="ssh -i \\"$GIT_SSH_KEY\\" -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o ConnectTimeout=30 -o ServerAliveInterval=20 -o ServerAliveCountMax=6"
|
||||
if [ ! -d .git ]; then
|
||||
git init
|
||||
git remote add origin "$GIT_REMOTE_URL"
|
||||
fi
|
||||
test "$(git remote get-url origin)" = "$GIT_REMOTE_URL"
|
||||
git fetch --no-tags origin "+refs/heads/$SOURCE_BRANCH:refs/remotes/origin/$SOURCE_BRANCH"
|
||||
fetch_log="$(mktemp "${TMPDIR:-/tmp}/agc-git-fetch.XXXXXX")"
|
||||
fetch_pid=""
|
||||
cleanup_fetch() {
|
||||
if [ -n "${fetch_pid}" ] && kill -0 "${fetch_pid}" 2>/dev/null; then
|
||||
kill "${fetch_pid}" 2>/dev/null || true
|
||||
wait "${fetch_pid}" 2>/dev/null || true
|
||||
fi
|
||||
rm -f "${fetch_log}"
|
||||
}
|
||||
trap cleanup_fetch EXIT INT TERM
|
||||
git fetch --no-tags origin "+refs/heads/$SOURCE_BRANCH:refs/remotes/origin/$SOURCE_BRANCH" >"${fetch_log}" 2>&1 &
|
||||
fetch_pid=$!
|
||||
fetch_started_at="$(date +%s)"
|
||||
fetch_timeout_seconds="${AGC_GIT_FETCH_TIMEOUT_SECONDS:-900}"
|
||||
while kill -0 "${fetch_pid}" 2>/dev/null; do
|
||||
fetch_elapsed=$(( $(date +%s) - fetch_started_at ))
|
||||
if [ "${fetch_elapsed}" -ge "${fetch_timeout_seconds}" ]; then
|
||||
echo "[agc-macos] git fetch 超时(${fetch_timeout_seconds}s),终止并输出诊断:${SOURCE_BRANCH}"
|
||||
kill "${fetch_pid}" 2>/dev/null || true
|
||||
wait "${fetch_pid}" 2>/dev/null || true
|
||||
cat "${fetch_log}"
|
||||
exit 1
|
||||
fi
|
||||
echo "[agc-macos] git fetch 仍在进行(${fetch_elapsed}s)"
|
||||
sleep 20
|
||||
done
|
||||
if ! wait "${fetch_pid}"; then
|
||||
cat "${fetch_log}"
|
||||
exit 1
|
||||
fi
|
||||
cat "${fetch_log}"
|
||||
fetch_pid=""
|
||||
ref="refs/remotes/origin/$SOURCE_BRANCH"
|
||||
if [ -n "$COMMIT_HASH" ]; then
|
||||
case "$COMMIT_HASH" in *[!0-9a-fA-F]* ) echo 'COMMIT_HASH 必须为十六进制'; exit 1;; esac
|
||||
|
||||
@@ -7725,6 +7725,28 @@ for (const [file, content] of [
|
||||
}
|
||||
}
|
||||
|
||||
for (const [snippet, reason] of [
|
||||
[
|
||||
'-o ServerAliveInterval=20 -o ServerAliveCountMax=6',
|
||||
'Mac AGC Checkout 的 Git SSH 必须启用保活,避免 WebSocket idle timeout 断开 agent。',
|
||||
],
|
||||
[
|
||||
'echo "[agc-macos] git fetch 仍在进行',
|
||||
'Mac AGC Checkout 的 git fetch 必须持续输出心跳,避免长时间无 stdout 被误判为 agent 失联。',
|
||||
],
|
||||
[
|
||||
'AGC_GIT_FETCH_TIMEOUT_SECONDS',
|
||||
'Mac AGC Checkout 的 git fetch 必须有可配置的超时和失败诊断。',
|
||||
],
|
||||
]) {
|
||||
if (!agcMacosPipelineContent.includes(snippet)) {
|
||||
failed = true;
|
||||
console.error(
|
||||
`[check:production-ops] jenkins/Jenkinsfile.ai-game-creator-shell-macos-build ${reason}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新签名私钥只允许经 Jenkins 凭据绑定注入构建进程:既不得把凭据本身写进 Jenkinsfile,
|
||||
// 也不得回显到日志或进归档(对应「AGC 更新发布管线渠道化」第 5 条的可静态复核部分)。
|
||||
for (const [file, content] of [
|
||||
|
||||
Reference in New Issue
Block a user