@tamyla/clodo-framework 2.0.1 ā 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/bin/clodo-service.js +167 -2
- package/bin/security/security-cli.js +1 -1
- package/bin/service-management/create-service.js +1 -1
- package/bin/service-management/init-service.js +1 -1
- package/bin/shared/config/cache.js +1230 -0
- package/bin/shared/config/command-config-manager.js +184 -0
- package/bin/shared/config/customer-cli.js +1 -1
- package/bin/shared/config/index.js +9 -0
- package/bin/shared/config/manager.js +315 -0
- package/dist/config/customer-config-loader.js +247 -0
- package/dist/shared/config/customer-cli.js +1 -1
- package/package.json +7 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## [2.0.3](https://github.com/tamylaa/clodo-framework/compare/v2.0.2...v2.0.3) (2025-10-11)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* Add reusable deployment command with Three-Tier input architecture ([0e13bfc](https://github.com/tamylaa/clodo-framework/commit/0e13bfcdda56d0a137bcd44cfd8a9ca49af30503))
|
|
7
|
+
* test - Remove placeholder tests that require unimplemented classes ([b009b34](https://github.com/tamylaa/clodo-framework/commit/b009b34cf1f9f7542fbaab2fa2419b2766c72f10))
|
|
8
|
+
|
|
9
|
+
## [2.0.2](https://github.com/tamylaa/clodo-framework/compare/v2.0.1...v2.0.2) (2025-10-11)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* resolve CLI tool import paths and runtime dependencies ([6a726b9](https://github.com/tamylaa/clodo-framework/commit/6a726b95a5e55055048a262d1c146a50a4f0b46f))
|
|
15
|
+
|
|
1
16
|
## [2.0.1](https://github.com/tamylaa/clodo-framework/compare/v2.0.0...v2.0.1) (2025-10-10)
|
|
2
17
|
|
|
3
18
|
|
package/bin/clodo-service.js
CHANGED
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
import { Command } from 'commander';
|
|
16
16
|
import { createInterface } from 'readline';
|
|
17
17
|
import chalk from 'chalk';
|
|
18
|
-
import { ServiceOrchestrator } from '../
|
|
19
|
-
import { InputCollector } from '../
|
|
18
|
+
import { ServiceOrchestrator } from '../dist/service-management/ServiceOrchestrator.js';
|
|
19
|
+
import { InputCollector } from '../dist/service-management/InputCollector.js';
|
|
20
20
|
|
|
21
21
|
const program = new Command();
|
|
22
22
|
|
|
@@ -413,4 +413,169 @@ program
|
|
|
413
413
|
}
|
|
414
414
|
});
|
|
415
415
|
|
|
416
|
+
// Deploy command - using existing InputCollector + CustomerConfigLoader (reusable pattern)
|
|
417
|
+
program
|
|
418
|
+
.command('deploy')
|
|
419
|
+
.description('Deploy service using three-tier input architecture')
|
|
420
|
+
.option('-c, --customer <name>', 'Customer name')
|
|
421
|
+
.option('-e, --env <environment>', 'Target environment (development, staging, production)')
|
|
422
|
+
.option('-i, --interactive', 'Interactive mode (review confirmations)', true)
|
|
423
|
+
.option('--non-interactive', 'Non-interactive mode (use stored config)')
|
|
424
|
+
.option('--dry-run', 'Simulate deployment without making changes')
|
|
425
|
+
.option('--domain <domain>', 'Domain name (overrides stored config)')
|
|
426
|
+
.option('--service-path <path>', 'Path to service directory', '.')
|
|
427
|
+
.action(async (options) => {
|
|
428
|
+
try {
|
|
429
|
+
// Use existing reusable components
|
|
430
|
+
const { InputCollector } = await import('../dist/service-management/InputCollector.js');
|
|
431
|
+
const { CustomerConfigLoader } = await import('../dist/config/customer-config-loader.js');
|
|
432
|
+
const { ConfirmationHandler } = await import('../dist/service-management/handlers/ConfirmationHandler.js');
|
|
433
|
+
const { MultiDomainOrchestrator } = await import('../dist/orchestration/multi-domain-orchestrator.js');
|
|
434
|
+
|
|
435
|
+
console.log(chalk.cyan('\nš Clodo Framework Deployment'));
|
|
436
|
+
console.log(chalk.white('Using Three-Tier Input Architecture\n'));
|
|
437
|
+
|
|
438
|
+
const isInteractive = options.interactive && !options.nonInteractive;
|
|
439
|
+
const configLoader = new CustomerConfigLoader();
|
|
440
|
+
const inputCollector = new InputCollector({ interactive: isInteractive });
|
|
441
|
+
const confirmationHandler = new ConfirmationHandler({ interactive: isInteractive });
|
|
442
|
+
|
|
443
|
+
let coreInputs = {};
|
|
444
|
+
let source = 'interactive';
|
|
445
|
+
|
|
446
|
+
try {
|
|
447
|
+
// Try to load existing customer config first
|
|
448
|
+
if (options.customer && options.env) {
|
|
449
|
+
const storedConfig = configLoader.loadConfig(options.customer, options.env);
|
|
450
|
+
|
|
451
|
+
if (storedConfig) {
|
|
452
|
+
source = 'stored-config';
|
|
453
|
+
console.log(chalk.cyan('š Found Existing Configuration\n'));
|
|
454
|
+
configLoader.displayConfig(storedConfig.parsed);
|
|
455
|
+
|
|
456
|
+
if (!isInteractive) {
|
|
457
|
+
// Non-interactive: use stored config as-is
|
|
458
|
+
coreInputs = storedConfig.parsed;
|
|
459
|
+
console.log(chalk.green('\nā
Using stored configuration (non-interactive mode)\n'));
|
|
460
|
+
} else {
|
|
461
|
+
// Interactive: ask to confirm or re-collect
|
|
462
|
+
const useStored = await inputCollector.question('\nUse this configuration? (Y/n): ');
|
|
463
|
+
|
|
464
|
+
if (useStored.toLowerCase() !== 'n') {
|
|
465
|
+
coreInputs = storedConfig.parsed;
|
|
466
|
+
console.log(chalk.green('\nā
Using stored configuration\n'));
|
|
467
|
+
} else {
|
|
468
|
+
console.log(chalk.white('\nCollecting new configuration...\n'));
|
|
469
|
+
source = 'interactive';
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
} else {
|
|
473
|
+
console.log(chalk.yellow(`ā ļø No configuration found for ${options.customer}/${options.env}`));
|
|
474
|
+
console.log(chalk.white('Collecting inputs interactively...\n'));
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Collect inputs if we don't have them from stored config
|
|
479
|
+
if (!coreInputs.cloudflareAccountId) {
|
|
480
|
+
console.log(chalk.cyan('š Tier 1: Core Input Collection\n'));
|
|
481
|
+
|
|
482
|
+
// Use InputCollector for what it does best - collecting inputs
|
|
483
|
+
coreInputs = {
|
|
484
|
+
customer: options.customer || await inputCollector.question('Customer name: '),
|
|
485
|
+
environment: options.env || await inputCollector.collectEnvironment(),
|
|
486
|
+
serviceName: await inputCollector.collectServiceName(),
|
|
487
|
+
serviceType: await inputCollector.collectServiceType(),
|
|
488
|
+
domainName: options.domain || await inputCollector.collectDomainName(),
|
|
489
|
+
cloudflareToken: process.env.CLOUDFLARE_API_TOKEN || await inputCollector.collectCloudflareToken(),
|
|
490
|
+
cloudflareAccountId: process.env.CLOUDFLARE_ACCOUNT_ID || await inputCollector.collectCloudflareAccountId(),
|
|
491
|
+
cloudflareZoneId: process.env.CLOUDFLARE_ZONE_ID || await inputCollector.collectCloudflareZoneId()
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
source = 'interactive';
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Allow domain override
|
|
498
|
+
if (options.domain) {
|
|
499
|
+
coreInputs.domainName = options.domain;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Tier 2: Generate smart confirmations using existing ConfirmationHandler
|
|
503
|
+
console.log(chalk.cyan('āļø Tier 2: Smart Confirmations\n'));
|
|
504
|
+
const confirmations = await confirmationHandler.generateAndConfirm(coreInputs);
|
|
505
|
+
|
|
506
|
+
// Show deployment summary
|
|
507
|
+
console.log(chalk.cyan('\nš Deployment Summary'));
|
|
508
|
+
console.log(chalk.gray('ā'.repeat(60)));
|
|
509
|
+
|
|
510
|
+
console.log(chalk.white(`Source: ${source}`));
|
|
511
|
+
console.log(chalk.white(`Customer: ${coreInputs.customer}`));
|
|
512
|
+
console.log(chalk.white(`Environment: ${coreInputs.environment}`));
|
|
513
|
+
console.log(chalk.white(`Domain: ${coreInputs.domainName}`));
|
|
514
|
+
console.log(chalk.white(`Account ID: ${coreInputs.cloudflareAccountId?.substring(0, 8)}...`));
|
|
515
|
+
console.log(chalk.white(`Zone ID: ${coreInputs.cloudflareZoneId?.substring(0, 8)}...`));
|
|
516
|
+
|
|
517
|
+
if (confirmations.workerName) {
|
|
518
|
+
console.log(chalk.white(`Worker: ${confirmations.workerName}`));
|
|
519
|
+
}
|
|
520
|
+
if (confirmations.databaseName) {
|
|
521
|
+
console.log(chalk.white(`Database: ${confirmations.databaseName}`));
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
console.log(chalk.gray('ā'.repeat(60)));
|
|
525
|
+
|
|
526
|
+
if (options.dryRun) {
|
|
527
|
+
console.log(chalk.yellow('\nš DRY RUN MODE - No changes will be made\n'));
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Tier 3: Execute deployment
|
|
531
|
+
console.log(chalk.cyan('\nāļø Tier 3: Automated Deployment\n'));
|
|
532
|
+
|
|
533
|
+
const orchestrator = new MultiDomainOrchestrator({
|
|
534
|
+
domains: [coreInputs.domainName],
|
|
535
|
+
environment: coreInputs.environment,
|
|
536
|
+
dryRun: options.dryRun,
|
|
537
|
+
servicePath: options.servicePath
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
await orchestrator.initialize();
|
|
541
|
+
|
|
542
|
+
console.log(chalk.cyan('š Starting deployment...\n'));
|
|
543
|
+
|
|
544
|
+
const result = await orchestrator.deployDomain(coreInputs.domainName, {
|
|
545
|
+
...coreInputs,
|
|
546
|
+
...confirmations,
|
|
547
|
+
servicePath: options.servicePath
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
// Display results
|
|
551
|
+
console.log(chalk.green('\nā
Deployment Completed Successfully!'));
|
|
552
|
+
console.log(chalk.gray('ā'.repeat(60)));
|
|
553
|
+
console.log(chalk.white(` Worker: ${confirmations.workerName || 'N/A'}`));
|
|
554
|
+
console.log(chalk.white(` URL: ${result.url || confirmations.deploymentUrl || 'N/A'}`));
|
|
555
|
+
console.log(chalk.white(` Status: ${result.status || 'deployed'}`));
|
|
556
|
+
|
|
557
|
+
if (result.health) {
|
|
558
|
+
console.log(chalk.white(` Health: ${result.health}`));
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
console.log(chalk.gray('ā'.repeat(60)));
|
|
562
|
+
|
|
563
|
+
console.log(chalk.cyan('\nš Next Steps:'));
|
|
564
|
+
console.log(chalk.white(` ⢠Test deployment: curl ${result.url || confirmations.deploymentUrl}/health`));
|
|
565
|
+
console.log(chalk.white(` ⢠Monitor logs: wrangler tail ${confirmations.workerName}`));
|
|
566
|
+
console.log(chalk.white(` ⢠View dashboard: https://dash.cloudflare.com`));
|
|
567
|
+
|
|
568
|
+
} finally {
|
|
569
|
+
inputCollector.close();
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
} catch (error) {
|
|
573
|
+
console.error(chalk.red(`\nā Deployment failed: ${error.message}`));
|
|
574
|
+
if (error.stack && process.env.DEBUG) {
|
|
575
|
+
console.error(chalk.gray(error.stack));
|
|
576
|
+
}
|
|
577
|
+
process.exit(1);
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
|
|
416
581
|
program.parse();
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Creates new ser console.log(`ā Using Clodo Framework ServiceCreator module`);ices from predefined templates
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { ServiceCreator } from '../../
|
|
8
|
+
import { ServiceCreator } from '../../dist/service-management/ServiceCreator.js';
|
|
9
9
|
|
|
10
10
|
const SERVICE_TYPES = ['data-service', 'auth-service', 'content-service', 'api-gateway', 'generic'];
|
|
11
11
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { program } from 'commander';
|
|
14
|
-
import { ServiceInitializer } from '../../
|
|
14
|
+
import { ServiceInitializer } from '../../dist/service-management/ServiceInitializer.js';
|
|
15
15
|
|
|
16
16
|
const SERVICE_TYPES = ['generic', 'data-service', 'auth-service', 'content-service', 'api-gateway'];
|
|
17
17
|
|