- Shell 98.7%
- Dockerfile 1.3%
|
|
||
|---|---|---|
| .forgejo/workflows | ||
| ci | ||
| docs | ||
| scripts | ||
| .dockerignore | ||
| Dockerfile | ||
| README.md | ||
ci-tools
Forgejo Runner 的统一构建、依赖发布、服务制品发布和服务部署实现。业务仓库只保留
触发条件与中央工作流调用,规则和脚本集中由 java/ci-tools 维护。
前端项目使用中央 Yarn/pnpm/npm 安装与构建校验,并可通过独立入口发布静态站点 ZIP、
frontend-static Manifest 和 OCI 镜像;Python 项目默认执行离线安全
的源码编译检查和已有单元测试。两类校验均不包含制品发布或环境部署权限。
中央工作流
reusable-ci.yml:依赖库与 Java 服务校验。reusable-frontend-ci.yml:Yarn、pnpm 或 npm 前端项目校验。reusable-frontend-publish.yml:校验并发布静态站点,不执行部署或修改业务版本。reusable-python-ci.yml:Python 源码与已有单元测试校验。reusable-package-publish.yml:Maven/npm 依赖制品统一发布。reusable-generic-service-release.yml:服务 ZIP 与 OCI 镜像统一发布。reusable-generic-service-deploy.yml:按制品版本向一个或多个服务器部署。reusable-initialize-work-branch.yml:新建工作分支后初始化独立 Snapshot 版本。reusable-create-release-tag.yml:主分支 POM 变为正式版本后自动创建.releaseTag。
本次调整直接替换现有接口,不保留第二套版本或兼容入口。
reusable-frontend-ci.yml 始终自动递归检出 Git 子模块,业务仓库不再通过代码选择是否
检出。中央入口优先使用显式映射的 FORGEJO_BOT_TOKEN,也支持调用方 secrets: inherit
直接提供的组织 Secret CI_BOT_TOKEN;两者都不存在时使用当前任务令牌,此时只能读取
当前仓库和公开子模块。私有子模块必须由机器人账号具备只读权限。
发布源码与通道
依赖和服务的手动发布使用相同的三个源码选择器,且每次必须且只能填写一个:
| 选择器 | 来源 | 发布类型 |
|---|---|---|
release_tag |
X.Y.Z[.N].release 指向的提交 |
Release |
source_ref |
完整 40 位提交 SHA | Snapshot |
source_branch |
feature/、bugfix/、hotfix/ 工作分支在检出时的最新提交 |
Snapshot |
发布类型由中央工作流推导,调用方不再传通道。分支构建会在 Manifest 中同时记录 分支名和实际检出的完整提交 SHA。
依赖仓库地址和服务镜像地址直接读取 Forgejo Variables:
PACKAGE_SNAPSHOT_REPOSITORY_URLPACKAGE_RELEASE_REPOSITORY_URLIMAGE_REPOSITORY
IMAGE_REPOSITORY 是业务仓库级 Variable,格式必须为
gitea.sankcrm.cn/<owner>/<源仓库名>,例如 gitea.sankcrm.cn/java/oauth-centre。
Registry 地址和镜像名不能改写;owner 可以与源代码组织不同,但目标组织必须通过
Forgejo API 校验为 public,并且 PACKAGE_REGISTRY_USERNAME 对该组织的权限响应必须
包含 can_write=true。公开组织只影响镜像的可读性,推送账号仍然需要目标组织的写权限。
中央脚本在构建前执行这些只读校验,失败时不会构建或推送镜像。
正式版本在构建前检查远端是否已存在;Snapshot 可以重复发布。Maven 工作分支
版本为 <base>-<ISSUE>-SNAPSHOT,npm 为 <base>-<issue>.snapshot。服务
Snapshot 最终制品版本还会附加源码 SHA、工作流运行号和重试号。
服务仓库和依赖库只保留一个 publish-snapshot.yml。推送到 develop 时自动
发布;手动运行时使用 Forgejo 自带的分支选择器,允许选择 develop 或
feature|bugfix|hotfix/<PROJECT>-<ISSUE_NUMBER>[-description]。PROJECT 非空且不含
/、- 或控制字符;除此之外不限制大小写、字母数字组成或前导零。Issue 编号必须是无
前导零的正整数。
例如 feature/0-40-work-item、bugfix/030-5-fix 和 hotfix/app_name-7-fix 均符合
分支结构。npm 保留既有小写工作项版本标记,生成结果仍须满足 npm 自身的 SemVer 规则。
服务发布同时生成 ZIP 和 OCI 镜像,项目版本还必须符合 Docker Tag 原生格式
[A-Za-z0-9_][A-Za-z0-9_.-]{0,127};不符合时会在构建前拒绝,这属于制品生态限制,
不改变分支项目标记规则。
薄工作流统一传入
automatic_snapshot: true 和 ${{ forgejo.ref_name }},中央 CI 校验分支后锁定
该次事件的完整 SHA,因此构建期间分支继续推进也不会改变源码。
所有入口都只允许测试版本。develop 可使用 Maven <base>-SNAPSHOT、npm
<base>-snapshot,也可保留工作分支合入后的 Issue Snapshot;工作分支必须使用
<base>-<ISSUE>-SNAPSHOT(npm <base>-<issue>.snapshot),且版本中的 Issue
必须与分支一致。原 publish-snapshot-manual.yml 已删除,不保留重复入口。
工作分支版本初始化
开发工作分支指每个具体需求或缺陷使用的分支:
feature/OAUTH-123-login
bugfix/OAUTH-456-token
hotfix/OAUTH-789-expiry
Forgejo create 事件触发业务仓库的薄工作流,并调用中央初始化入口。中央脚本
确认分支刚创建且尚未被推进后,在新分支修改版本、提交并执行普通 fast-forward
push;不会 force-push。一个活动 Issue 不允许同时存在多个上述类型的工作分支。
Java 服务工作分支通常从 develop 创建;develop 可能携带正式基础版本、
<base>-SNAPSHOT 或上一 Issue 的 <base>-<ISSUE>-SNAPSHOT,中央脚本统一
提取数字基础版本后设置为 <base>-<当前ISSUE>-SNAPSHOT。Maven 依赖和 npm
项目仍必须从构建文件中的正式基础版本创建。版本只在 Effective POM 或
package.json 中定义,交付模型不再重复保存版本。
主分支校验成功后可调用中央自动 Tag 工作流。只有 Effective POM 版本相对上一个
main 提交发生变化并变为 X.Y.Z[.N] 时,中央机器人才能创建对应的
X.Y.Z[.N].release Tag;Tag 随后触发现有正式发布流程。
多目标版本部署
部署工作流只接收已经发布的 Maven 声明 artifact_version 和目标 ID 数组,不区分测试、
生产等环境。一个版本可选择多个目标,每个目标分别配置 Runner、服务器、账号、
部署根目录、部署方式和凭据名称。
目标定义来自 Forgejo Variable DEPLOY_TARGETS_JSON,例如:
[
{
"id": "server-a",
"runner": "deploy-a",
"host": "server-a.internal",
"user": "deployer_a",
"deploy_root": "/opt/apps/example",
"deployment_type": "zip",
"deploy_command": "/opt/deploy/sankcrm/bin/deploy-generic-service",
"health_url": "https://server-a.internal/actuator/health",
"password_secret": "SERVER_A_PASSWORD",
"known_hosts_secret": "SERVER_A_KNOWN_HOSTS"
}
]
部署只解析 Generic Package 中已经发布并带摘要的 Manifest,不检出源码、不执行 Maven,也不重新构建制品。每个目标独立产生部署证据,单个目标串行执行,不同目标 可并行。
ZIP 服务器目录
DEPLOY_ROOT/
├── runtime.env
├── releases/
│ ├── <application>-run-<artifact_version>-1.war
│ └── <application>-run-<artifact_version>-2.war
├── <application>-run.war -> releases/<application>-run-<artifact_version>-2.war
├── startup
├── conf/
│ ├── logback-spring.xml
│ ├── app/
│ └── i18n/
├── .state/
└── logs/
└── gc/
ZIP 制品本身只包含 <application>-run.war。startup 仅在 DEPLOY_ROOT 中不存在时
由中央 CI 安装统一模板;若已存在由 root 或部署账号所有、可执行且不被 group/other
写入的普通 startup 文件,则原样保留并拒绝覆盖。中央部署器使用 SSH 登录账号自动创建
DEPLOY_ROOT、releases/、.state/、logs/gc/、conf/app/ 和 conf/i18n/;
runtime.env、配置文件、.state/ 和 logs/ 跨版本保留。首次运行新版部署器时,
已有的 state/ 会在安全校验后原子迁移为 .state/;两个目录同时存在时拒绝部署。
每次 ZIP 部署都会先在 .state/next-index 原子预占新索引,再安装为
<application>-run-<artifact_version>-<index>.war。相同版本可以重复部署;
索引全局递增,部署失败的索引也不复用。活动软链接原子切换,启动或健康检查失败
时恢复上一软链接和上一进程。
首次接管已有的普通 <application>-run.war 时,会先将旧 WAR 归档到
releases/ 并转换为符号链接,因此新版本健康检查失败时仍可回滚到旧版本。
ZIP 部署不使用 deploy.env。
Docker 服务器目录
Docker 目标使用同一个 DEPLOY_ROOT 约定,并额外要求:
DEPLOY_ROOT/
├── deploy.env
├── runtime.env
├── releases/
├── .state/
└── logs/
deploy.env 只保存 Docker 运行配置,包括 CONTAINER_NAME、
PORT_MAPPINGS_JSON、VOLUME_MOUNTS_JSON,以及可选的
DOCKER_NETWORK、RESTART_POLICY 和健康检查重试参数。runtime.env 通过
Docker --env-file 注入容器。远端部署器拒绝 host 网络、Docker Socket 挂载和
非摘要镜像引用。
镜像仓库路径不在服务器重复配置。中央部署客户端从不可变 IMAGE_REFERENCE(例如
gitea.sankcrm.cn/java/oauth-centre@sha256:...)自动提取
java/oauth-centre,远端再次校验 Registry、Digest、镜像路径与源仓库名;同组织和
跨组织部署均不需要 EXPECTED_REPOSITORY。
业务仓库的 PACKAGE_USERNAME/PACKAGE_TOKEN 由中央工作流分别映射为脚本运行时的
PACKAGE_REGISTRY_USERNAME/PACKAGE_REGISTRY_PASSWORD(Maven/Generic)或
PACKAGE_REGISTRY_USERNAME/PACKAGE_REGISTRY_TOKEN(npm);跨组织发布的 Token
还必须能查询目标组织权限并确认 can_write=true。
目标服务器前置条件
固定中央入口为:
/opt/deploy/sankcrm/bin/deploy-generic-service
部署账号直接同步并执行中央脚本,不使用 sudo。运维只需保证中央入口和
DEPLOY_ROOT 的最近已存在父目录由部署账号所有且可写;中央部署器会逐级创建缺失
目录,并校验它们不是符号链接、由部署账号所有、可写且不能被 group/other 写入。
Java 或 Docker、curl、jq、unzip 等运行依赖仍由运维安装。runtime.env、
logback-spring.xml、application-*.yaml、deploy.env 等配置文件不会自动生成
或覆盖,应由 root 或部署账号维护,且不能被 group/other 写入;startup 一经存在
同样由服务器维护,中央部署器不再按摘要更新或覆盖。
完整调用示例、变量、Secret 和服务器配置见
docs/中央CI工作流与脚本使用说明.md;
设计决策、实施范围与验收标准见
docs/中央CI统一发布与部署调整方案.md。
工具镜像
gitea.sankcrm.cn/java/ci-tools:2026.09.2:中央作业固定版本。gitea.sankcrm.cn/java/ci-tools:runner-stable:Runner 永久使用的稳定入口; 只有固定版本镜像完成全部校验后才会提升该标签。gitea.sankcrm.cn/java/ci-tools:latest:仅用于人工验证。
业务工作流只声明 ci、build-image、publish-package、deploy-test 或
deploy-production 等稳定 Runner 标签,不声明任务镜像。Runner 配置永久引用
runner-stable 并启用强制拉取,因此中央工具镜像升级不需要修改业务工作流或
重新注册 Runner。每个不可变版本标签仍保留,用于审计和快速回滚稳定入口。
镜像包含 Azul Zulu JDK 11、17、21、Maven、Bash、Git、curl、jq、Node.js/npm、
Yarn Classic 1.22.22、PNPM 12.6.0、
OpenSSH、zip/unzip 与摘要工具。Maven 依赖库和 Java 服务根据包含 Parent 的 Effective POM
自动选择 JDK。当前 Runner 在可复用工作流中不执行任务镜像覆盖,因此中央脚本
只接受预装的同版本 Azul JDK。三套 JDK 都来自 Forgejo 内部
java/azul-alpine:11-jdk、17-jdk、21-jdk;缺失或厂商不符时立即失败,
运行中的任务不再安装或下载另一套 JDK。中央流程不使用高版本
JDK 加 --release 的回退方式,因为该参数不能兼容
访问 javac 内部 API 的 Lombok、QueryDSL 等注解处理器。业务仓库不配置 Java
版本或任务镜像。
系统工具与三套内部 JDK 镜像使用独立稳定缓存层,Yarn/PNPM 及最终镜像版本位于其后; 升级前端工具或镜像元数据不会重新从公共仓库下载 JDK。Docker Engine 构建与推送日志 实时显示当前 Layer、目标镜像、耗时和最终状态,并在失败时分别报告传输、日志留存和 JSON 解析状态,便于直接从 Actions 日志定位最后完成的步骤。
镜像把 ci/maven-settings.xml 安装为 Maven 全局配置。业务 POM 仍先解析
https://gitea.sankcrm.cn/api/packages/java/maven;内部制品缺失时,仅仓库 ID
jkpPublic 可以回退到固定地址
http://maven.sankcrm.cn/repository/maven-public。其他外部 HTTP 仓库继续被 Maven
阻断,业务仓库无需复制 settings.xml 或传入额外参数。
Java 服务校验与发布统一显式设置 maven.javadoc.skip=true。服务交付仍完整执行
编译、测试、WAR、ZIP 和 OCI 镜像构建,但不会因为仅影响文档附件的 Javadoc
错误阻断运行制品;Maven 依赖库不采用该规则,仍按库发布流程处理源码和文档附件。
服务 Dockerfile 的全部基础镜像必须来自 gitea.sankcrm.cn。历史 Tag 仍引用
Docker Hub 时,中央流程会按该 Tag 的 Effective POM Java 版本使用对应内网
JRE 模板,不移动历史 Tag,也不让生产 DIND 访问外部镜像仓库。
本地验证:
docker build --build-arg IMAGE_VERSION=2026.09.2 \
-t gitea.sankcrm.cn/java/ci-tools:2026.09.2 .
docker run --rm gitea.sankcrm.cn/java/ci-tools:2026.09.2 verify-ci-toolchain