|
| 1 | +/** |
| 2 | + * adt checkout - Download ABAP objects to abapgit-compatible local files |
| 3 | + * |
| 4 | + * Mirrors sapcli's `checkout` command. Creates abapgit-compatible local files |
| 5 | + * from SAP repository objects. |
| 6 | + * |
| 7 | + * Usage: |
| 8 | + * adt checkout class ZCL_MY_CLASS ./src |
| 9 | + * adt checkout interface ZIF_MY_INTF ./src |
| 10 | + * adt checkout program ZMYPROGRAM ./src |
| 11 | + * adt checkout package $ZMYPKG ./src |
| 12 | + * adt checkout package $ZMYPKG ./src --object-types CLAS,INTF |
| 13 | + * |
| 14 | + * This is a convenience alias for `adt import object` / `adt import package`. |
| 15 | + */ |
| 16 | + |
| 17 | +import { Command } from 'commander'; |
| 18 | +import { ImportService } from '../services/import/service'; |
| 19 | +import { IconRegistry } from '../utils/icon-registry'; |
| 20 | +import { getAdtClientV2 } from '../utils/adt-client-v2'; |
| 21 | +import { |
| 22 | + handleImportError, |
| 23 | + displayImportResults, |
| 24 | +} from '../utils/command-helpers'; |
| 25 | + |
| 26 | +const OBJECT_TYPE_ALIASES: Record<string, string> = { |
| 27 | + class: 'CLAS', |
| 28 | + clas: 'CLAS', |
| 29 | + interface: 'INTF', |
| 30 | + intf: 'INTF', |
| 31 | + program: 'PROG', |
| 32 | + prog: 'PROG', |
| 33 | + domain: 'DOMA', |
| 34 | + doma: 'DOMA', |
| 35 | + dataelement: 'DTEL', |
| 36 | + dtel: 'DTEL', |
| 37 | + table: 'TABL', |
| 38 | + tabl: 'TABL', |
| 39 | + structure: 'TABL', |
| 40 | + functiongroup: 'FUGR', |
| 41 | + fugr: 'FUGR', |
| 42 | + ddl: 'DDLS', |
| 43 | + ddls: 'DDLS', |
| 44 | + dcl: 'DCLS', |
| 45 | + dcls: 'DCLS', |
| 46 | + package: 'DEVC', |
| 47 | + devc: 'DEVC', |
| 48 | +}; |
| 49 | + |
| 50 | +function createObjectTypeSubcommand(typeName: string): Command { |
| 51 | + const upper = |
| 52 | + OBJECT_TYPE_ALIASES[typeName.toLowerCase()] ?? typeName.toUpperCase(); |
| 53 | + |
| 54 | + return new Command(typeName) |
| 55 | + .description(`Download ${typeName} to abapgit-compatible local files`) |
| 56 | + .argument('<name>', `${typeName} name`) |
| 57 | + .argument('[targetFolder]', 'Target output folder', '.') |
| 58 | + .option('-o, --output <path>', 'Output directory (overrides targetFolder)') |
| 59 | + .option('--format <format>', 'Output format (default: abapgit)', 'abapgit') |
| 60 | + .option('--debug', 'Enable debug output', false) |
| 61 | + .action( |
| 62 | + async ( |
| 63 | + name: string, |
| 64 | + targetFolder: string, |
| 65 | + options: { output?: string; format: string; debug: boolean }, |
| 66 | + ) => { |
| 67 | + try { |
| 68 | + await getAdtClientV2(); |
| 69 | + |
| 70 | + const importService = new ImportService(); |
| 71 | + const outputPath = options.output || targetFolder || './src'; |
| 72 | + |
| 73 | + console.log(`🔍 Downloading ${upper} ${name.toUpperCase()}...`); |
| 74 | + |
| 75 | + const result = await importService.importObject({ |
| 76 | + objectName: name.toUpperCase(), |
| 77 | + outputPath, |
| 78 | + format: options.format, |
| 79 | + debug: options.debug, |
| 80 | + }); |
| 81 | + |
| 82 | + if (result.results.success > 0) { |
| 83 | + const icon = IconRegistry.getIcon(result.objectType || upper); |
| 84 | + console.log( |
| 85 | + `\n${icon} ${result.objectType || upper} ${result.objectName}: downloaded`, |
| 86 | + ); |
| 87 | + console.log(`✨ Files written to: ${result.outputPath}`); |
| 88 | + } else { |
| 89 | + console.error( |
| 90 | + `❌ Failed to download ${upper} ${name.toUpperCase()}`, |
| 91 | + ); |
| 92 | + process.exit(1); |
| 93 | + } |
| 94 | + } catch (error) { |
| 95 | + handleImportError(error, options.debug); |
| 96 | + } |
| 97 | + }, |
| 98 | + ); |
| 99 | +} |
| 100 | + |
| 101 | +function createPackageSubcommand(): Command { |
| 102 | + return new Command('package') |
| 103 | + .description( |
| 104 | + 'Download a package and its contents to abapgit-compatible local files', |
| 105 | + ) |
| 106 | + .argument('<packageName>', 'ABAP package name') |
| 107 | + .argument('[targetFolder]', 'Target output folder', '.') |
| 108 | + .option('-o, --output <path>', 'Output directory (overrides targetFolder)') |
| 109 | + .option( |
| 110 | + '-t, --object-types <types>', |
| 111 | + 'Comma-separated object types (e.g., CLAS,INTF,DDLS)', |
| 112 | + ) |
| 113 | + .option('--no-sub-packages', 'Exclude subpackages') |
| 114 | + .option('--format <format>', 'Output format (default: abapgit)', 'abapgit') |
| 115 | + .option('--debug', 'Enable debug output', false) |
| 116 | + .action( |
| 117 | + async ( |
| 118 | + packageName: string, |
| 119 | + targetFolder: string, |
| 120 | + options: { |
| 121 | + output?: string; |
| 122 | + objectTypes?: string; |
| 123 | + subPackages: boolean; |
| 124 | + format: string; |
| 125 | + debug: boolean; |
| 126 | + }, |
| 127 | + ) => { |
| 128 | + try { |
| 129 | + await getAdtClientV2(); |
| 130 | + |
| 131 | + const importService = new ImportService(); |
| 132 | + const outputPath = options.output || targetFolder || './src'; |
| 133 | + const objectTypes = options.objectTypes |
| 134 | + ? options.objectTypes |
| 135 | + .split(',') |
| 136 | + .map((t: string) => t.trim().toUpperCase()) |
| 137 | + : undefined; |
| 138 | + |
| 139 | + console.log(`🚀 Downloading package: ${packageName.toUpperCase()}`); |
| 140 | + console.log(`📁 Target folder: ${outputPath}`); |
| 141 | + |
| 142 | + const result = await importService.importPackage({ |
| 143 | + packageName: packageName.toUpperCase(), |
| 144 | + outputPath, |
| 145 | + objectTypes, |
| 146 | + includeSubpackages: options.subPackages, |
| 147 | + format: options.format, |
| 148 | + debug: options.debug, |
| 149 | + }); |
| 150 | + |
| 151 | + displayImportResults( |
| 152 | + result, |
| 153 | + 'Package', |
| 154 | + result.packageName ?? packageName, |
| 155 | + ); |
| 156 | + } catch (error) { |
| 157 | + handleImportError(error, options.debug); |
| 158 | + } |
| 159 | + }, |
| 160 | + ); |
| 161 | +} |
| 162 | + |
| 163 | +export function createCheckoutCommand(): Command { |
| 164 | + const cmd = new Command('checkout').description( |
| 165 | + 'Download ABAP objects to abapgit-compatible local files', |
| 166 | + ); |
| 167 | + |
| 168 | + // Object type subcommands |
| 169 | + const objectTypes = [ |
| 170 | + 'class', |
| 171 | + 'interface', |
| 172 | + 'program', |
| 173 | + 'domain', |
| 174 | + 'dataelement', |
| 175 | + 'table', |
| 176 | + 'functiongroup', |
| 177 | + 'ddl', |
| 178 | + 'dcl', |
| 179 | + ]; |
| 180 | + for (const type of objectTypes) { |
| 181 | + cmd.addCommand(createObjectTypeSubcommand(type)); |
| 182 | + } |
| 183 | + |
| 184 | + // Package subcommand |
| 185 | + cmd.addCommand(createPackageSubcommand()); |
| 186 | + |
| 187 | + return cmd; |
| 188 | +} |
0 commit comments