Skip to content

Commit 173f4e0

Browse files
killaguclaude
andcommitted
docs: add manifest documentation and remove Node.js 20 from CI
- Add startup manifest docs (zh-CN and English) under core/ covering: usage, CLI commands, invalidation, deployment tips - Add manifest to sidebar navigation - Remove Node.js 20 from CI test matrix (LTS ending soon) - Simplify example test condition (no longer need Node 20 check) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 98cc593 commit 173f4e0

File tree

4 files changed

+312
-2
lines changed

4 files changed

+312
-2
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ jobs:
6262
fail-fast: false
6363
matrix:
6464
os: ['ubuntu-latest', 'macos-latest', 'windows-latest']
65-
node: ['20', '22', '24']
65+
node: ['22', '24']
6666

6767
name: Test (${{ matrix.os }}, ${{ matrix.node }})
6868
runs-on: ${{ matrix.os }}
@@ -170,7 +170,7 @@ jobs:
170170
run: pnpm run ci
171171

172172
- name: Run example tests
173-
if: ${{ matrix.node != '20' && matrix.os != 'windows-latest' }}
173+
if: ${{ matrix.os != 'windows-latest' }}
174174
run: |
175175
pnpm run example:test:all
176176

site/.vitepress/config.mts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ function sidebarCore(): DefaultTheme.SidebarItem[] {
312312
{ text: 'Internationalization', link: 'i18n' },
313313
{ text: 'View Template', link: 'view' },
314314
{ text: 'Security', link: 'security' },
315+
{ text: 'Startup Manifest', link: 'manifest' },
315316
],
316317
},
317318
];
@@ -422,6 +423,7 @@ function sidebarCoreZhCN(): DefaultTheme.SidebarItem[] {
422423
{ text: '国际化', link: 'i18n' },
423424
{ text: '模板渲染', link: 'view' },
424425
{ text: '安全', link: 'security' },
426+
{ text: '启动清单', link: 'manifest' },
425427
],
426428
},
427429
];

site/docs/core/manifest.md

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# Startup Manifest
2+
3+
Egg provides a startup manifest mechanism that caches file discovery and module resolution results to accelerate application cold starts.
4+
5+
## How It Works
6+
7+
Every time an application starts, the framework performs extensive filesystem operations:
8+
9+
- **Module resolution**: Hundreds of `fs.existsSync` calls probing `.ts`, `.js`, `.mjs` extensions
10+
- **File discovery**: Multiple `globby.sync` scans across plugin, config, and extension directories
11+
- **tegg module scanning**: Traversing module directories and `import()`-ing decorator files to collect metadata
12+
13+
The manifest mechanism collects these results on the first startup and writes them to `.egg/manifest.json`. Subsequent startups read from this cache, skipping redundant file I/O.
14+
15+
## Performance Improvement
16+
17+
Measured on cnpmcore in a container cold-start scenario (no filesystem page cache):
18+
19+
| Metric | No Manifest | With Manifest | Improvement |
20+
| ----------- | ----------- | ------------- | ----------- |
21+
| App Start | ~980ms | ~780ms | **~20%** |
22+
| Load Files | ~660ms | ~490ms | **~26%** |
23+
| Load app.js | ~280ms | ~150ms | **~46%** |
24+
25+
> Note: In local development, the OS page cache makes file I/O nearly zero-cost, so the improvement is negligible. The manifest primarily optimizes container cold starts and CI/CD environments without warm caches.
26+
27+
## Usage
28+
29+
### CLI Management (Recommended)
30+
31+
`egg-bin` provides a `manifest` command to manage the startup manifest:
32+
33+
#### Generate
34+
35+
```bash
36+
# Generate for production
37+
$ egg-bin manifest generate --env=prod
38+
39+
# Specify environment and scope
40+
$ egg-bin manifest generate --env=prod --scope=aliyun
41+
42+
# Specify framework
43+
$ egg-bin manifest generate --env=prod --framework=yadan
44+
```
45+
46+
The generation process boots the app in `metadataOnly` mode (skipping lifecycle hooks, only collecting metadata), then writes the results to `.egg/manifest.json`.
47+
48+
#### Validate
49+
50+
```bash
51+
$ egg-bin manifest validate --env=prod
52+
```
53+
54+
Example output:
55+
56+
```
57+
[manifest] Manifest is valid
58+
[manifest] version: 1
59+
[manifest] generatedAt: 2026-03-29T12:13:18.039Z
60+
[manifest] serverEnv: prod
61+
[manifest] serverScope:
62+
[manifest] resolveCache entries: 416
63+
[manifest] fileDiscovery entries: 31
64+
[manifest] extension entries: 1
65+
```
66+
67+
If the manifest is invalid or missing, the command exits with a non-zero code.
68+
69+
#### Clean
70+
71+
```bash
72+
$ egg-bin manifest clean
73+
```
74+
75+
### Automatic Generation
76+
77+
After a normal startup, the framework automatically generates a manifest during the `ready` phase (via `dumpManifest`). On the next startup, if the manifest is valid, it is automatically used.
78+
79+
## Invalidation
80+
81+
The manifest includes fingerprint data and is automatically invalidated when:
82+
83+
- **Lockfile changes**: `pnpm-lock.yaml`, `package-lock.json`, or `yarn.lock` mtime or size changes
84+
- **Config directory changes**: Files in `config/` change (MD5 fingerprint)
85+
- **Environment mismatch**: `serverEnv` or `serverScope` differs from the manifest
86+
- **TypeScript state change**: `EGG_TYPESCRIPT` enabled/disabled state changes
87+
- **Version mismatch**: Manifest format version differs
88+
89+
When the manifest is invalid, the framework falls back to normal file discovery — startup is never blocked.
90+
91+
## Environment Variables
92+
93+
| Variable | Description | Default |
94+
| -------------- | ---------------------------- | ----------------------------------------------------- |
95+
| `EGG_MANIFEST` | Enable manifest in local env | `false` (manifest not loaded in local env by default) |
96+
97+
> Local development (`serverEnv=local`) does not load the manifest by default, since files change frequently. Set `EGG_MANIFEST=true` to force-enable.
98+
99+
## Deployment Recommendations
100+
101+
### Container Deployment
102+
103+
Generate the manifest in your Dockerfile after building:
104+
105+
```dockerfile
106+
# Install dependencies and build
107+
RUN npm install --production
108+
RUN npm run build
109+
110+
# Generate startup manifest
111+
RUN npx egg-bin manifest generate --env=prod
112+
113+
# Start the app (manifest is used automatically)
114+
CMD ["npm", "start"]
115+
```
116+
117+
### CI/CD Pipelines
118+
119+
Generate the manifest during the build stage and deploy it with the artifact:
120+
121+
```bash
122+
# Build
123+
npm run build
124+
125+
# Generate manifest
126+
npx egg-bin manifest generate --env=prod
127+
128+
# Validate manifest
129+
npx egg-bin manifest validate --env=prod
130+
131+
# Package (includes .egg/manifest.json)
132+
tar -zcvf release.tgz .
133+
```
134+
135+
## Important Notes
136+
137+
1. **Environment-bound**: The manifest is bound to `serverEnv` and `serverScope` (deployment type, e.g. `aliyun`). Different environments or deployment types require separate manifests.
138+
2. **Regenerate after dependency changes**: Installing or updating dependencies changes the lockfile, which automatically invalidates the manifest.
139+
3. **`.egg` directory**: The manifest is stored at `.egg/manifest.json`. Consider adding `.egg/` to `.gitignore`.
140+
4. **Safe fallback**: A missing or invalid manifest causes the framework to fall back to normal discovery — startup is never broken.
141+
5. **metadataOnly mode**: `manifest generate` does not run the full application lifecycle — no database connections or external services are started.

site/docs/zh-CN/core/manifest.md

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# 启动清单(Startup Manifest)
2+
3+
Egg 提供了启动清单(Manifest)机制,通过缓存文件发现和模块解析结果来加速应用的冷启动。
4+
5+
## 原理
6+
7+
应用每次启动时,框架需要执行大量文件系统操作:
8+
9+
- **模块解析**:数百次 `fs.existsSync` 调用,探测 `.ts``.js``.mjs` 等后缀
10+
- **文件发现**:多次 `globby.sync` 扫描插件、配置、扩展等目录
11+
- **tegg 模块扫描**:遍历模块目录,`import()` 装饰器文件收集元数据
12+
13+
Manifest 机制在首次启动时收集这些结果,写入 `.egg/manifest.json`。后续启动直接读取缓存,跳过重复的文件 I/O。
14+
15+
## 性能提升
16+
17+
在容器冷启动场景下(无文件系统缓存),以 cnpmcore 项目实测:
18+
19+
| 指标 | 无 Manifest | 有 Manifest | 提升 |
20+
| ----------- | ----------- | ----------- | -------- |
21+
| 应用启动 | ~980ms | ~780ms | **~20%** |
22+
| 文件加载 | ~660ms | ~490ms | **~26%** |
23+
| 加载 app.js | ~280ms | ~150ms | **~46%** |
24+
25+
> 注意:在本地开发环境中,操作系统的页面缓存(page cache)会使文件 I/O 接近零开销,因此提升不明显。Manifest 主要优化的是容器冷启动、CI/CD 等无缓存场景。
26+
27+
## 使用方式
28+
29+
### 通过 CLI 管理(推荐)
30+
31+
`egg-bin` 提供了 `manifest` 命令来管理启动清单:
32+
33+
#### 生成清单
34+
35+
```bash
36+
# 为生产环境生成
37+
$ egg-bin manifest generate --env=prod
38+
39+
# 指定环境和作用域
40+
$ egg-bin manifest generate --env=prod --scope=aliyun
41+
42+
# 指定框架
43+
$ egg-bin manifest generate --env=prod --framework=yadan
44+
```
45+
46+
生成过程会以 `metadataOnly` 模式启动应用(跳过生命周期钩子,只收集元数据),然后将结果写入 `.egg/manifest.json`
47+
48+
#### 验证清单
49+
50+
```bash
51+
$ egg-bin manifest validate --env=prod
52+
```
53+
54+
输出示例:
55+
56+
```
57+
[manifest] Manifest is valid
58+
[manifest] version: 1
59+
[manifest] generatedAt: 2026-03-29T12:13:18.039Z
60+
[manifest] serverEnv: prod
61+
[manifest] serverScope:
62+
[manifest] resolveCache entries: 416
63+
[manifest] fileDiscovery entries: 31
64+
[manifest] extension entries: 1
65+
```
66+
67+
如果清单无效或不存在,命令将以非零退出码退出。
68+
69+
#### 清理清单
70+
71+
```bash
72+
$ egg-bin manifest clean
73+
```
74+
75+
### 自动生成
76+
77+
应用正常启动后,框架会在 `ready` 阶段自动生成清单(通过 `dumpManifest`)。下次启动时如果清单有效,则自动使用。
78+
79+
## 清单失效机制
80+
81+
清单包含指纹信息,以下情况会自动失效:
82+
83+
- **锁文件变更**`pnpm-lock.yaml``package-lock.json``yarn.lock` 的修改时间或大小变化
84+
- **配置目录变更**`config/` 目录下的文件发生变化(基于 MD5 指纹)
85+
- **环境不匹配**`serverEnv``serverScope` 与清单中记录的不一致
86+
- **TypeScript 状态变更**`EGG_TYPESCRIPT` 启用/禁用状态发生变化
87+
- **版本不匹配**:清单格式版本号不一致
88+
89+
清单失效时,框架会回退到正常的文件发现流程,不会影响启动。
90+
91+
## 环境变量
92+
93+
| 变量 | 说明 | 默认值 |
94+
| -------------- | ------------------ | --------------------------------- |
95+
| `EGG_MANIFEST` | 在本地环境启用清单 | `false`(本地环境默认不加载清单) |
96+
97+
> 本地开发环境(`serverEnv=local`)默认不加载清单,因为开发时文件频繁变更,清单容易失效。设置 `EGG_MANIFEST=true` 可强制启用。
98+
99+
## 部署建议
100+
101+
### 容器化部署
102+
103+
在 Dockerfile 中,构建完成后生成清单:
104+
105+
```dockerfile
106+
# 安装依赖并构建
107+
RUN npm install --production
108+
RUN npm run build
109+
110+
# 生成启动清单
111+
RUN npx egg-bin manifest generate --env=prod
112+
113+
# 启动应用(将自动使用清单)
114+
CMD ["npm", "start"]
115+
```
116+
117+
### CI/CD 流水线
118+
119+
在构建阶段生成清单,随制品一起部署:
120+
121+
```bash
122+
# 构建
123+
npm run build
124+
125+
# 生成清单
126+
npx egg-bin manifest generate --env=prod
127+
128+
# 验证清单
129+
npx egg-bin manifest validate --env=prod
130+
131+
# 打包(包含 .egg/manifest.json)
132+
tar -zcvf release.tgz .
133+
```
134+
135+
## 注意事项
136+
137+
1. **清单与环境绑定**:清单绑定了 `serverEnv``serverScope`(部署类型,如 `aliyun`),不同环境或部署类型需要分别生成
138+
2. **依赖变更后需重新生成**:安装或更新依赖后,锁文件变更会自动使清单失效
139+
3. **`.egg` 目录**:清单存储在 `.egg/manifest.json`,建议将 `.egg/` 加入 `.gitignore`
140+
4. **回退安全**:清单缺失或无效时,框架会自动回退到正常流程,不会导致启动失败
141+
5. **metadataOnly 模式**`manifest generate` 不会启动完整的应用生命周期,不会连接数据库或外部服务
142+
143+
## 清单结构
144+
145+
```json
146+
{
147+
"version": 1,
148+
"generatedAt": "2026-03-29T12:13:18.039Z",
149+
"invalidation": {
150+
"lockfileFingerprint": "pnpm-lock.yaml:1711234567890:12345",
151+
"configFingerprint": "a1b2c3d4e5f6...",
152+
"serverEnv": "prod",
153+
"serverScope": "",
154+
"typescriptEnabled": true
155+
},
156+
"extensions": {
157+
"tegg": { ... }
158+
},
159+
"resolveCache": {
160+
"config/plugin": "config/plugin.ts",
161+
"config/plugin.default": null
162+
},
163+
"fileDiscovery": {
164+
"app/middleware": ["trace.ts", "auth.ts"]
165+
}
166+
}
167+
```

0 commit comments

Comments
 (0)