Caddy 软链被替换
Caddy 2.11.7 自身替换可能把 Homebrew 入口软链写成普通文件,导致后续链接冲突。保留定制产物后恢复链接,并将 xcaddy 构建与包管理路径分开。
通过 Homebrew 安装 Caddy,执行 add-package 后,下次 brew upgrade caddy 却报 Could not symlink bin/caddy、目标 already exists。检查 /opt/homebrew/bin/caddy 是否从软链变成了普通文件:Caddy 2.11.7 的自身替换路径可能把入口软链当成待替换的程序。
被替换的是入口
正常入口指向 Cellar 中的版本目录。受影响路径使用 os.Stat 检查软链,但这个函数会跟随软链;接着检查软链标记的分支没有生效。程序把入口路径改名备份,再在该处写入下载的定制二进制,原 Cellar 中的程序反而未变。
之后 Homebrew 安装的新版本仍可能是标准构建,而链接步骤被现有普通文件挡住。仅看到新版本已下载,不等于新入口、补全文件或服务程序都已完成更新。
核对终端实际调用路径、入口文件类型、安装版本目录,以及服务配置里真正执行的路径。正在运行的进程与当前终端中的 caddy 不一定使用同一份程序。remove-package 和 upgrade 也共享这条替换路径,不宜换一个命令继续试。
定制构建放到独立产物
需要第三方模块时,可用 xcaddy 在自己的构建环境生成单独文件,固定核心与模块版本。以下是命令形式,要求已有 Go 和 xcaddy;尖括号内容必须换成项目实际模块及固定版本:
xcaddy build v2.11.7 --output ./caddy-custom \
--with <模块路径>@<固定版本>
这是针对已确认版本的构建示例,不是让所有项目升级或降级到 2.11.7。多个模块要分别提供 --with,保留原构建需要的模块和目标平台。输出路径不能指向 Homebrew 入口、Cellar 文件或正在使用的程序。
检查新产物版本、模块集合和原配置的兼容性,再按既有发布方式切换服务。不要一边让 Homebrew 管理同一个文件,一边让自更新命令覆盖它;这会留下两套互相冲突的升级状态。
已有冲突先保留定制程序
恢复 Homebrew 链接前,先保存已确认造成冲突的普通文件及其模块信息,并确认服务当前是否依赖它。之后把这个冲突文件移出包管理器入口,由 Homebrew 恢复链接;不要直接删除仍可能需要的定制程序,也不要使用强制覆盖来掩盖其他冲突。
在已确认安装的 keg 与入口对应、冲突已处理后,可以执行 brew link caddy,再核对入口确实恢复为指向预期版本的软链。恢复后的标准程序未必含原第三方模块,不能据此直接重启依赖模块的服务。
验收分别看包管理器链接和服务实际产物:链接步骤成功,服务使用的模块与配置也一致,才算恢复了这次安装状态。回退则使用此前保留的相容程序与服务路径,而不是把软链冲突文件删掉后再猜原来装了哪些模块。


