
Research
/Security News
Weaponizing Discord for Command and Control Across npm, PyPI, and RubyGems.org
Socket researchers uncover how threat actors weaponize Discord across the npm, PyPI, and RubyGems ecosystems to exfiltrate sensitive data.
@winner-fed/deploy-cli
Advanced tools
一站式前端项目部署命令行工具
@winner-fed/deploy-cli
是一个集成了 SEE 平台部署包生成和 FTP/SFTP 文件上传功能的命令行工具。它简化了前端项目的部署流程,支持多种配置方式和部署场景,完美支持 monorepo 环境,让部署变得更加简单高效。
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.jsonsource
配置的目录.git
目录,支持在子包中运行并正确获取 git 信息详细的更新日志请查看 CHANGELOG.md。
ISC License
欢迎提交 Issue 和 Pull Request 来改进这个项目。
FAQs
CLI tool for deploy.
We found that @winner-fed/deploy-cli demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
/Security News
Socket researchers uncover how threat actors weaponize Discord across the npm, PyPI, and RubyGems ecosystems to exfiltrate sensitive data.
Security News
Socket now integrates with Bun 1.3’s Security Scanner API to block risky packages at install time and enforce your organization’s policies in local dev and CI.
Research
The Socket Threat Research Team is tracking weekly intrusions into the npm registry that follow a repeatable adversarial playbook used by North Korean state-sponsored actors.