CliBootstrap.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. using System.ComponentModel.DataAnnotations;
  2. using DocMigrator.Yaml;
  3. using YamlDotNet.Serialization;
  4. using YamlDotNet.Serialization.NamingConventions;
  5. using Microsoft.Extensions.Logging;
  6. using Microsoft.Extensions.Configuration;
  7. using Microsoft.Extensions.DependencyInjection;
  8. using RackPeek.Domain;
  9. using RackPeek.Domain.Helpers;
  10. using RackPeek.Domain.Persistence;
  11. using RackPeek.Domain.Persistence.Yaml;
  12. using Shared.Rcl.Commands;
  13. using Shared.Rcl.Commands.AccessPoints;
  14. using Shared.Rcl.Commands.AccessPoints.Labels;
  15. using Shared.Rcl.Commands.Ansible;
  16. using Shared.Rcl.Commands.Desktops;
  17. using Shared.Rcl.Commands.Desktops.Labels;
  18. using Shared.Rcl.Commands.Desktops.Cpus;
  19. using Shared.Rcl.Commands.Desktops.Drive;
  20. using Shared.Rcl.Commands.Desktops.Gpus;
  21. using Shared.Rcl.Commands.Desktops.Nics;
  22. using Shared.Rcl.Commands.Firewalls;
  23. using Shared.Rcl.Commands.Firewalls.Labels;
  24. using Shared.Rcl.Commands.Firewalls.Ports;
  25. using Shared.Rcl.Commands.Laptops;
  26. using Shared.Rcl.Commands.Laptops.Labels;
  27. using Shared.Rcl.Commands.Laptops.Cpus;
  28. using Shared.Rcl.Commands.Laptops.Drive;
  29. using Shared.Rcl.Commands.Laptops.Gpus;
  30. using Shared.Rcl.Commands.Routers;
  31. using Shared.Rcl.Commands.Routers.Labels;
  32. using Shared.Rcl.Commands.Routers.Ports;
  33. using Shared.Rcl.Commands.Servers;
  34. using Shared.Rcl.Commands.Servers.Cpus;
  35. using Shared.Rcl.Commands.Servers.Drives;
  36. using Shared.Rcl.Commands.Servers.Gpus;
  37. using Shared.Rcl.Commands.Servers.Labels;
  38. using Shared.Rcl.Commands.Servers.Nics;
  39. using Shared.Rcl.Commands.Services;
  40. using Shared.Rcl.Commands.Services.Labels;
  41. using Shared.Rcl.Commands.Switches;
  42. using Shared.Rcl.Commands.Switches.Labels;
  43. using Shared.Rcl.Commands.Switches.Ports;
  44. using Shared.Rcl.Commands.Systems;
  45. using Shared.Rcl.Commands.Systems.Labels;
  46. using Shared.Rcl.Commands.Ups;
  47. using Shared.Rcl.Commands.Ups.Labels;
  48. using Spectre.Console;
  49. using Spectre.Console.Cli;
  50. namespace Shared.Rcl;
  51. public static class CliBootstrap
  52. {
  53. private static string[]? _lastArgs;
  54. private static CommandApp? _app;
  55. private static bool _showingHelp;
  56. public static void SetContext(string[] args, CommandApp app)
  57. {
  58. _lastArgs = args;
  59. _app = app;
  60. }
  61. public static async Task RegisterInternals(
  62. IServiceCollection services,
  63. IConfiguration configuration,
  64. string yamlDir,
  65. string yamlFile)
  66. {
  67. services.AddSingleton(configuration);
  68. var appBasePath = AppContext.BaseDirectory;
  69. var resolvedYamlDir = Path.IsPathRooted(yamlDir)
  70. ? yamlDir
  71. : Path.Combine(appBasePath, yamlDir);
  72. Directory.CreateDirectory(resolvedYamlDir);
  73. var fullYamlPath = Path.Combine(resolvedYamlDir, yamlFile);
  74. if (!File.Exists(fullYamlPath)) await File.WriteAllTextAsync(fullYamlPath, "");
  75. services.AddLogging();
  76. services.AddScoped<RackPeekConfigMigrationDeserializer>();
  77. services.AddScoped<IResourceYamlMigrationService, ResourceYamlMigrationService>();
  78. var b = services.BuildServiceProvider();
  79. var collection = new YamlResourceCollection(
  80. fullYamlPath,
  81. new PhysicalTextFileStore(),
  82. new ResourceCollection(),
  83. b.GetRequiredService<IResourceYamlMigrationService>());
  84. await collection.LoadAsync();
  85. services.AddSingleton<IResourceCollection>(collection);
  86. // Infrastructure
  87. services.AddYamlRepos();
  88. // Application
  89. services.AddUseCases();
  90. services.AddCommands();
  91. }
  92. public static void BuildApp(CommandApp app)
  93. {
  94. // Spectre bootstrap
  95. app.Configure(config =>
  96. {
  97. config.SetApplicationName("rpk");
  98. config.ValidateExamples();
  99. config.SetApplicationVersion(RpkConstants.Version);
  100. config.SetExceptionHandler(HandleException);
  101. // Global summary
  102. config.AddCommand<GetTotalSummaryCommand>("summary")
  103. .WithDescription("Show a summarized report of all resources in the system.");
  104. // ----------------------------
  105. // Server commands (CRUD-style)
  106. // ----------------------------
  107. config.AddBranch("servers", server =>
  108. {
  109. server.SetDescription("Manage servers and their components.");
  110. server.AddCommand<ServerReportCommand>("summary")
  111. .WithDescription("Show a summarized hardware report for all servers.");
  112. server.AddCommand<ServerAddCommand>("add").WithDescription("Add a new server to the inventory.");
  113. server.AddCommand<ServerGetByNameCommand>("get")
  114. .WithDescription("List all servers or retrieve a specific server by name.");
  115. server.AddCommand<ServerDescribeCommand>("describe")
  116. .WithDescription("Display detailed information about a specific server.");
  117. server.AddCommand<ServerSetCommand>("set").WithDescription("Update properties of an existing server.");
  118. server.AddCommand<ServerDeleteCommand>("del").WithDescription("Delete a server from the inventory.");
  119. server.AddCommand<ServerTreeCommand>("tree")
  120. .WithDescription("Display the dependency tree of a server.");
  121. // Server CPUs
  122. server.AddBranch("cpu", cpu =>
  123. {
  124. cpu.SetDescription("Manage CPUs attached to a server.");
  125. cpu.AddCommand<ServerCpuAddCommand>("add").WithDescription("Add a CPU to a specific server.");
  126. cpu.AddCommand<ServerCpuSetCommand>("set").WithDescription("Update configuration of a server CPU.");
  127. cpu.AddCommand<ServerCpuRemoveCommand>("del").WithDescription("Remove a CPU from a server.");
  128. });
  129. // Server Drives
  130. server.AddBranch("drive", drive =>
  131. {
  132. drive.SetDescription("Manage drives attached to a server.");
  133. drive.AddCommand<ServerDriveAddCommand>("add").WithDescription("Add a storage drive to a server.");
  134. drive.AddCommand<ServerDriveUpdateCommand>("set")
  135. .WithDescription("Update properties of a server drive.");
  136. drive.AddCommand<ServerDriveRemoveCommand>("del").WithDescription("Remove a drive from a server.");
  137. });
  138. // Server GPUs
  139. server.AddBranch("gpu", gpu =>
  140. {
  141. gpu.SetDescription("Manage GPUs attached to a server.");
  142. gpu.AddCommand<ServerGpuAddCommand>("add").WithDescription("Add a GPU to a server.");
  143. gpu.AddCommand<ServerGpuUpdateCommand>("set").WithDescription("Update properties of a server GPU.");
  144. gpu.AddCommand<ServerGpuRemoveCommand>("del").WithDescription("Remove a GPU from a server.");
  145. });
  146. // Server NICs
  147. server.AddBranch("nic", nic =>
  148. {
  149. nic.SetDescription("Manage network interface cards (NICs) for a server.");
  150. nic.AddCommand<ServerNicAddCommand>("add").WithDescription("Add a NIC to a server.");
  151. nic.AddCommand<ServerNicUpdateCommand>("set").WithDescription("Update properties of a server NIC.");
  152. nic.AddCommand<ServerNicRemoveCommand>("del").WithDescription("Remove a NIC from a server.");
  153. });
  154. // Server Labels
  155. server.AddBranch("label", label =>
  156. {
  157. label.SetDescription("Manage labels on a server.");
  158. label.AddCommand<ServerLabelAddCommand>("add").WithDescription("Add a label to a server.");
  159. label.AddCommand<ServerLabelRemoveCommand>("remove").WithDescription("Remove a label from a server.");
  160. });
  161. });
  162. // ----------------------------
  163. // Switch commands
  164. // ----------------------------
  165. config.AddBranch("switches", switches =>
  166. {
  167. switches.SetDescription("Manage network switches.");
  168. switches.AddCommand<SwitchReportCommand>("summary")
  169. .WithDescription("Show a hardware report for all switches.");
  170. switches.AddCommand<SwitchAddCommand>("add")
  171. .WithDescription("Add a new network switch to the inventory.");
  172. switches.AddCommand<SwitchGetCommand>("list").WithDescription("List all switches in the system.");
  173. switches.AddCommand<SwitchGetByNameCommand>("get")
  174. .WithDescription("Retrieve details of a specific switch by name.");
  175. switches.AddCommand<SwitchDescribeCommand>("describe")
  176. .WithDescription("Show detailed information about a switch.");
  177. switches.AddCommand<SwitchSetCommand>("set").WithDescription("Update properties of a switch.");
  178. switches.AddCommand<SwitchDeleteCommand>("del").WithDescription("Delete a switch from the inventory.");
  179. switches.AddBranch("port", port =>
  180. {
  181. port.SetDescription("Manage ports on a network switch.");
  182. port.AddCommand<SwitchPortAddCommand>("add").WithDescription("Add a port to a switch.");
  183. port.AddCommand<SwitchPortUpdateCommand>("set").WithDescription("Update a switch port.");
  184. port.AddCommand<SwitchPortRemoveCommand>("del").WithDescription("Remove a port from a switch.");
  185. });
  186. switches.AddBranch("label", label =>
  187. {
  188. label.SetDescription("Manage labels on a switch.");
  189. label.AddCommand<SwitchLabelAddCommand>("add").WithDescription("Add a label to a switch.");
  190. label.AddCommand<SwitchLabelRemoveCommand>("remove").WithDescription("Remove a label from a switch.");
  191. });
  192. });
  193. // ----------------------------
  194. // Routers commands
  195. // ----------------------------
  196. config.AddBranch("routers", routers =>
  197. {
  198. routers.SetDescription("Manage network routers.");
  199. routers.AddCommand<RouterReportCommand>("summary")
  200. .WithDescription("Show a hardware report for all routers.");
  201. routers.AddCommand<RouterAddCommand>("add")
  202. .WithDescription("Add a new network router to the inventory.");
  203. routers.AddCommand<RouterGetCommand>("list").WithDescription("List all routers in the system.");
  204. routers.AddCommand<RouterGetByNameCommand>("get")
  205. .WithDescription("Retrieve details of a specific router by name.");
  206. routers.AddCommand<RouterDescribeCommand>("describe")
  207. .WithDescription("Show detailed information about a router.");
  208. routers.AddCommand<RouterSetCommand>("set").WithDescription("Update properties of a router.");
  209. routers.AddCommand<RouterDeleteCommand>("del").WithDescription("Delete a router from the inventory.");
  210. routers.AddBranch("port", port =>
  211. {
  212. port.SetDescription("Manage ports on a router.");
  213. port.AddCommand<RouterPortAddCommand>("add").WithDescription("Add a port to a router.");
  214. port.AddCommand<RouterPortUpdateCommand>("set").WithDescription("Update a router port.");
  215. port.AddCommand<RouterPortRemoveCommand>("del").WithDescription("Remove a port from a router.");
  216. });
  217. routers.AddBranch("label", label =>
  218. {
  219. label.SetDescription("Manage labels on a router.");
  220. label.AddCommand<RouterLabelAddCommand>("add").WithDescription("Add a label to a router.");
  221. label.AddCommand<RouterLabelRemoveCommand>("remove").WithDescription("Remove a label from a router.");
  222. });
  223. });
  224. // ----------------------------
  225. // Firewalls commands
  226. // ----------------------------
  227. config.AddBranch("firewalls", firewalls =>
  228. {
  229. firewalls.SetDescription("Manage firewalls.");
  230. firewalls.AddCommand<FirewallReportCommand>("summary")
  231. .WithDescription("Show a hardware report for all firewalls.");
  232. firewalls.AddCommand<FirewallAddCommand>("add").WithDescription("Add a new firewall to the inventory.");
  233. firewalls.AddCommand<FirewallGetCommand>("list").WithDescription("List all firewalls in the system.");
  234. firewalls.AddCommand<FirewallGetByNameCommand>("get")
  235. .WithDescription("Retrieve details of a specific firewall by name.");
  236. firewalls.AddCommand<FirewallDescribeCommand>("describe")
  237. .WithDescription("Show detailed information about a firewall.");
  238. firewalls.AddCommand<FirewallSetCommand>("set").WithDescription("Update properties of a firewall.");
  239. firewalls.AddCommand<FirewallDeleteCommand>("del")
  240. .WithDescription("Delete a firewall from the inventory.");
  241. firewalls.AddBranch("port", port =>
  242. {
  243. port.SetDescription("Manage ports on a firewall.");
  244. port.AddCommand<FirewallPortAddCommand>("add").WithDescription("Add a port to a firewall.");
  245. port.AddCommand<FirewallPortUpdateCommand>("set").WithDescription("Update a firewall port.");
  246. port.AddCommand<FirewallPortRemoveCommand>("del").WithDescription("Remove a port from a firewall.");
  247. });
  248. firewalls.AddBranch("label", label =>
  249. {
  250. label.SetDescription("Manage labels on a firewall.");
  251. label.AddCommand<FirewallLabelAddCommand>("add").WithDescription("Add a label to a firewall.");
  252. label.AddCommand<FirewallLabelRemoveCommand>("remove").WithDescription("Remove a label from a firewall.");
  253. });
  254. });
  255. // ----------------------------
  256. // System commands
  257. // ----------------------------
  258. config.AddBranch("systems", system =>
  259. {
  260. system.SetDescription("Manage systems and their dependencies.");
  261. system.AddCommand<SystemReportCommand>("summary")
  262. .WithDescription("Show a summary report for all systems.");
  263. system.AddCommand<SystemAddCommand>("add").WithDescription("Add a new system to the inventory.");
  264. system.AddCommand<SystemGetCommand>("list").WithDescription("List all systems.");
  265. system.AddCommand<SystemGetByNameCommand>("get").WithDescription("Retrieve a system by name.");
  266. system.AddCommand<SystemDescribeCommand>("describe")
  267. .WithDescription("Display detailed information about a system.");
  268. system.AddCommand<SystemSetCommand>("set").WithDescription("Update properties of a system.");
  269. system.AddCommand<SystemDeleteCommand>("del").WithDescription("Delete a system from the inventory.");
  270. system.AddCommand<SystemTreeCommand>("tree")
  271. .WithDescription("Display the dependency tree for a system.");
  272. system.AddBranch("label", label =>
  273. {
  274. label.SetDescription("Manage labels on a system.");
  275. label.AddCommand<SystemLabelAddCommand>("add").WithDescription("Add a label to a system.");
  276. label.AddCommand<SystemLabelRemoveCommand>("remove").WithDescription("Remove a label from a system.");
  277. });
  278. });
  279. // ----------------------------
  280. // Access Points
  281. // ----------------------------
  282. config.AddBranch("accesspoints", ap =>
  283. {
  284. ap.SetDescription("Manage access points.");
  285. ap.AddCommand<AccessPointReportCommand>("summary")
  286. .WithDescription("Show a hardware report for all access points.");
  287. ap.AddCommand<AccessPointAddCommand>("add").WithDescription("Add a new access point.");
  288. ap.AddCommand<AccessPointGetCommand>("list").WithDescription("List all access points.");
  289. ap.AddCommand<AccessPointGetByNameCommand>("get").WithDescription("Retrieve an access point by name.");
  290. ap.AddCommand<AccessPointDescribeCommand>("describe")
  291. .WithDescription("Show detailed information about an access point.");
  292. ap.AddCommand<AccessPointSetCommand>("set").WithDescription("Update properties of an access point.");
  293. ap.AddCommand<AccessPointDeleteCommand>("del").WithDescription("Delete an access point.");
  294. ap.AddBranch("label", label =>
  295. {
  296. label.SetDescription("Manage labels on an access point.");
  297. label.AddCommand<AccessPointLabelAddCommand>("add").WithDescription("Add a label to an access point.");
  298. label.AddCommand<AccessPointLabelRemoveCommand>("remove").WithDescription("Remove a label from an access point.");
  299. });
  300. });
  301. // ----------------------------
  302. // UPS units
  303. // ----------------------------
  304. config.AddBranch("ups", ups =>
  305. {
  306. ups.SetDescription("Manage UPS units.");
  307. ups.AddCommand<UpsReportCommand>("summary")
  308. .WithDescription("Show a hardware report for all UPS units.");
  309. ups.AddCommand<UpsAddCommand>("add").WithDescription("Add a new UPS unit.");
  310. ups.AddCommand<UpsGetCommand>("list").WithDescription("List all UPS units.");
  311. ups.AddCommand<UpsGetByNameCommand>("get").WithDescription("Retrieve a UPS unit by name.");
  312. ups.AddCommand<UpsDescribeCommand>("describe")
  313. .WithDescription("Show detailed information about a UPS unit.");
  314. ups.AddCommand<UpsSetCommand>("set").WithDescription("Update properties of a UPS unit.");
  315. ups.AddCommand<UpsDeleteCommand>("del").WithDescription("Delete a UPS unit.");
  316. ups.AddBranch("label", label =>
  317. {
  318. label.SetDescription("Manage labels on a UPS unit.");
  319. label.AddCommand<UpsLabelAddCommand>("add").WithDescription("Add a label to a UPS unit.");
  320. label.AddCommand<UpsLabelRemoveCommand>("remove").WithDescription("Remove a label from a UPS unit.");
  321. });
  322. });
  323. // ----------------------------
  324. // Desktops
  325. // ----------------------------
  326. config.AddBranch("desktops", desktops =>
  327. {
  328. desktops.SetDescription("Manage desktop computers and their components.");
  329. // CRUD
  330. desktops.AddCommand<DesktopAddCommand>("add").WithDescription("Add a new desktop.");
  331. desktops.AddCommand<DesktopGetCommand>("list").WithDescription("List all desktops.");
  332. desktops.AddCommand<DesktopGetByNameCommand>("get").WithDescription("Retrieve a desktop by name.");
  333. desktops.AddCommand<DesktopDescribeCommand>("describe")
  334. .WithDescription("Show detailed information about a desktop.");
  335. desktops.AddCommand<DesktopSetCommand>("set").WithDescription("Update properties of a desktop.");
  336. desktops.AddCommand<DesktopDeleteCommand>("del")
  337. .WithDescription("Delete a desktop from the inventory.");
  338. desktops.AddCommand<DesktopReportCommand>("summary")
  339. .WithDescription("Show a summarized hardware report for all desktops.");
  340. desktops.AddCommand<DesktopTreeCommand>("tree")
  341. .WithDescription("Display the dependency tree for a desktop.");
  342. // CPU
  343. desktops.AddBranch("cpu", cpu =>
  344. {
  345. cpu.SetDescription("Manage CPUs attached to desktops.");
  346. cpu.AddCommand<DesktopCpuAddCommand>("add").WithDescription("Add a CPU to a desktop.");
  347. cpu.AddCommand<DesktopCpuSetCommand>("set").WithDescription("Update a desktop CPU.");
  348. cpu.AddCommand<DesktopCpuRemoveCommand>("del").WithDescription("Remove a CPU from a desktop.");
  349. });
  350. // Drives
  351. desktops.AddBranch("drive", drive =>
  352. {
  353. drive.SetDescription("Manage storage drives attached to desktops.");
  354. drive.AddCommand<DesktopDriveAddCommand>("add").WithDescription("Add a drive to a desktop.");
  355. drive.AddCommand<DesktopDriveSetCommand>("set").WithDescription("Update a desktop drive.");
  356. drive.AddCommand<DesktopDriveRemoveCommand>("del")
  357. .WithDescription("Remove a drive from a desktop.");
  358. });
  359. // GPUs
  360. desktops.AddBranch("gpu", gpu =>
  361. {
  362. gpu.SetDescription("Manage GPUs attached to desktops.");
  363. gpu.AddCommand<DesktopGpuAddCommand>("add").WithDescription("Add a GPU to a desktop.");
  364. gpu.AddCommand<DesktopGpuSetCommand>("set").WithDescription("Update a desktop GPU.");
  365. gpu.AddCommand<DesktopGpuRemoveCommand>("del").WithDescription("Remove a GPU from a desktop.");
  366. });
  367. // NICs
  368. desktops.AddBranch("nic", nic =>
  369. {
  370. nic.SetDescription("Manage network interface cards (NICs) for desktops.");
  371. nic.AddCommand<DesktopNicAddCommand>("add").WithDescription("Add a NIC to a desktop.");
  372. nic.AddCommand<DesktopNicSetCommand>("set").WithDescription("Update a desktop NIC.");
  373. nic.AddCommand<DesktopNicRemoveCommand>("del").WithDescription("Remove a NIC from a desktop.");
  374. });
  375. desktops.AddBranch("label", label =>
  376. {
  377. label.SetDescription("Manage labels on a desktop.");
  378. label.AddCommand<DesktopLabelAddCommand>("add").WithDescription("Add a label to a desktop.");
  379. label.AddCommand<DesktopLabelRemoveCommand>("remove").WithDescription("Remove a label from a desktop.");
  380. });
  381. });
  382. // ----------------------------
  383. // Laptops
  384. // ----------------------------
  385. config.AddBranch("laptops", laptops =>
  386. {
  387. laptops.SetDescription("Manage Laptop computers and their components.");
  388. // CRUD
  389. laptops.AddCommand<LaptopAddCommand>("add").WithDescription("Add a new Laptop.");
  390. laptops.AddCommand<LaptopGetCommand>("list").WithDescription("List all Laptops.");
  391. laptops.AddCommand<LaptopGetByNameCommand>("get").WithDescription("Retrieve a Laptop by name.");
  392. laptops.AddCommand<LaptopDescribeCommand>("describe")
  393. .WithDescription("Show detailed information about a Laptop.");
  394. laptops.AddCommand<LaptopSetCommand>("set").WithDescription("Update properties of a laptop.");
  395. laptops.AddCommand<LaptopDeleteCommand>("del").WithDescription("Delete a Laptop from the inventory.");
  396. laptops.AddCommand<LaptopReportCommand>("summary")
  397. .WithDescription("Show a summarized hardware report for all Laptops.");
  398. laptops.AddCommand<LaptopTreeCommand>("tree")
  399. .WithDescription("Display the dependency tree for a Laptop.");
  400. // CPU
  401. laptops.AddBranch("cpu", cpu =>
  402. {
  403. cpu.SetDescription("Manage CPUs attached to Laptops.");
  404. cpu.AddCommand<LaptopCpuAddCommand>("add").WithDescription("Add a CPU to a Laptop.");
  405. cpu.AddCommand<LaptopCpuSetCommand>("set").WithDescription("Update a Laptop CPU.");
  406. cpu.AddCommand<LaptopCpuRemoveCommand>("del").WithDescription("Remove a CPU from a Laptop.");
  407. });
  408. // Drives
  409. laptops.AddBranch("drives", drives =>
  410. {
  411. drives.SetDescription("Manage storage drives attached to Laptops.");
  412. drives.AddCommand<LaptopDriveAddCommand>("add").WithDescription("Add a drive to a Laptop.");
  413. drives.AddCommand<LaptopDriveSetCommand>("set").WithDescription("Update a Laptop drive.");
  414. drives.AddCommand<LaptopDriveRemoveCommand>("del").WithDescription("Remove a drive from a Laptop.");
  415. });
  416. // GPUs
  417. laptops.AddBranch("gpu", gpu =>
  418. {
  419. gpu.SetDescription("Manage GPUs attached to Laptops.");
  420. gpu.AddCommand<LaptopGpuAddCommand>("add").WithDescription("Add a GPU to a Laptop.");
  421. gpu.AddCommand<LaptopGpuSetCommand>("set").WithDescription("Update a Laptop GPU.");
  422. gpu.AddCommand<LaptopGpuRemoveCommand>("del").WithDescription("Remove a GPU from a Laptop.");
  423. });
  424. laptops.AddBranch("label", label =>
  425. {
  426. label.SetDescription("Manage labels on a laptop.");
  427. label.AddCommand<LaptopLabelAddCommand>("add").WithDescription("Add a label to a laptop.");
  428. label.AddCommand<LaptopLabelRemoveCommand>("remove").WithDescription("Remove a label from a laptop.");
  429. });
  430. });
  431. // ----------------------------
  432. // Services
  433. // ----------------------------
  434. config.AddBranch("services", service =>
  435. {
  436. service.SetDescription("Manage services and their configurations.");
  437. service.AddCommand<ServiceReportCommand>("summary")
  438. .WithDescription("Show a summary report for all services.");
  439. service.AddCommand<ServiceAddCommand>("add").WithDescription("Add a new service.");
  440. service.AddCommand<ServiceGetCommand>("list").WithDescription("List all services.");
  441. service.AddCommand<ServiceGetByNameCommand>("get").WithDescription("Retrieve a service by name.");
  442. service.AddCommand<ServiceDescribeCommand>("describe")
  443. .WithDescription("Show detailed information about a service.");
  444. service.AddCommand<ServiceSetCommand>("set").WithDescription("Update properties of a service.");
  445. service.AddCommand<ServiceDeleteCommand>("del").WithDescription("Delete a service.");
  446. service.AddCommand<ServiceSubnetsCommand>("subnets")
  447. .WithDescription("List subnets associated with a service, optionally filtered by CIDR.");
  448. service.AddBranch("label", label =>
  449. {
  450. label.SetDescription("Manage labels on a service.");
  451. label.AddCommand<ServiceLabelAddCommand>("add").WithDescription("Add a label to a service.");
  452. label.AddCommand<ServiceLabelRemoveCommand>("remove").WithDescription("Remove a label from a service.");
  453. });
  454. });
  455. // ----------------------------
  456. // Ansible
  457. // ----------------------------
  458. config.AddBranch("ansible", ansible =>
  459. {
  460. ansible.SetDescription("Generate and manage Ansible inventory.");
  461. ansible.AddCommand<GenerateAnsibleInventoryCommand>("inventory")
  462. .WithDescription("Generate an Ansible inventory.");
  463. });
  464. });
  465. }
  466. private static int HandleException(Exception ex, Spectre.Console.Cli.ITypeResolver? arg2)
  467. {
  468. switch (ex)
  469. {
  470. case ValidationException ve:
  471. AnsiConsole.MarkupLine($"[yellow]Validation error:[/] {ve.Message}");
  472. return 2;
  473. case ConflictException ce:
  474. AnsiConsole.MarkupLine($"[red]Conflict:[/] {ce.Message}");
  475. return 3;
  476. case NotFoundException ne:
  477. AnsiConsole.MarkupLine($"[red]Not found:[/] {ne.Message}");
  478. return 4;
  479. case CommandParseException pe:
  480. if (_showingHelp) return 1; // suppress errors during help lookup
  481. AnsiConsole.MarkupLine($"[red]Invalid command:[/] {pe.Message}");
  482. if (pe.Pretty != null) AnsiConsole.Write(pe.Pretty);
  483. ShowContextualHelp();
  484. return 1;
  485. case CommandRuntimeException re:
  486. if (_showingHelp) return 1;
  487. AnsiConsole.MarkupLine($"[red]Error:[/] {re.Message}");
  488. if (re.Pretty != null) AnsiConsole.Write(re.Pretty);
  489. ShowContextualHelp();
  490. return 1;
  491. default:
  492. AnsiConsole.MarkupLine("[red]Unexpected error occurred.[/]");
  493. AnsiConsole.WriteException(ex, ExceptionFormats.ShortenEverything);
  494. return 99;
  495. }
  496. }
  497. private static void ShowContextualHelp()
  498. {
  499. if (_lastArgs == null || _app == null || _showingHelp) return;
  500. _showingHelp = true;
  501. try
  502. {
  503. // Extract command path (args before any --flags)
  504. var commandPath = _lastArgs.TakeWhile(a => !a.StartsWith("-")).ToList();
  505. // Try progressively shorter command paths until --help succeeds
  506. while (commandPath.Count > 0)
  507. {
  508. var helpArgs = commandPath.Append("--help").ToArray();
  509. AnsiConsole.WriteLine();
  510. var result = _app.Run(helpArgs);
  511. if (result == 0) return;
  512. commandPath.RemoveAt(commandPath.Count - 1);
  513. }
  514. }
  515. finally
  516. {
  517. _showingHelp = false;
  518. }
  519. }
  520. }