AGC 定时发布增加变更路径过滤
Project CI / AI game creator shell Rust shard 1/4 (push) Successful in 7m29s
Project CI / AI game creator shell Rust shard 3/4 (push) Successful in 7m37s
Project CI / AI game creator shell Rust shard 2/4 (push) Failing after 7m46s
Project CI / AI game creator shell Rust shard 4/4 (push) Failing after 4m53s
Project CI / AI game creator shell Rust smoke (push) Successful in 2m17s
Project CI / AI game creator shell Rust crates (push) Successful in 2m43s
Project CI / Native shell tests (push) Failing after 1m10s
Project CI / Frontend tests (push) Failing after 3m37s
Project CI / AI game creator shell web tests (push) Failing after 3m25s
Project CI / Repository checks (push) Successful in 4m19s
Project CI / Backend tests (push) Successful in 8m48s

- 调度管线新增 AGC 发布范围判定:比较上一轮 revision 与本次 revision 的变更路径
- 只有客户端、共享包、server-rs/crates、AGC 插件、桌面壳图标或根依赖清单变化时才触发 AGC Windows Build
- 纯文档与流水线提交只触发 Full Build,不再推高客户端版本号
- 判定失败或浅取窗口取不到该 revision 时按需要发布处理,FORCE_TRIGGER 仍强制两条都触发
- 同步开发运维文档、技术方案与调度 job 配置描述
This commit is contained in:
kdletters
2026-09-17 19:00:45 +08:00
parent 55af6014ba
commit 6017d46088
4 changed files with 71 additions and 4 deletions
@@ -91,6 +91,7 @@
- 发布入口:`npm run ai-game-creator-shell:release:upload`(构建 + 按渠道上传);仅构建不发布的 smoke 使用 `--no-bundle` 分支,不读远端版本、不改版本、不生成清单。
- 渠道由构建参数显式指定,并按目标平台校验:Windows 目标只允许 `dev-win`macOS 目标只允许 `dev-mac`;未显式指定时按目标平台取默认渠道。
- 定时调度只在本轮到达的提交包含 AGC 相关路径(客户端、共享包、`server-rs/crates`、AGC 插件、桌面壳图标、根依赖清单)时才触发渠道发布;纯文档或流水线自身的提交只跑 Full Build,不推高客户端版本号。判定失败或勾选强制触发时按"需要发布"处理。
- 上传:安装包与 `.sig` 上传到 `agc/<channel>/<version>/`,清单以 `--force` 覆盖上传到 `agc/<channel>/latest.json`,保证 latest 指针与清单内 URL 指向已存在的对象。
- Jenkins 流水线需要新增渠道参数与签名凭据;签名私钥与密码只以受保护凭据注入当前进程,不写入 workspace、日志或归档产物。
- 归档证据:安装包、`.sig`、渠道清单与源码 commit。
@@ -137,7 +137,7 @@ BgFilter 对已经落入私有 OSS 的生成原图、动作抽取帧和手动去
`Genarrative-Scheduled-Revision-Trigger` 是唯一的定时入口,每小时检查一次(`H * * * *`,分钟由 Jenkins 按 Job 名散列,不等同于整点)。它只用 `git ls-remote` 解析 `SOURCE_BRANCH`(默认 `master`)的远端 HEAD,不 checkout 工作区;解析出的完整 commit 与上一次触发过的 revision 相同则标记 `NOT_BUILT` 并结束,不触发任何下游。
revision 变化时,调度管线把同一个完整 commit 通过 `COMMIT_HASH` 同时传给 `Genarrative-Full-Build-And-Deploy``Genarrative-Agc-Windows-Build`,两条管线都按这个 commit 检出(Full Job 继续把 `env.SOURCE_COMMIT` 透传给 Web / API / Stdb 的 Build、Publish、Deploy),因此两个产物必然来自同一个版本,不会各自解析分支 HEAD 造成漂移。两条下游管线自身不带任何定时触发器,也不在管线内部做版本比较。Full Job 默认以 `DEPLOY_TARGET=development``STDB_API_ROLLOUT_MODE=normal` 对仅供开发使用的 dev 服务器执行 Stdb → API → Web 完整发布,不进入人工 rollout gate;三个下游 Build 都由 Full Job 显式传 `PUBLISH_AFTER_BUILD=false`,统一 Build 完成后仍由 Full Job 按固定顺序发布。人工维护窗口才选择 `pause-after-stdb`,且必须配置 `STDB_API_ROLLOUT_APPROVERS`
revision 变化时,调度管线把同一个完整 commit 通过 `COMMIT_HASH` 同时传给 `Genarrative-Full-Build-And-Deploy``Genarrative-Agc-Windows-Build`,两条管线都按这个 commit 检出(Full Job 继续把 `env.SOURCE_COMMIT` 透传给 Web / API / Stdb 的 Build、Publish、Deploy),因此两个产物必然来自同一个版本,不会各自解析分支 HEAD 造成漂移。两条下游管线自身不带任何定时触发器,也不在管线内部做版本比较。Windows 客户端发布额外按路径过滤:调度管线比较「上一轮已触发的 revision」与本次 revision 之间的变更路径,只有出现 `apps/ai-game-creator-shell/``packages/``server-rs/crates/``plugins/agc-cocos-editor/``apps/desktop-shell/src-tauri/icons/``package.json``package-lock.json` 时才触发 `Genarrative-Agc-Windows-Build`,纯文档或流水线自身的提交只触发 Full Build、不推高客户端版本号;判定取消或失败一律按「需要发布」处理,勾选 `FORCE_TRIGGER` 可强制两条都触发。Full Job 默认以 `DEPLOY_TARGET=development``STDB_API_ROLLOUT_MODE=normal` 对仅供开发使用的 dev 服务器执行 Stdb → API → Web 完整发布,不进入人工 rollout gate;三个下游 Build 都由 Full Job 显式传 `PUBLISH_AFTER_BUILD=false`,统一 Build 完成后仍由 Full Job 按固定顺序发布。人工维护窗口才选择 `pause-after-stdb`,且必须配置 `STDB_API_ROLLOUT_APPROVERS`
调度状态是调度 Job 工作区里的 `.jenkins-last-triggered-revision`,构建描述同时回显本次 revision 与结果。工作区被清理(例如 `Wipe Out Workspace`)或状态文件缺失时,下一次运行按“版本变化”处理并触发一次,之后恢复稳定;需要重建同一版本时勾选 `FORCE_TRIGGER`。Job 按仓库内 `jenkins/scheduled-revision-trigger-job-config.xml` 创建:`scriptPath=jenkins/Jenkinsfile.scheduled-revision-trigger`、Git 入口 `ssh://git@127.0.0.1:2222/GenarrativeAI/Genarrative.git`、凭据 `genarrative-local-gitea-ssh``<triggers/>` 留空(定时器写在 Jenkinsfile 里)。推送后必须让三个 live Job 各自加载一次新 Jenkinsfile,并只读核对 `config.xml`Full 与 AGC 不再有 cron,定时只来自新调度 Job;只改 Jenkinsfile 而不确认 live 配置时,旧 cron 仍会继续触发。
+68 -2
View File
@@ -21,6 +21,7 @@ pipeline {
FULL_BUILD_JOB_NAME = 'Genarrative-Full-Build-And-Deploy'
AGC_BUILD_JOB_NAME = 'Genarrative-Agc-Windows-Build'
REVISION_STATE_FILE = '.jenkins-last-triggered-revision'
AGC_SCOPE_CACHE_DIR = '.agc-release-scope-cache'
}
parameters {
@@ -57,6 +58,63 @@ pipeline {
}
}
// 只有在「本轮到达的提交」里出现 AGC 相关路径时,Windows 客户端才发布新版本;
// 纯文档或流水线自身的提交仍然触发 Full Build,但不再推高客户端版本号。
stage('Resolve AGC Release Scope') {
when {
expression { return env.REVISION_CHANGED == 'true' }
}
steps {
withCredentials([sshUserPrivateKey(credentialsId: env.GIT_REMOTE_CREDENTIAL_ID, keyFileVariable: 'GENARRATIVE_GIT_SSH_KEY')]) {
script {
// 判定失败一律按「需要发布」处理,避免这段逻辑影响其它下游管线。
def scope = 'changed'
try {
scope = sh(script: '''#!/usr/bin/env bash
set -uo pipefail
export GIT_SSH_COMMAND="ssh -i ${GENARRATIVE_GIT_SSH_KEY:?缺少 Git SSH 凭据} -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new"
previous="$(cat "${REVISION_STATE_FILE}" 2>/dev/null || true)"
if [[ -z "${previous}" ]]; then
echo changed
exit 0
fi
mkdir -p "${AGC_SCOPE_CACHE_DIR}"
if [[ ! -d "${AGC_SCOPE_CACHE_DIR}/.git" ]]; then
git -C "${AGC_SCOPE_CACHE_DIR}" init --quiet
git -C "${AGC_SCOPE_CACHE_DIR}" remote add origin "${GIT_REMOTE_URL}" 2>/dev/null || true
fi
refspec="+refs/heads/${SOURCE_BRANCH}:refs/remotes/origin/${SOURCE_BRANCH}"
if ! git -C "${AGC_SCOPE_CACHE_DIR}" fetch --quiet --depth=200 --no-tags --filter=blob:none origin "${refspec}"; then
git -C "${AGC_SCOPE_CACHE_DIR}" fetch --quiet --depth=200 --no-tags origin "${refspec}" || { echo changed; exit 0; }
fi
if ! git -C "${AGC_SCOPE_CACHE_DIR}" cat-file -e "${previous}^{commit}" 2>/dev/null; then
echo "浅取窗口内没有 ${previous},按需要发布处理" >&2
echo changed
exit 0
fi
changed_paths="$(git -C "${AGC_SCOPE_CACHE_DIR}" diff --name-only "${previous}" "${REMOTE_REVISION}" 2>/dev/null || true)"
while IFS= read -r changed_path; do
[[ -z "${changed_path}" ]] && continue
case "${changed_path}" in
apps/ai-game-creator-shell/*|packages/*|server-rs/crates/*|plugins/agc-cocos-editor/*|apps/desktop-shell/src-tauri/icons/*|package.json|package-lock.json)
echo changed
exit 0
;;
esac
done <<< "${changed_paths}"
echo unchanged
''', returnStdout: true).trim()
} catch (error) {
echo "AGC 发布范围判定失败,按需要发布处理:${error}"
scope = 'changed'
}
env.AGC_RELEASE_SCOPE = (scope == 'unchanged') ? 'unchanged' : 'changed'
echo "AGC 发布范围:${env.AGC_RELEASE_SCOPE}(上一轮已触发 revision=${env.LAST_TRIGGERED_REVISION ?: '无'}"
}
}
}
}
stage('Trigger Downstream Pipelines') {
when {
expression { return env.REVISION_CHANGED == 'true' }
@@ -71,9 +129,17 @@ pipeline {
string(name: 'DATABASE_BACKUP_MODE', value: 'skip'),
]
build job: env.FULL_BUILD_JOB_NAME, wait: false, propagate: false, parameters: pinnedParameters
build job: env.AGC_BUILD_JOB_NAME, wait: false, propagate: false, parameters: pinnedParameters
def agcTriggered = false
if (params.FORCE_TRIGGER || env.AGC_RELEASE_SCOPE != 'unchanged') {
build job: env.AGC_BUILD_JOB_NAME, wait: false, propagate: false, parameters: pinnedParameters
agcTriggered = true
} else {
echo "本轮提交不含 AGC 相关路径,跳过 ${env.AGC_BUILD_JOB_NAME};需要强制发布时勾选 FORCE_TRIGGER"
}
writeFile file: env.REVISION_STATE_FILE, text: pinnedRevision
currentBuild.description = "已触发 ${env.FULL_BUILD_JOB_NAME} 与 ${env.AGC_BUILD_JOB_NAME}${env.SOURCE_BRANCH}@${pinnedRevision.take(12)}"
currentBuild.description = agcTriggered
? "已触发 ${env.FULL_BUILD_JOB_NAME} 与 ${env.AGC_BUILD_JOB_NAME}${env.SOURCE_BRANCH}@${pinnedRevision.take(12)}"
: "已触发 ${env.FULL_BUILD_JOB_NAME}AGC 渠道未发布:本次提交不含 AGC 相关路径):${env.SOURCE_BRANCH}@${pinnedRevision.take(12)}"
echo currentBuild.description
}
}
@@ -1,7 +1,7 @@
<?xml version='1.1' encoding='UTF-8'?>
<flow-definition plugin="workflow-job">
<actions/>
<description>按小时检查源码分支版本,只有版本变化时用同一个 commit 触发 Full BuildAGC Windows Build。</description>
<description>按小时检查源码分支版本,只有版本变化时用同一个 commit 触发 Full BuildAGC Windows Build 额外按变更路径过滤,只有本轮提交触及客户端相关路径时才触发</description>
<keepDependencies>false</keepDependencies>
<properties>
<hudson.model.ParametersDefinitionProperty>