目录名末尾多了个空格,Windows CI 直接 exit 128
某天往仓库里提交了一批静态资产,里面有按公司/机构名命名的子目录,名字是从 Word 文档直接粘过来的。在 macOS 本地一切正常,push 上去之后 Linux runner 也绿了。唯独 Windows runner 一直卡在第一步 Run actions/checkout@v4,连 setup-node 都没跑到就红叉退出:
invalid path 'asset/file/template/company/某科技有限公司 /合同文本-空白A4.doc'
The process 'C:\Program Files\Git\bin\git.exe' failed with exit code 128
把报错 copy 出来仔细看,才发现 某科技有限公司 后面跟着一个空格,再后面才是 /。
现象
- macOS / Linux clone 同一个 commit,完全没问题。
- Windows runner(
windows-latest)在actions/checkout@v4报invalid path,exit code 128。 - 错误路径里的目录名紧跟着一个不易察觉的空格字符,CJK 字符在等宽字体下尤其难看出来。
- 用
find可以精确定位:
find . -type d -name "* " -not -path "./.git/*"
# 有输出 = 有污染根因
Win32 文件系统 API(CreateFile / WriteFile 等)有一条历史遗留规则:文件名和目录名末尾的空格或英文句点会被静默截断。微软文档明确写道:
"Do not end a file or directory name with a space or a period."
git.exe 在 Windows 下调用 WriteFile() 写出路径 某科技有限公司 / 时,OS 把它截断成 某科技有限公司/,导致 git 的 object checksum 与实际写出路径不一致,校验失败,exit 128。
macOS(APFS/HFS+)和 Linux(ext4 等)不做这个截断,所以本地从无感知。
从 Word/PDF/邮件粘贴中文名称时,尾部常带有一个 U+0020(普通空格)或 U+3000(全角空格)。Finder、IDE、终端等宽字体下都看不出来,ls 也不会高亮,只有 find . -name "* " 才能抓到。偏偏 Win32 API 对这个字符零容忍,导致跨平台 CI 的奇异双标现象。
正解
用 git mv 两步走彻底清除尾空格。直接 git mv "a " "a" 在 macOS(大小写不敏感文件系统)上会报"源和目标相同",所以必须借一个临时名中转:
# 第一步:定位所有污染目录(从仓库根执行)
find . -type d -name "* " -not -path "./.git/*"
find . -type f -name "* " -not -path "./.git/*"
# 第二步:每个污染项两步改名
cd path/to/parent
git mv "某科技有限公司 " "某科技有限公司__rename_tmp"
git mv "某科技有限公司__rename_tmp" "某科技有限公司"
# 第三步:提交推送
git commit -am "fix: strip trailing space from dir names · Windows NTFS checkout"
git push多个目录批量处理:
cd path/to/parent
for d in "公司甲 " "公司乙 "; do
clean="${d% }"
git mv "$d" "${clean}__rename_tmp"
git mv "${clean}__rename_tmp" "$clean"
done
git commit -am "fix: strip trailing space · Windows CI"
git push修复后验证:
find . -type d -name "* " -not -path "./.git/*"
# 应无输出这是永久修复。git mv 不改变 blob 内容,rename detection 显示 100% similarity,reviewer diff 没有噪音。macOS/Linux checkout 不受影响——去掉一个无语义的尾空格对它们是零 breaking change。
看到 invalid path 'asset/...' 很容易想到"把 asset/ 排除出 sparse-checkout 不就行了"。但 actions/checkout@v4 的 sparse-checkout pattern 解析逻辑和原生 git 有出入,/* + !/asset 经常无声失效,asset 目录照样被写出来,checkout 照样挂。更重要的是:repo 里的污染路径仍然存在,Windows 开发者拉取、未来新增的 Windows CI matrix 都会撞同一堵墙。治标不治本。
一句话外卖
跨平台 CI 的 Windows exit 128:先查路径里有没有尾空格或尾句点,
find . -name "* "比肉眼可靠一万倍。