Skip to content

Commit bf97ca6

Browse files
CopilotThePlenkov
andauthored
feat: add checkout command and remaining CDS/DDIC subtask completions
Agent-Logs-Url: https://github.com/abapify/adt-cli/sessions/b10040d3-5194-47fe-a5ef-eafa9488a1b4 Co-authored-by: ThePlenkov <6381507+ThePlenkov@users.noreply.github.com>
1 parent 6b49e9a commit bf97ca6

2 files changed

Lines changed: 192 additions & 0 deletions

File tree

packages/adt-cli/src/lib/cli.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
import { createDatapreviewCommand } from './commands/datapreview';
4343
import { createAbapCommand } from './commands/abap';
4444
import { ddlCommand, dclCommand } from './commands/cds';
45+
import { createCheckoutCommand } from './commands/checkout';
4546
import { refreshCommand } from './commands/auth/refresh';
4647
// Deploy command moved to @abapify/adt-export plugin
4748
// Add '@abapify/adt-export/commands/export' to adt.config.ts commands array to enable
@@ -264,6 +265,9 @@ export async function createCLI(options?: {
264265
program.addCommand(ddlCommand);
265266
program.addCommand(dclCommand);
266267

268+
// Checkout command (download SAP objects to abapgit-compatible files)
269+
program.addCommand(createCheckoutCommand());
270+
267271
// REPL - Interactive hypermedia navigator
268272
program.addCommand(createReplCommand());
269273

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
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

Comments
 (0)