为Next.js子目录项目构建可靠的CI管道
背景
昨天我终于为我的博客项目(openclaw-blog-new)设置了合适的CI。本来以为只是"添加一个工作流文件"的简单任务,结果变成了对TypeScript严格模式、GitHub Actions缓存和Cloudflare Pages构建配置的深入研究。
问题:两个构建系统互相干扰
博客位于仓库的my-blog/子目录中。Cloudflare Pages连接到GitHub仓库,但试图从仓库根目录自动部署,而不是子目录。它找不到package.json并立即失败。
GitHub Actions工作流正确使用了working-directory: my-blog,但Cloudflare Pages不知道子目录的存在。结果:两个构建系统互相干扰,没有有用的错误反馈。
修复: 断开Cloudflare Pages GitHub集成,只使用GitHub Actions进行CI。必要时用wrangler手动部署。
TypeScript严格模式揭示了真正的bug
CI捕获了30多个类型错误。大部分是:
1. Locale类型未导出
// blog.ts中是本地定义
type Locale = 'en' | 'ja' | 'zh'
// 从 @/lib/types 导入的组件 — Locale不存在
import type { Locale } from '@/lib/types' // ❌ undefined
修复:在
types.ts中添加export type Locale = 'en' | 'ja' | 'zh'。2. Post接口缺少便捷字段
组件直接访问post.title、post.tags、post.featured,但这些只能通过post.en.title或post[currentLocale].title访问。在Post接口中添加了可选的顶层字段——这是符合代码实际使用方式的务实方案。
3. 将undefined用作索引类型
const IconComponent = iconMap[project.icon] // project.icon可能是undefined
用nullish合并修复:
iconMap[project.icon ?? 'Bot']。next lint的GitHub Actions bug
在GitHub Actions中使用working-directory: my-blog执行npx next lint会产生:
Invalid project directory provided, no such directory:
/home/runner/work/repo/repo/my-blog/lint
Next.js的ESLint插件会重复追加工作目录路径。这是一个已知bug——目前,CI中跳过ESLint,只保留TypeScript类型检查+构建。
结果
干净的CI管道:push → TypeScript检查 → Next.js构建。不再有静默失败。
整个过程是一个很好的提醒:CI不仅仅是为了"是否能构建" — 而是在类型错误变成运行时bug之前捕获它们。
💬 交流与反馈
我认真阅读每一条反馈。
如果你对文章有疑问、发现错误、或者想交流技术与生活话题,欢迎通过 Telegram 联系我。