Linux下zip命令行为变更导致CI部署失败排查与解决
背景概述
6月13日生产服务器部署失败,6月10日部署正常,构建命令与CI配置未做任何变更。使用环境为GitLab CI + node:lts(v18)Docker镜像,通过zip命令打包构建包后上传至AWS ElasticBeanstalk。
经排查,部署失败原因是根目录隐藏文件.npmrc未处于zip包根目录,而是被嵌套进子文件夹。原CI执行的zip命令如下:
cd customer-app zip ./customer-app-final.zip -r * .[^.]*
此前该命令能正常打包根目录所有文件,近期却递归打包了上级customer-app目录,导致包结构异常。将命令改为以下形式后问题修复:
cd customer-app zip ./customer-app-final.zip -r .
疑问解答
1. 原zip命令为何在CI中失效?
原命令依赖shell的**文件名扩展(globbing)**来匹配文件,当node:lts镜像中的shell或zip版本变更后,.[^.]*的匹配逻辑发生了变化:原本该模式只匹配当前目录下除.和..之外的隐藏文件,但新环境中可能错误匹配到了..(上级目录标识),导致zip递归打包时将上级目录的customer-app本身作为子目录纳入包中,最终破坏了预期的目录结构。
另外,部分bash版本在特定配置下,*会匹配隐藏文件,此时*与.[^.]*的组合可能出现重复匹配或路径解析异常,进一步触发zip的递归逻辑错误。
2. 该命令为何正常运行两年?
过去两年使用的node:lts镜像中,默认shell(bash)的glob配置、zip版本始终保持稳定,.[^.]*的扩展行为严格限制在当前目录的非./..隐藏文件范围内,zip处理这些路径时只会递归打包当前目录内容,不会涉及上级目录。期间镜像未出现影响该逻辑的更新,因此命令一直正常工作。
3. 原命令为何在Mac(Ventura)上仍可用?
Mac Ventura默认使用zsh作为系统shell,zsh的glob匹配逻辑与bash存在差异:
- zsh中
.[^.]*默认不会匹配..,严格只匹配当前目录下的隐藏文件(排除.); - 同时Mac自带的zip版本与node:lts镜像中的zip版本行为不同,对路径解析更严格,不会将当前目录的上级路径纳入打包范围。
因此在Mac环境下,原命令的glob扩展和zip执行逻辑均符合预期,仍能正常工作。
4. node:lts Docker镜像发生了哪些变更?
部署失败前后使用的node:lts(v18)镜像存在以下关键差异:
- 基础镜像升级:大概率从Debian旧版本(如bullseye)切换到了新版本(如bookworm),导致系统默认shell配置、工具链(包括zip)版本更新;
- shell行为调整:bash版本升级后,glob扩展的默认选项(如
globstar、dotglob)发生变化,直接影响*和.[^.]*的匹配结果; - zip版本更新:新版本zip对路径解析、递归打包的逻辑做了优化或调整,对模糊匹配的路径处理方式改变,导致原本正常的命令出现异常。
内容的提问来源于stack exchange,提问作者Shashank Agrawal

