|
| 1 | +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; |
| 2 | +import { Messages } from '@salesforce/core'; |
| 3 | +import { PythonChecker, type PythonVersionInfo } from '../utils/pythonChecker.js'; |
| 4 | +import { PipChecker, type PipPackageInfo } from '../utils/pipChecker.js'; |
| 5 | +import { DatacodeBinaryChecker, type DatacodeBinaryInfo, type DatacodeRunExecutionResult } from '../utils/datacodeBinaryChecker.js'; |
| 6 | + |
| 7 | +export type RunResult = { |
| 8 | + success: boolean; |
| 9 | + pythonVersion: PythonVersionInfo; |
| 10 | + packageInfo?: PipPackageInfo; |
| 11 | + binaryInfo?: DatacodeBinaryInfo; |
| 12 | + codeType: 'script' | 'function'; |
| 13 | + packageDir: string; |
| 14 | + targetOrg: string; |
| 15 | + status?: string; |
| 16 | + output?: string; |
| 17 | + message: string; |
| 18 | + executionResult?: DatacodeRunExecutionResult; |
| 19 | +}; |
| 20 | + |
| 21 | +// eslint-disable-next-line sf-plugin/command-summary, sf-plugin/command-example |
| 22 | +export abstract class RunBase extends SfCommand<RunResult> { |
| 23 | + // Override baseFlags to hide global flags |
| 24 | + public static readonly baseFlags = { |
| 25 | + ...SfCommand.baseFlags, |
| 26 | + // eslint-disable-next-line sf-plugin/no-hardcoded-messages-flags |
| 27 | + 'flags-dir': Flags.directory({ |
| 28 | + summary: 'Import flag values from a directory.', |
| 29 | + helpGroup: 'GLOBAL', |
| 30 | + hidden: false, |
| 31 | + }), |
| 32 | + // eslint-disable-next-line sf-plugin/no-json-flag, sf-plugin/no-hardcoded-messages-flags |
| 33 | + json: Flags.boolean({ |
| 34 | + summary: 'Format output as json.', |
| 35 | + helpGroup: 'GLOBAL', |
| 36 | + hidden: true, |
| 37 | + }), |
| 38 | + }; |
| 39 | + |
| 40 | + public async run(): Promise<RunResult> { |
| 41 | + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument |
| 42 | + const { flags } = await this.parse(this.constructor as any); |
| 43 | + const codeType = this.getCodeType(); |
| 44 | + const messages = this.getMessages(); |
| 45 | + |
| 46 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
| 47 | + const packageDir = flags['package-dir']; |
| 48 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
| 49 | + const targetOrg = flags['target-org']; |
| 50 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
| 51 | + const configFile = flags['config-file']; |
| 52 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
| 53 | + const dependencies = flags['dependencies']; |
| 54 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
| 55 | + const profile = flags['profile']; |
| 56 | + |
| 57 | + this.spinner.start(messages.getMessage('info.checkingPython')); |
| 58 | + |
| 59 | + try { |
| 60 | + // Check Python 3.11+ is installed |
| 61 | + const pythonInfo = await PythonChecker.checkPython311(); |
| 62 | + |
| 63 | + this.spinner.stop(); |
| 64 | + this.log(messages.getMessage('info.pythonFound', [pythonInfo.version, pythonInfo.command])); |
| 65 | + |
| 66 | + // Check required pip packages |
| 67 | + this.spinner.start(messages.getMessage('info.checkingPackages')); |
| 68 | + const packageInfo = await PipChecker.checkPackage('salesforce-data-customcode'); |
| 69 | + |
| 70 | + this.spinner.stop(); |
| 71 | + this.log(messages.getMessage('info.packageFound', [packageInfo.name, packageInfo.version])); |
| 72 | + |
| 73 | + // Check datacustomcode binary |
| 74 | + this.spinner.start(messages.getMessage('info.checkingBinary')); |
| 75 | + const binaryInfo = await DatacodeBinaryChecker.checkBinary(); |
| 76 | + |
| 77 | + this.spinner.stop(); |
| 78 | + this.log(messages.getMessage('info.binaryFound', [binaryInfo.version])); |
| 79 | + |
| 80 | + // Authenticate with the target org |
| 81 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call |
| 82 | + const orgUsername = targetOrg.getUsername() || 'target org'; |
| 83 | + this.spinner.start(messages.getMessage('info.authenticating', [orgUsername])); |
| 84 | + |
| 85 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call |
| 86 | + const connection = targetOrg.getConnection(); |
| 87 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call |
| 88 | + await connection.refreshAuth(); |
| 89 | + |
| 90 | + this.spinner.stop(); |
| 91 | + this.log(messages.getMessage('info.authenticated', [orgUsername])); |
| 92 | + |
| 93 | + // Execute datacustomcode run |
| 94 | + this.spinner.start(messages.getMessage('info.runningPackage')); |
| 95 | + const executionResult = await DatacodeBinaryChecker.executeBinaryRun( |
| 96 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument |
| 97 | + packageDir, |
| 98 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument |
| 99 | + orgUsername, |
| 100 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument |
| 101 | + configFile, |
| 102 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument |
| 103 | + dependencies, |
| 104 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument |
| 105 | + profile |
| 106 | + ); |
| 107 | + |
| 108 | + this.spinner.stop(); |
| 109 | + this.log(messages.getMessage('info.runComplete', [packageDir])); |
| 110 | + |
| 111 | + if (executionResult.status) { |
| 112 | + this.log(messages.getMessage('info.runStatus', [executionResult.status])); |
| 113 | + } |
| 114 | + |
| 115 | + if (executionResult.output) { |
| 116 | + this.log(messages.getMessage('info.runOutput', [executionResult.output])); |
| 117 | + } |
| 118 | + |
| 119 | + this.log(messages.getMessage('info.runSuccess')); |
| 120 | + |
| 121 | + return { |
| 122 | + success: true, |
| 123 | + pythonVersion: pythonInfo, |
| 124 | + packageInfo, |
| 125 | + binaryInfo, |
| 126 | + codeType, |
| 127 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
| 128 | + packageDir, |
| 129 | + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment |
| 130 | + targetOrg: orgUsername, |
| 131 | + status: executionResult.status, |
| 132 | + output: executionResult.output, |
| 133 | + executionResult, |
| 134 | + message: messages.getMessage('info.runSuccess'), |
| 135 | + }; |
| 136 | + } catch (error) { |
| 137 | + this.spinner.stop(); |
| 138 | + throw error; |
| 139 | + } |
| 140 | + } |
| 141 | + |
| 142 | + // Abstract methods that subclasses must implement |
| 143 | + protected abstract getCodeType(): 'script' | 'function'; |
| 144 | + protected abstract getMessages(): Messages<string>; |
| 145 | +} |
0 commit comments