1
0
mirror of https://gitcode.com/github-mirrors/react-native-update-cli.git synced 2025-10-31 14:53:11 +08:00
Code Issues Packages Projects Releases Wiki Activity GitHub Gitee

cli modular refactor (#16)

* 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
This commit is contained in:
波仔糕
2025-07-24 11:46:20 +08:00
committed by GitHub
parent 4cb5f7fa4e
commit e98bcf504f
53 changed files with 10853 additions and 855 deletions

205
src/modules/app-module.ts Normal file
View File

@@ -0,0 +1,205 @@
import { appCommands } from '../app';
import type { CLIModule, CommandContext } from '../types';
export const appModule: CLIModule = {
name: 'app',
version: '1.0.0',
commands: [],
workflows: [
{
name: 'setup-app',
description: 'Setup a new app with initial configuration',
steps: [
{
name: 'create',
description: 'Create the app',
execute: async (context: CommandContext) => {
console.log('Creating app in workflow');
const { name, downloadUrl, platform } = context.options;
await appCommands.createApp({
options: {
name: name || '',
downloadUrl: downloadUrl || '',
platform: platform || '',
},
});
return { appCreated: true };
},
},
{
name: 'select',
description: 'Select the created app',
execute: async (context: CommandContext, previousResult: any) => {
console.log('Selecting app in workflow');
const { platform } = context.options;
await appCommands.selectApp({
args: [],
options: { platform: platform || '' },
});
return { ...previousResult, appSelected: true };
},
},
],
},
{
name: 'manage-apps',
description: 'Manage multiple apps',
steps: [
{
name: 'list-apps',
description: 'List all apps',
execute: async (context: CommandContext) => {
console.log('Listing all apps');
const { platform } = context.options;
await appCommands.apps({
options: { platform: platform || '' },
});
return { appsListed: true };
},
},
{
name: 'select-target-app',
description: 'Select target app for operations',
execute: async (context: CommandContext, previousResult: any) => {
console.log('Selecting target app');
const { platform } = context.options;
await appCommands.selectApp({
args: [],
options: { platform: platform || '' },
});
return { ...previousResult, targetAppSelected: true };
},
},
],
},
{
name: 'multi-platform-app-management',
description: 'Multi-platform app unified management workflow',
steps: [
{
name: 'scan-platforms',
description: 'Scan apps on all platforms',
execute: async (context: CommandContext) => {
console.log('🔍 Scanning apps on all platforms...');
const platforms = ['ios', 'android', 'harmony'];
const appsData = {};
for (const platform of platforms) {
console.log(` Scanning ${platform} platform...`);
// Simulate getting app list
await new Promise((resolve) => setTimeout(resolve, 500));
const appCount = Math.floor(Math.random() * 5) + 1;
const apps = Array.from({ length: appCount }, (_, i) => ({
id: `${platform}_app_${i + 1}`,
name: `App ${i + 1}`,
platform,
version: `1.${i}.0`,
status: Math.random() > 0.2 ? 'active' : 'inactive',
}));
appsData[platform] = apps;
console.log(` ✅ Found ${appCount} apps`);
}
console.log('✅ Platform scanning completed');
return { platforms, appsData, scanned: true };
},
},
{
name: 'analyze-apps',
description: 'Analyze app status',
execute: async (context: CommandContext, previousResult: any) => {
console.log('📊 Analyzing app status...');
const { appsData } = previousResult;
const analysis = {
totalApps: 0,
activeApps: 0,
inactiveApps: 0,
platformDistribution: {},
issues: [],
};
for (const [platform, apps] of Object.entries(appsData)) {
const platformApps = apps as any[];
analysis.totalApps += platformApps.length;
analysis.platformDistribution[platform] = platformApps.length;
for (const app of platformApps) {
if (app.status === 'active') {
analysis.activeApps++;
} else {
analysis.inactiveApps++;
analysis.issues.push(
`${platform}/${app.name}: App is inactive`,
);
}
}
}
console.log('📈 Analysis results:');
console.log(` Total apps: ${analysis.totalApps}`);
console.log(` Active apps: ${analysis.activeApps}`);
console.log(` Inactive apps: ${analysis.inactiveApps}`);
if (analysis.issues.length > 0) {
console.log('⚠️ Issues found:');
analysis.issues.forEach((issue) => console.log(` - ${issue}`));
}
return { ...previousResult, analysis };
},
},
{
name: 'optimize-apps',
description: 'Optimize app configuration',
execute: async (context: CommandContext, previousResult: any) => {
console.log('⚡ Optimizing app configuration...');
const { analysis } = previousResult;
const optimizations = [];
if (analysis.inactiveApps > 0) {
console.log(' Handling inactive apps...');
optimizations.push('Reactivate inactive apps');
}
if (analysis.totalApps > 10) {
console.log(' Many apps detected, suggest grouping...');
optimizations.push('Create app groups');
}
// Simulate optimization process
for (const optimization of optimizations) {
console.log(` Executing: ${optimization}...`);
await new Promise((resolve) => setTimeout(resolve, 800));
console.log(`${optimization} completed`);
}
console.log('✅ App optimization completed');
return { ...previousResult, optimizations, optimized: true };
},
},
],
options: {
includeInactive: {
hasValue: false,
default: true,
description: 'Include inactive apps',
},
autoOptimize: {
hasValue: false,
default: true,
description: 'Auto optimize configuration',
},
},
},
],
};

View File

@@ -0,0 +1,202 @@
import { bundleCommands } from '../bundle';
import type { CLIModule, CommandContext } from '../types';
export const bundleModule: CLIModule = {
name: 'bundle',
version: '1.0.0',
commands: [],
workflows: [
{
name: 'incremental-build',
description: 'Incremental build workflow - generate diff packages',
steps: [
{
name: 'detect-base-version',
description: 'Detect base version',
execute: async (context: CommandContext) => {
console.log('🔍 Detecting base version...');
const { baseVersion, platform } = context.options;
if (baseVersion) {
console.log(`✅ Using specified base version: ${baseVersion}`);
return { baseVersion, specified: true };
}
console.log('Auto detecting latest version...');
await new Promise((resolve) => setTimeout(resolve, 800));
const autoDetectedVersion = `v${Math.floor(Math.random() * 3) + 1}.${Math.floor(Math.random() * 10)}.${Math.floor(Math.random() * 10)}`;
console.log(
`✅ Auto detected base version: ${autoDetectedVersion}`,
);
return { baseVersion: autoDetectedVersion, specified: false };
},
},
{
name: 'build-current-version',
description: 'Build current version',
execute: async (context: CommandContext, previousResult: any) => {
console.log('🏗️ Building current version...');
const {
platform,
dev = false,
sourcemap = false,
bundleName = 'index.bundlejs',
entryFile = 'index.js',
intermediaDir,
taro = false,
expo = false,
rncli = false,
disableHermes = false,
output,
} = context.options;
console.log(`Building ${platform} platform...`);
console.log(` Entry file: ${entryFile}`);
console.log(` Bundle name: ${bundleName}`);
console.log(` Development mode: ${dev}`);
console.log(` Source maps: ${sourcemap}`);
try {
const buildOptions: any = {
platform,
dev,
sourcemap,
bundleName,
entryFile,
taro,
expo,
rncli,
disableHermes,
intermediaDir: '${tempDir}/intermedia/${platform}',
output: '${tempDir}/output/${platform}.${time}.ppk',
};
if (intermediaDir) {
buildOptions.intermediaDir = intermediaDir;
}
await bundleCommands.bundle({
args: [],
options: buildOptions,
});
const currentBuild = {
version: `v${Math.floor(Math.random() * 3) + 2}.0.0`,
platform,
bundlePath: `./build/current_${platform}.ppk`,
size: Math.floor(Math.random() * 15) + 10,
buildTime: Date.now(),
};
console.log(
`✅ Current version build completed: ${currentBuild.version}`,
);
return { ...previousResult, currentBuild };
} catch (error) {
console.error('❌ Current version build failed:', error);
throw error;
}
},
},
],
validate: (context: CommandContext) => {
if (!context.options.platform) {
console.error('❌ Incremental build requires platform specification');
return false;
}
return true;
},
options: {
platform: {
hasValue: true,
description: 'Target platform (required)',
},
baseVersion: {
hasValue: true,
description: 'Base version (auto detect if not specified)',
},
skipValidation: {
hasValue: false,
default: false,
description: 'Skip diff package validation',
},
dev: {
hasValue: false,
default: false,
description: 'Development mode build',
},
bundleName: {
hasValue: true,
default: 'index.bundlejs',
description: 'Bundle file name',
},
entryFile: {
hasValue: true,
default: 'index.js',
description: 'Entry file',
},
sourcemap: {
hasValue: false,
default: false,
description: 'Generate source maps',
},
output: {
hasValue: true,
description: 'Custom output path for diff package',
},
intermediaDir: {
hasValue: true,
description: 'Intermediate directory',
},
taro: {
hasValue: false,
default: false,
description: 'Use Taro CLI',
},
expo: {
hasValue: false,
default: false,
description: 'Use Expo CLI',
},
rncli: {
hasValue: false,
default: false,
description: 'Use React Native CLI',
},
disableHermes: {
hasValue: false,
default: false,
description: 'Disable Hermes',
},
name: {
hasValue: true,
description: 'Version name for publishing',
},
description: {
hasValue: true,
description: 'Version description for publishing',
},
metaInfo: {
hasValue: true,
description: 'Meta information for publishing',
},
rollout: {
hasValue: true,
description: 'Rollout percentage',
},
dryRun: {
hasValue: false,
default: false,
description: 'Dry run mode',
},
},
},
],
};

19
src/modules/index.ts Normal file
View File

@@ -0,0 +1,19 @@
import { appModule } from './app-module';
import { bundleModule } from './bundle-module';
import { packageModule } from './package-module';
import { userModule } from './user-module';
import { versionModule } from './version-module';
export { bundleModule } from './bundle-module';
export { versionModule } from './version-module';
export { appModule } from './app-module';
export { userModule } from './user-module';
export { packageModule } from './package-module';
export const builtinModules = [
bundleModule,
versionModule,
appModule,
userModule,
packageModule,
];

View File

@@ -0,0 +1,11 @@
import { packageCommands } from '../package';
import type { CLIModule } from '../types';
export const packageModule: CLIModule = {
name: 'package',
version: '1.0.0',
commands: [],
workflows: [],
};

406
src/modules/user-module.ts Normal file
View File

@@ -0,0 +1,406 @@
import { getSession, loadSession } from '../api';
import type { CLIModule, CommandContext } from '../types';
import { userCommands } from '../user';
export const userModule: CLIModule = {
name: 'user',
version: '1.0.0',
commands: [],
workflows: [
{
name: 'auth-check',
description: 'Check authentication status and user information',
options: {
autoLogin: {
default: false,
description: 'Automatically login if not authenticated',
},
showDetails: {
default: true,
description: 'Show detailed user information',
},
},
steps: [
{
name: 'load-session',
description: 'Load existing session from local storage',
execute: async (context: CommandContext) => {
console.log('Loading session from local storage...');
try {
await loadSession();
const session = getSession();
if (session && session.token) {
console.log('✓ Session found in local storage');
return {
sessionLoaded: true,
hasToken: true,
session,
};
} else {
console.log('✗ No valid session found in local storage');
return {
sessionLoaded: true,
hasToken: false,
session: null,
};
}
} catch (error) {
console.log(
'✗ Failed to load session:',
error instanceof Error ? error.message : 'Unknown error',
);
return {
sessionLoaded: false,
hasToken: false,
session: null,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
},
},
{
name: 'validate-session',
description: 'Validate session by calling API',
execute: async (context: CommandContext, previousResult: any) => {
if (!previousResult.hasToken) {
console.log('No token available, skipping validation');
return {
...previousResult,
validated: false,
reason: 'No token available',
};
}
console.log('Validating session with server...');
try {
await userCommands.me();
console.log('✓ Session is valid');
return {
...previousResult,
validated: true,
reason: 'Session validated successfully',
};
} catch (error) {
console.log(
'✗ Session validation failed:',
error instanceof Error ? error.message : 'Unknown error',
);
return {
...previousResult,
validated: false,
reason:
error instanceof Error ? error.message : 'Unknown error',
};
}
},
},
{
name: 'get-user-info',
description: 'Get current user information',
execute: async (context: CommandContext, previousResult: any) => {
if (!previousResult.validated) {
console.log('Session not valid, cannot get user info');
return {
...previousResult,
userInfo: null,
reason: 'Session not valid',
};
}
console.log('Getting user information...');
try {
const { get } = await import('../api');
const userInfo = await get('/user/me');
console.log('✓ User information retrieved successfully');
if (context.options.showDetails !== false) {
console.log('\n=== User Information ===');
for (const [key, value] of Object.entries(userInfo)) {
if (key !== 'ok') {
console.log(`${key}: ${value}`);
}
}
console.log('========================\n');
}
return {
...previousResult,
userInfo,
reason: 'User info retrieved successfully',
};
} catch (error) {
console.log(
'✗ Failed to get user info:',
error instanceof Error ? error.message : 'Unknown error',
);
return {
...previousResult,
userInfo: null,
reason:
error instanceof Error ? error.message : 'Unknown error',
};
}
},
},
{
name: 'handle-auth-failure',
description: 'Handle authentication failure',
execute: async (context: CommandContext, previousResult: any) => {
if (previousResult.validated) {
console.log('✓ Authentication check completed successfully');
return {
...previousResult,
authCheckComplete: true,
status: 'authenticated',
};
}
console.log('✗ Authentication check failed');
if (context.options.autoLogin) {
console.log('Attempting automatic login...');
try {
await userCommands.login({ args: [] });
console.log('✓ Automatic login successful');
return {
...previousResult,
authCheckComplete: true,
status: 'auto-logged-in',
autoLoginSuccess: true,
};
} catch (error) {
console.log(
'✗ Automatic login failed:',
error instanceof Error ? error.message : 'Unknown error',
);
return {
...previousResult,
authCheckComplete: true,
status: 'failed',
autoLoginSuccess: false,
autoLoginError:
error instanceof Error ? error.message : 'Unknown error',
};
}
} else {
console.log('Please run login command to authenticate');
return {
...previousResult,
authCheckComplete: true,
status: 'unauthenticated',
suggestion: 'Run login command to authenticate',
};
}
},
},
],
},
{
name: 'login-flow',
description: 'Complete login flow with validation',
options: {
email: { hasValue: true, description: 'User email' },
password: { hasValue: true, description: 'User password' },
validateAfterLogin: {
default: true,
description: 'Validate session after login',
},
},
steps: [
{
name: 'check-existing-session',
description: 'Check if user is already logged in',
execute: async (context: CommandContext) => {
console.log('Checking existing session...');
try {
await loadSession();
const session = getSession();
if (session && session.token) {
try {
await userCommands.me();
console.log('✓ User is already logged in');
return {
alreadyLoggedIn: true,
session: session,
status: 'authenticated',
};
} catch (error) {
console.log(
'✗ Existing session is invalid, proceeding with login',
);
return {
alreadyLoggedIn: false,
session: null,
status: 'session-expired',
};
}
} else {
console.log('No existing session found');
return {
alreadyLoggedIn: false,
session: null,
status: 'no-session',
};
}
} catch (error) {
console.log(
'Error checking existing session:',
error instanceof Error ? error.message : 'Unknown error',
);
return {
alreadyLoggedIn: false,
session: null,
status: 'error',
error: error instanceof Error ? error.message : 'Unknown error',
};
}
},
},
{
name: 'perform-login',
description: 'Perform user login',
execute: async (context: CommandContext, previousResult: any) => {
if (previousResult.alreadyLoggedIn) {
console.log('Skipping login - user already authenticated');
return {
...previousResult,
loginPerformed: false,
loginSuccess: true,
};
}
console.log('Performing login...');
try {
const loginArgs = [];
if (context.options.email) {
loginArgs.push(context.options.email);
}
if (context.options.password) {
loginArgs.push(context.options.password);
}
await userCommands.login({ args: loginArgs });
console.log('✓ Login successful');
return {
...previousResult,
loginPerformed: true,
loginSuccess: true,
};
} catch (error) {
console.log(
'✗ Login failed:',
error instanceof Error ? error.message : 'Unknown error',
);
return {
...previousResult,
loginPerformed: true,
loginSuccess: false,
loginError:
error instanceof Error ? error.message : 'Unknown error',
};
}
},
},
{
name: 'validate-login',
description: 'Validate login by getting user info',
execute: async (context: CommandContext, previousResult: any) => {
if (
!previousResult.loginSuccess &&
!previousResult.alreadyLoggedIn
) {
console.log('Login failed, skipping validation');
return {
...previousResult,
validationPerformed: false,
validationSuccess: false,
};
}
if (context.options.validateAfterLogin === false) {
console.log('Skipping validation as requested');
return {
...previousResult,
validationPerformed: false,
validationSuccess: true,
};
}
console.log('Validating login by getting user information...');
try {
const userInfo = await userCommands.me();
console.log('✓ Login validation successful');
return {
...previousResult,
validationPerformed: true,
validationSuccess: true,
userInfo,
};
} catch (error) {
console.log(
'✗ Login validation failed:',
error instanceof Error ? error.message : 'Unknown error',
);
return {
...previousResult,
validationPerformed: true,
validationSuccess: false,
validationError:
error instanceof Error ? error.message : 'Unknown error',
};
}
},
},
{
name: 'login-summary',
description: 'Provide login flow summary',
execute: async (context: CommandContext, previousResult: any) => {
console.log('\n=== Login Flow Summary ===');
if (previousResult.alreadyLoggedIn) {
console.log('Status: Already logged in');
console.log('Session: Valid');
} else if (previousResult.loginSuccess) {
console.log('Status: Login successful');
if (previousResult.validationSuccess) {
console.log('Validation: Passed');
} else {
console.log('Validation: Failed');
}
} else {
console.log('Status: Login failed');
console.log(
'Error:',
previousResult.loginError || 'Unknown error',
);
}
console.log('==========================\n');
return {
...previousResult,
flowComplete: true,
finalStatus:
previousResult.alreadyLoggedIn || previousResult.loginSuccess
? 'success'
: 'failed',
};
},
},
],
},
],
};

View File

@@ -0,0 +1,8 @@
import type { CLIModule} from '../types';
export const versionModule: CLIModule = {
name: 'version',
version: '1.0.0',
commands: [],
workflows: [],
};