mirror of
https://gitcode.com/github-mirrors/react-native-update-cli.git
synced 2025-09-16 09:41:38 +08:00
Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cd890347d1 | ||
![]() |
e6de3eeef3 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -104,4 +104,7 @@ dist
|
||||
.tern-port
|
||||
|
||||
lib/
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
# react-native-update
|
||||
.update
|
||||
.pushy
|
177
README.md
177
README.md
@@ -49,14 +49,14 @@ const provider = moduleManager.getProvider();
|
||||
const bundleResult = await provider.bundle({
|
||||
platform: 'ios',
|
||||
dev: false,
|
||||
sourcemap: true
|
||||
sourcemap: true,
|
||||
});
|
||||
|
||||
// Publish version
|
||||
const publishResult = await provider.publish({
|
||||
name: 'v1.2.3',
|
||||
description: 'Bug fixes and improvements',
|
||||
rollout: 100
|
||||
rollout: 100,
|
||||
});
|
||||
```
|
||||
|
||||
@@ -65,12 +65,16 @@ const publishResult = await provider.publish({
|
||||
### 1. Define Module
|
||||
|
||||
```typescript
|
||||
import type { CLIModule, CommandDefinition, CustomWorkflow } from 'react-native-update-cli';
|
||||
import type {
|
||||
CLIModule,
|
||||
CommandDefinition,
|
||||
CustomWorkflow,
|
||||
} from 'react-native-update-cli';
|
||||
|
||||
export const myCustomModule: CLIModule = {
|
||||
name: 'my-custom',
|
||||
version: '1.0.0',
|
||||
|
||||
|
||||
commands: [
|
||||
{
|
||||
name: 'custom-command',
|
||||
@@ -79,15 +83,15 @@ export const myCustomModule: CLIModule = {
|
||||
console.log('Executing custom command...');
|
||||
return {
|
||||
success: true,
|
||||
data: { message: 'Custom command executed' }
|
||||
data: { message: 'Custom command executed' },
|
||||
};
|
||||
},
|
||||
options: {
|
||||
param: { hasValue: true, description: 'Custom parameter' }
|
||||
}
|
||||
}
|
||||
param: { hasValue: true, description: 'Custom parameter' },
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
|
||||
workflows: [
|
||||
{
|
||||
name: 'my-workflow',
|
||||
@@ -99,7 +103,7 @@ export const myCustomModule: CLIModule = {
|
||||
execute: async (context, previousResult) => {
|
||||
console.log('Executing step 1...');
|
||||
return { step1Completed: true };
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'step2',
|
||||
@@ -107,19 +111,19 @@ export const myCustomModule: CLIModule = {
|
||||
execute: async (context, previousResult) => {
|
||||
console.log('Executing step 2...');
|
||||
return { ...previousResult, step2Completed: true };
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
|
||||
init: (provider) => {
|
||||
console.log('Custom module initialized');
|
||||
},
|
||||
|
||||
|
||||
cleanup: () => {
|
||||
console.log('Custom module cleanup');
|
||||
}
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
@@ -135,13 +139,13 @@ moduleManager.registerModule(myCustomModule);
|
||||
// Execute custom command
|
||||
const result = await moduleManager.executeCommand('custom-command', {
|
||||
args: [],
|
||||
options: { param: 'value' }
|
||||
options: { param: 'value' },
|
||||
});
|
||||
|
||||
// Execute custom workflow
|
||||
const workflowResult = await moduleManager.executeWorkflow('my-workflow', {
|
||||
args: [],
|
||||
options: {}
|
||||
options: {},
|
||||
});
|
||||
```
|
||||
|
||||
@@ -191,6 +195,7 @@ Each workflow step contains:
|
||||
## 📋 Built-in Modules
|
||||
|
||||
### Bundle Module (`bundle`)
|
||||
|
||||
- `bundle`: Bundle JavaScript code and optionally publish
|
||||
- `diff`: Generate differences between two PPK files
|
||||
- `hdiff`: Generate hdiff between two PPK files
|
||||
@@ -201,27 +206,31 @@ Each workflow step contains:
|
||||
- `hdiffFromIpa`: Generate hdiff from IPA files
|
||||
|
||||
### Version Module (`version`)
|
||||
|
||||
- `publish`: Publish new version
|
||||
- `versions`: List all versions
|
||||
- `update`: Update version information
|
||||
- `updateVersionInfo`: Update version metadata
|
||||
|
||||
### App Module (`app`)
|
||||
|
||||
- `createApp`: Create new application
|
||||
- `apps`: List all applications
|
||||
- `selectApp`: Select application
|
||||
- `deleteApp`: Delete application
|
||||
|
||||
### Package Module (`package`)
|
||||
- `uploadIpa`: Upload IPA files
|
||||
- `uploadApk`: Upload APK files
|
||||
- `uploadApp`: Upload APP files
|
||||
|
||||
- `uploadIpa`: Upload IPA files (supports `--version` to override extracted version)
|
||||
- `uploadApk`: Upload APK files (supports `--version` to override extracted version)
|
||||
- `uploadApp`: Upload APP files (supports `--version` to override extracted version)
|
||||
- `parseApp`: Parse APP file information
|
||||
- `parseIpa`: Parse IPA file information
|
||||
- `parseApk`: Parse APK file information
|
||||
- `packages`: List packages
|
||||
|
||||
### User Module (`user`)
|
||||
|
||||
- `login`: Login
|
||||
- `logout`: Logout
|
||||
- `me`: Show user information
|
||||
@@ -234,36 +243,45 @@ Each workflow step contains:
|
||||
interface CLIProvider {
|
||||
// Bundle
|
||||
bundle(options: BundleOptions): Promise<CommandResult>;
|
||||
|
||||
|
||||
// Publish
|
||||
publish(options: PublishOptions): Promise<CommandResult>;
|
||||
|
||||
|
||||
// Upload
|
||||
upload(options: UploadOptions): Promise<CommandResult>;
|
||||
|
||||
|
||||
// Application management
|
||||
getSelectedApp(platform?: Platform): Promise<{ appId: string; platform: Platform }>;
|
||||
getSelectedApp(
|
||||
platform?: Platform,
|
||||
): Promise<{ appId: string; platform: Platform }>;
|
||||
listApps(platform?: Platform): Promise<CommandResult>;
|
||||
createApp(name: string, platform: Platform): Promise<CommandResult>;
|
||||
|
||||
|
||||
// Version management
|
||||
listVersions(appId: string): Promise<CommandResult>;
|
||||
getVersion(appId: string, versionId: string): Promise<CommandResult>;
|
||||
updateVersion(appId: string, versionId: string, updates: Partial<Version>): Promise<CommandResult>;
|
||||
|
||||
updateVersion(
|
||||
appId: string,
|
||||
versionId: string,
|
||||
updates: Partial<Version>,
|
||||
): Promise<CommandResult>;
|
||||
|
||||
// Package management
|
||||
listPackages(appId: string, platform?: Platform): Promise<CommandResult>;
|
||||
getPackage(appId: string, packageId: string): Promise<CommandResult>;
|
||||
|
||||
|
||||
// Utility functions
|
||||
getPlatform(platform?: Platform): Promise<Platform>;
|
||||
loadSession(): Promise<Session>;
|
||||
saveToLocal(key: string, value: string): void;
|
||||
question(prompt: string): Promise<string>;
|
||||
|
||||
|
||||
// Workflows
|
||||
registerWorkflow(workflow: CustomWorkflow): void;
|
||||
executeWorkflow(workflowName: string, context: CommandContext): Promise<CommandResult>;
|
||||
executeWorkflow(
|
||||
workflowName: string,
|
||||
context: CommandContext,
|
||||
): Promise<CommandResult>;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -276,8 +294,8 @@ const bundleResult = await moduleManager.executeCommand('custom-bundle', {
|
||||
options: {
|
||||
platform: 'android',
|
||||
validate: true,
|
||||
optimize: true
|
||||
}
|
||||
optimize: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Generate diff file
|
||||
@@ -286,8 +304,8 @@ const diffResult = await moduleManager.executeCommand('diff', {
|
||||
options: {
|
||||
origin: './build/v1.0.0.ppk',
|
||||
next: './build/v1.1.0.ppk',
|
||||
output: './build/diff.patch'
|
||||
}
|
||||
output: './build/diff.patch',
|
||||
},
|
||||
});
|
||||
|
||||
// Generate diff from APK files
|
||||
@@ -296,8 +314,8 @@ const apkDiffResult = await moduleManager.executeCommand('diffFromApk', {
|
||||
options: {
|
||||
origin: './build/app-v1.0.0.apk',
|
||||
next: './build/app-v1.1.0.apk',
|
||||
output: './build/apk-diff.patch'
|
||||
}
|
||||
output: './build/apk-diff.patch',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
@@ -349,29 +367,31 @@ Provider provides a concise programming interface suitable for integrating React
|
||||
### 📋 Core API Methods
|
||||
|
||||
#### Core Business Functions
|
||||
|
||||
```typescript
|
||||
// Bundle application
|
||||
await provider.bundle({
|
||||
platform: 'ios',
|
||||
dev: false,
|
||||
sourcemap: true
|
||||
sourcemap: true,
|
||||
});
|
||||
|
||||
// Publish version
|
||||
await provider.publish({
|
||||
name: 'v1.0.0',
|
||||
description: 'Bug fixes',
|
||||
rollout: 100
|
||||
rollout: 100,
|
||||
});
|
||||
|
||||
// Upload file
|
||||
await provider.upload({
|
||||
filePath: 'app.ipa',
|
||||
platform: 'ios'
|
||||
platform: 'ios',
|
||||
});
|
||||
```
|
||||
|
||||
#### Application Management
|
||||
|
||||
```typescript
|
||||
// Create application
|
||||
await provider.createApp('MyApp', 'ios');
|
||||
@@ -384,6 +404,7 @@ const { appId, platform } = await provider.getSelectedApp('ios');
|
||||
```
|
||||
|
||||
#### Version Management
|
||||
|
||||
```typescript
|
||||
// List versions
|
||||
await provider.listVersions('app123');
|
||||
@@ -391,11 +412,12 @@ await provider.listVersions('app123');
|
||||
// Update version
|
||||
await provider.updateVersion('app123', 'version456', {
|
||||
name: 'v1.1.0',
|
||||
description: 'New features'
|
||||
description: 'New features',
|
||||
});
|
||||
```
|
||||
|
||||
#### Utility Functions
|
||||
|
||||
```typescript
|
||||
// Get platform
|
||||
const platform = await provider.getPlatform('ios');
|
||||
@@ -407,72 +429,75 @@ const session = await provider.loadSession();
|
||||
### 🎯 Use Cases
|
||||
|
||||
#### 1. Automated Build Scripts
|
||||
|
||||
```typescript
|
||||
import { moduleManager } from 'react-native-update-cli';
|
||||
|
||||
async function buildAndPublish() {
|
||||
const provider = moduleManager.getProvider();
|
||||
|
||||
|
||||
// 1. Bundle
|
||||
const bundleResult = await provider.bundle({
|
||||
platform: 'ios',
|
||||
dev: false,
|
||||
sourcemap: true
|
||||
sourcemap: true,
|
||||
});
|
||||
|
||||
|
||||
if (!bundleResult.success) {
|
||||
throw new Error(`Bundle failed: ${bundleResult.error}`);
|
||||
}
|
||||
|
||||
|
||||
// 2. Publish
|
||||
const publishResult = await provider.publish({
|
||||
name: 'v1.2.3',
|
||||
description: 'Bug fixes and performance improvements',
|
||||
rollout: 100
|
||||
rollout: 100,
|
||||
});
|
||||
|
||||
|
||||
if (!publishResult.success) {
|
||||
throw new Error(`Publish failed: ${publishResult.error}`);
|
||||
}
|
||||
|
||||
|
||||
console.log('Build and publish completed!');
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. CI/CD Integration
|
||||
|
||||
```typescript
|
||||
async function ciBuild() {
|
||||
const provider = moduleManager.getProvider();
|
||||
|
||||
|
||||
const result = await provider.bundle({
|
||||
platform: process.env.PLATFORM as 'ios' | 'android',
|
||||
dev: process.env.NODE_ENV !== 'production',
|
||||
sourcemap: process.env.NODE_ENV === 'production'
|
||||
sourcemap: process.env.NODE_ENV === 'production',
|
||||
});
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. Application Management Service
|
||||
|
||||
```typescript
|
||||
class AppManagementService {
|
||||
private provider = moduleManager.getProvider();
|
||||
|
||||
|
||||
async setupNewApp(name: string, platform: Platform) {
|
||||
// Create application
|
||||
const createResult = await this.provider.createApp(name, platform);
|
||||
|
||||
|
||||
if (createResult.success) {
|
||||
// Get application information
|
||||
const { appId } = await this.provider.getSelectedApp(platform);
|
||||
|
||||
|
||||
// List versions
|
||||
await this.provider.listVersions(appId);
|
||||
|
||||
|
||||
return { appId, success: true };
|
||||
}
|
||||
|
||||
|
||||
return { success: false, error: createResult.error };
|
||||
}
|
||||
}
|
||||
@@ -488,6 +513,7 @@ class AppManagementService {
|
||||
### 🔧 Advanced Features
|
||||
|
||||
#### Custom Workflows
|
||||
|
||||
```typescript
|
||||
// Register custom workflow
|
||||
provider.registerWorkflow({
|
||||
@@ -498,7 +524,7 @@ provider.registerWorkflow({
|
||||
name: 'bundle',
|
||||
execute: async () => {
|
||||
return await provider.bundle({ platform: 'ios', dev: false });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'publish',
|
||||
@@ -507,9 +533,9 @@ provider.registerWorkflow({
|
||||
throw new Error('Bundle failed, cannot publish');
|
||||
}
|
||||
return await provider.publish({ name: 'auto-release', rollout: 50 });
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Execute workflow
|
||||
@@ -523,50 +549,49 @@ import { moduleManager } from 'react-native-update-cli';
|
||||
|
||||
class ReactNativeUpdateService {
|
||||
private provider = moduleManager.getProvider();
|
||||
|
||||
|
||||
async initialize() {
|
||||
// Load session
|
||||
await this.provider.loadSession();
|
||||
}
|
||||
|
||||
|
||||
async buildAndDeploy(platform: Platform, version: string) {
|
||||
try {
|
||||
// 1. Bundle
|
||||
const bundleResult = await this.provider.bundle({
|
||||
platform,
|
||||
dev: false,
|
||||
sourcemap: true
|
||||
sourcemap: true,
|
||||
});
|
||||
|
||||
|
||||
if (!bundleResult.success) {
|
||||
throw new Error(`Bundle failed: ${bundleResult.error}`);
|
||||
}
|
||||
|
||||
|
||||
// 2. Publish
|
||||
const publishResult = await this.provider.publish({
|
||||
name: version,
|
||||
description: `Release ${version}`,
|
||||
rollout: 100
|
||||
rollout: 100,
|
||||
});
|
||||
|
||||
|
||||
if (!publishResult.success) {
|
||||
throw new Error(`Publish failed: ${publishResult.error}`);
|
||||
}
|
||||
|
||||
|
||||
return { success: true, data: publishResult.data };
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error'
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async getAppInfo(platform: Platform) {
|
||||
const { appId } = await this.provider.getSelectedApp(platform);
|
||||
const versions = await this.provider.listVersions(appId);
|
||||
|
||||
|
||||
return { appId, versions };
|
||||
}
|
||||
}
|
||||
@@ -575,4 +600,4 @@ class ReactNativeUpdateService {
|
||||
const service = new ReactNativeUpdateService();
|
||||
await service.initialize();
|
||||
await service.buildAndDeploy('ios', 'v1.0.0');
|
||||
```
|
||||
```
|
||||
|
235
README.zh-CN.md
235
README.zh-CN.md
@@ -1,15 +1,15 @@
|
||||
# React Native Update CLI
|
||||
|
||||
这是一个统一的React Native Update CLI,同时支持传统命令和模块化架构以及自定义发布流程。
|
||||
这是一个统一的 React Native Update CLI,同时支持传统命令和模块化架构以及自定义发布流程。
|
||||
|
||||
## 🚀 特性
|
||||
|
||||
- **统一CLI**: 使用单个`pushy`命令提供所有功能
|
||||
- **统一 CLI**: 使用单个`pushy`命令提供所有功能
|
||||
- **向后兼容**: 所有现有命令都能正常工作
|
||||
- **模块化架构**: 将CLI功能拆分为独立的模块
|
||||
- **模块化架构**: 将 CLI 功能拆分为独立的模块
|
||||
- **自定义工作流**: 支持创建自定义的发布流程
|
||||
- **可扩展性**: 用户可以导入和注册自定义模块
|
||||
- **类型安全**: 完整的TypeScript类型支持
|
||||
- **类型安全**: 完整的 TypeScript 类型支持
|
||||
|
||||
## 📦 安装
|
||||
|
||||
@@ -47,14 +47,14 @@ const provider = moduleManager.getProvider();
|
||||
const bundleResult = await provider.bundle({
|
||||
platform: 'ios',
|
||||
dev: false,
|
||||
sourcemap: true
|
||||
sourcemap: true,
|
||||
});
|
||||
|
||||
// 发布版本
|
||||
const publishResult = await provider.publish({
|
||||
name: 'v1.2.3',
|
||||
description: 'Bug fixes and improvements',
|
||||
rollout: 100
|
||||
rollout: 100,
|
||||
});
|
||||
```
|
||||
|
||||
@@ -63,12 +63,16 @@ const publishResult = await provider.publish({
|
||||
### 1. 定义模块
|
||||
|
||||
```typescript
|
||||
import type { CLIModule, CommandDefinition, CustomWorkflow } from 'react-native-update-cli';
|
||||
import type {
|
||||
CLIModule,
|
||||
CommandDefinition,
|
||||
CustomWorkflow,
|
||||
} from 'react-native-update-cli';
|
||||
|
||||
export const myCustomModule: CLIModule = {
|
||||
name: 'my-custom',
|
||||
version: '1.0.0',
|
||||
|
||||
|
||||
commands: [
|
||||
{
|
||||
name: 'custom-command',
|
||||
@@ -77,15 +81,15 @@ export const myCustomModule: CLIModule = {
|
||||
console.log('Executing custom command...');
|
||||
return {
|
||||
success: true,
|
||||
data: { message: 'Custom command executed' }
|
||||
data: { message: 'Custom command executed' },
|
||||
};
|
||||
},
|
||||
options: {
|
||||
param: { hasValue: true, description: 'Custom parameter' }
|
||||
}
|
||||
}
|
||||
param: { hasValue: true, description: 'Custom parameter' },
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
|
||||
workflows: [
|
||||
{
|
||||
name: 'my-workflow',
|
||||
@@ -97,7 +101,7 @@ export const myCustomModule: CLIModule = {
|
||||
execute: async (context, previousResult) => {
|
||||
console.log('Executing step 1...');
|
||||
return { step1Completed: true };
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'step2',
|
||||
@@ -105,19 +109,19 @@ export const myCustomModule: CLIModule = {
|
||||
execute: async (context, previousResult) => {
|
||||
console.log('Executing step 2...');
|
||||
return { ...previousResult, step2Completed: true };
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
|
||||
init: (provider) => {
|
||||
console.log('Custom module initialized');
|
||||
},
|
||||
|
||||
|
||||
cleanup: () => {
|
||||
console.log('Custom module cleanup');
|
||||
}
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
@@ -133,13 +137,13 @@ moduleManager.registerModule(myCustomModule);
|
||||
// 执行自定义命令
|
||||
const result = await moduleManager.executeCommand('custom-command', {
|
||||
args: [],
|
||||
options: { param: 'value' }
|
||||
options: { param: 'value' },
|
||||
});
|
||||
|
||||
// 执行自定义工作流
|
||||
const workflowResult = await moduleManager.executeWorkflow('my-workflow', {
|
||||
args: [],
|
||||
options: {}
|
||||
options: {},
|
||||
});
|
||||
```
|
||||
|
||||
@@ -188,43 +192,48 @@ const workflowResult = await moduleManager.executeWorkflow('my-workflow', {
|
||||
|
||||
## 📋 内置模块
|
||||
|
||||
### Bundle模块 (`bundle`)
|
||||
- `bundle`: 打包JavaScript代码并可选发布
|
||||
- `diff`: 生成两个PPK文件之间的差异
|
||||
- `hdiff`: 生成两个PPK文件之间的hdiff
|
||||
- `diffFromApk`: 从APK文件生成差异
|
||||
- `hdiffFromApk`: 从APK文件生成hdiff
|
||||
- `hdiffFromApp`: 从APP文件生成hdiff
|
||||
- `diffFromIpa`: 从IPA文件生成差异
|
||||
- `hdiffFromIpa`: 从IPA文件生成hdiff
|
||||
### Bundle 模块 (`bundle`)
|
||||
|
||||
- `bundle`: 打包 JavaScript 代码并可选发布
|
||||
- `diff`: 生成两个 PPK 文件之间的差异
|
||||
- `hdiff`: 生成两个 PPK 文件之间的 hdiff
|
||||
- `diffFromApk`: 从 APK 文件生成差异
|
||||
- `hdiffFromApk`: 从 APK 文件生成 hdiff
|
||||
- `hdiffFromApp`: 从 APP 文件生成 hdiff
|
||||
- `diffFromIpa`: 从 IPA 文件生成差异
|
||||
- `hdiffFromIpa`: 从 IPA 文件生成 hdiff
|
||||
|
||||
### Version 模块 (`version`)
|
||||
|
||||
### Version模块 (`version`)
|
||||
- `publish`: 发布新版本
|
||||
- `versions`: 列出所有版本
|
||||
- `update`: 更新版本信息
|
||||
- `updateVersionInfo`: 更新版本元数据
|
||||
|
||||
### App模块 (`app`)
|
||||
### App 模块 (`app`)
|
||||
|
||||
- `createApp`: 创建新应用
|
||||
- `apps`: 列出所有应用
|
||||
- `selectApp`: 选择应用
|
||||
- `deleteApp`: 删除应用
|
||||
|
||||
### Package模块 (`package`)
|
||||
- `uploadIpa`: 上传IPA文件
|
||||
- `uploadApk`: 上传APK文件
|
||||
- `uploadApp`: 上传APP文件
|
||||
- `parseApp`: 解析APP文件信息
|
||||
- `parseIpa`: 解析IPA文件信息
|
||||
- `parseApk`: 解析APK文件信息
|
||||
### Package 模块 (`package`)
|
||||
|
||||
- `uploadIpa`: 上传 IPA 文件(支持 `--version` 参数覆盖提取的版本)
|
||||
- `uploadApk`: 上传 APK 文件(支持 `--version` 参数覆盖提取的版本)
|
||||
- `uploadApp`: 上传 APP 文件(支持 `--version` 参数覆盖提取的版本)
|
||||
- `parseApp`: 解析 APP 文件信息
|
||||
- `parseIpa`: 解析 IPA 文件信息
|
||||
- `parseApk`: 解析 APK 文件信息
|
||||
- `packages`: 列出包
|
||||
|
||||
### User模块 (`user`)
|
||||
### User 模块 (`user`)
|
||||
|
||||
- `login`: 登录
|
||||
- `logout`: 登出
|
||||
- `me`: 显示用户信息
|
||||
|
||||
## 🛠️ CLI提供者API
|
||||
## 🛠️ CLI 提供者 API
|
||||
|
||||
### 核心功能
|
||||
|
||||
@@ -232,36 +241,45 @@ const workflowResult = await moduleManager.executeWorkflow('my-workflow', {
|
||||
interface CLIProvider {
|
||||
// 打包
|
||||
bundle(options: BundleOptions): Promise<CommandResult>;
|
||||
|
||||
|
||||
// 发布
|
||||
publish(options: PublishOptions): Promise<CommandResult>;
|
||||
|
||||
|
||||
// 上传
|
||||
upload(options: UploadOptions): Promise<CommandResult>;
|
||||
|
||||
|
||||
// 应用管理
|
||||
getSelectedApp(platform?: Platform): Promise<{ appId: string; platform: Platform }>;
|
||||
getSelectedApp(
|
||||
platform?: Platform,
|
||||
): Promise<{ appId: string; platform: Platform }>;
|
||||
listApps(platform?: Platform): Promise<CommandResult>;
|
||||
createApp(name: string, platform: Platform): Promise<CommandResult>;
|
||||
|
||||
|
||||
// 版本管理
|
||||
listVersions(appId: string): Promise<CommandResult>;
|
||||
getVersion(appId: string, versionId: string): Promise<CommandResult>;
|
||||
updateVersion(appId: string, versionId: string, updates: Partial<Version>): Promise<CommandResult>;
|
||||
|
||||
updateVersion(
|
||||
appId: string,
|
||||
versionId: string,
|
||||
updates: Partial<Version>,
|
||||
): Promise<CommandResult>;
|
||||
|
||||
// 包管理
|
||||
listPackages(appId: string, platform?: Platform): Promise<CommandResult>;
|
||||
getPackage(appId: string, packageId: string): Promise<CommandResult>;
|
||||
|
||||
|
||||
// 工具函数
|
||||
getPlatform(platform?: Platform): Promise<Platform>;
|
||||
loadSession(): Promise<Session>;
|
||||
saveToLocal(key: string, value: string): void;
|
||||
question(prompt: string): Promise<string>;
|
||||
|
||||
|
||||
// 工作流
|
||||
registerWorkflow(workflow: CustomWorkflow): void;
|
||||
executeWorkflow(workflowName: string, context: CommandContext): Promise<CommandResult>;
|
||||
executeWorkflow(
|
||||
workflowName: string,
|
||||
context: CommandContext,
|
||||
): Promise<CommandResult>;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -274,8 +292,8 @@ const bundleResult = await moduleManager.executeCommand('custom-bundle', {
|
||||
options: {
|
||||
platform: 'android',
|
||||
validate: true,
|
||||
optimize: true
|
||||
}
|
||||
optimize: true,
|
||||
},
|
||||
});
|
||||
|
||||
// 生成差异文件
|
||||
@@ -284,8 +302,8 @@ const diffResult = await moduleManager.executeCommand('diff', {
|
||||
options: {
|
||||
origin: './build/v1.0.0.ppk',
|
||||
next: './build/v1.1.0.ppk',
|
||||
output: './build/diff.patch'
|
||||
}
|
||||
output: './build/diff.patch',
|
||||
},
|
||||
});
|
||||
|
||||
// 从APK文件生成差异
|
||||
@@ -294,8 +312,8 @@ const apkDiffResult = await moduleManager.executeCommand('diffFromApk', {
|
||||
options: {
|
||||
origin: './build/app-v1.0.0.apk',
|
||||
next: './build/app-v1.1.0.apk',
|
||||
output: './build/apk-diff.patch'
|
||||
}
|
||||
output: './build/apk-diff.patch',
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
@@ -330,46 +348,48 @@ export NO_INTERACTIVE=true
|
||||
|
||||
## 🚨 注意事项
|
||||
|
||||
1. **向后兼容**: 新的模块化CLI保持与现有CLI的兼容性
|
||||
2. **类型安全**: 所有API都有完整的TypeScript类型定义
|
||||
1. **向后兼容**: 新的模块化 CLI 保持与现有 CLI 的兼容性
|
||||
2. **类型安全**: 所有 API 都有完整的 TypeScript 类型定义
|
||||
3. **错误处理**: 所有操作都返回标准化的结果格式
|
||||
4. **资源清理**: 模块支持清理函数来释放资源
|
||||
5. **模块分离**: 功能按逻辑分离到不同模块中,便于维护和扩展
|
||||
|
||||
## 🤝 贡献
|
||||
|
||||
欢迎提交Issue和Pull Request来改进这个项目!
|
||||
欢迎提交 Issue 和 Pull Request 来改进这个项目!
|
||||
|
||||
## 🚀 Provider API 使用指南
|
||||
|
||||
Provider提供了简洁的编程接口,适合在应用程序中集成React Native Update CLI功能。
|
||||
Provider 提供了简洁的编程接口,适合在应用程序中集成 React Native Update CLI 功能。
|
||||
|
||||
### 📋 核心API方法
|
||||
### 📋 核心 API 方法
|
||||
|
||||
#### 核心业务功能
|
||||
|
||||
```typescript
|
||||
// 打包应用
|
||||
await provider.bundle({
|
||||
platform: 'ios',
|
||||
dev: false,
|
||||
sourcemap: true
|
||||
sourcemap: true,
|
||||
});
|
||||
|
||||
// 发布版本
|
||||
await provider.publish({
|
||||
name: 'v1.0.0',
|
||||
description: 'Bug fixes',
|
||||
rollout: 100
|
||||
rollout: 100,
|
||||
});
|
||||
|
||||
// 上传文件
|
||||
await provider.upload({
|
||||
filePath: 'app.ipa',
|
||||
platform: 'ios'
|
||||
platform: 'ios',
|
||||
});
|
||||
```
|
||||
|
||||
#### 应用管理
|
||||
|
||||
```typescript
|
||||
// 创建应用
|
||||
await provider.createApp('MyApp', 'ios');
|
||||
@@ -382,6 +402,7 @@ const { appId, platform } = await provider.getSelectedApp('ios');
|
||||
```
|
||||
|
||||
#### 版本管理
|
||||
|
||||
```typescript
|
||||
// 列出版本
|
||||
await provider.listVersions('app123');
|
||||
@@ -389,11 +410,12 @@ await provider.listVersions('app123');
|
||||
// 更新版本
|
||||
await provider.updateVersion('app123', 'version456', {
|
||||
name: 'v1.1.0',
|
||||
description: 'New features'
|
||||
description: 'New features',
|
||||
});
|
||||
```
|
||||
|
||||
#### 工具函数
|
||||
|
||||
```typescript
|
||||
// 获取平台
|
||||
const platform = await provider.getPlatform('ios');
|
||||
@@ -405,72 +427,75 @@ const session = await provider.loadSession();
|
||||
### 🎯 使用场景
|
||||
|
||||
#### 1. 自动化构建脚本
|
||||
|
||||
```typescript
|
||||
import { moduleManager } from 'react-native-update-cli';
|
||||
|
||||
async function buildAndPublish() {
|
||||
const provider = moduleManager.getProvider();
|
||||
|
||||
|
||||
// 1. 打包
|
||||
const bundleResult = await provider.bundle({
|
||||
platform: 'ios',
|
||||
dev: false,
|
||||
sourcemap: true
|
||||
sourcemap: true,
|
||||
});
|
||||
|
||||
|
||||
if (!bundleResult.success) {
|
||||
throw new Error(`打包失败: ${bundleResult.error}`);
|
||||
}
|
||||
|
||||
|
||||
// 2. 发布
|
||||
const publishResult = await provider.publish({
|
||||
name: 'v1.2.3',
|
||||
description: 'Bug fixes and performance improvements',
|
||||
rollout: 100
|
||||
rollout: 100,
|
||||
});
|
||||
|
||||
|
||||
if (!publishResult.success) {
|
||||
throw new Error(`发布失败: ${publishResult.error}`);
|
||||
}
|
||||
|
||||
|
||||
console.log('构建和发布完成!');
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. CI/CD集成
|
||||
#### 2. CI/CD 集成
|
||||
|
||||
```typescript
|
||||
async function ciBuild() {
|
||||
const provider = moduleManager.getProvider();
|
||||
|
||||
|
||||
const result = await provider.bundle({
|
||||
platform: process.env.PLATFORM as 'ios' | 'android',
|
||||
dev: process.env.NODE_ENV !== 'production',
|
||||
sourcemap: process.env.NODE_ENV === 'production'
|
||||
sourcemap: process.env.NODE_ENV === 'production',
|
||||
});
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 应用管理服务
|
||||
|
||||
```typescript
|
||||
class AppManagementService {
|
||||
private provider = moduleManager.getProvider();
|
||||
|
||||
|
||||
async setupNewApp(name: string, platform: Platform) {
|
||||
// 创建应用
|
||||
const createResult = await this.provider.createApp(name, platform);
|
||||
|
||||
|
||||
if (createResult.success) {
|
||||
// 获取应用信息
|
||||
const { appId } = await this.provider.getSelectedApp(platform);
|
||||
|
||||
|
||||
// 列出版本
|
||||
await this.provider.listVersions(appId);
|
||||
|
||||
|
||||
return { appId, success: true };
|
||||
}
|
||||
|
||||
|
||||
return { success: false, error: createResult.error };
|
||||
}
|
||||
}
|
||||
@@ -478,14 +503,15 @@ class AppManagementService {
|
||||
|
||||
### ⚠️ 注意事项
|
||||
|
||||
1. **错误处理**: 所有Provider方法都返回`CommandResult`,需要检查`success`字段
|
||||
2. **类型安全**: Provider提供完整的TypeScript类型支持
|
||||
1. **错误处理**: 所有 Provider 方法都返回`CommandResult`,需要检查`success`字段
|
||||
2. **类型安全**: Provider 提供完整的 TypeScript 类型支持
|
||||
3. **会话管理**: 使用前确保已登录,可通过`loadSession()`检查
|
||||
4. **平台支持**: 支持`'ios' | 'android' | 'harmony'`三个平台
|
||||
|
||||
### 🔧 高级功能
|
||||
|
||||
#### 自定义工作流
|
||||
|
||||
```typescript
|
||||
// 注册自定义工作流
|
||||
provider.registerWorkflow({
|
||||
@@ -496,7 +522,7 @@ provider.registerWorkflow({
|
||||
name: 'bundle',
|
||||
execute: async () => {
|
||||
return await provider.bundle({ platform: 'ios', dev: false });
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'publish',
|
||||
@@ -505,9 +531,9 @@ provider.registerWorkflow({
|
||||
throw new Error('打包失败,无法发布');
|
||||
}
|
||||
return await provider.publish({ name: 'auto-release', rollout: 50 });
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// 执行工作流
|
||||
@@ -521,50 +547,49 @@ import { moduleManager } from 'react-native-update-cli';
|
||||
|
||||
class ReactNativeUpdateService {
|
||||
private provider = moduleManager.getProvider();
|
||||
|
||||
|
||||
async initialize() {
|
||||
// 加载会话
|
||||
await this.provider.loadSession();
|
||||
}
|
||||
|
||||
|
||||
async buildAndDeploy(platform: Platform, version: string) {
|
||||
try {
|
||||
// 1. 打包
|
||||
const bundleResult = await this.provider.bundle({
|
||||
platform,
|
||||
dev: false,
|
||||
sourcemap: true
|
||||
sourcemap: true,
|
||||
});
|
||||
|
||||
|
||||
if (!bundleResult.success) {
|
||||
throw new Error(`打包失败: ${bundleResult.error}`);
|
||||
}
|
||||
|
||||
|
||||
// 2. 发布
|
||||
const publishResult = await this.provider.publish({
|
||||
name: version,
|
||||
description: `Release ${version}`,
|
||||
rollout: 100
|
||||
rollout: 100,
|
||||
});
|
||||
|
||||
|
||||
if (!publishResult.success) {
|
||||
throw new Error(`发布失败: ${publishResult.error}`);
|
||||
}
|
||||
|
||||
|
||||
return { success: true, data: publishResult.data };
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error'
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async getAppInfo(platform: Platform) {
|
||||
const { appId } = await this.provider.getSelectedApp(platform);
|
||||
const versions = await this.provider.listVersions(appId);
|
||||
|
||||
|
||||
return { appId, versions };
|
||||
}
|
||||
}
|
||||
@@ -573,4 +598,4 @@ class ReactNativeUpdateService {
|
||||
const service = new ReactNativeUpdateService();
|
||||
await service.initialize();
|
||||
await service.buildAndDeploy('ios', 'v1.0.0');
|
||||
```
|
||||
```
|
||||
|
24
cli.json
24
cli.json
@@ -31,9 +31,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"uploadIpa": {},
|
||||
"uploadApk": {},
|
||||
"uploadApp": {},
|
||||
"uploadIpa": {
|
||||
"options": {
|
||||
"version": {
|
||||
"hasValue": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"uploadApk": {
|
||||
"options": {
|
||||
"version": {
|
||||
"hasValue": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"uploadApp": {
|
||||
"options": {
|
||||
"version": {
|
||||
"hasValue": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"parseApp": {},
|
||||
"parseIpa": {},
|
||||
"parseApk": {},
|
||||
|
53
example/USAGE_CUSTOM_VERSION.md
Normal file
53
example/USAGE_CUSTOM_VERSION.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Custom Version Parameter Usage
|
||||
|
||||
This document demonstrates how to use the `--version` parameter with upload commands to override the version extracted from APK/IPA/APP files.
|
||||
|
||||
## Commands Supporting Custom Version
|
||||
|
||||
- `uploadApk --version <version>`
|
||||
- `uploadIpa --version <version>`
|
||||
- `uploadApp --version <version>`
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Upload APK with Custom Version
|
||||
|
||||
```bash
|
||||
# Upload APK and override version to "1.2.3-custom"
|
||||
cresc uploadApk app-release.apk --version "1.2.3-custom"
|
||||
```
|
||||
|
||||
### Upload IPA with Custom Version
|
||||
|
||||
```bash
|
||||
# Upload IPA and override version to "2.0.0-beta"
|
||||
cresc uploadIpa MyApp.ipa --version "2.0.0-beta"
|
||||
```
|
||||
|
||||
### Upload APP with Custom Version
|
||||
|
||||
```bash
|
||||
# Upload APP and override version to "3.1.0-harmony"
|
||||
cresc uploadApp MyApp.app --version "3.1.0-harmony"
|
||||
```
|
||||
|
||||
## Behavior
|
||||
|
||||
1. **Without `--version`**: The command uses the version extracted from the package file (APK/IPA/APP)
|
||||
2. **With `--version`**: The command uses the provided custom version instead of the extracted version
|
||||
3. **Console Output**: When using a custom version, the CLI will display "Using custom version: <version>" message
|
||||
|
||||
## Use Cases
|
||||
|
||||
- **Testing**: Upload test builds with specific version identifiers
|
||||
- **Hotfixes**: Override version numbers for emergency releases
|
||||
- **Version Alignment**: Ensure consistent versioning across different platforms
|
||||
- **Manual Override**: When the extracted version is incorrect or inappropriate
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
$ cresc uploadApk app-release.apk --version "1.2.3-hotfix"
|
||||
Using custom version: 1.2.3-hotfix
|
||||
Successfully uploaded APK native package (id: 12345, version: 1.2.3-hotfix, buildTime: 2024-01-15T10:30:00Z)
|
||||
```
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-update-cli",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"description": "command line tool for react-native-update (remote updates for react native)",
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
|
@@ -130,4 +130,5 @@ This can reduce the risk of inconsistent dependencies and supply chain attacks.
|
||||
updateNativePackageQuestion: 'Bind to native package now?(Y/N)',
|
||||
unnamed: '(Unnamed)',
|
||||
dryRun: 'Below is the dry-run result, no actual operation will be performed:',
|
||||
usingCustomVersion: 'Using custom version: {{version}}',
|
||||
};
|
||||
|
@@ -123,4 +123,5 @@ export default {
|
||||
updateNativePackageQuestion: '是否现在将此热更应用到原生包上?(Y/N)',
|
||||
unnamed: '(未命名)',
|
||||
dryRun: '以下是 dry-run 模拟运行结果,不会实际执行任何操作:',
|
||||
usingCustomVersion: '使用自定义版本:{{version}}',
|
||||
};
|
||||
|
@@ -23,9 +23,9 @@ export async function listPackage(appId: string) {
|
||||
let versionInfo = '';
|
||||
if (version) {
|
||||
const versionObj = version as any;
|
||||
versionInfo = t('boundTo', {
|
||||
name: versionObj.name || version,
|
||||
id: versionObj.id || version
|
||||
versionInfo = t('boundTo', {
|
||||
name: versionObj.name || version,
|
||||
id: versionObj.id || version,
|
||||
});
|
||||
}
|
||||
let output = pkg.name;
|
||||
@@ -57,16 +57,19 @@ export async function choosePackage(appId: string) {
|
||||
}
|
||||
|
||||
export const packageCommands = {
|
||||
uploadIpa: async ({ args }: { args: string[] }) => {
|
||||
uploadIpa: async ({
|
||||
args,
|
||||
options,
|
||||
}: {
|
||||
args: string[];
|
||||
options: Record<string, any>;
|
||||
}) => {
|
||||
const fn = args[0];
|
||||
if (!fn || !fn.endsWith('.ipa')) {
|
||||
throw new Error(t('usageUploadIpa'));
|
||||
}
|
||||
const ipaInfo = await getIpaInfo(fn);
|
||||
const {
|
||||
versionName,
|
||||
buildTime,
|
||||
} = ipaInfo;
|
||||
const { versionName: extractedVersionName, buildTime } = ipaInfo;
|
||||
const appIdInPkg = (ipaInfo as any).appId;
|
||||
const appKeyInPkg = (ipaInfo as any).appKey;
|
||||
const { appId, appKey } = await getSelectedApp('ios');
|
||||
@@ -79,6 +82,12 @@ export const packageCommands = {
|
||||
throw new Error(t('appKeyMismatchIpa', { appKeyInPkg, appKey }));
|
||||
}
|
||||
|
||||
// Use custom version if provided, otherwise use extracted version
|
||||
const versionName = options.version || extractedVersionName;
|
||||
if (options.version) {
|
||||
console.log(t('usingCustomVersion', { version: versionName }));
|
||||
}
|
||||
|
||||
const { hash } = await uploadFile(fn);
|
||||
|
||||
const { id } = await post(`/app/${appId}/package/create`, {
|
||||
@@ -89,20 +98,21 @@ export const packageCommands = {
|
||||
commit: await getCommitInfo(),
|
||||
});
|
||||
saveToLocal(fn, `${appId}/package/${id}.ipa`);
|
||||
console.log(
|
||||
t('ipaUploadSuccess', { id, version: versionName, buildTime }),
|
||||
);
|
||||
console.log(t('ipaUploadSuccess', { id, version: versionName, buildTime }));
|
||||
},
|
||||
uploadApk: async ({ args }: { args: string[] }) => {
|
||||
uploadApk: async ({
|
||||
args,
|
||||
options,
|
||||
}: {
|
||||
args: string[];
|
||||
options: Record<string, any>;
|
||||
}) => {
|
||||
const fn = args[0];
|
||||
if (!fn || !fn.endsWith('.apk')) {
|
||||
throw new Error(t('usageUploadApk'));
|
||||
}
|
||||
const apkInfo = await getApkInfo(fn);
|
||||
const {
|
||||
versionName,
|
||||
buildTime,
|
||||
} = apkInfo;
|
||||
const { versionName: extractedVersionName, buildTime } = apkInfo;
|
||||
const appIdInPkg = (apkInfo as any).appId;
|
||||
const appKeyInPkg = (apkInfo as any).appKey;
|
||||
const { appId, appKey } = await getSelectedApp('android');
|
||||
@@ -115,6 +125,12 @@ export const packageCommands = {
|
||||
throw new Error(t('appKeyMismatchApk', { appKeyInPkg, appKey }));
|
||||
}
|
||||
|
||||
// Use custom version if provided, otherwise use extracted version
|
||||
const versionName = options.version || extractedVersionName;
|
||||
if (options.version) {
|
||||
console.log(t('usingCustomVersion', { version: versionName }));
|
||||
}
|
||||
|
||||
const { hash } = await uploadFile(fn);
|
||||
|
||||
const { id } = await post(`/app/${appId}/package/create`, {
|
||||
@@ -125,20 +141,21 @@ export const packageCommands = {
|
||||
commit: await getCommitInfo(),
|
||||
});
|
||||
saveToLocal(fn, `${appId}/package/${id}.apk`);
|
||||
console.log(
|
||||
t('apkUploadSuccess', { id, version: versionName, buildTime }),
|
||||
);
|
||||
console.log(t('apkUploadSuccess', { id, version: versionName, buildTime }));
|
||||
},
|
||||
uploadApp: async ({ args }: { args: string[] }) => {
|
||||
uploadApp: async ({
|
||||
args,
|
||||
options,
|
||||
}: {
|
||||
args: string[];
|
||||
options: Record<string, any>;
|
||||
}) => {
|
||||
const fn = args[0];
|
||||
if (!fn || !fn.endsWith('.app')) {
|
||||
throw new Error(t('usageUploadApp'));
|
||||
}
|
||||
const appInfo = await getAppInfo(fn);
|
||||
const {
|
||||
versionName,
|
||||
buildTime,
|
||||
} = appInfo;
|
||||
const { versionName: extractedVersionName, buildTime } = appInfo;
|
||||
const appIdInPkg = (appInfo as any).appId;
|
||||
const appKeyInPkg = (appInfo as any).appKey;
|
||||
const { appId, appKey } = await getSelectedApp('harmony');
|
||||
@@ -151,6 +168,12 @@ export const packageCommands = {
|
||||
throw new Error(t('appKeyMismatchApp', { appKeyInPkg, appKey }));
|
||||
}
|
||||
|
||||
// Use custom version if provided, otherwise use extracted version
|
||||
const versionName = options.version || extractedVersionName;
|
||||
if (options.version) {
|
||||
console.log(t('usingCustomVersion', { version: versionName }));
|
||||
}
|
||||
|
||||
const { hash } = await uploadFile(fn);
|
||||
|
||||
const { id } = await post(`/app/${appId}/package/create`, {
|
||||
@@ -161,9 +184,7 @@ export const packageCommands = {
|
||||
commit: await getCommitInfo(),
|
||||
});
|
||||
saveToLocal(fn, `${appId}/package/${id}.app`);
|
||||
console.log(
|
||||
t('appUploadSuccess', { id, version: versionName, buildTime }),
|
||||
);
|
||||
console.log(t('appUploadSuccess', { id, version: versionName, buildTime }));
|
||||
},
|
||||
parseApp: async ({ args }: { args: string[] }) => {
|
||||
const fn = args[0];
|
||||
|
@@ -110,7 +110,7 @@ export class CLIProviderImpl implements CLIProvider {
|
||||
|
||||
const context: CommandContext = {
|
||||
args: [filePath],
|
||||
options: { platform, appId },
|
||||
options: { platform, appId, version: options.version },
|
||||
};
|
||||
|
||||
const { packageCommands } = await import('./package');
|
||||
|
@@ -85,6 +85,7 @@ export interface UploadOptions {
|
||||
platform?: Platform;
|
||||
filePath: string;
|
||||
appId?: string;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
export interface WorkflowStep {
|
||||
|
Reference in New Issue
Block a user