DooTask 更新流程实战:`sudo ./cmd update` 的前置检查、本地改动决策与失败处理

原创2026-10-14 11:40:211,964 阅读
文章标签:后端前端桌面应用协同办公

DooTask 更新流程实战:sudo ./cmd update 的前置检查、本地改动决策与失败处理

本篇基于 DooTask 仓库内置的更新操作技能文档(.claude/skills/dootask-update/SKILL.md),系统讲解一套"刚性"的服务端更新流程:如何通过 ./cmd update 一条命令完成拉代码、Composer 依赖更新、数据库迁移与服务重启,以及如何正确执行前置检查、在"本地有改动"这一关键岔路做出安全决策、并妥善处理各类失败。读完后,你可以对已部署的 DooTask 实例执行一次不丢数据、不丢本地改动的安全更新。

核心原则:脚本负责执行,流程负责把关

DooTask 把整套更新逻辑封装在根目录的管理脚本 cmd 的 handle_update 函数中,一条命令即可串联完整链路:检测本地改动 → git fetch → 必要时自动备份数据库 → git pull/git reset → composer install → php artisan migrate → 重启 php/nginx 容器 → 写入 UPDATE_TIME。

更新流程文档将其定位为"刚性技能",核心原则是:

  • 任何一步失败立即停止,不自动重试、不自动跳过;
  • 前置检查通过 → 向用户确认一次 → 执行 → 报告结果,不多不少;
  • 尤其不要替用户决定强制更新——--force 会丢弃本地改动,这个决定只能由人拍板。

所谓"刚性",意味着违反字面规则就等于违反流程精神:不要擅自加步骤、不要绕过 sudo、不要省略确认环节。脚本本身能力很强(见 cmd),但技能文档的职责是更新前把关、选对参数、处理本地改动这一关键岔路、执行前确认。

前置检查:五项全部通过才能继续

执行 sudo ./cmd update 之前,需要依次确认以下五项(源码中大部分检查脚本自身也会做,提前确认可以让报错更早、定位更准):

# 检查项 判定方法 未通过的后果
1 已安装 项目根存在 vendor/autoload.php 脚本直接报错 请先执行安装命令(cmd 中 if <a href="https://link.gitcode.com/i/bdbea88dd75ac8d754bb6200d56ded0b" target="_blank"> ! -f "${WORK_DIR}/vendor/autoload.php" ]),此时应改走 [dootask-install 安装流程
2 工作目录 必须在项目根目录(存在 cmd、docker-compose.yml、.env.docker) 脚本以 pwd 作为 WORK_DIR,目录不对会导致所有相对路径与容器探测失败
3 当前分支 / 目标分支 默认更新当前分支;需要换分支时用 --branch <分支> 用户没有明确要换分支时,应确认是否就更新当前分支(本仓库主分支为 pro)
4 本地改动 git status 查看是否有未提交改动 这是下文详述的"关键岔路",必须先有决策再执行
5 sudo update 入口先执行 check_sudo,非 root 直接退出 必须以 sudo ./cmd update 方式运行(cmd)

检查通过后,向用户汇报检查结果并确认一次,再实际执行。另外注意:除 electron 和 permission 外,./cmd 的所有子命令都会先执行 check_docker(要求 Docker 已安装、docker-compose/docker compose 为 v2+,见 cmd)和 env_init(从 .env.docker 初始化 .env),因此宿主机必须具备可用的 Docker 环境。

关键岔路:本地有改动时停下,交用户决定

从源码结构看,远程更新模式下脚本会用 git diff --quiet 与 git diff --cached --quiet 双重检测工作区与暂存区是否干净(cmd)。一旦发现本地改动且未带 --force,脚本会交互式询问:

[警告] 检测到本地修改,是否强制更新?[Y/n]

这里的"强制更新"在源码中的真实含义是:

git checkout -f <分支>          # 切分支时强制切换(--force 场景)
git reset --hard origin/<分支>  # 丢弃所有本地改动,对齐远程

即 --force = 丢弃全部本地改动,属于不可逆操作。因此更新流程文档规定:

  • 发现本地有改动 → 停下,把改动清单报告给用户,让用户决定:先提交/暂存改动,还是明确接受强制更新;
  • 不要替用户选 --force;只有用户明确说"丢掉改动强制更新"时,才带 --force 执行;
  • 一个容易踩的坑:该交互提示在直接回车(空输入)时默认取 Y(cmd 中 [[ -z ${confirm_force} ]] && confirm_force="Y")。也就是说如果执行者不留意直接回车,就会进入强制更新路径——这正是要"先停下、向用户报告改动、由用户决定"的原因。

正确姿势示例:检测到 git status 有改动后,先向用户展示改动文件列表,说明"--force 将执行 git reset --hard origin/<分支>,所有未提交的本地修改会永久丢失,请先提交或暂存这些改动,或明确确认强制更新",得到明确答复前不执行。

参数选择:--branch、--force、--local

./cmd update 支持三个参数(与 cmd 脚本帮助信息 一致),选择依据如下表:

参数 作用 何时用
--branch <分支> 切到指定分支再更新 用户要换分支(如切 dev/pro)
--force 强制更新:git checkout -f + git reset --hard 危险:仅用户明确接受"丢弃本地改动"后
--local 本地更新模式:只备份库 + migrate + 重启,不拉远程代码 代码已就位(如手动改过/CI 拉过),只需迁移+重启

结合 cmd 的分支处理逻辑,各参数的行为细节可以进一步展开:

--branch <分支>:脚本先检查本地是否已有该分支的远程跟踪配置,缺失时自动补 git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*';若本地无该分支则先 git fetch origin <分支>:<分支> 拉取;最后执行 git checkout <分支>(--force 时为 git checkout -f <分支>)。

--force:跳过 git pull,直接对目标分支执行 git reset --hard origin/<分支>(cmd)。注意它只在"本地有改动且用户已确认"或"切换的分支存在冲突"的场景下使用。

--local:完全跳过 git 相关步骤(不 fetch、不 pull),只做数据库备份 + php artisan migrate + 重启服务(cmd)。适合代码已经就位、只需要补数据库迁移和重启的场景,例如开发者刚在服务器上手动改完代码。

数据库:迁移变动自动备份,重操作前可额外加备

更新对数据的最大风险来自数据库迁移。脚本内置了两道保护,均无需手动干预:

  1. 远程模式下的自动备份:拉取前,脚本用 git diff --name-only HEAD..origin/<分支> 检测 database/ 目录在本地与远程之间是否有变动;只要命中(即远程有新迁移文件),就先调用 mysql_snapshot backup 备份数据库再继续(cmd)。备份实现是对 mariadb 容器执行 mysqldump 后 gzip,落盘到 docker/mysql/backup/<库名>_<时间戳>.sql.gz(cmd)。
  2. 本地模式下的无条件备份:--local 模式进入前固定先执行一次数据库备份(cmd),因为本地模式无法预知代码里有没有新迁移。

在满足以下任一情况时,建议执行前额外提醒用户:本次可能是大版本升级,或用户对数据特别在意。此时可先手动加一次备份:

./cmd mysql backup

备份产物同样落在 docker/mysql/backup/。若更新后发现问题,可用交互式还原命令选择某个备份文件恢复(恢复后脚本会自动再跑一次 migrate 对齐结构):

./cmd mysql recovery

执行与成功后的报告

确认(含本地改动决策)后,按用户选择执行对应命令:

sudo ./cmd update
# 切分支: sudo ./cmd update --branch pro
# 强制(丢改动,用户确认后): sudo ./cmd update --force
# 本地模式: sudo ./cmd update --local

从源码看,迁移完成后的收尾链路是:docker-compose stop php nginx → rm -f php nginx → up -d --remove-orphans 重新拉起全部服务;若 up 失败则兜底执行 down 后再 up -d(cmd)。随后 wait_php_healthy 会轮询 php 容器健康状态(最多约 90 秒),必要时再次兜底拉起 nginx(cmd),最后通过 env_set UPDATE_TIME $(date +%s) 把更新时间戳写入 .env,输出"更新完成"。

执行成功后,向用户报告三项结果:

  1. 更新到的分支(当前分支或 --branch 指定的分支);
  2. 是否做了库备份/迁移(是否命中 database/ 变动触发的自动备份、备份文件名,migrate 是否执行);
  3. 服务是否重启完成(php/nginx 容器是否 healthy/running)。

失败处理:立即停止,原样报告

更新流程文档对失败处理只有一条总原则——任何步骤失败立即停止,原样报告错误;不自动重试、不自动跳过、不要因为 git pull 失败就自己改成 --force。常见失败与对应处理如下(提示文案均出自 cmd 的中英文对照表):

报错 原因 处理
请先执行安装命令 未执行过 ./cmd install,vendor/autoload.php 不存在 停止更新,引导用户改走 dootask-install 安装流程
代码拉取失败,可能存在冲突,请使用 --force 参数 git pull --ff-only 失败,本地与远程出现分叉 报告给用户,由用户决定是先处理冲突,还是明确接受 --force 丢改动(cmd)
获取远程更新失败 git fetch --all 失败 原样报告,通常是网络或远端仓库问题
重启服务失败 容器重启异常 脚本自身会先尝试 down 后重起;若仍失败,报告交用户(cmd)
请使用 sudo 运行此脚本 未以 root 运行 改用 sudo ./cmd update

禁止项与 Red Flags

更新流程文档用一张对照表固化了"错误做法 → 正确做法",这是整个流程的底线约束:

错误做法 正确做法
检测到本地改动就自动 --force 停下,报告改动,交用户决定
git pull 失败就自动改用 --force 报告冲突,交用户
不加 sudo sudo ./cmd update
未装就更新 先走 dootask-install
失败后自动重试/跳过 立即停止

与之配套的是"Red Flags"清单——执行更新时,一旦出现以下念头应立即停下:

  • "有点本地改动,强制更新一下就好了" → 不,--force 会丢改动,必须用户拍板;
  • "拉取冲突了,我 reset 一下" → 不,交用户决定;
  • "已经装过了吧,直接更新" → 不,先确认 vendor/autoload.php 存在。

小结

DooTask 的更新流程把"重活"交给 cmd 脚本(git、composer、migrate、容器重启、自动备份一应俱全),把"判断"交给更新操作者:五项前置检查、一次执行前确认、本地改动的用户决策、失败即停。掌握这套流程后,你可以:

  • 对生产实例执行安全更新:sudo ./cmd update(含自动备份迁移);
  • 按需切分支:sudo ./cmd update --branch <分支>;
  • 代码已就位时只补迁移和重启:sudo ./cmd update --local;
  • 重大更新前用 ./cmd mysql backup 加一道备份、出问题时用 ./cmd mysql recovery 回滚。

相关资源:更新技能文档 SKILL.md、安装流程 dootask-install、数据备份流程 dootask-backup、管理脚本 cmd、编排文件 docker-compose.yml。

登录后查看全文
dootask