mirror of
https://gitcode.com/github-mirrors/react-native-update-cli.git
synced 2025-09-16 01:41:37 +08:00

* add logic to support SENTRY_PROPERTIES parameter * remove update.json and meta.json files in ppk * udpapte * refactor modles * update * add package-module file * update * update readme file * modifu cli.json file * fix command issues * improve version workflow logic * udpate * update * update * update * udpate * udpate * add example * update readme file * udpate version * change logic to use pushy command uniformly
583 lines
16 KiB
TypeScript
583 lines
16 KiB
TypeScript
import type {
|
||
CLIProvider,
|
||
CommandContext,
|
||
CustomWorkflow,
|
||
} from '../../src/types';
|
||
|
||
/**
|
||
* 自定义工作流集合
|
||
* 演示各种复杂的工作流场景
|
||
*/
|
||
|
||
/**
|
||
* 灰度发布工作流
|
||
*/
|
||
export const canaryDeploymentWorkflow: CustomWorkflow = {
|
||
name: 'canary-deployment',
|
||
description: '灰度发布工作流 - 逐步增加用户覆盖率',
|
||
steps: [
|
||
{
|
||
name: 'prepare-canary',
|
||
description: '准备灰度发布',
|
||
execute: async (context: CommandContext) => {
|
||
console.log('🔧 准备灰度发布环境...');
|
||
|
||
const { version, initialRollout = 5 } = context.options;
|
||
|
||
console.log(`📦 版本: ${version}`);
|
||
console.log(`📊 初始覆盖率: ${initialRollout}%`);
|
||
|
||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||
|
||
return {
|
||
version,
|
||
currentRollout: initialRollout,
|
||
stage: 'prepared',
|
||
};
|
||
},
|
||
},
|
||
{
|
||
name: 'initial-deployment',
|
||
description: '初始小范围部署',
|
||
execute: async (context: CommandContext, previousResult: any) => {
|
||
console.log('🚀 执行初始小范围部署...');
|
||
|
||
const { currentRollout } = previousResult;
|
||
|
||
console.log(`部署到 ${currentRollout}% 用户...`);
|
||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||
|
||
console.log('✅ 初始部署完成');
|
||
|
||
return {
|
||
...previousResult,
|
||
deploymentTime: new Date().toISOString(),
|
||
stage: 'initial-deployed',
|
||
};
|
||
},
|
||
},
|
||
{
|
||
name: 'monitor-metrics',
|
||
description: '监控关键指标',
|
||
execute: async (context: CommandContext, previousResult: any) => {
|
||
console.log('📊 监控关键指标...');
|
||
|
||
// 模拟监控数据
|
||
const metrics = {
|
||
crashRate: Math.random() * 0.01, // 0-1%
|
||
responseTime: 150 + Math.random() * 100, // 150-250ms
|
||
userSatisfaction: 85 + Math.random() * 10, // 85-95%
|
||
errorRate: Math.random() * 0.005, // 0-0.5%
|
||
};
|
||
|
||
console.log('📈 监控结果:');
|
||
console.log(` 崩溃率: ${(metrics.crashRate * 100).toFixed(3)}%`);
|
||
console.log(` 响应时间: ${metrics.responseTime.toFixed(1)}ms`);
|
||
console.log(` 用户满意度: ${metrics.userSatisfaction.toFixed(1)}%`);
|
||
console.log(` 错误率: ${(metrics.errorRate * 100).toFixed(3)}%`);
|
||
|
||
// 判断是否可以继续扩大范围
|
||
const canProceed =
|
||
metrics.crashRate < 0.005 &&
|
||
metrics.errorRate < 0.003 &&
|
||
metrics.userSatisfaction > 80;
|
||
|
||
console.log(`🔍 健康检查: ${canProceed ? '✅ 通过' : '❌ 未通过'}`);
|
||
|
||
return {
|
||
...previousResult,
|
||
metrics,
|
||
canProceed,
|
||
stage: 'monitored',
|
||
};
|
||
},
|
||
},
|
||
{
|
||
name: 'expand-rollout',
|
||
description: '扩大发布范围',
|
||
execute: async (context: CommandContext, previousResult: any) => {
|
||
const { canProceed, currentRollout } = previousResult;
|
||
|
||
if (!canProceed) {
|
||
console.log('⚠️ 指标不达标,停止扩大发布范围');
|
||
return {
|
||
...previousResult,
|
||
stage: 'rollout-stopped',
|
||
};
|
||
}
|
||
|
||
console.log('📈 扩大发布范围...');
|
||
|
||
const newRollout = Math.min(currentRollout * 2, 100);
|
||
console.log(`覆盖率从 ${currentRollout}% 扩大到 ${newRollout}%`);
|
||
|
||
await new Promise((resolve) => setTimeout(resolve, 1200));
|
||
|
||
return {
|
||
...previousResult,
|
||
currentRollout: newRollout,
|
||
stage: newRollout >= 100 ? 'fully-deployed' : 'expanded',
|
||
};
|
||
},
|
||
condition: (context: CommandContext) => {
|
||
// 只有在启用自动扩大的情况下才执行
|
||
return context.options.autoExpand !== false;
|
||
},
|
||
},
|
||
{
|
||
name: 'final-verification',
|
||
description: '最终验证',
|
||
execute: async (context: CommandContext, previousResult: any) => {
|
||
console.log('🔍 执行最终验证...');
|
||
|
||
const { stage, currentRollout } = previousResult;
|
||
|
||
if (stage === 'rollout-stopped') {
|
||
console.log('❌ 灰度发布因指标不达标而停止');
|
||
return {
|
||
...previousResult,
|
||
finalStatus: 'failed',
|
||
reason: 'metrics-failed',
|
||
};
|
||
}
|
||
|
||
console.log('✅ 灰度发布验证完成');
|
||
console.log(`📊 最终覆盖率: ${currentRollout}%`);
|
||
|
||
return {
|
||
...previousResult,
|
||
finalStatus: 'success',
|
||
completedAt: new Date().toISOString(),
|
||
};
|
||
},
|
||
},
|
||
],
|
||
validate: (context: CommandContext) => {
|
||
if (!context.options.version) {
|
||
console.error('❌ 灰度发布必须指定版本号');
|
||
return false;
|
||
}
|
||
return true;
|
||
},
|
||
options: {
|
||
version: {
|
||
hasValue: true,
|
||
description: '发布版本号 (必需)',
|
||
},
|
||
initialRollout: {
|
||
hasValue: true,
|
||
default: 5,
|
||
description: '初始覆盖率百分比',
|
||
},
|
||
autoExpand: {
|
||
hasValue: false,
|
||
default: true,
|
||
description: '自动扩大发布范围',
|
||
},
|
||
},
|
||
};
|
||
|
||
/**
|
||
* 多环境发布工作流
|
||
*/
|
||
export const multiEnvironmentDeployWorkflow: CustomWorkflow = {
|
||
name: 'multi-env-deploy',
|
||
description: '多环境依次发布工作流',
|
||
steps: [
|
||
{
|
||
name: 'deploy-to-dev',
|
||
description: '部署到开发环境',
|
||
execute: async (context: CommandContext) => {
|
||
console.log('🔧 部署到开发环境...');
|
||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||
|
||
const devResult = {
|
||
environment: 'development',
|
||
deployTime: new Date().toISOString(),
|
||
success: true,
|
||
};
|
||
|
||
console.log('✅ 开发环境部署完成');
|
||
return { dev: devResult };
|
||
},
|
||
},
|
||
{
|
||
name: 'run-integration-tests',
|
||
description: '运行集成测试',
|
||
execute: async (context: CommandContext, previousResult: any) => {
|
||
console.log('🧪 运行集成测试...');
|
||
|
||
const testSuites = ['API测试', '数据库测试', '第三方服务测试'];
|
||
const results = [];
|
||
|
||
for (const suite of testSuites) {
|
||
console.log(` 运行 ${suite}...`);
|
||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||
|
||
const passed = Math.random() > 0.1; // 90% 通过率
|
||
results.push({ suite, passed });
|
||
console.log(` ${passed ? '✅' : '❌'} ${suite}`);
|
||
}
|
||
|
||
const allPassed = results.every((r) => r.passed);
|
||
console.log(
|
||
`🧪 集成测试结果: ${allPassed ? '✅ 全部通过' : '❌ 有失败项'}`,
|
||
);
|
||
|
||
return {
|
||
...previousResult,
|
||
integrationTests: { results, allPassed },
|
||
};
|
||
},
|
||
},
|
||
{
|
||
name: 'deploy-to-staging',
|
||
description: '部署到预发布环境',
|
||
execute: async (context: CommandContext, previousResult: any) => {
|
||
const { integrationTests } = previousResult;
|
||
|
||
if (!integrationTests.allPassed) {
|
||
throw new Error('集成测试未通过,无法部署到预发布环境');
|
||
}
|
||
|
||
console.log('🎭 部署到预发布环境...');
|
||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||
|
||
const stagingResult = {
|
||
environment: 'staging',
|
||
deployTime: new Date().toISOString(),
|
||
success: true,
|
||
};
|
||
|
||
console.log('✅ 预发布环境部署完成');
|
||
return {
|
||
...previousResult,
|
||
staging: stagingResult,
|
||
};
|
||
},
|
||
},
|
||
{
|
||
name: 'run-e2e-tests',
|
||
description: '运行端到端测试',
|
||
execute: async (context: CommandContext, previousResult: any) => {
|
||
console.log('🎯 运行端到端测试...');
|
||
|
||
const e2eTests = [
|
||
'用户登录流程',
|
||
'核心业务流程',
|
||
'支付流程',
|
||
'数据同步',
|
||
];
|
||
|
||
const results = [];
|
||
|
||
for (const test of e2eTests) {
|
||
console.log(` 测试 ${test}...`);
|
||
await new Promise((resolve) => setTimeout(resolve, 800));
|
||
|
||
const passed = Math.random() > 0.05; // 95% 通过率
|
||
results.push({ test, passed });
|
||
console.log(` ${passed ? '✅' : '❌'} ${test}`);
|
||
}
|
||
|
||
const allPassed = results.every((r) => r.passed);
|
||
console.log(
|
||
`🎯 E2E测试结果: ${allPassed ? '✅ 全部通过' : '❌ 有失败项'}`,
|
||
);
|
||
|
||
return {
|
||
...previousResult,
|
||
e2eTests: { results, allPassed },
|
||
};
|
||
},
|
||
},
|
||
{
|
||
name: 'deploy-to-production',
|
||
description: '部署到生产环境',
|
||
execute: async (context: CommandContext, previousResult: any) => {
|
||
const { e2eTests } = previousResult;
|
||
|
||
if (!e2eTests.allPassed) {
|
||
console.log('⚠️ E2E测试未全部通过,需要手动确认是否继续部署');
|
||
|
||
if (!context.options.forceProduction) {
|
||
throw new Error('E2E测试未通过,使用 --force-production 强制部署');
|
||
}
|
||
}
|
||
|
||
console.log('🚀 部署到生产环境...');
|
||
|
||
// 生产部署需要更长时间
|
||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||
|
||
const productionResult = {
|
||
environment: 'production',
|
||
deployTime: new Date().toISOString(),
|
||
success: true,
|
||
version: context.options.version,
|
||
};
|
||
|
||
console.log('🎉 生产环境部署完成');
|
||
return {
|
||
...previousResult,
|
||
production: productionResult,
|
||
};
|
||
},
|
||
condition: (context: CommandContext) => {
|
||
// 只有在非跳过生产部署的情况下才执行
|
||
return !context.options.skipProduction;
|
||
},
|
||
},
|
||
{
|
||
name: 'post-deployment-verification',
|
||
description: '部署后验证',
|
||
execute: async (context: CommandContext, previousResult: any) => {
|
||
console.log('🔍 执行部署后验证...');
|
||
|
||
const verifications = [
|
||
'健康检查',
|
||
'关键接口测试',
|
||
'监控数据验证',
|
||
'用户访问验证',
|
||
];
|
||
|
||
for (const verification of verifications) {
|
||
console.log(` ${verification}...`);
|
||
await new Promise((resolve) => setTimeout(resolve, 300));
|
||
console.log(` ✅ ${verification} 通过`);
|
||
}
|
||
|
||
console.log('✅ 部署后验证完成');
|
||
|
||
return {
|
||
...previousResult,
|
||
postDeploymentVerification: {
|
||
completed: true,
|
||
verifiedAt: new Date().toISOString(),
|
||
},
|
||
};
|
||
},
|
||
},
|
||
],
|
||
validate: (context: CommandContext) => {
|
||
if (!context.options.version) {
|
||
console.error('❌ 多环境部署必须指定版本号');
|
||
return false;
|
||
}
|
||
return true;
|
||
},
|
||
options: {
|
||
version: {
|
||
hasValue: true,
|
||
description: '发布版本号 (必需)',
|
||
},
|
||
skipProduction: {
|
||
hasValue: false,
|
||
default: false,
|
||
description: '跳过生产环境部署',
|
||
},
|
||
forceProduction: {
|
||
hasValue: false,
|
||
default: false,
|
||
description: '强制部署到生产环境(即使测试未全部通过)',
|
||
},
|
||
},
|
||
};
|
||
|
||
/**
|
||
* 回滚工作流
|
||
*/
|
||
export const rollbackWorkflow: CustomWorkflow = {
|
||
name: 'rollback-workflow',
|
||
description: '应用回滚工作流',
|
||
steps: [
|
||
{
|
||
name: 'validate-target-version',
|
||
description: '验证目标版本',
|
||
execute: async (context: CommandContext) => {
|
||
console.log('🔍 验证目标回滚版本...');
|
||
|
||
const { targetVersion } = context.options;
|
||
|
||
if (!targetVersion) {
|
||
throw new Error('必须指定目标回滚版本');
|
||
}
|
||
|
||
// 模拟版本验证
|
||
console.log(`验证版本 ${targetVersion} 是否存在...`);
|
||
await new Promise((resolve) => setTimeout(resolve, 800));
|
||
|
||
const versionExists = true; // 模拟版本存在
|
||
|
||
if (!versionExists) {
|
||
throw new Error(`版本 ${targetVersion} 不存在`);
|
||
}
|
||
|
||
console.log(`✅ 版本 ${targetVersion} 验证通过`);
|
||
|
||
return {
|
||
targetVersion,
|
||
validated: true,
|
||
};
|
||
},
|
||
},
|
||
{
|
||
name: 'backup-current-state',
|
||
description: '备份当前状态',
|
||
execute: async (context: CommandContext, previousResult: any) => {
|
||
console.log('💾 备份当前应用状态...');
|
||
|
||
const backup = {
|
||
backupId: `backup-${Date.now()}`,
|
||
timestamp: new Date().toISOString(),
|
||
currentVersion: 'current-version', // 在实际应用中获取当前版本
|
||
configSnapshot: 'config-data', // 在实际应用中获取配置快照
|
||
};
|
||
|
||
await new Promise((resolve) => setTimeout(resolve, 1500));
|
||
|
||
console.log(`✅ 状态备份完成,备份ID: ${backup.backupId}`);
|
||
|
||
return {
|
||
...previousResult,
|
||
backup,
|
||
};
|
||
},
|
||
},
|
||
{
|
||
name: 'execute-rollback',
|
||
description: '执行回滚',
|
||
execute: async (context: CommandContext, previousResult: any) => {
|
||
console.log('🔄 执行回滚操作...');
|
||
|
||
const { targetVersion } = previousResult;
|
||
|
||
console.log(`回滚到版本: ${targetVersion}`);
|
||
|
||
// 模拟回滚过程
|
||
const rollbackSteps = [
|
||
'停止当前服务',
|
||
'切换到目标版本',
|
||
'更新配置',
|
||
'重启服务',
|
||
];
|
||
|
||
for (const step of rollbackSteps) {
|
||
console.log(` ${step}...`);
|
||
await new Promise((resolve) => setTimeout(resolve, 600));
|
||
console.log(` ✅ ${step} 完成`);
|
||
}
|
||
|
||
console.log('🎉 回滚执行完成');
|
||
|
||
return {
|
||
...previousResult,
|
||
rollbackCompleted: true,
|
||
rollbackTime: new Date().toISOString(),
|
||
};
|
||
},
|
||
},
|
||
{
|
||
name: 'verify-rollback',
|
||
description: '验证回滚结果',
|
||
execute: async (context: CommandContext, previousResult: any) => {
|
||
console.log('🔍 验证回滚结果...');
|
||
|
||
const verificationChecks = [
|
||
'服务可用性检查',
|
||
'功能完整性检查',
|
||
'性能基线检查',
|
||
'数据一致性检查',
|
||
];
|
||
|
||
const results = [];
|
||
|
||
for (const check of verificationChecks) {
|
||
console.log(` ${check}...`);
|
||
await new Promise((resolve) => setTimeout(resolve, 400));
|
||
|
||
const passed = Math.random() > 0.05; // 95% 通过率
|
||
results.push({ check, passed });
|
||
console.log(` ${passed ? '✅' : '❌'} ${check}`);
|
||
}
|
||
|
||
const allPassed = results.every((r) => r.passed);
|
||
|
||
if (!allPassed) {
|
||
console.log('⚠️ 部分验证未通过,可能需要进一步检查');
|
||
} else {
|
||
console.log('✅ 回滚验证全部通过');
|
||
}
|
||
|
||
return {
|
||
...previousResult,
|
||
verification: { results, allPassed },
|
||
};
|
||
},
|
||
},
|
||
{
|
||
name: 'notify-stakeholders',
|
||
description: '通知相关人员',
|
||
execute: async (context: CommandContext, previousResult: any) => {
|
||
console.log('📧 通知相关人员...');
|
||
|
||
const { targetVersion, verification } = previousResult;
|
||
|
||
const notification = {
|
||
type: 'rollback-completed',
|
||
targetVersion,
|
||
success: verification.allPassed,
|
||
timestamp: new Date().toISOString(),
|
||
notifiedStakeholders: [
|
||
'开发团队',
|
||
'运维团队',
|
||
'产品团队',
|
||
'测试团队',
|
||
],
|
||
};
|
||
|
||
console.log('📬 发送通知给:');
|
||
notification.notifiedStakeholders.forEach((stakeholder) => {
|
||
console.log(` - ${stakeholder}`);
|
||
});
|
||
|
||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||
|
||
console.log('✅ 通知发送完成');
|
||
|
||
return {
|
||
...previousResult,
|
||
notification,
|
||
};
|
||
},
|
||
},
|
||
],
|
||
validate: (context: CommandContext) => {
|
||
if (!context.options.targetVersion) {
|
||
console.error('❌ 回滚操作必须指定目标版本');
|
||
return false;
|
||
}
|
||
return true;
|
||
},
|
||
options: {
|
||
targetVersion: {
|
||
hasValue: true,
|
||
description: '目标回滚版本 (必需)',
|
||
},
|
||
skipVerification: {
|
||
hasValue: false,
|
||
default: false,
|
||
description: '跳过回滚后验证',
|
||
},
|
||
},
|
||
};
|
||
|
||
/**
|
||
* 导出所有工作流
|
||
*/
|
||
export const customWorkflows = [
|
||
canaryDeploymentWorkflow,
|
||
multiEnvironmentDeployWorkflow,
|
||
rollbackWorkflow,
|
||
];
|