# @winner-fed/winner-deploy

> 用于生成 SEE 平台部署包的工具库

## 简介

`@winner-fed/winner-deploy` 是一个专门为 SEE 平台设计的部署包生成工具。它可以将前端项目打包成符合 SEE 平台规范的部署包，支持多种应用类型和部署模式。

## 特性

- 🚀 支持外框架（bizframe）和子系统（subsystem）两种应用类型
- 📦 自动生成 SEE 平台所需的 deploy.xml 与 template 配置文件
- 🐳 支持 Docker 容器化部署（`seePackageType: 'docker'`）
- 🔧 灵活的模板变量配置系统（`variablesFunc` / `tplPath`）
- 📝 支持 Python 和 Bash 两种脚本类型
- 🔒 可选的安装后配置 JS 混淆（`enableObfuscation`）：有 node 走强混淆，无 node 回退 bash 轻量混淆
- 🎯 可自定义文件拷贝和配置生成逻辑

## 安装

```bash
npm install @winner-fed/winner-deploy
# 或
yarn add @winner-fed/winner-deploy
# 或
pnpm add @winner-fed/winner-deploy
```

## 快速开始

### 基础用法

```javascript
import { generateSeePackageZip } from '@winner-fed/winner-deploy';

generateSeePackageZip(
  {
    name: 'my-app',
    version: '1.0.0',
    description: '我的应用'
  },
  () => {
    console.log('生成完成');
  }
);
```

### 完整配置示例

```javascript
import { generateSeePackageZip } from '@winner-fed/winner-deploy';

generateSeePackageZip(
  {
    system: 'winner-front',
    type: 'subsystem',
    name: 'my-app',
    appType: 'my-app',
    appName: 'my-app',
    version: '1.0.0',
    group: 'bizframe',
    configName: 'config.local',
    outputName: 'dist',
    description: '我的应用部署包',
    seePackagePath: 'package',
    seePackageType: 'web',
    scriptsType: 'python',
    enableObfuscation: false,
    copyFiles: ['version.js'],
    tplPath: {},
    templateFunc: () => './dist/my-app/sysconfig.js',
    variablesFunc: () => [
      {
        type: 'input',
        label: '应用标题',
        name: 'APP_TITLE'
      }
    ]
  },
  () => {
    console.log('生成成功');
  }
);
```

## API 文档

### generateSeePackageZip(options, callback)

生成 SEE 平台部署包。`name`、`version` 缺失时会抛出错误；`name` 与 `seePackageName` 中的 `@`、`/` 会自动替换为 `-`。

#### 参数说明

| 参数名            | 类型                                                              | 默认值                               | 必填 | 说明                                                                                                                                                                                                                                                                                       |
| ----------------- | ----------------------------------------------------------------- | ------------------------------------ | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| system            | string                                                            | `'winner-front'`                     | 否   | 系统类型，SEE 平台分组维度                                                                                                                                                                                                                                                                 |
| type              | `'bizframe' \| 'subsystem'`                                       | `'bizframe'`                         | 否   | 应用类型：外框架或子系统                                                                                                                                                                                                                                                                   |
| name              | string                                                            | -                                    | 是   | 发布物名称（子系统标识、脚本目录名等）                                                                                                                                                                                                                                                     |
| version           | string                                                            | -                                    | 是   | 发布包版本                                                                                                                                                                                                                                                                                 |
| description       | string                                                            | -                                    | 否   | 发布包说明，写入 deploy.xml                                                                                                                                                                                                                                                                |
| appType           | string                                                            | `name`                               | 否   | 发布物类型                                                                                                                                                                                                                                                                                 |
| appName           | string                                                            | `name`                               | 否   | deploy.xml 中 `appName` 标签                                                                                                                                                                                                                                                               |
| group             | string                                                            | `'bizframe'`                         | 否   | 应用分组                                                                                                                                                                                                                                                                                   |
| configName        | string                                                            | `'config.local'`                     | 否   | 配置文件名（不含 `.js`）。为空时可构建不含配置 JS 的静态包，此时 `scriptsType` 应使用 `bash`。实际写入 template 的文件名还会按项目类型自动推断：存在 `dist/config.local.js` 时用 `config.local`，存在 `dist/sysconfig.js`（或子包路径下）时用 `sysconfig`                                  |
| outputName        | string                                                            | `'dist'`                             | 否   | 构建产物目录名（相对项目根目录）                                                                                                                                                                                                                                                           |
| seePackageName    | string                                                            | `` `${system}-${name}-${version}` `` | 否   | zip 包文件名（不含 `.zip`）                                                                                                                                                                                                                                                                |
| seePackagePath    | string                                                            | `'package'`                          | 否   | zip 输出目录，相对项目根目录；设为 `'/'` 时输出到当前目录                                                                                                                                                                                                                                  |
| seePackageType    | `'web' \| 'docker'`                                               | `'web'`                              | 否   | 发布物类型                                                                                                                                                                                                                                                                                 |
| dockerImage       | string                                                            | -                                    | 否   | Docker 镜像名，`seePackageType` 为 `'docker'` 时生效                                                                                                                                                                                                                                       |
| scriptsType       | `'python' \| 'bash'`                                              | `'python'`                           | 否   | 集群脚本语言                                                                                                                                                                                                                                                                               |
| enableObfuscation | boolean                                                           | `false`                              | 否   | 为 `true` 时打包 `scripts/{name}/js/`，并在 `afterInstall` 中对已注入变量后的配置 JS 做混淆：有 `node` 用 `confuse.js`（javascript-obfuscator 强混淆），无 `node` 用 `confuse.sh`（base64 + `TextDecoder`/`new Function` 轻量包裹，UTF-8 安全，强度较弱）；为 `false` 时不拷贝、不执行混淆 |
| tplPath           | `Record<string, string \| { path: string; transform?: boolean }>` | `{}`                                 | 否   | 写入 SEE 包 `template/`。字符串或 `{ path }`：原样拷贝已编辑模板（js/html 等）；`{ path, transform: true }`：仅 `.js` 经 genTemplate 后再写入；`deploy.xml` 覆盖自动生成。进 template 的文件会登记为 `<config>`                                                                            |
| copyFiles         | `string[]`                                                        | -                                    | 否   | 额外拷贝到包内 `source/{name}/` 的文件名列表，源路径为 `` `${outputName}/${name}/${file}` ``                                                                                                                                                                                               |
| templateFunc      | `() => string \| undefined`                                       | 见下文                               | 否   | 返回待写入 template 的配置 JS 源文件路径                                                                                                                                                                                                                                                   |
| variablesFunc     | `() => FieldAttributes[] \| undefined`                            | 见下文                               | 否   | 返回 deploy.xml 动态表单项                                                                                                                                                                                                                                                                 |

#### templateFunc 默认值

未传入时按 `type` 与 `outputName` 解析：

```javascript
// type === 'bizframe'
() => `./${outputName}/config.local.js`

// type === 'subsystem'
() => `./${outputName}/${name}/sysconfig.js`
```

#### variablesFunc 默认值

未传入时尝试加载项目根目录下 `build/package/variables.js` 的 `variables` 字段；加载失败则返回 `[]`（与 `type` 无关）。

```javascript
variablesFunc: () => {
  try {
    const { variables } = require('./build/package/variables.js');
    return variables || [];
  } catch {
    return [];
  }
};
```

#### tplPath 说明

已编辑好的 SEE 模板（含 `$!{VAR}` 等占位）默认**原样拷贝**到包内 `template/`，不再强制走 Babel 转换。

```javascript
tplPath: {
  // 已手写好的模板：直接复制到 template/
  'config.local.js': path.resolve(__dirname, './tpl/config.local.js'),
  'index.html': path.resolve(__dirname, './tpl/index.html'),
  'logo.js': path.resolve(__dirname, './tpl/logo.js'),

  // 需要从源码自动替换 variables 时再显式开启
  // 'theme.js': { path: path.resolve(__dirname, './src/theme.js'), transform: true },

  // 'deploy.xml': path.resolve(__dirname, './tpl/deploy.xml'),
};
```

- 字符串 / `{ path, transform: false }`：原样拷贝到 `template/`（js、html 等均可）。
- `{ path, transform: true }`：仅对 `.js` 调用 `genTemplate` 后再写入。
- `deploy.xml`：原样覆盖包内 deploy.xml。
- 进入 `template/` 的文件会写入 `deploy.xml` 的 `<config>`；其中 `.js` 在 `enableObfuscation` 时由 `afterInstall` 混淆。
- 若未通过 `tplPath` 放入任何配置文件，仍使用 `configName` + `templateFunc` 生成单个配置。

#### copyFiles 说明

适用于 manifest 未收录、但需要打进 SEE 包的文件：

```javascript
// 将 dist/my-app/version.js 拷贝到包内 source/my-app/version.js
copyFiles: ['version.js'];
```

#### callback 回调

第二个参数为无参函数，在 zip 写入完成且临时目录清理后调用，**不传递 error / result**：

```javascript
generateSeePackageZip(options, () => {
  console.log('打包结束');
});
```

打包过程中会先组装 `tmp/`，在回调阶段写入 `source/`（及 python 的 manifest 相关文件），再压缩为 zip。

## 模板变量配置

SEE 平台支持多种类型的模板变量控件，用于在部署时动态配置应用参数。详细说明见 [TYPE.md](./TYPE.md)。

### 支持的控件类型

- **input**: 普通文本输入框
- **editor**: 大文本编辑框
- **select**: 单选下拉框
- **switch**: 开关组件
- **smallfile**: 文件上传控件（小于 2M）
- **switchForm**: 开关表单
- **complexSelect**: 复杂单选框
- **table**: 表格控件
- **division**: 分栏折叠控件
- **password**: 密码输入框（加密存储）
- **hidden**: 隐藏输入框

### 示例配置

```javascript
const variables = [
  {
    type: 'input',
    label: '应用标题',
    tooltip: '设置应用的显示标题',
    name: 'APP_TITLE'
  },
  {
    type: 'select',
    label: '环境选择',
    options: 'dev:开发环境;test:测试环境;prod:生产环境',
    name: 'ENVIRONMENT'
  },
  {
    type: 'switch',
    label: '启用调试',
    name: 'DEBUG_MODE'
  }
];
```

## 发布包结构

默认输出路径：`{seePackagePath}/{seePackageName}.zip`（例如 `package/winner-front-my-app-1.0.0.zip`）。

```
see-package.zip
├── deploy.xml
├── template/
│   └── config.local.js | sysconfig.js | 自定义多个 *.js
├── scripts/
│   └── {name}/
│       ├── install.py | install.sh
│       ├── afterInstall.py | afterInstall.sh
│       ├── uninstall.py | uninstall.sh
│       ├── utils.py              # 仅 python
│       ├── beforeInstall.sh        # 仅 python
│       ├── conf/manifest.*.json    # 仅 python 且 dist 含 manifest 时
│       └── js/                     # 仅 enableObfuscation === true
│           ├── confuse.js          # node 强混淆入口
│           ├── confuse.sh          # 无 node 时的 bash 轻量混淆
│           └── obfuscator.js       # javascript-obfuscator 运行时
└── source/                         # 安装脚本从 tmp/source 部署到 workspace
    └── ...
```

`scriptsType === 'python'` 且存在 `manifest.{timestamp}.json` 时，按 manifest 拷贝文件到 `source/`；否则将整个 `outputName` 目录拷贝到 `source/`。`scriptsType === 'bash'` 时始终拷贝整个 `outputName` 到 `source/`。

## 使用场景

### 1. 外框架应用（bizframe）

```javascript
generateSeePackageZip(
  {
    type: 'bizframe',
    name: 'main-frame',
    version: '1.0.0',
    description: '主框架应用'
  },
  () => {}
);
```

### 2. 子系统应用（subsystem）

```javascript
generateSeePackageZip(
  {
    type: 'subsystem',
    name: 'user-management',
    version: '1.0.0',
    description: '用户管理子系统'
  },
  () => {}
);
```

子系统安装目录需指向具体前端根目录（如 `/home/hsiar/hsiar-green/html/`），详见控制台完成提示。

### 3. 开启配置 JS 混淆

混淆必须在 SEE 注入配置变量之后执行，因此放在 `afterInstall`，不能提前到打包阶段。

```javascript
generateSeePackageZip(
  {
    name: 'my-app',
    version: '1.0.0',
    scriptsType: 'bash',
    enableObfuscation: true,
    description: '开启 afterInstall 混淆'
  },
  () => {}
);
```

运行时策略（bash / python 脚本一致）：

1. 检测到 `node` → 执行 `confuse.js`（AST 级强混淆）
2. 未检测到 `node` → 执行 `confuse.sh`（整文件 base64 + `TextDecoder`/`new Function` 包裹，UTF-8 安全）
3. 文件已含 `__WINNER_BASH_OBFUSCATED__` 标记时，`confuse.sh` 会跳过，避免重复包裹

`confuse.sh` 仅作无 node 环境的兼容兜底，防护强度远低于 `confuse.js`。

### 4. Docker 容器化部署

```javascript
generateSeePackageZip(
  {
    name: 'my-app',
    version: '1.0.0',
    seePackageType: 'docker',
    dockerImage: 'my-app:1.0.0',
    description: '容器化应用'
  },
  () => {}
);
```

## 注意事项

1. **copyFiles**：源文件路径为 `` `${outputName}/${name}/文件名` ``，不是项目根目录直写路径。
2. **configName**：除手动指定外，会根据 `outputName` 下是否存在 `config.local.js` / `sysconfig.js` 自动选用文件名。
3. **name 特殊字符**：`@`、`/` 会在打包前替换为 `-`。
4. **Docker**：使用 Docker 部署时需确保镜像已构建并可被 SEE 环境拉取。
5. **混淆**：`enableObfuscation: true` 时打包 `scripts/{name}/js/`；`afterInstall` 优先用 `node`+`confuse.js`，无 `node` 时回退 `bash`+`confuse.sh`。纯 bash 无法复现 javascript-obfuscator 级别的混淆。
6. **Python 脚本**：目标环境需具备 Python 运行时；开启混淆时建议有 `node`，否则走 bash 轻量混淆（仍需系统有 `bash` / `base64`）。

## 更新日志

详见 [CHANGELOG.md](./CHANGELOG.md)。

## 许可证

MIT License

## 贡献

欢迎提交 Issue 和 Pull Request。
