一站式前端项目部署命令行工具
@winner-fed/deploy-cli
是一个集成了 SEE 平台部署包生成和 FTP/SFTP 文件上传功能的命令行工具。它简化了前端项目的部署流程,支持多种配置方式和部署场景,完美支持 monorepo 环境,让部署变得更加简单高效。
- 🚀 一键部署: 支持 SEE 包生成和 FTP 上传的一体化部署
- 📦 SEE 平台集成: 完整支持 SEE 平台的部署包规范
- 🌐 FTP/SFTP 支持: 灵活的文件传输协议支持
- 🏗️ Monorepo 支持: 智能识别 monorepo 环境,自动向上查找 git 仓库根目录
- ⚙️ 多配置格式: 支持 JS、TS、JSON 等多种配置文件格式
- 🎯 TypeScript 支持: 完整的类型定义和智能提示
- 🔧 灵活配置: 支持环境变量、命令行参数等多种配置方式
- 📊 详细日志: 提供详细的部署过程日志和错误信息
npm install -g @winner-fed/deploy-cli
# 或
yarn global add @winner-fed/deploy-cli
# 或
pnpm add -g @winner-fed/deploy-cli
npm install @winner-fed/deploy-cli --save-dev
# 或
yarn add @winner-fed/deploy-cli --dev
# 或
pnpm add @winner-fed/deploy-cli -D
# 全局安装后
deploy-cli see
# 项目内安装
npx deploy-cli see
# 或
yarn deploy-cli see
# 在 monorepo 子包中使用
cd packages/my-app
npx deploy-cli see
# 全局安装后
deploy-cli ftp
# 项目内安装
npx deploy-cli ftp
# 或
yarn deploy-cli ftp
# 在 monorepo 子包中使用
cd packages/my-app
npx deploy-cli ftp
deploy-cli deploy
# 在 monorepo 环境中,工具会自动识别并向上查找 git 仓库根目录
# 无需额外配置即可获取正确的 git 信息
deploy-cli [command] [options]
Options:
--config <path> 指定配置文件路径
--help 显示帮助信息
--version 显示版本信息
deploy-cli see [options]
Options:
--config <path> 指定配置文件路径
--output <dir> 指定输出目录
--type <type> 应用类型 (bizframe|subsystem)
--docker 生成 Docker 容器化包
deploy-cli ftp [options]
Options:
--config <path> 指定配置文件路径
--host <host> FTP 服务器地址
--port <port> FTP 服务器端口
--sftp 使用 SFTP 协议
deploy-cli
支持多种配置文件格式,会按以下优先级查找配置:
deploy.config.ts
deploy.config.js
deploy.config.cjs
deploy.config.mjs
deploy.config.json
-
package.json
中的deploy
属性
// deploy.config.js
module.exports = {
// 项目源码路径
source: process.cwd(),
// SEE 包配置
seeConfig: {
system: 'my-system',
type: 'subsystem',
name: 'my-app',
version: '1.0.0',
description: '我的应用',
outputName: 'dist'
},
// FTP 配置
ftpConfig: {
user: 'deploy-user',
password: 'your-password',
host: 'your-server.com',
port: 22,
localPath: './dist',
remotePath: '/var/www/html',
include: ['*', '**/*'],
exclude: ['**/*.map'],
sftp: true
}
};
// deploy.config.ts
import type { UserConfig } from '@winner-fed/deploy-cli';
const config: UserConfig = {
source: process.cwd(),
seeConfig: {
system: 'winner-front',
type: 'bizframe',
name: 'main-app',
version: '2.0.0',
description: '主框架应用',
variables: [
{
type: 'input',
label: 'API 基础路径',
name: 'API_BASE_URL',
required: true,
tooltip: '后端 API 服务地址',
default: 'https://api.example.com'
},
{
type: 'switch',
label: '启用调试模式',
name: 'DEBUG_MODE',
options: 'true:是;false:否',
required: false,
tooltip: '是否启用调试模式',
default: 'false'
},
{
type: 'password',
label: '数据库密码',
name: 'DB_PASSWORD',
required: true,
tooltip: '数据库连接密码',
default: ''
},
{
type: 'hidden',
label: '应用版本号',
name: 'APP_VERSION',
required: false,
tooltip: '应用内部版本号,用于系统识别',
default: '1.0.0'
}
],
isProduction: process.env.NODE_ENV === 'production',
dockerImage: 'my-app:latest'
},
ftpConfig: {
user: process.env.FTP_USER!,
password: process.env.FTP_PASSWORD!,
host: process.env.FTP_HOST!,
port: parseInt(process.env.FTP_PORT || '22'),
localPath: './dist',
remotePath: '/usr/local/nginx/html/my-app',
include: ['*', '**/*'],
exclude: ['**/*.map', '**/*.log', '.DS_Store', 'Thumbs.db'],
deleteRemote: true,
sftp: true
}
};
export default config;
参数 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
source | string | 否 | process.cwd() | 项目源码路径 |
seeConfig | SeeConfig | 否 | - | SEE 包配置 |
ftpConfig | FtpConfig | 否 | - | FTP 配置 |
config | string | 否 | - | 自定义配置文件路径 |
参数 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
system | string | 否 | 'winner-front' | 系统类型,SEE 平台分组维度 |
type | 'bizframe' | 'subsystem' | 否 | 'bizframe' | 应用类型 |
name | string | 否 | package.json.name | 发布物名称 |
version | string | 否 | package.json.version | 发布包版本 |
description | string | 否 | package.json.description | 发布包描述 |
appType | string | 否 | name | 发布物类型 |
group | string | 否 | 'bizframe' | 应用分组 |
configName | string | 否 | 'config.local' | 配置文件名称(不含.js) |
outputName | string | 否 | 'dist' | 输出目录名称 |
templateFunc | function | 否 | - | 自定义变量配置文件生成函数 |
variables | Array | 否 | [] | deploy.xml 模板变量 |
copyFiles | string[] | 否 | [] | 需要直接拷贝的文件列表 |
seePackageName | string | 否 | ${system}-${name}-${version} |
SEE 包名称 |
seePackageType | 'web' | 'docker' | 否 | 'web' | SEE 包类型 |
isDocker | boolean | 否 | false | 是否为 Docker 包 |
dockerImage | string | 否 | - | Docker 镜像名 |
scriptsType | 'python' | 'bash' | 否 | 'python' | 脚本类型 |
buildVersion | string | 否 | - | 构建版本 |
isProduction | boolean | 否 | false | 是否为生产包 |
参数 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
user | string | 是 | - | 服务器登录用户名 |
password | string | 是 | - | 服务器登录密码 |
host | string | 是 | - | 服务器主机地址 |
port | number | 是 | - | 服务器端口 |
localPath | string | 是 | - | 本地文件路径 |
remotePath | string | 是 | - | 远程服务器路径 |
include | string[] | 是 | - | 包含的文件模式 |
exclude | string[] | 否 | [] | 排除的文件模式 |
deleteRemote | boolean | 否 | false | 上传前是否删除远程文件 |
forcePasv | boolean | 否 | true | 是否强制被动模式 |
sftp | boolean | 否 | false | 是否使用 SFTP |
interface variables {
type:
| 'input'
| 'select'
| 'editor'
| 'switch'
| 'smallfile'
| 'table'
| 'mselect'
| 'switchForm'
| 'complexSelect'
| 'division'
| 'password'
| 'hidden';
label: string; // 控件标签
name: string; // 变量名称
options?: string; // 选项配置(select 类型使用)
required: boolean; // 是否必填
fold?: boolean; // 是否可折叠
children?: variables[]; // 子控件
tooltip: string; // 提示信息
default: string; // 默认值
}
// deploy.config.js
module.exports = {
seeConfig: {
system: 'my-system',
type: 'subsystem',
name: 'user-management',
version: '1.0.0',
description: '用户管理系统',
variables: [
{
type: 'input',
label: 'API 地址',
name: 'API_URL',
required: true,
tooltip: '后端 API 服务地址',
default: 'https://api.example.com'
},
{
type: 'password',
label: '数据库密码',
name: 'DB_PASSWORD',
required: true,
tooltip: '数据库连接密码',
default: ''
},
{
type: 'hidden',
label: '应用版本号',
name: 'APP_VERSION',
required: false,
tooltip: '应用内部版本号,用于系统识别',
default: '1.0.0'
}
],
isProduction: true
},
ftpConfig: {
user: 'deploy',
password: process.env.DEPLOY_PASSWORD,
host: 'prod.example.com',
port: 22,
localPath: './dist',
remotePath: '/var/www/html/user-management',
include: ['*', '**/*'],
exclude: ['**/*.map', '**/*.log'],
deleteRemote: true,
sftp: true
}
};
// deploy.config.js
const env = process.env.NODE_ENV || 'development';
const environments = {
development: {
host: 'dev.example.com',
remotePath: '/var/www/dev'
},
staging: {
host: 'staging.example.com',
remotePath: '/var/www/staging'
},
production: {
host: 'prod.example.com',
remotePath: '/var/www/prod'
}
};
module.exports = {
seeConfig: {
system: 'my-system',
type: 'subsystem',
isProduction: env === 'production'
},
ftpConfig: {
user: 'deploy',
password: process.env.DEPLOY_PASSWORD,
host: environments[env].host,
port: 22,
localPath: './dist',
remotePath: environments[env].remotePath,
include: ['*', '**/*'],
exclude: ['**/*.map'],
deleteRemote: true,
sftp: true
}
};
// deploy.config.js
module.exports = {
seeConfig: {
system: 'container-system',
type: 'subsystem',
name: 'my-app',
version: '1.0.0',
seePackageType: 'docker',
isDocker: true,
dockerImage: 'my-app:1.0.0',
scriptsType: 'bash'
}
};
// packages/my-app/deploy.config.js
module.exports = {
// 工具会自动从子包目录向上查找 git 仓库根目录
// 例如:从 /monorepo/packages/my-app 向上查找到 /monorepo/.git
source: __dirname, // 子包目录
seeConfig: {
system: 'monorepo-system',
type: 'subsystem',
name: 'my-app',
version: '1.0.0',
description: 'Monorepo 中的子应用',
outputName: 'dist',
// 工具会自动获取正确的 git hash,即使在子包中运行
isProduction: process.env.NODE_ENV === 'production'
},
ftpConfig: {
user: process.env.FTP_USER,
password: process.env.FTP_PASSWORD,
host: 'example.com',
port: 22,
localPath: './dist',
remotePath: '/var/www/html/my-app',
include: ['*', '**/*'],
exclude: ['**/*.map'],
sftp: true
}
};
{
"name": "my-app",
"version": "1.0.0",
"scripts": {
"build": "npm run build:prod",
"deploy:see": "deploy-cli see",
"deploy:ftp": "deploy-cli ftp",
"deploy": "npm run build && deploy-cli see && deploy-cli ftp"
},
"deploy": {
"seeConfig": {
"system": "my-system",
"type": "subsystem",
"outputName": "dist"
},
"ftpConfig": {
"user": "deploy",
"host": "example.com",
"port": 22,
"localPath": "./dist",
"remotePath": "/var/www/html",
"include": ["*", "**/*"],
"exclude": ["**/*.map"],
"sftp": true
}
}
}
为了安全起见,建议将敏感信息通过环境变量配置:
# .env 文件
FTP_USER=deploy-user
FTP_PASSWORD=your-secure-password
FTP_HOST=your-server.com
FTP_PORT=22
NODE_ENV=production
// deploy.config.js
require('dotenv').config();
module.exports = {
ftpConfig: {
user: process.env.FTP_USER,
password: process.env.FTP_PASSWORD,
host: process.env.FTP_HOST
// ... 其他配置
}
};
# 使用自定义配置文件
deploy-cli see --config ./build/deploy.config.js
deploy-cli ftp --config ./config/production.js
# 使用相对路径
deploy-cli see --config ../shared/deploy.config.js
-
配置文件未找到
Error: Configuration file not found
解决方案:确保配置文件存在且路径正确
-
SEE 包生成失败
Error: Failed to generate SEE package
解决方案:检查
outputName
目录是否存在,确保有足够的磁盘空间 -
FTP 连接失败
Error: FTP connection failed
解决方案:检查网络连接、服务器地址、端口和凭据
# 启用详细日志
DEBUG=deploy-cli* deploy-cli see
DEBUG=deploy-cli* deploy-cli ftp
// 使用配置工厂函数
function createConfig(env) {
const baseConfig = {
seeConfig: {
system: 'my-system',
type: 'subsystem'
}
};
const envConfigs = {
development: {
ftpConfig: {
host: 'dev.example.com',
deleteRemote: false
}
},
production: {
ftpConfig: {
host: 'prod.example.com',
deleteRemote: true
}
}
};
return {
...baseConfig,
...envConfigs[env]
};
}
module.exports = createConfig(process.env.NODE_ENV || 'development');
// 使用环境变量和配置验证
const requiredEnvVars = ['FTP_USER', 'FTP_PASSWORD', 'FTP_HOST'];
const missingVars = requiredEnvVars.filter((varName) => !process.env[varName]);
if (missingVars.length > 0) {
throw new Error(`Missing required environment variables: ${missingVars.join(', ')}`);
}
module.exports = {
ftpConfig: {
user: process.env.FTP_USER,
password: process.env.FTP_PASSWORD,
host: process.env.FTP_HOST
// ... 其他配置
}
};
{
"scripts": {
"build": "npm run build:prod",
"deploy:dev": "NODE_ENV=development deploy-cli deploy",
"deploy:staging": "NODE_ENV=staging deploy-cli deploy",
"deploy:prod": "NODE_ENV=production deploy-cli deploy",
"deploy": "npm run build && npm run deploy:prod"
}
}
// packages/my-app/package.json
{
"name": "@my-org/my-app",
"version": "1.0.0",
"scripts": {
"build": "vite build",
"deploy:dev": "NODE_ENV=development deploy-cli deploy",
"deploy:prod": "NODE_ENV=production deploy-cli deploy"
}
}
// packages/my-app/deploy.config.js
module.exports = {
// 当前子包目录
source: __dirname,
seeConfig: {
system: 'my-system',
type: 'subsystem',
name: 'my-app',
version: require('./package.json').version,
description: require('./package.json').description,
outputName: 'dist',
// 工具会自动向上查找 git 仓库,获取正确的 git hash
isProduction: process.env.NODE_ENV === 'production'
},
ftpConfig: {
// 使用环境变量配置敏感信息
user: process.env.FTP_USER,
password: process.env.FTP_PASSWORD,
host: process.env.FTP_HOST,
port: parseInt(process.env.FTP_PORT || '22'),
localPath: './dist',
remotePath: `/var/www/html/${require('./package.json').name}`,
include: ['*', '**/*'],
exclude: ['**/*.map', '**/*.log'],
sftp: true
}
};
-
配置文件优先级: 命令行
--config
参数 > 项目根目录配置文件 > package.json - 环境变量: 建议使用环境变量管理敏感信息
-
路径处理: 所有路径都相对于
source
配置的目录 -
Monorepo 支持: 工具会自动向上查找
.git
目录,支持在子包中运行并正确获取 git 信息 - Docker 部署: 使用 Docker 模式时,确保镜像已构建并推送到仓库
- 网络连接: FTP/SFTP 部署需要确保网络连接和防火墙配置正确
- Monorepo 支持增强: 新增智能 git 目录查找功能,支持在 monorepo 子包中正确获取 git 信息
- 类型安全改进: 修复了 TypeScript 类型错误,提供更好的类型支持
- API 改进: 将部分内部工具函数暴露为公共 API,方便扩展使用
详细的更新日志请查看 CHANGELOG.md。
ISC License
欢迎提交 Issue 和 Pull Request 来改进这个项目。
- @winner-fed/winner-deploy - SEE 平台部署包生成工具
- @winner-fed/ftp-deploy - FTP/SFTP 文件上传工具