新增 AGC release 每日调度
Project CI / AI game creator shell Rust lane 2/2 (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
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled
Project CI / AI game creator shell Rust smoke (push) Has been cancelled

新增 Genarrative-Scheduled-Release-Trigger,每天 04:00 发布 Windows/macOS release 渠道

release 调度复用统一发号源,并为 macOS 启用过期构建让位

现有小时 dev 调度保持同时触发 Windows 与 macOS

更新生产运维门禁、发号权限、技术方案与共享记忆
This commit is contained in:
2026-09-22 16:31:56 +08:00
parent 3775e05970
commit fdc14404b2
9 changed files with 347 additions and 17 deletions
@@ -1,5 +1,13 @@
# 决策记录
## 2026-09-22 AGC release 增加每日调度,dev 调度保持双平台
- 背景:原有 `Genarrative-Scheduled-Revision-Trigger` 每小时跟随 revision 发布 dev 客户端,但没有对应的 release 渠道自动入口;Mac 节点此前已纳入小时 dev 调度,需要避免新增 release 调度时再退回 Windows-only。
- 决策:新增 `Genarrative-Scheduled-Release-Trigger`,每天 04:00 检查 `SOURCE_BRANCH`,使用独立的 `.jenkins-last-release-revision` 与客户端相关路径白名单;上一轮 release 调度后有客户端变更时,先经 `Genarrative-Agc-Global-Version-Issue` 发统一总号,再以同一固定 `COMMIT_HASH` 触发 `Genarrative-Agc-Windows-Build``Genarrative-Agc-MacOS-Build``AGC_UPDATE_CHANNEL=release` 分区,Mac 继续带 `SKIP_IF_SUPERSEDED=true`。小时 dev 调度保持同时触发 Windows 与 macOS dev。
- 原因:release 与 dev 是不同渠道和发布节奏,不能靠同一个小时 Job 隐式切换;独立 Job 能分别去重、记录状态和审计发号。release 调度不触发 Full Build,避免把桌面客户端发布与线上全栈部署绑定。
- 影响范围:`jenkins/Jenkinsfile.scheduled-release-trigger``jenkins/scheduled-release-trigger-job-config.xml``jenkins/Jenkinsfile.agc-global-version-issue``scripts/check-production-ops-guardrails.mjs`、开发运维文档、共享开发工作流与 AGC 总版本号技术方案。
- 验证方式:`npm run check:production-ops` 校验 release Job 的 cron、发号、双平台 release 参数、Mac 让位和 Copy Artifact 授权;`npm run check:encoding``git diff --check` 校验文件与补丁。
## 2026-09-21 合并 origin/masterSupervisor 永久退役,策划 V1V2 退役落到当前两条产品路径
- 背景:`refactor/split-direct-project`DirectProject 独立聊天容器)与 `origin/master`#355 退役策划 Agent V1/V2)在 2026-09-18 之后各走一条线:本分支删掉 Supervisor 前端链路、把立项策划收敛到 `view/project-development/planning/`master 删掉整套策划 V1/V2(前端会话 / 审批卡 / 适配器 / 类型与 Rust `planning_*_v2` 命令、`planning_gdd_model.rs``planning_policy_v2.rs``planning_session_v2.rs`)只保留 Design Agent。两边都在删 Supervisor,冲突集中在 `App.tsx`、聊天视图(`PlanningChatView``DirectProjectTurn``ToolCallGroup`)、Direct composer / 引用输入区、`styles.css`、Rust direct user item 与 appSurface 用例。
@@ -92,7 +92,7 @@ SpacetimeDB 任务统一先读取 `.codex/skills/genarrative-spacetimedb/SKILL.m
## Jenkins 定时版本调度
定时与版本比较只保留在 `Genarrative-Scheduled-Revision-Trigger` 一处:每小时用 `git ls-remote` 解析 `SOURCE_BRANCH` 远端 HEAD,与上一次触发过的 revision 比较,变化时才把同一个 `COMMIT_HASH` 传给 `Genarrative-Full-Build-And-Deploy`,并先经 `Genarrative-Agc-Global-Version-Issue` 发号、再把同一个总版本号透传给 `Genarrative-Agc-Windows-Build``Genarrative-Agc-MacOS-Build`,保证两个客户端的平台分区发布同一个版本。这三个下游 Job 都不得自带 `triggers` / `cron`,也不得在管线内再做一套版本去重;`npm run check:production-ops` 会拦住这两类回退。macOS 节点是日常办公机,调度触发它时置 `SKIP_IF_SUPERSEDED=true`:节点离线期间排队的旧构建在恢复后会自行让位,不发布过期版本。调度状态与生效步骤见 `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`
定时与版本比较收口到两条调度器:`Genarrative-Scheduled-Revision-Trigger` 每小时处理 dev 渠道,变化时触发 Full Build、AGC Windows/macOS dev,并让两个客户端平台共用同一个总版本号;`Genarrative-Scheduled-Release-Trigger` 每天 04:00 处理 AGC release 渠道,只在上一轮 release 调度后出现客户端相关路径变化时,经 `Genarrative-Agc-Global-Version-Issue` 发同一个总号并触发 AGC Windows/macOS release。各下游 Job 都不得自带 `triggers` / `cron`,也不得在管线内再做一套版本去重;`npm run check:production-ops` 会拦住回退。macOS 节点是日常办公机,两条调度触发它时`SKIP_IF_SUPERSEDED=true`:节点离线期间排队的旧构建在恢复后会自行让位,不发布过期版本。调度状态与生效步骤见 `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md`
## Gitea CI 依赖闭合
@@ -1,6 +1,6 @@
# AGC 总版本号与发号
更新时间:`2026-09-20`
更新时间:`2026-09-22`
## 背景
@@ -18,7 +18,7 @@ AGC 客户端此前按「渠道各自比高水位自增」发号:`dev-win` 与
- `next = 总号 + 1`;总号不存在时先一次性播种,见下节。
- 顺序固定为「先写总号 → 再构建 → 再发渠道清单」;任何一步失败都不回滚,只烧号。
- 统一构建:发一次号,通过 `AGC_RELEASE_VERSION` 同时传给 `dev-win``dev-mac`(以及后续渠道),各渠道共用同一个号。
- 统一构建:发一次号,通过 `AGC_RELEASE_VERSION` 同时传给同一渠道组的两个平台;dev 调度写 `dev-win` / `dev-mac`,每日 release 调度写 `release-win` / `release-mac`,组内共用同一个号。
- 单渠道热修:发一次号,只传给该渠道;其他渠道清单保持原值。
- 显式传入 `AGC_RELEASE_VERSION` 的构建不再自行发号,只做高水位断言。
@@ -39,8 +39,8 @@ AGC 客户端此前按「渠道各自比高水位自增」发号:`dev-win` 与
- 发号模块:`apps/ai-game-creator-shell/scripts/agc-global-version.mjs`(读总号 / 播种 / 发号 / 写后回读 / 渠道高水位断言 / dry-run 预览)。
- 发号入口:`apps/ai-game-creator-shell/scripts/issue-global-version.mjs`CI 与本地共用,输出固定为 `AGC_GLOBAL_VERSION=<version>`)。
- 构建侧:`build-release.mjs``prepareReleaseVersion()` 优先采用传入总号,未传入时现场发号;`resolveRemoteHighWaterVersion()` 降级为断言来源,只用于「请求号低于本渠道清单版本即失败关闭」。
- CI`jenkins/Jenkinsfile.agc-global-version-issue` + `jenkins/agc-global-version-issue-job-config.xml`;调度管线 `jenkins/Jenkinsfile.scheduled-revision-trigger` 与手动管线先调发号 Job,再把号透传给 AGC Build。
- 发号 Job 的 Copy Artifact 采用生产权限模式,`options` 里的 `copyArtifactPermission(...)` 必须显式列出**全部**消费者:定时调度 `Genarrative-Scheduled-Revision-Trigger` 与手动发布 `Genarrative-Manual-Build-And-Deploy`。用户触发的构建按「认证用户」判权(SYSTEM 定时构建短路放行),漏列时手动发布会报 `Unable to find project for artifact copy: Genarrative-Agc-Global-Version-Issue`,且失败点在发号之后。改完该 `options` 后必须先单独跑一次发号 Job,让 Declarative Pipeline 把 Job property 写回 Jenkins。
- CI`jenkins/Jenkinsfile.agc-global-version-issue` + `jenkins/agc-global-version-issue-job-config.xml`dev 调度管线 `jenkins/Jenkinsfile.scheduled-revision-trigger`、每日 release 调度管线 `jenkins/Jenkinsfile.scheduled-release-trigger` 与手动管线先调发号 Job,再把号透传给 AGC Build。
- 发号 Job 的 Copy Artifact 采用生产权限模式,`options` 里的 `copyArtifactPermission(...)` 必须显式列出**全部**消费者:小时 dev 调度 `Genarrative-Scheduled-Revision-Trigger`、每日 release 调度 `Genarrative-Scheduled-Release-Trigger` 与手动发布 `Genarrative-Manual-Build-And-Deploy`。用户触发的构建按「认证用户」判权(SYSTEM 定时构建短路放行),漏列时手动发布会报 `Unable to find project for artifact copy: Genarrative-Agc-Global-Version-Issue`,且失败点在发号之后。改完该 `options` 后必须先单独跑一次发号 Job,让 Declarative Pipeline 把 Job property 写回 Jenkins。
## 不改的东西
File diff suppressed because one or more lines are too long
+7 -6
View File
@@ -16,14 +16,15 @@ pipeline {
skipDefaultCheckout(true)
buildDiscarder(logRotator(numToKeepStr: '100', artifactNumToKeepStr: '20'))
// Copy Artifact 保持 Production 权限模式:生产者必须显式授权消费者。
// 调度管线与手动发布管线都要 copy 本 Job 的 agc-global-version.txt 才能把总号
// 所有调度与手动发布管线都要 copy 本 Job 的 agc-global-version.txt 才能把总号
// 透传给渠道构建;缺授权时 copyArtifacts 会报「Unable to find project for
// artifact copy」(用户触发的构建按「认证用户」判权,SYSTEM 定时构建会短路放行,
// 所以定时调度能过、手动发布必挂;2026-09-20 #103 与 2026-09-21 手动发布
// #6/#7 均命中)。这里必须同时列出两条消费者:Genarrative-Scheduled-Revision-Trigger
// 与 Genarrative-Manual-Build-And-Deploy。改完本文件后必须先单独跑一次本 Job,
// 让 Declarative Pipeline 把 Job property 写回 Jenkins,再重跑调度或手动发布。
copyArtifactPermission('Genarrative-Scheduled-Revision-Trigger,Genarrative-Manual-Build-And-Deploy')
// #6/#7 均命中)。这里必须列出全部消费者:Genarrative-Scheduled-Revision-Trigger
// Genarrative-Scheduled-Release-Trigger 与 Genarrative-Manual-Build-And-Deploy。
// 改完本文件后必须先单独跑一次本 Job,让 Declarative Pipeline 把 Job property
// 写回 Jenkins,再重跑调度或手动发布。
copyArtifactPermission('Genarrative-Scheduled-Revision-Trigger,Genarrative-Scheduled-Release-Trigger,Genarrative-Manual-Build-And-Deploy')
}
environment {
@@ -34,7 +35,7 @@ pipeline {
}
parameters {
choice(name: 'AGC_CHANNEL', choices: ['unified', 'dev-win', 'dev-mac'], description: '发号用途:unified 为统一构建(一个号同时给 dev-win 与 dev-mac,其余为单渠道热修')
choice(name: 'AGC_CHANNEL', choices: ['unified', 'dev-win', 'dev-mac', 'release-unified', 'release-win', 'release-mac'], description: '发号用途标签unified / release-unified 为一个号同时给两个平台,其余为单渠道热修')
string(name: 'SOURCE_BRANCH', defaultValue: 'master', description: '取发号脚本的源码分支;默认 master')
string(name: 'COMMIT_HASH', defaultValue: '', description: '可选:固定提交;留空则解析 SOURCE_BRANCH 当前 revision')
booleanParam(name: 'SEED_ONLY', defaultValue: false, description: '仅做一次性播种:写入渠道与仓库版本的最大值作为基线,不递增、不烧号')
@@ -0,0 +1,181 @@
// AGC release 每日调度管线:每天 04:00 检查源码分支,
// 只有上一轮 release 调度之后出现客户端相关路径变更时,才用同一个固定 commit
// 触发 AGC Windows/macOS 两个 release 分区构建。
pipeline {
agent {
label 'linux && genarrative-build'
}
options {
disableConcurrentBuilds()
skipDefaultCheckout(true)
buildDiscarder(logRotator(numToKeepStr: '100', artifactNumToKeepStr: '10'))
}
triggers {
cron('0 4 * * *')
}
environment {
GIT_REMOTE_URL = 'ssh://git@127.0.0.1:2222/GenarrativeAI/Genarrative.git'
GIT_REMOTE_CREDENTIAL_ID = 'genarrative-local-gitea-ssh'
AGC_WINDOWS_BUILD_JOB_NAME = 'Genarrative-Agc-Windows-Build'
AGC_MACOS_BUILD_JOB_NAME = 'Genarrative-Agc-MacOS-Build'
AGC_GLOBAL_VERSION_JOB_NAME = 'Genarrative-Agc-Global-Version-Issue'
AGC_GLOBAL_VERSION_ARTIFACT = 'agc-global-version.txt'
REVISION_STATE_FILE = '.jenkins-last-release-revision'
AGC_SCOPE_CACHE_DIR = '.agc-release-scope-cache'
}
parameters {
string(name: 'SOURCE_BRANCH', defaultValue: 'master', description: '被检查的源码分支;默认 master')
booleanParam(name: 'FORCE_TRIGGER', defaultValue: false, description: '勾选后忽略版本与路径比较,强制发布 AGC release 渠道')
}
stages {
stage('Resolve Revision') {
steps {
withCredentials([sshUserPrivateKey(credentialsId: env.GIT_REMOTE_CREDENTIAL_ID, keyFileVariable: 'GENARRATIVE_GIT_SSH_KEY')]) {
sh '''#!/usr/bin/env bash
set -euo pipefail
export GIT_SSH_COMMAND="ssh -i ${GENARRATIVE_GIT_SSH_KEY:?缺少 Git SSH 凭据} -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new"
revision="$(git ls-remote "${GIT_REMOTE_URL:?缺少 GIT_REMOTE_URL}" "refs/heads/${SOURCE_BRANCH}" | awk 'NR == 1 { print $1 }')"
if [[ ! "${revision}" =~ ^[0-9a-f]{40}$ ]]; then
echo "无法解析远端分支版本: branch=${SOURCE_BRANCH} revision=${revision:-<empty>}" >&2
exit 1
fi
printf '%s' "${revision}" > .jenkins-remote-revision
echo "release 远端版本: branch=${SOURCE_BRANCH} revision=${revision}"
'''
}
script {
env.REMOTE_REVISION = readFile('.jenkins-remote-revision').trim()
env.LAST_TRIGGERED_REVISION = fileExists(env.REVISION_STATE_FILE) ? readFile(env.REVISION_STATE_FILE).trim() : ''
env.REVISION_CHANGED = (params.FORCE_TRIGGER || env.LAST_TRIGGERED_REVISION != env.REMOTE_REVISION) ? 'true' : 'false'
if (env.REVISION_CHANGED != 'true') {
currentBuild.result = 'NOT_BUILT'
currentBuild.description = "release 版本未变化,跳过:${env.SOURCE_BRANCH} 仍是 ${env.REMOTE_REVISION.take(12)}"
echo currentBuild.description
}
}
}
}
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 output = 'agc=changed'
try {
output = 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
printf 'agc=changed\n'
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}" || { printf 'agc=changed\n'; exit 0; }
fi
if ! git -C "${AGC_SCOPE_CACHE_DIR}" cat-file -e "${previous}^{commit}" 2>/dev/null; then
echo "浅取窗口内没有 ${previous},按需要发布处理" >&2
printf 'agc=changed\n'
exit 0
fi
changed_paths="$(git -C "${AGC_SCOPE_CACHE_DIR}" diff --name-only "${previous}" "${REMOTE_REVISION}" 2>/dev/null || true)"
agc_scope=unchanged
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/*|plugins/agc-unity-editor/*|plugins/agc-godot-editor/*|apps/desktop-shell/src-tauri/icons/*|package.json|package-lock.json)
agc_scope=changed
;;
esac
done <<< "${changed_paths}"
printf 'agc=%s\n' "${agc_scope}"
''', returnStdout: true).trim()
} catch (error) {
echo "release 发布范围判定失败,按需要发布处理:${error}"
output = 'agc=changed'
}
def values = output.split('\n').collect { it.trim() }.findAll { it }
def entry = values.find { it.startsWith('agc=') }
def value = entry == null ? '' : entry.split('=')[1]
env.AGC_RELEASE_SCOPE = (value == 'unchanged') ? 'unchanged' : 'changed'
echo "release 发布范围:AGC=${env.AGC_RELEASE_SCOPE}(上一轮已触发 revision=${env.LAST_TRIGGERED_REVISION ?: '无'}"
}
}
}
}
stage('Trigger AGC Release Builds') {
when {
expression { return env.REVISION_CHANGED == 'true' }
}
steps {
script {
def pinnedRevision = env.REMOTE_REVISION
def agcTriggered = false
if (params.FORCE_TRIGGER || env.AGC_RELEASE_SCOPE != 'unchanged') {
// Windows 与 macOS 共用一个总号;发号 Job 是唯一发号源。
def versionRun = build job: env.AGC_GLOBAL_VERSION_JOB_NAME,
wait: true,
propagate: true,
parameters: [
string(name: 'SOURCE_BRANCH', value: env.SOURCE_BRANCH),
string(name: 'COMMIT_HASH', value: pinnedRevision),
string(name: 'AGC_CHANNEL', value: 'release-unified'),
]
copyArtifacts(
projectName: env.AGC_GLOBAL_VERSION_JOB_NAME,
selector: specific(versionRun.number.toString()),
filter: env.AGC_GLOBAL_VERSION_ARTIFACT,
target: '.',
flatten: true,
)
env.AGC_GLOBAL_VERSION = readFile(env.AGC_GLOBAL_VERSION_ARTIFACT).trim()
echo "本轮 AGC release 客户端总版本号: ${env.AGC_GLOBAL_VERSION}"
def agcParameters = [
string(name: 'SOURCE_BRANCH', value: env.SOURCE_BRANCH),
string(name: 'COMMIT_HASH', value: pinnedRevision),
string(name: 'AGC_UPDATE_CHANNEL', value: 'release'),
string(name: 'AGC_RELEASE_VERSION', value: env.AGC_GLOBAL_VERSION),
]
build job: env.AGC_WINDOWS_BUILD_JOB_NAME, wait: false, propagate: false, parameters: agcParameters
// macOS 节点是办公机:离线期间排队的旧构建在节点回来后必须自行让位。
def agcMacosParameters = agcParameters + [
booleanParam(name: 'SKIP_IF_SUPERSEDED', value: true),
]
build job: env.AGC_MACOS_BUILD_JOB_NAME, wait: false, propagate: false, parameters: agcMacosParameters
agcTriggered = true
} else {
echo "上一轮 release 调度后没有 AGC 客户端相关变更,跳过 ${env.AGC_WINDOWS_BUILD_JOB_NAME} 与 ${env.AGC_MACOS_BUILD_JOB_NAME};需要强制发布时勾选 FORCE_TRIGGER"
}
writeFile file: env.REVISION_STATE_FILE, text: pinnedRevision
def target = "${env.SOURCE_BRANCH}@${pinnedRevision.take(12)}"
currentBuild.description = agcTriggered
? "已触发 AGC release${env.AGC_WINDOWS_BUILD_JOB_NAME} 与 ${env.AGC_MACOS_BUILD_JOB_NAME} ${target}"
: "本轮没有 AGC 客户端相关变更,未触发 release 发布:${target}"
echo currentBuild.description
}
}
}
}
post {
always {
echo "release 调度结束: branch=${env.SOURCE_BRANCH} revision=${env.REMOTE_REVISION ?: ''} changed=${env.REVISION_CHANGED ?: ''} agcScope=${env.AGC_RELEASE_SCOPE ?: ''}"
}
}
}
@@ -1,19 +1,22 @@
<?xml version='1.1' encoding='UTF-8'?>
<flow-definition plugin="workflow-job">
<actions/>
<description>AGC 总版本号唯一发号入口:先写 OSS agc/global-version.json,再由各渠道构建接收号发布。统一构建发一次号给所有渠道dry-run 只预览不烧号。</description>
<description>AGC 总版本号唯一发号入口:先写 OSS agc/global-version.json,再由各渠道构建接收号发布。dev / release 的统一构建各自发一次号给两个平台dry-run 只预览不烧号。</description>
<keepDependencies>false</keepDependencies>
<properties>
<hudson.model.ParametersDefinitionProperty>
<parameterDefinitions>
<hudson.model.ChoiceParameterDefinition>
<name>AGC_CHANNEL</name>
<description>发号用途:unified 为统一构建(一个号同时给 dev-win 与 dev-mac,其余为单渠道热修</description>
<description>发号用途标签unified / release-unified 为一个号同时给两个平台,其余为单渠道热修</description>
<choices class="java.util.Arrays$ArrayList">
<a class="string-array">
<string>unified</string>
<string>dev-win</string>
<string>dev-mac</string>
<string>release-unified</string>
<string>release-win</string>
<string>release-mac</string>
</a>
</choices>
</hudson.model.ChoiceParameterDefinition>
@@ -0,0 +1,55 @@
<?xml version='1.1' encoding='UTF-8'?>
<flow-definition plugin="workflow-job">
<actions/>
<description>每天 04:00 检查源码分支版本(cron 定义在 Jenkinsfile 内):revision 变化且出现客户端相关路径变更时,先经 Genarrative-Agc-Global-Version-Issue 发号,再用同一个固定 commit 与总版本号触发 AGC Windows、macOS 两个 release 分区构建;macOS 额外带 SKIP_IF_SUPERSEDED。下游 Job 一律不自带触发器。</description>
<keepDependencies>false</keepDependencies>
<properties>
<hudson.model.ParametersDefinitionProperty>
<parameterDefinitions>
<hudson.model.StringParameterDefinition>
<name>SOURCE_BRANCH</name>
<description>被检查的源码分支;默认 master</description>
<defaultValue>master</defaultValue>
<trim>true</trim>
</hudson.model.StringParameterDefinition>
<hudson.model.BooleanParameterDefinition>
<name>FORCE_TRIGGER</name>
<description>勾选后忽略版本与路径比较,强制发布 AGC release 渠道</description>
<defaultValue>false</defaultValue>
</hudson.model.BooleanParameterDefinition>
</parameterDefinitions>
</hudson.model.ParametersDefinitionProperty>
</properties>
<definition class="org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition" plugin="workflow-cps">
<scm class="hudson.plugins.git.GitSCM" plugin="git">
<configVersion>2</configVersion>
<userRemoteConfigs>
<hudson.plugins.git.UserRemoteConfig>
<url>ssh://git@127.0.0.1:2222/GenarrativeAI/Genarrative.git</url>
<credentialsId>genarrative-local-gitea-ssh</credentialsId>
<refspec>+refs/heads/master:refs/remotes/origin/master</refspec>
</hudson.plugins.git.UserRemoteConfig>
</userRemoteConfigs>
<branches>
<hudson.plugins.git.BranchSpec>
<name>*/master</name>
</hudson.plugins.git.BranchSpec>
</branches>
<doGenerateSubmoduleConfigurations>false</doGenerateSubmoduleConfigurations>
<submoduleCfg class="empty-list"/>
<extensions>
<hudson.plugins.git.extensions.impl.CloneOption>
<shallow>true</shallow>
<noTags>true</noTags>
<reference></reference>
<depth>1</depth>
<honorRefspec>true</honorRefspec>
</hudson.plugins.git.extensions.impl.CloneOption>
</extensions>
</scm>
<scriptPath>jenkins/Jenkinsfile.scheduled-release-trigger</scriptPath>
<lightweight>false</lightweight>
</definition>
<triggers/>
<disabled>false</disabled>
</flow-definition>
@@ -54,6 +54,17 @@ const checks = [
reason:
'Copy Artifact Production 模式下,AGC 发号 Job 还必须授权手动发布管线读取总号,否则用户触发的手动发布会停在 copyArtifactsSYSTEM 定时构建不受影响)。',
},
{
file: 'jenkins/Jenkinsfile.agc-global-version-issue',
includes: 'Genarrative-Scheduled-Release-Trigger',
reason:
'Copy Artifact Production 模式下,AGC 发号 Job 必须授权每日 release 调度器读取总号。',
},
{
file: 'jenkins/agc-global-version-issue-job-config.xml',
includes: '<string>release-unified</string>',
reason: 'AGC 发号 Job 配置必须保留 release 调度的统一发号用途标签。',
},
{
file: 'jenkins/Jenkinsfile.production-stdb-module-build',
includes: 'npm run check:rustfmt',
@@ -7733,6 +7744,14 @@ const scheduledRevisionTriggerJobConfig = readFileSync(
'jenkins/scheduled-revision-trigger-job-config.xml',
'utf8',
);
const scheduledReleaseTriggerContent = readFileSync(
'jenkins/Jenkinsfile.scheduled-release-trigger',
'utf8',
);
const scheduledReleaseTriggerJobConfig = readFileSync(
'jenkins/scheduled-release-trigger-job-config.xml',
'utf8',
);
// 定时与版本比较统一收敛到调度管线,两个下游流水线不得再自带触发器。
for (const [file, content] of [
@@ -7883,6 +7902,69 @@ if (
'[check:production-ops] scheduled-revision-trigger Job 必须指向调度 Jenkinsfile、使用本机 Git 入口与既有 SSH 凭据,并把定时器留在 Jenkinsfile。',
);
}
for (const [snippet, reason] of [
["cron('0 4 * * *')", 'release 调度器必须在每天 04:00 检查远端版本'],
['disableConcurrentBuilds()', '必须禁止 release 调度器并发触发'],
['skipDefaultCheckout(true)', 'release 调度器不得依赖本地 SCM 工作区'],
['git ls-remote', 'release 调度器必须直接从远端解析分支版本'],
[
"AGC_WINDOWS_BUILD_JOB_NAME = 'Genarrative-Agc-Windows-Build'",
'必须声明 AGC Windows release 构建目标 Job',
],
[
"AGC_MACOS_BUILD_JOB_NAME = 'Genarrative-Agc-MacOS-Build'",
'必须声明 AGC macOS release 构建目标 Job',
],
[
"string(name: 'AGC_CHANNEL', value: 'release-unified')",
'release 调度器必须先通过发号 Job 获取统一总版本号',
],
[
"string(name: 'AGC_UPDATE_CHANNEL', value: 'release')",
'release 调度器必须把 Windows 与 macOS 都发到 release 分区',
],
[
'build job: env.AGC_WINDOWS_BUILD_JOB_NAME, wait: false, propagate: false, parameters: agcParameters',
'release 调度器必须触发 AGC Windows release 构建',
],
[
'build job: env.AGC_MACOS_BUILD_JOB_NAME, wait: false, propagate: false, parameters: agcMacosParameters',
'release 调度器必须触发 AGC macOS release 构建',
],
[
"booleanParam(name: 'SKIP_IF_SUPERSEDED', value: true)",
'release 调度器必须让 macOS 节点恢复后跳过过期排队构建',
],
]) {
if (!scheduledReleaseTriggerContent.includes(snippet)) {
failed = true;
console.error(`[check:production-ops] release 调度管线${reason}`);
}
}
if (scheduledReleaseTriggerContent.includes('FULL_BUILD_JOB_NAME')) {
failed = true;
console.error(
'[check:production-ops] release 调度管线只负责 AGC release 客户端构建,不得触发 Full Build。',
);
}
if (
!scheduledReleaseTriggerJobConfig.includes(
'<scriptPath>jenkins/Jenkinsfile.scheduled-release-trigger</scriptPath>',
) ||
!scheduledReleaseTriggerJobConfig.includes(
'ssh://git@127.0.0.1:2222/GenarrativeAI/Genarrative.git',
) ||
!scheduledReleaseTriggerJobConfig.includes(
'<credentialsId>genarrative-local-gitea-ssh</credentialsId>',
) ||
!scheduledReleaseTriggerJobConfig.includes('<triggers/>')
) {
failed = true;
console.error(
'[check:production-ops] scheduled-release-trigger Job 必须指向 release 调度 Jenkinsfile、使用本机 Git 入口与既有 SSH 凭据,并把定时器留在 Jenkinsfile。',
);
}
if (
!fullPipelineContent.includes(
"string(name: 'COMMIT_HASH', value: env.SOURCE_COMMIT)",