Files
Genarrative/jenkins/Jenkinsfile.production-notify-email
kdletters 02a7abeb3f
Project CI / AI game creator shell Rust crates (push) Successful in 2m53s
Project CI / AI game creator shell Rust smoke (push) Successful in 3m42s
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / Backend 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 / Native shell tests (push) Has been cancelled
新增后台 AGC 模板管理与模板库内容寻址落库
- 后台新增「模板管理」页:按权限查看并编辑模板名称/简介/标签/封面,支持上架与下架;路由、侧栏、tab 权限、API 客户端与 DTO 同步接入
- api-server 新增 /admin/api/agc-templates 读取与更新路由,复用现有后台鉴权与 tab 权限校验,权限不足按既有失败关闭口径拒绝
- module-assets 收敛模板库纯规则(active/inactive 投影、索引 schema 与重复项校验、ZIP 大小与摘要核对),platform-oss 承担对象存储读写、不可变对象复用与发布互斥锁
- shared-contracts 补后台模板 DTO;spacetime-module 账号存储适配保持既有 schema 不变
- AGC 壳内置模板索引与建项读取改为内容寻址对象,补模板安装、越界归档、索引不匹配等用例
- 模板库发布脚本支持定向发布与上架/下架合并、内容地址复用与锁语义,CLI 守卫用例同步;jenkins 两条客户端打包管线统一触发邮件通知 Job 并传递首装包链接
- 内置 5 个 Cocos 官方模板载荷与索引 fixture(内容地址对象,共 90 个文件)
- 补「后台模板管理」里程碑与实施计划,更新模板库技术方案、开发运维文档与共享记忆
- 验证:admin-web 39 项用例与 typecheck、发布脚本 25 项用例、api-server 与 AGC 壳编译、模板库定向 Rust 用例(AGC 壳 18 项、module-assets/platform-oss 16 项、后台权限 1 项)、check:encoding / check:doc-index / check:production-ops / rustfmt 全部通过
2026-09-21 13:56:20 +08:00

84 lines
3.7 KiB
Plaintext

pipeline {
agent {
label 'linux && genarrative-build'
}
options {
disableConcurrentBuilds()
skipDefaultCheckout(true)
buildDiscarder(logRotator(numToKeepStr: '50', artifactNumToKeepStr: '0'))
}
parameters {
string(name: 'EMAIL_RECIPIENTS_CREDENTIAL_ID', defaultValue: 'genarrative-notification-emails', description: '持久收件人 Secret Text 凭据 ID,凭据内容为逗号分隔邮箱;留空则只使用本次追加收件人')
string(name: 'EMAIL_RECIPIENTS', defaultValue: '', description: '本次运行追加邮件通知收件人;会与持久收件人凭据合并发送')
string(name: 'SOURCE_JOB_NAME', defaultValue: '', description: '来源流水线名称')
string(name: 'SOURCE_BUILD_NUMBER', defaultValue: '', description: '来源构建号')
string(name: 'SOURCE_BUILD_URL', defaultValue: '', description: '来源构建 URL')
string(name: 'SOURCE_RESULT', defaultValue: 'UNKNOWN', description: '来源流水线结果')
string(name: 'SOURCE_BRANCH', defaultValue: '', description: '源码分支')
string(name: 'SOURCE_COMMIT', defaultValue: '', description: '源码 commit')
string(name: 'BUILD_VERSION', defaultValue: '', description: '发布版本号')
string(name: 'DEPLOY_TARGET', defaultValue: '', description: '部署目标')
string(name: 'DATABASE', defaultValue: '', description: 'SpacetimeDB database')
string(name: 'OSS_DOWNLOAD_URL', defaultValue: '', description: '客户端或其它产物的 OSS 下载链接')
string(name: 'SUMMARY', defaultValue: '', description: '补充摘要')
}
stages {
stage('Send Email') {
steps {
script {
def sendNotification = { persistedRecipients ->
def recipientList = []
[persistedRecipients, params.EMAIL_RECIPIENTS].each { rawRecipients ->
rawRecipients?.split(',')?.each { recipient ->
def normalized = recipient.trim()
if (normalized && !recipientList.contains(normalized)) {
recipientList.add(normalized)
}
}
}
def recipients = recipientList.join(',')
if (!recipients) {
echo '[notify-email] 持久收件人凭据与 EMAIL_RECIPIENTS 均未配置,跳过邮件发送。'
return
}
def result = params.SOURCE_RESULT?.trim() ?: 'UNKNOWN'
def jobName = params.SOURCE_JOB_NAME?.trim() ?: 'unknown-job'
def buildNumber = params.SOURCE_BUILD_NUMBER?.trim() ?: 'unknown-build'
def subject = "[Genarrative][${result}] ${jobName} #${buildNumber}"
def body = """Genarrative Jenkins 流水线执行结果
结果: ${result}
流水线: ${jobName}
构建号: ${buildNumber}
构建 URL: ${params.SOURCE_BUILD_URL ?: ''}
源码分支: ${params.SOURCE_BRANCH ?: ''}
源码 commit: ${params.SOURCE_COMMIT ?: ''}
发布版本: ${params.BUILD_VERSION ?: ''}
部署目标: ${params.DEPLOY_TARGET ?: ''}
数据库: ${params.DATABASE ?: ''}
OSS 下载链接: ${params.OSS_DOWNLOAD_URL?.trim() ?: '(无)'}
摘要: ${params.SUMMARY ?: ''}
"""
mail to: recipients, subject: subject, body: body
echo "[notify-email] 已发送邮件: recipientCount=${recipientList.size()}, source=${jobName} #${buildNumber}, result=${result}"
}
def credentialId = params.EMAIL_RECIPIENTS_CREDENTIAL_ID?.trim()
if (credentialId) {
withCredentials([string(credentialsId: credentialId, variable: 'PERSISTED_EMAIL_RECIPIENTS')]) {
sendNotification(env.PERSISTED_EMAIL_RECIPIENTS)
}
} else {
sendNotification('')
}
}
}
}
}
}