Skip to content

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.ts
  • nil.config.js
  • nil.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 原生支持
SSHnode-ssh + ssh2高级 API,兼容 Bun(fallback)
配置加载import()Bun 支持 .ts 动态导入
子进程execa跨平台、Promise 风格
Gitsimple-git轻量、易用
COScos-nodejs-sdk-v5腾讯云官方 SDK
Dockerdockerode功能完整

6. 安全与密钥

  • 所有密钥通过 环境变量 注入
  • 配置文件中只写非敏感信息(bucket、region、host)
  • 推荐 .env + dotenv 管理

7. 安装与使用

7.1 安装

bash
bun add nil-cli

7.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 deploy

8. 未来扩展

功能说明
nil deploy --target docker命令行指定目标
多环境配置--config nil.config.prod.ts
插件系统nil-plugin-ali-oss
部署状态检查COS 是否可写、Docker 登录状态
Web UI可视化部署面板

9. 成功标准

  • [x] 支持 initdeploy 命令
  • [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',
};