nil-cli 总技术文档(完整版)
版本:v1.1
目标: 构建一个可扩展、配置化、支持多部署目标的现代化 CLI 工具
1. 背景与目标
1.1 背景
前端项目部署流程碎片化,缺乏统一工具。现有方案:
- 手写 shell 脚本
- 硬编码部署逻辑
- 缺乏类型安全
- 不支持现代运行时(如 Bun)
1.2 目标
构建 nil-cli,实现:
- 命令式交互:
nil init,nil deploy - 配置驱动:
nil.config.ts定义部署策略 - 多部署目标:COS / Docker / SSH / OSS / S3
- Bun 原生支持:快启动、TS 直接运行
- 模块化架构:易于扩展和维护
- 类型安全:TypeScript + IDE 提示
2. 系统架构
nil-cli/
├── bin/
│ └── nil # 可执行文件
├── src/
│ ├── types/
│ │ └── index.ts # 全局类型
│ ├── utils/
│ │ ├── registerCommands.ts # 命令注册器
│ │ └── loadConfig.ts # 配置加载器
│ ├── commands/
│ │ ├── init.ts # 初始化项目
│ │ └── deploy.ts # 部署命令
│ ├── ssh/ # SSH 封装
│ └── deploy/
│ ├── tasks/
│ │ ├── git.ts
│ │ ├── build.ts
│ │ ├── cos.ts
│ │ ├── docker.ts
│ │ └── ssh.ts
│ └── index.ts # 部署主流程
└── docs/ # 文档3. CLI 命令系统
3.1 命令架构
基于 yargs 实现命令注册系统,支持:
- 命令发现
- 参数解析
- 自动 help
- 类型安全
3.2 核心命令
| 命令 | 描述 |
|---|---|
nil init | 初始化新项目(创建目录、package.json) |
nil deploy | 执行部署流程(拉取、构建、上传) |
3.3 命令注册机制
src/utils/registerCommands.ts
ts
import type { Argv } from 'yargs';
import type { YargsCommand } from '../types';
import { commands } from '../commands';
export const registerCommands = (yargs: Argv) => {
commands.forEach((cmd: YargsCommand) => {
yargs.command(cmd.command, cmd.describe, cmd.builder, cmd.handler);
});
return yargs;
};src/types/index.ts
ts
import type { Argv } from 'yargs';
export type YargsCommand = {
command: string;
describe: string;
builder?: (yargs: Argv) => Argv;
handler: (argv: any) => Promise<void> | void;
};src/commands/index.ts
ts
import init from './init';
import deploy from './deploy';
export const commands = [init, deploy] as const;3.4 命令实现
src/commands/init.ts
ts
import type { YargsCommand } from '../types';
const init: YargsCommand = {
command: 'init',
describe: '初始化一个新项目',
builder: (yargs) => yargs.option('name', { type: 'string', default: 'my-nil-project' }),
handler: async (argv) => {
console.log(`🚀 正在初始化项目: ${argv.name}`);
// 实际创建文件逻辑...
console.log('✅ 项目初始化完成!');
},
};
export default init;src/commands/deploy.ts
ts
import type { YargsCommand } from '../types';
import { runDeploy } from '../deploy/index';
const deploy: YargsCommand = {
command: 'deploy',
describe: '部署当前项目',
builder: (yargs) =>
yargs.option('cwd', { type: 'string', default: '.', describe: '项目根目录' }),
handler: async (argv) => {
await runDeploy(argv.cwd);
},
};
export default deploy;4. 部署系统(完整集成)
4.1 配置系统
支持格式
nil.config.tsnil.config.jsnil.config.json
配置结构
ts
interface NilConfig {
deploy: {
type: 'cos' | 'docker' | 'ssh' | 'oss' | 's3';
cos?: { bucket: string; region: string; prefix?: string };
docker?: { registry: string; imageName: string; tag?: string };
ssh?: { host: string; port?: number; username: string };
remoteAppPath: string;
localDist: string;
};
buildCommand?: string;
gitBranch?: string;
}加载器
ts
// src/utils/loadConfig.ts
export async function loadConfig(cwd: string): Promise<NilConfig> {
for (const file of ['nil.config.ts', 'nil.config.js', 'nil.config.json']) {
const path = join(cwd, file);
if (existsSync(path)) {
const mod = await import(`file://${resolve(path)}`);
return (mod.default || mod) as NilConfig;
}
}
throw new Error('配置文件未找到');
}4.2 部署主流程
src/deploy/index.ts
ts
import { loadConfig } from '../utils/loadConfig';
import { NilConfig } from '../types';
import { gitPull } from './tasks/git';
import { execa } from 'execa';
import { deployToCos } from './tasks/cos';
import { buildAndPushDocker } from './tasks/docker';
export async function runDeploy(cwd: string) {
const config = await loadConfig(cwd);
// 拉取代码
await gitPull(cwd, config.gitBranch ?? 'master');
// 构建
const buildCmd = config.buildCommand ?? 'bun run docs:build';
await execa(buildCmd, { stdio: 'inherit', cwd });
// 分发
switch (config.deploy.type) {
case 'cos':
await deployToCos({
localDist: resolve(cwd, config.deploy.localDist),
cosConfig: {
...config.deploy.cos!,
secretId: process.env.TENCENT_CLOUD_SECRET_ID!,
secretKey: process.env.TENCENT_CLOUD_SECRET_KEY!,
},
});
break;
case 'docker':
await buildAndPushDocker({
localDist: resolve(cwd, config.deploy.localDist),
dockerConfig: config.deploy.docker!,
});
break;
case 'ssh':
// 实现 SSH 上传
break;
default:
throw new Error(`不支持的部署类型: ${config.deploy.type}`);
}
console.log('🎉 部署成功!');
}5. 技术栈与依赖
| 类别 | 技术 | 理由 |
|---|---|---|
| CLI 框架 | yargs | 成熟、类型安全、自动 help |
| 运行时 | Bun | 快速启动、TS 原生支持 |
| SSH | node-ssh + ssh2 | 高级 API,兼容 Bun(fallback) |
| 配置加载 | import() | Bun 支持 .ts 动态导入 |
| 子进程 | execa | 跨平台、Promise 风格 |
| Git | simple-git | 轻量、易用 |
| COS | cos-nodejs-sdk-v5 | 腾讯云官方 SDK |
| Docker | dockerode | 功能完整 |
6. 安全与密钥
- 所有密钥通过 环境变量 注入
- 配置文件中只写非敏感信息(bucket、region、host)
- 推荐
.env+dotenv管理
7. 安装与使用
7.1 安装
bash
bun add nil-cli7.2 配置
ts
// nil.config.ts
export default {
deploy: {
type: 'cos',
cos: { bucket: 'my-docs', region: 'ap-shanghai' },
localDist: './docs/.vitepress/dist',
remoteAppPath: '/var/www/docs',
},
};7.3 命令
bash
# 初始化
bunx nil init
# 部署
bunx nil deploy8. 未来扩展
| 功能 | 说明 |
|---|---|
nil deploy --target docker | 命令行指定目标 |
| 多环境配置 | --config nil.config.prod.ts |
| 插件系统 | nil-plugin-ali-oss |
| 部署状态检查 | COS 是否可写、Docker 登录状态 |
| Web UI | 可视化部署面板 |
9. 成功标准
- [x] 支持
init和deploy命令 - [x] 配置文件驱动部署流程
- [x] 支持至少 3 种部署目标
- [x] 在 Bun 下稳定运行
- [x] 类型安全,IDE 自动提示
- [x] 提供清晰错误信息
附录 A:CLI 入口
bin/nil
bash
#!/usr/bin/env bun
import yargs from 'yargs';
import { registerCommands } from '../src/utils/registerCommands';
import { hideBin } from 'yargs/helpers';
registerCommands(yargs(hideBin(Bun.argv)))
.demandCommand(1, '请提供一个命令')
.help()
.parse();附录 B:配置示例
COS 部署
ts
export default {
deploy: {
type: 'cos',
cos: { bucket: 'docs-1250000000', region: 'ap-beijing', prefix: 'v1/' },
localDist: './dist',
},
buildCommand: 'bun run build',
};