Files
Genarrative/docs/technical/JENKINS_RUST_BUILD_DEPLOY_PIPELINES_2026-04-23.md

7.8 KiB
Raw Blame History

Jenkins Rust 构建与部署流水线方案

日期:2026-04-23

1. 目标

本方案为当前仓库补齐 3 条 Jenkins 流水线:

  1. 构建:只负责在仓库根目录执行 npm run deploy:rust:remote -- --skip-upload,生成发布包。
  2. 部署:只负责把指定发布版本部署到 /var/lib/jenkins/deploy/Genarrative/,禁止人工直接点击执行。
  3. 构建并部署:先构建,再把构建出的版本号传给 部署 流水线并等待部署完成。

本次只补 Jenkins 编排与本地部署脚本,不改现有 Rust 发布包构建逻辑,不恢复旧 server-node 部署链。

2. 执行约束

  1. 构建产物目录统一使用 build/<版本号>/
  2. 默认使用 Jenkins BUILD_NUMBER 作为版本号,避免依赖时间戳;如有需要也允许显式传 BUILD_VERSION
  3. 部署 流水线必须校验当前构建原因包含上游触发 cause没有上游触发则直接失败。
  4. 部署 流水线额外校验上游作业名与传入的 EXPECTED_UPSTREAM_JOB 一致;如配置了环境变量 GENARRATIVE_ALLOWED_UPSTREAM_JOB,还必须与该值一致。
  5. 构建并部署 在触发 部署 前先释放自己的构建节点,避免单执行器节点出现死锁。
  6. 部署 不重新构建,不重新上传,不从 Jenkins 插件仓库复制产物,直接使用上游构建节点的本地 build/<版本号>/ 目录。
  7. 部署 流水线读取触发原因时必须使用 currentBuild.getBuildCauses(...) 这类白名单方法,不能直接访问 currentBuild.rawBuild,否则会被 Jenkins Script Security 拦截。
  8. 由于 Jenkins Pipeline 的 build 步骤触发下游时,原因类型通常是 org.jenkinsci.plugins.workflow.support.steps.build.BuildUpstreamCause,实现上需要同时兼容它和经典的 hudson.model.Cause$UpstreamCause,否则会把真实的上游触发误判成人工执行。
  9. 如果线上进程的启停必须经过 sudo,只允许 start.sh / stop.sh 这两个 hook 使用 sudo -n 执行,部署目录清空与文件覆盖仍保持普通权限。

3. 节点与工作区要求

这套方案依赖“本地目录发布”,因此有两个前提:

  1. 构建并部署部署 必须落到同一台 Ubuntu Jenkins Agent或者落到同一块共享文件系统。
  2. 构建并部署 触发 部署 时,必须把 SOURCE_NODE_NAMESOURCE_WORKSPACE_ROOT 一并传下去。

仓库中提供的 Jenkinsfile 已按这个约束实现:

  1. 构建 / 构建并部署 在指定源码目录内 checkout scm 并生成 build/<版本号>/
  2. 构建并部署 结束构建节点占用后,再触发 部署
  3. 部署 优先按 SOURCE_NODE_NAME 调度到同名节点,再读取 SOURCE_WORKSPACE_ROOT/build/<版本号>/

4. 三条流水线定义

4.1 构建

脚本路径:

jenkins/Jenkinsfile.build

核心流程:

  1. 可选执行 npm ci
  2. 在源码根目录执行:
npm run deploy:rust:remote -- --skip-upload --name <BUILD_VERSION>
  1. 校验 build/<BUILD_VERSION>/ 存在。
  2. 归档 build/<BUILD_VERSION>/** 作为 Jenkins 产物。

默认版本号:

BUILD_VERSION = Jenkins BUILD_NUMBER

4.2 部署

脚本路径:

jenkins/Jenkinsfile.deploy

核心流程:

  1. 校验触发原因必须是上游流水线,而不是人工点击;实现上同时兼容 BuildUpstreamCause 与经典 UpstreamCause
  2. 校验 BUILD_VERSIONSOURCE_WORKSPACE_ROOTDEPLOY_DIRECTORY 非空。
  3. 执行:
scripts/jenkins-deploy-release.sh \
  --source-dir <SOURCE_WORKSPACE_ROOT>/build/<BUILD_VERSION> \
  --deploy-dir /var/lib/jenkins/deploy/Genarrative \
  --hook-with-sudo

脚本语义:

  1. 若部署目录已有旧版本且存在 stop.sh,先执行旧版本 stop.sh
  2. 只删除发布产物白名单中的旧文件,例如 web/api-serverspacetime_module.wasm.env*start.shstop.shweb-server.mjsREADME.md
  3. 将指定版本目录中的同名发布产物移动到部署目录。
  4. 执行新版本 start.sh

如果 RUN_DEPLOY_HOOKS_WITH_SUDO=true,第 1 步和第 4 步会改为 sudo -n 调用;这要求 Jenkins 运行用户提前配置免密 sudo否则部署会直接失败不会进入交互式密码提示。

这样可以满足“发布文件直接覆盖”的要求,同时保留部署目录里像 spacetimedb-data/logs/run/ 这类运行态目录,不会因为部署被整体删除。发布白名单内的 .env.env.local 仍会以构建产物中的文件为准。

4.3 构建并部署

脚本路径:

jenkins/Jenkinsfile.build-and-deploy

核心流程:

  1. 复用与 构建 相同的构建命令生成 build/<BUILD_VERSION>/
  2. 归档 build/<BUILD_VERSION>/**
  3. 记录当前 NODE_NAME、源码根目录、版本号。
  4. 触发 部署 流水线,并传递:
    • BUILD_VERSION
    • SOURCE_WORKSPACE_ROOT
    • SOURCE_NODE_NAME
    • DEPLOY_DIRECTORY
    • EXPECTED_UPSTREAM_JOB

5. Jenkins 参数建议

三条流水线统一建议暴露以下参数:

  1. AGENT_LABEL:默认执行节点标签。
  2. GENARRATIVE_WORKSPACE_ROOT:源码根目录;为空时回退到 Jenkins 当前工作区。
  3. BUILD_VERSION:发布版本号;为空时回退到 BUILD_NUMBER
  4. RUN_NPM_CI:是否在构建前执行 npm ci

如果当前 Jenkins 没有额外配置独立 Agent而是直接在控制器自身执行任务AGENT_LABEL 应填写 built-in。 如果 Jenkins 进程以默认 jenkins 用户运行,部署目录建议直接放在 /var/lib/jenkins/deploy/Genarrative 这类 Jenkins 自有目录下,避免再依赖 /home/ubuntu/* 的额外写权限。 如果目标 Ubuntu 的 Jenkins sh 默认实际落到 /bin/sh -> dash,而流水线脚本又使用了 set -euo pipefail,则必须显式通过 bash -lc 执行命令,不能直接依赖 Jenkins 默认 sh 解释器。

其中仅 部署 流水线还需要:

  1. SOURCE_WORKSPACE_ROOT
  2. SOURCE_NODE_NAME
  3. DEPLOY_DIRECTORY
  4. RUN_DEPLOY_HOOKS_WITH_SUDO
  5. EXPECTED_UPSTREAM_JOB

其中仅 构建并部署 流水线还需要:

  1. DEPLOY_JOB_NAME
  2. RUN_DEPLOY_HOOKS_WITH_SUDO

如果你选择启用 RUN_DEPLOY_HOOKS_WITH_SUDO=true,推荐提前在服务器上增加一份最小 sudoers 配置,例如:

jenkins ALL=(root) NOPASSWD: /var/lib/jenkins/deploy/Genarrative/start.sh
jenkins ALL=(root) NOPASSWD: /var/lib/jenkins/deploy/Genarrative/stop.sh

这样可以把提权范围收敛到固定部署目录下的启停脚本,而不是把整个部署流程都交给 sudo

6. 推荐 Job 命名

建议在 Jenkins 中创建以下 3 个 Pipeline Job并分别指向仓库中的脚本路径

  1. Genarrative-Build -> jenkins/Jenkinsfile.build
  2. Genarrative-Deploy -> jenkins/Jenkinsfile.deploy
  3. Genarrative-Build-And-Deploy -> jenkins/Jenkinsfile.build-and-deploy

同时给 Genarrative-Deploy 配置环境变量:

GENARRATIVE_ALLOWED_UPSTREAM_JOB=Genarrative-Build-And-Deploy

如果 Job 在 Jenkins Folder 下,值应填写完整上游作业名,例如:

game/Genarrative-Build-And-Deploy

7. 文件清单

本方案对应的仓库文件:

jenkins/Jenkinsfile.build
jenkins/Jenkinsfile.deploy
jenkins/Jenkinsfile.build-and-deploy
scripts/jenkins-deploy-release.sh

8. 风险与边界

  1. 该方案依赖本地目录切换,不适用于“构建节点”和“部署节点”完全隔离且不共享文件系统的 Jenkins 架构。
  2. 当前 部署 采取的是“覆盖固定部署目录”的方式,不包含版本回滚目录管理;如需保留完整历史版本,应在后续单独补一层 release/current 软链接结构。
  3. 当前 start.sh / stop.sh 仍以发布包内脚本为准,不替代 systemdsupervisornginxtls 与日志轮转治理。