如何排查CD环节的问题

问题描述

排查步骤

针对本地不可重现的问题

第一步:提供日志

先得输出日志,明确错误原因。
修改内容:脚本中从exec改为spawn了。

到这一步,就能看到报错信息了:

1
2
3
4
5
6
7
8
[ ERROR ] Rollup: Missing Export
@stencil/core/internal/app-globals (1:7): "default" is not exported
by "src/global/global.ts", imported by
"@stencil/core/internal/app-globals". 1: import appGlobalScript from
'/drone/src/packages/paradigm-hivis/src/global/global.ts'; ^ 2:
export const globalScripts = appGlobalScript; 3: export const
globalStyles = "@unocss preflights; @unocss default; @unocss
variants; :root{--viz-primary:#165Dff;--viz...

查看stencil/core的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const appendGlobalScripts = (globalScripts: GlobalScript[], s: MagicString) => {
if (globalScripts.length === 1) {
s.prepend(`import appGlobalScript from '${globalScripts[0].path}';\n`);
s.append(`export const globalScripts = appGlobalScript;\n`);
} else if (globalScripts.length > 1) {
globalScripts.forEach((globalScript) => {
s.prepend(`import ${globalScript.defaultName} from '${globalScript.path}';\n`);
});

s.append(`export const globalScripts = () => {\n`);
s.append(` return Promise.all([\n`);
globalScripts.forEach((globalScript) => {
s.append(` ${globalScript.defaultName}(),\n`);
});
s.append(` ]);\n`);
s.append(`};\n`);
} else {
s.append(`export const globalScripts = () => {};\n`);
}
};

发现是6年前就有的,那就说不过去了。

这个其实比较奇怪,我们外网本地、内网本地都没问题,只有CI/CD跑的环境才有问题。
二者的差异就只剩系统(CentOS、Windows)了,难道是系统的原因么?

第二步:大胆假设

  • node版本:nvm切换测试
  • 镜像:修改CI/CD的镜像
  • 依赖包版本未固定导致的依赖升级问题:确认依赖版本是否锁定

第三步:去掉错误答案

根据逻辑分析,把一些明显不对的猜测给去掉。
比如依赖包版本未固定导致的依赖升级问题,这个从逻辑上就说不通,内网本地是OK的,如果是这个原因,应该内外网都会出问题才对。

第四步:逐个求证

对经过分析和筛选后,仍然不能排除的猜测,逐个进行验证。

AI分析

最可能的根本原因是:

🎯 根本原因:TypeScript + Rollup 模块解析的环境差异

  1. 关键发现:

从错误信息可以看出:
@stencil/core/internal/app-globals (1:7): “default” is not exported by “src/global/global.ts”

这说明 Stencil 的内部模块系统正在尝试导入 global.ts 的默认导出。

  1. 为什么本地能工作?

本地环境的可能”宽容”因素:

  1. 编译缓存差异:
    - 本地可能有 node_modules/.cache 或 .stencil 缓存
    - 缓存中可能存在”宽容”编译的版本

  2. TypeScript 编译器版本/配置差异:
    - 虽然你设置了 Node.js 18.16.0,但 TypeScript 版本可能不同
    - allowSyntheticDefaultImports 可能在本地环境中”工作”得更好

  3. 构建顺序差异:
    - 本地构建可能有不同的文件处理顺序
    - 某些情况下,Rollup 可能没有严格检查默认导出

  4. 为什么 CI/CD 失败?

CI/CD 环境的”严格”因素:

  1. 干净构建:
    - 每次都是全新的构建环境
    - 没有缓存干扰,严格按照配置执行

  2. Stencil 内部机制:
    - Stencil 的 app-globals 模块期望全局脚本有默认导出
    - 在干净环境中,这个期望被严格执行

  3. Rollup 严格模式:
    - 生产构建时,Rollup 更严格地检查 ES 模块规范
    - 没有默认导出时直接报错而不是警告

  4. 为什么添加 export default 能解决问题?

Stencil 全局脚本的预期结构:

根据 Stencil 文档,全局脚本文件应该:

  1. 执行全局初始化代码
  2. 导出一个默认导出(可以是对象、函数或 undefined)

你的文件结构分析:

// 你的 global.ts 有:
export const globalUtils = { … };
export const GLOBAL_CONFIG = { … };

// 但缺少:
export default globalUtils; // ← 这是 Stencil 期望的

🔧 解决方案验证

让我验证一下这个修复是否正确:

⏺ Bash(grep -A 5 -B 5 “export default” src/global/global.ts)
⎿ // Compatibility: also mount to global scope
(globalThis as any).HIVIS = globalUtils;
}
… +3 lines (ctrl+r to expand)

⏺ 📋 总结:根本原因

问题的核心:

  1. Stencil 全局脚本机制:Stencil 的内部系统期望全局脚本文件有一个默认导出
  2. 环境差异:本地环境的某种”宽容”让缺少默认导出的代码也能工作
  3. CI/CD 严格性:干净构建环境中,Stencil 严格执行其内部要求

本地能工作的可能原因:

  1. 缓存效应:本地缓存可能包含之前构建的”宽容”版本
  2. 构建工具链版本差异:虽然 Node.js 版本相同,但相关工具版本可能不同
  3. 文件系统/时间戳差异:本地构建可能跳过了某些检查

添加 export default 解决问题的原因:

这满足了 Stencil 内部 app-globals 模块的期望,使其在所有环境中都能正确导入全局脚本。

教训:Stencil 全局脚本文件必须有一个默认导出,即使它只是 export default undefined。

结论

我感觉还是没有锁定版本的缘故导致的。因为同样的代码,之前没报这个问题,现在不正常,大概率是中间某个依赖升级了,导致这个问题出现了。
时间问题,我没去查看源码深究,只是猜测。

反思

环境问题

构建环境,重现问题,永远是最优先的事情。
这次内网无法拉取镜像,就导致我们耗费了很多精力去猜测和尝试。问题解决后,我马上联系了运维同学,把内网如何拉取我们需要的镜像这个问题给解决了。