@tamyla/clodo-framework 3.1.24 ā 3.1.26
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 +14 -0
- package/dist/cli/clodo-simple.js +111 -0
- package/dist/cli/commands/assess.js +5 -5
- package/dist/cli/commands/create.js +22 -31
- package/dist/cli/commands/deploy.js +22 -357
- package/dist/cli/commands/diagnose.js +5 -5
- package/dist/cli/commands/helpers/deployment-verification.js +2 -2
- package/dist/cli/commands/helpers/error-recovery.js +1 -1
- package/dist/cli/commands/helpers/resource-detection.js +1 -1
- package/dist/cli/commands/init-config.js +1 -1
- package/dist/cli/commands/update.js +5 -5
- package/dist/cli/commands/validate.js +24 -48
- package/dist/cli/security-cli.js +1 -1
- package/dist/deployment/wrangler-deployer.js +1 -1
- package/dist/index.js +5 -2
- package/dist/lib/deployment/modules/DeploymentOrchestrator.js +2 -2
- package/dist/lib/deployment/modules/EnvironmentManager.js +2 -2
- package/dist/lib/shared/cloudflare/domain-manager.js +1 -1
- package/dist/lib/shared/cloudflare/ops.js +4 -4
- package/dist/lib/shared/config/command-config-manager.js +1 -1
- package/dist/lib/shared/config/index.js +1 -1
- package/dist/lib/shared/deployment/credential-collector.js +1 -1
- package/dist/lib/shared/deployment/index.js +2 -2
- package/dist/lib/shared/deployment/rollback-manager.js +1 -1
- package/dist/lib/shared/deployment/utilities/d1-error-recovery.js +1 -1
- package/dist/lib/shared/deployment/validator.js +1 -1
- package/dist/lib/shared/deployment/workflows/interactive-database-workflow.js +1 -1
- package/dist/lib/shared/monitoring/health-checker.js +2 -2
- package/dist/lib/shared/routing/domain-router.js +1 -1
- package/dist/lib/shared/validation/ValidationRegistry.js +1 -1
- package/dist/orchestration/cross-domain-coordinator.js +5 -5
- package/dist/orchestration/multi-domain-orchestrator.js +51 -0
- package/dist/security/index.js +2 -2
- package/dist/service-management/ConfirmationEngine.js +1 -1
- package/dist/service-management/ErrorTracker.js +1 -1
- package/dist/service-management/InputCollector.js +1 -1
- package/dist/service-management/ServiceOrchestrator.js +77 -0
- package/dist/service-management/generators/testing/UnitTestsGenerator.js +4 -4
- package/dist/simple-api.js +94 -0
- package/dist/utils/cloudflare/ops.js +5 -1
- package/dist/utils/file-manager.js +14 -2
- package/dist/utils/formatters.js +5 -2
- package/dist/utils/logger.js +5 -2
- package/dist/worker/integration.js +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [3.1.26](https://github.com/tamylaa/clodo-framework/compare/v3.1.25...v3.1.26) (2025-12-02)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* correct import paths for npm package distribution - use proper depth for compiled dist/ files ([d346563](https://github.com/tamylaa/clodo-framework/commit/d3465637e02077e014661e1039421e9cd0010d97))
|
|
7
|
+
|
|
8
|
+
## [3.1.25](https://github.com/tamylaa/clodo-framework/compare/v3.1.24...v3.1.25) (2025-12-02)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* correct all import paths and add prevention documentation ([dbd3ce6](https://github.com/tamylaa/clodo-framework/commit/dbd3ce6d6b3ceceb9aa839358923c6096c762ceb))
|
|
14
|
+
|
|
1
15
|
## [3.1.24](https://github.com/tamylaa/clodo-framework/compare/v3.1.23...v3.1.24) (2025-11-08)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Simple CLI - Simplified interface for Clodo Framework
|
|
5
|
+
*
|
|
6
|
+
* A streamlined CLI that provides easy access to core framework functionality
|
|
7
|
+
* using the unified simple API with sensible defaults.
|
|
8
|
+
*/
|
|
9
|
+
import { Command } from 'commander';
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import { Clodo } from '../src/simple-api.js';
|
|
12
|
+
|
|
13
|
+
// Create program instance
|
|
14
|
+
const program = new Command();
|
|
15
|
+
program.name('clodo').description('Simple CLI for Clodo Framework - streamlined service management').version('1.0.0');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Create command - Simple service creation
|
|
19
|
+
*/
|
|
20
|
+
program.command('create <name>').description('Create a new service with minimal configuration').option('-t, --type <type>', 'Service type', 'generic').option('-d, --domain <domain>', 'Domain name', 'example.com').option('-e, --env <env>', 'Environment', 'development').option('-o, --output <path>', 'Output directory', '.').option('--token <token>', 'Cloudflare API token').option('--account-id <id>', 'Cloudflare account ID').option('--zone-id <id>', 'Cloudflare zone ID').action(async (name, options) => {
|
|
21
|
+
try {
|
|
22
|
+
console.log(chalk.cyan(`š Creating service: ${name}`));
|
|
23
|
+
const result = await Clodo.createService({
|
|
24
|
+
name,
|
|
25
|
+
type: options.type,
|
|
26
|
+
domain: options.domain,
|
|
27
|
+
environment: options.env,
|
|
28
|
+
outputPath: options.output,
|
|
29
|
+
credentials: {
|
|
30
|
+
token: options.token,
|
|
31
|
+
accountId: options.accountId,
|
|
32
|
+
zoneId: options.zoneId
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
console.log(chalk.green(`ā
${result.message}`));
|
|
36
|
+
console.log(chalk.white(`š Created in: ${result.outputPath}`));
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error(chalk.red(`ā ${error.message}`));
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Deploy command - Simple service deployment
|
|
45
|
+
*/
|
|
46
|
+
program.command('deploy').description('Deploy a service with smart configuration detection').option('-p, --path <path>', 'Service directory path', '.').option('-e, --env <env>', 'Target environment', 'production').option('-d, --domain <domain>', 'Specific domain to deploy to').option('--dry-run', 'Simulate deployment without changes').option('--token <token>', 'Cloudflare API token').option('--account-id <id>', 'Cloudflare account ID').option('--zone-id <id>', 'Cloudflare zone ID').action(async options => {
|
|
47
|
+
try {
|
|
48
|
+
console.log(chalk.cyan(`š Deploying service...`));
|
|
49
|
+
const result = await Clodo.deploy({
|
|
50
|
+
servicePath: options.path,
|
|
51
|
+
environment: options.env,
|
|
52
|
+
domain: options.domain,
|
|
53
|
+
dryRun: options.dryRun,
|
|
54
|
+
credentials: {
|
|
55
|
+
token: options.token,
|
|
56
|
+
accountId: options.accountId,
|
|
57
|
+
zoneId: options.zoneId
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
console.log(chalk.green(`ā
${result.message}`));
|
|
61
|
+
if (result.deployedDomains) {
|
|
62
|
+
console.log(chalk.white(`š Deployed to: ${result.deployedDomains.join(', ')}`));
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error(chalk.red(`ā ${error.message}`));
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Validate command - Simple service validation
|
|
72
|
+
*/
|
|
73
|
+
program.command('validate').description('Validate a service configuration').option('-p, --path <path>', 'Service directory path', '.').option('-r, --report', 'Export validation report').action(async options => {
|
|
74
|
+
try {
|
|
75
|
+
console.log(chalk.cyan(`š Validating service...`));
|
|
76
|
+
const result = await Clodo.validate({
|
|
77
|
+
servicePath: options.path,
|
|
78
|
+
exportReport: options.report
|
|
79
|
+
});
|
|
80
|
+
if (result.success) {
|
|
81
|
+
console.log(chalk.green(`ā
${result.message}`));
|
|
82
|
+
} else {
|
|
83
|
+
console.log(chalk.yellow(`ā ļø ${result.message}`));
|
|
84
|
+
if (result.issues && result.issues.length > 0) {
|
|
85
|
+
console.log(chalk.white('Issues found:'));
|
|
86
|
+
result.issues.forEach((issue, index) => {
|
|
87
|
+
console.log(chalk.white(` ${index + 1}. ${issue}`));
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.error(chalk.red(`ā ${error.message}`));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Info command - Show framework information
|
|
99
|
+
*/
|
|
100
|
+
program.command('info').description('Show framework information').action(() => {
|
|
101
|
+
const info = Clodo.getInfo();
|
|
102
|
+
console.log(chalk.cyan(`${info.name} v${info.version}`));
|
|
103
|
+
console.log(chalk.white(info.description));
|
|
104
|
+
console.log(chalk.white('\nFeatures:'));
|
|
105
|
+
info.features.forEach(feature => {
|
|
106
|
+
console.log(chalk.white(` ⢠${feature}`));
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Parse command line arguments
|
|
111
|
+
program.parse();
|
|
@@ -5,16 +5,16 @@
|
|
|
5
5
|
|
|
6
6
|
import chalk from 'chalk';
|
|
7
7
|
import path from 'path';
|
|
8
|
-
import { ServiceOrchestrator } from '
|
|
9
|
-
import { StandardOptions } from '
|
|
10
|
-
import { ServiceConfigManager } from '
|
|
8
|
+
import { ServiceOrchestrator } from '../../src/service-management/ServiceOrchestrator.js';
|
|
9
|
+
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
10
|
+
import { ServiceConfigManager } from '../../lib/shared/utils/service-config-manager.js';
|
|
11
11
|
export function registerAssessCommand(program) {
|
|
12
12
|
const command = program.command('assess [service-path]').description('Run intelligent capability assessment (requires @tamyla/clodo-orchestration)').option('--export <file>', 'Export assessment results to JSON file').option('--domain <domain>', 'Domain name for assessment').option('--service-type <type>', 'Service type for assessment').option('--token <token>', 'Cloudflare API token').option('--show-config-sources', 'Display all configuration sources and merged result');
|
|
13
13
|
|
|
14
14
|
// Add standard options (--verbose, --quiet, --json, --no-color, --config-file)
|
|
15
15
|
StandardOptions.define(command).action(async (servicePath, options) => {
|
|
16
16
|
try {
|
|
17
|
-
const output = new (await import('
|
|
17
|
+
const output = new (await import('../../lib/shared/utils/output-formatter.js')).OutputFormatter(options);
|
|
18
18
|
const configManager = new ServiceConfigManager({
|
|
19
19
|
verbose: options.verbose,
|
|
20
20
|
quiet: options.quiet,
|
|
@@ -111,7 +111,7 @@ export function registerAssessCommand(program) {
|
|
|
111
111
|
output.success(`š Results exported to: ${mergedOptions.export}`);
|
|
112
112
|
}
|
|
113
113
|
} catch (error) {
|
|
114
|
-
const output = new (await import('
|
|
114
|
+
const output = new (await import('../../lib/shared/utils/output-formatter.js')).OutputFormatter(options || {});
|
|
115
115
|
output.error(`Assessment failed: ${error.message}`);
|
|
116
116
|
if (process.env.DEBUG) {
|
|
117
117
|
output.debug(error.stack);
|
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import chalk from 'chalk';
|
|
9
|
-
import {
|
|
10
|
-
import { StandardOptions } from '
|
|
11
|
-
import { ConfigLoader } from '
|
|
9
|
+
import { Clodo } from '../../src/simple-api.js';
|
|
10
|
+
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
11
|
+
import { ConfigLoader } from '../../lib/shared/utils/config-loader.js';
|
|
12
12
|
export function registerCreateCommand(program) {
|
|
13
13
|
const command = program.command('create').description('Create a new Clodo service with conversational setup').option('-n, --non-interactive', 'Run in non-interactive mode with all required parameters').option('--service-name <name>', 'Service name (required in non-interactive mode)').option('--service-type <type>', 'Service type: data-service, auth-service, content-service, api-gateway, generic', 'generic').option('--domain-name <domain>', 'Domain name (required in non-interactive mode)').option('--cloudflare-token <token>', 'Cloudflare API token (required in non-interactive mode)').option('--cloudflare-account-id <id>', 'Cloudflare account ID (required in non-interactive mode)').option('--cloudflare-zone-id <id>', 'Cloudflare zone ID (required in non-interactive mode)').option('--environment <env>', 'Target environment: development, staging, production', 'development').option('--output-path <path>', 'Output directory for generated service', '.').option('--template-path <path>', 'Path to service templates', './templates').option('--force', 'Skip confirmation prompts').option('--validate', 'Validate service after creation');
|
|
14
14
|
|
|
15
15
|
// Add standard options (--verbose, --quiet, --json, --no-color, --config-file)
|
|
16
16
|
StandardOptions.define(command).action(async options => {
|
|
17
17
|
try {
|
|
18
|
-
const output = new (await import('
|
|
18
|
+
const output = new (await import('../../lib/shared/utils/output-formatter.js')).OutputFormatter(options);
|
|
19
19
|
const configLoader = new ConfigLoader({
|
|
20
20
|
verbose: options.verbose,
|
|
21
21
|
quiet: options.quiet,
|
|
@@ -33,40 +33,31 @@ export function registerCreateCommand(program) {
|
|
|
33
33
|
|
|
34
34
|
// Merge config file defaults with CLI options (CLI takes precedence)
|
|
35
35
|
const mergedOptions = configLoader.merge(configFileData, options);
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
|
|
37
|
+
// Use simple API for service creation
|
|
38
|
+
const result = await Clodo.createService({
|
|
39
|
+
name: mergedOptions.serviceName,
|
|
40
|
+
type: mergedOptions.serviceType,
|
|
41
|
+
domain: mergedOptions.domainName,
|
|
42
|
+
environment: mergedOptions.environment,
|
|
38
43
|
outputPath: mergedOptions.outputPath,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const missing = required.filter(key => !mergedOptions[key]);
|
|
45
|
-
if (missing.length > 0) {
|
|
46
|
-
output.error(`Missing required parameters: ${missing.join(', ')}`);
|
|
47
|
-
output.info('Use --help for parameter details');
|
|
48
|
-
process.exit(1);
|
|
44
|
+
interactive: !mergedOptions.nonInteractive,
|
|
45
|
+
credentials: {
|
|
46
|
+
token: mergedOptions.cloudflareToken,
|
|
47
|
+
accountId: mergedOptions.cloudflareAccountId,
|
|
48
|
+
zoneId: mergedOptions.cloudflareZoneId
|
|
49
49
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
serviceName: mergedOptions.serviceName,
|
|
54
|
-
serviceType: mergedOptions.serviceType,
|
|
55
|
-
domainName: mergedOptions.domainName,
|
|
56
|
-
cloudflareToken: mergedOptions.cloudflareToken,
|
|
57
|
-
cloudflareAccountId: mergedOptions.cloudflareAccountId,
|
|
58
|
-
cloudflareZoneId: mergedOptions.cloudflareZoneId,
|
|
59
|
-
environment: mergedOptions.environment
|
|
60
|
-
};
|
|
61
|
-
await orchestrator.runNonInteractive(coreInputs);
|
|
50
|
+
});
|
|
51
|
+
if (result.success) {
|
|
52
|
+
output.success(result.message);
|
|
62
53
|
} else {
|
|
63
|
-
|
|
54
|
+
output.error('Service creation failed');
|
|
55
|
+
process.exit(1);
|
|
64
56
|
}
|
|
65
|
-
output.success('Service creation completed successfully!');
|
|
66
57
|
output.section('Next steps');
|
|
67
58
|
output.list(['cd into your new service directory', 'Run npm install', 'Configure additional settings in src/config/domains.js', 'Run npm run deploy to deploy to Cloudflare']);
|
|
68
59
|
} catch (error) {
|
|
69
|
-
const output = new (await import('
|
|
60
|
+
const output = new (await import('../../lib/shared/utils/output-formatter.js')).OutputFormatter(options || {});
|
|
70
61
|
output.error(`Service creation failed: ${error.message}`);
|
|
71
62
|
if (error.details) {
|
|
72
63
|
output.warning(`Details: ${error.details}`);
|
|
@@ -1,42 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deploy Command - Smart minimal input deployment with service detection
|
|
3
|
-
*
|
|
4
|
-
* Input Strategy: SMART MINIMAL WITH DOMAIN ROUTING
|
|
5
|
-
* - Detects Clodo services OR legacy services (wrangler.toml)
|
|
6
|
-
* - Supports multiple manifest locations
|
|
7
|
-
* - Uses DomainRouter for intelligent domain selection
|
|
8
|
-
* - Gathers credentials smartly: env vars ā flags ā interactive collection with auto-fetch
|
|
9
|
-
* - Validates token and fetches account ID & zone ID from Cloudflare
|
|
10
|
-
* - REFACTORED (Task 3.2): Integrates with MultiDomainOrchestrator for full deployment orchestration
|
|
11
|
-
* - REFACTORED (UX): Modularized with helper functions for better maintainability
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
1
|
import chalk from 'chalk';
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import { CloudflareServiceValidator } from '../lib/shared/config/cloudflare-service-validator.js';
|
|
19
|
-
import { DeploymentCredentialCollector } from '../lib/shared/deployment/credential-collector.js';
|
|
20
|
-
import { StandardOptions } from '../lib/shared/utils/cli-options.js';
|
|
21
|
-
import { ConfigLoader } from '../lib/shared/utils/config-loader.js';
|
|
22
|
-
import { ServiceConfigManager } from '../lib/shared/utils/service-config-manager.js';
|
|
23
|
-
import { DomainRouter } from '../lib/shared/routing/domain-router.js';
|
|
24
|
-
import { MultiDomainOrchestrator } from '../orchestration/multi-domain-orchestrator.js';
|
|
25
|
-
|
|
26
|
-
// Import modular helpers
|
|
27
|
-
import { detectExistingResources, displayDeploymentPlan } from './helpers/resource-detection.js';
|
|
28
|
-
import { confirmDeployment, displayDeploymentResults, displayNextSteps } from './helpers/deployment-ui.js';
|
|
29
|
-
import { runPostDeploymentVerification } from './helpers/deployment-verification.js';
|
|
30
|
-
import { handleDeploymentError } from './helpers/error-recovery.js';
|
|
2
|
+
import { Clodo } from '../../src/simple-api.js';
|
|
3
|
+
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
4
|
+
import { ConfigLoader } from '../../lib/shared/utils/config-loader.js';
|
|
31
5
|
export function registerDeployCommand(program) {
|
|
32
6
|
const command = program.command('deploy').description('Deploy a Clodo service with smart credential handling and domain selection')
|
|
33
7
|
// Cloudflare-specific options
|
|
34
|
-
.option('--token <token>', 'Cloudflare API token').option('--account-id <id>', 'Cloudflare account ID').option('--zone-id <id>', 'Cloudflare zone ID').option('--domain <domain>', 'Specific domain to deploy to (otherwise prompted if multiple exist)').option('--environment <env>', 'Target environment (development, staging, production)', 'production').option('--development', 'Deploy to development environment (shorthand for --environment development)').option('--staging', 'Deploy to staging environment (shorthand for --environment staging)').option('--production', 'Deploy to production environment (shorthand for --environment production)').option('--
|
|
8
|
+
.option('--token <token>', 'Cloudflare API token').option('--account-id <id>', 'Cloudflare account ID').option('--zone-id <id>', 'Cloudflare zone ID').option('--domain <domain>', 'Specific domain to deploy to (otherwise prompted if multiple exist)').option('--environment <env>', 'Target environment (development, staging, production)', 'production').option('--development', 'Deploy to development environment (shorthand for --environment development)').option('--staging', 'Deploy to staging environment (shorthand for --environment staging)').option('--production', 'Deploy to production environment (shorthand for --environment production)').option('--dry-run', 'Simulate deployment without making changes').option('-y, --yes', 'Skip confirmation prompts (for CI/CD)').option('--service-path <path>', 'Path to service directory', '.');
|
|
35
9
|
|
|
36
10
|
// Add standard options (--verbose, --quiet, --json, --no-color, --config-file)
|
|
37
11
|
StandardOptions.define(command).action(async options => {
|
|
38
12
|
try {
|
|
39
|
-
const output = new (await import('
|
|
13
|
+
const output = new (await import('../../lib/shared/utils/output-formatter.js')).OutputFormatter(options);
|
|
40
14
|
const configLoader = new ConfigLoader({
|
|
41
15
|
verbose: options.verbose,
|
|
42
16
|
quiet: options.quiet,
|
|
@@ -61,342 +35,33 @@ export function registerDeployCommand(program) {
|
|
|
61
35
|
}
|
|
62
36
|
}
|
|
63
37
|
|
|
64
|
-
//
|
|
65
|
-
const
|
|
66
|
-
verbose: options.verbose,
|
|
67
|
-
quiet: options.quiet,
|
|
68
|
-
json: options.json,
|
|
69
|
-
showSources: options.showConfigSources
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// For deploy command, we use current directory as service path
|
|
73
|
-
const deployServicePath = resolve(options.servicePath || '.');
|
|
74
|
-
|
|
75
|
-
// Load and merge all configurations
|
|
76
|
-
const mergedOptions = await configManager.loadServiceConfig(deployServicePath, {
|
|
77
|
-
...options,
|
|
78
|
-
configFile: options.configFile // Pass through the config file option
|
|
79
|
-
}, {
|
|
80
|
-
token: null,
|
|
81
|
-
accountId: null,
|
|
82
|
-
zoneId: null,
|
|
83
|
-
domain: null,
|
|
84
|
-
environment: 'production',
|
|
85
|
-
allDomains: false,
|
|
86
|
-
dryRun: false,
|
|
87
|
-
yes: false,
|
|
88
|
-
servicePath: '.'
|
|
89
|
-
});
|
|
90
|
-
output.info('š Clodo Service Deployment');
|
|
91
|
-
|
|
92
|
-
// Step 1: Load and validate service configuration
|
|
93
|
-
const servicePath = resolve(mergedOptions.servicePath || deployServicePath);
|
|
94
|
-
const serviceConfig = await ManifestLoader.loadAndValidateCloudflareService(servicePath);
|
|
95
|
-
if (!serviceConfig.manifest) {
|
|
96
|
-
if (serviceConfig.error === 'NOT_A_CLOUDFLARE_SERVICE') {
|
|
97
|
-
ManifestLoader.printNotCloudflareServiceError(servicePath);
|
|
98
|
-
} else if (serviceConfig.error === 'CLOUDFLARE_SERVICE_INVALID') {
|
|
99
|
-
// Pass false because we're validating a detected service, not a Clodo manifest
|
|
100
|
-
ManifestLoader.printValidationErrors(serviceConfig.validationResult, false);
|
|
101
|
-
}
|
|
102
|
-
process.exit(1);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Print service info and validation results
|
|
106
|
-
if (serviceConfig.validationResult) {
|
|
107
|
-
CloudflareServiceValidator.printValidationReport(serviceConfig.validationResult.validation);
|
|
108
|
-
}
|
|
109
|
-
ManifestLoader.printManifestInfo(serviceConfig.manifest);
|
|
110
|
-
console.log(chalk.blue(`ā¹ļø Configuration loaded from: ${serviceConfig.foundAt}`));
|
|
38
|
+
// Merge config file defaults with CLI options (CLI takes precedence)
|
|
39
|
+
const mergedOptions = configLoader.merge(configFileData, options);
|
|
111
40
|
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
servicePath: servicePath,
|
|
120
|
-
quiet: mergedOptions.quiet
|
|
121
|
-
});
|
|
122
|
-
let credentials;
|
|
123
|
-
try {
|
|
124
|
-
credentials = await credentialCollector.collectCredentials({
|
|
41
|
+
// Use simple API for deployment
|
|
42
|
+
const result = await Clodo.deploy({
|
|
43
|
+
servicePath: mergedOptions.servicePath || '.',
|
|
44
|
+
environment: mergedOptions.environment || 'production',
|
|
45
|
+
domain: mergedOptions.domain,
|
|
46
|
+
dryRun: mergedOptions.dryRun || false,
|
|
47
|
+
credentials: {
|
|
125
48
|
token: mergedOptions.token,
|
|
126
49
|
accountId: mergedOptions.accountId,
|
|
127
50
|
zoneId: mergedOptions.zoneId
|
|
128
|
-
});
|
|
129
|
-
} finally {
|
|
130
|
-
credentialCollector.cleanup();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Step 3: Initialize DomainRouter for intelligent domain selection
|
|
134
|
-
// REFACTORED (Task 3.2): Use DomainRouter for domain detection and selection
|
|
135
|
-
console.log(chalk.cyan('\nšŗļø Detecting available domains...\n'));
|
|
136
|
-
const router = new DomainRouter({
|
|
137
|
-
environment: mergedOptions.environment || 'production',
|
|
138
|
-
verbose: options.verbose,
|
|
139
|
-
configPath: options.configPath,
|
|
140
|
-
disableOrchestrator: false,
|
|
141
|
-
// We'll use the orchestrator for deployment
|
|
142
|
-
orchestratorOptions: {
|
|
143
|
-
dryRun: options.dryRun || false,
|
|
144
|
-
cloudflareToken: credentials.token,
|
|
145
|
-
cloudflareAccountId: credentials.accountId
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
// Detect domains from manifest
|
|
150
|
-
let detectedDomains = [];
|
|
151
|
-
const manifest = serviceConfig.manifest;
|
|
152
|
-
const config = manifest.deployment || manifest.configuration || {};
|
|
153
|
-
const domainsConfig = config.domains || [];
|
|
154
|
-
if (Array.isArray(domainsConfig) && domainsConfig.length > 0) {
|
|
155
|
-
// Handle both array of strings and array of objects
|
|
156
|
-
detectedDomains = domainsConfig.map(d => {
|
|
157
|
-
if (typeof d === 'string') return d;
|
|
158
|
-
if (typeof d === 'object' && d.name) return d.name;
|
|
159
|
-
return null;
|
|
160
|
-
}).filter(d => d !== null);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// If no domains in manifest but we have a selected zone from credentials, use that
|
|
164
|
-
if (detectedDomains.length === 0 && credentials.zoneName) {
|
|
165
|
-
detectedDomains = [credentials.zoneName];
|
|
166
|
-
if (!options.quiet) {
|
|
167
|
-
console.log(chalk.blue(`ā¹ļø Using selected Cloudflare domain: ${credentials.zoneName}`));
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// If still no domains and it's a detected CF service, use default
|
|
172
|
-
if (detectedDomains.length === 0 && manifest._source === 'cloudflare-service-detected') {
|
|
173
|
-
// For detected CF services, use default
|
|
174
|
-
detectedDomains = ['workers.cloudflare.com'];
|
|
175
|
-
if (!options.quiet) {
|
|
176
|
-
console.log(chalk.blue(`ā¹ļø No custom domains configured, using default: workers.cloudflare.com`));
|
|
177
|
-
}
|
|
178
|
-
} else if (detectedDomains.length > 0 && !options.quiet && !credentials.zoneName) {
|
|
179
|
-
console.log(chalk.blue(`ā¹ļø Found ${detectedDomains.length} configured domain(s) in manifest`));
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Domain selection: check CLI flag first, then prompt user
|
|
183
|
-
let selectedDomain = mergedOptions.domain;
|
|
184
|
-
if (selectedDomain && !options.quiet) {
|
|
185
|
-
console.log(chalk.blue(`ā¹ļø Using domain from --domain flag: ${selectedDomain}`));
|
|
186
|
-
}
|
|
187
|
-
if (!selectedDomain && detectedDomains.length > 0) {
|
|
188
|
-
if (detectedDomains.length === 1) {
|
|
189
|
-
// Only one domain, use it directly
|
|
190
|
-
selectedDomain = detectedDomains[0];
|
|
191
|
-
if (!options.quiet) {
|
|
192
|
-
console.log(chalk.green(`ā Auto-selected only available domain: ${selectedDomain}`));
|
|
193
|
-
}
|
|
194
|
-
} else {
|
|
195
|
-
// Multiple domains available - let user choose
|
|
196
|
-
console.log(chalk.cyan('š Available domains:'));
|
|
197
|
-
detectedDomains.forEach((d, i) => {
|
|
198
|
-
console.log(chalk.white(` ${i + 1}) ${d}`));
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
// If running interactively, prompt user
|
|
202
|
-
if (process.stdin.isTTY) {
|
|
203
|
-
const {
|
|
204
|
-
createPromptModule
|
|
205
|
-
} = await import('inquirer');
|
|
206
|
-
const prompt = createPromptModule();
|
|
207
|
-
const response = await prompt([{
|
|
208
|
-
type: 'list',
|
|
209
|
-
name: 'selectedDomain',
|
|
210
|
-
message: 'Select domain to deploy to:',
|
|
211
|
-
choices: detectedDomains,
|
|
212
|
-
default: detectedDomains[0]
|
|
213
|
-
}]);
|
|
214
|
-
selectedDomain = response.selectedDomain;
|
|
215
|
-
} else {
|
|
216
|
-
// Non-interactive mode: use first domain
|
|
217
|
-
selectedDomain = detectedDomains[0];
|
|
218
|
-
console.log(chalk.yellow(`ā ļø Non-interactive mode: using first domain: ${selectedDomain}`));
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
if (!selectedDomain) {
|
|
223
|
-
if (!options.quiet) {
|
|
224
|
-
console.error(chalk.yellow('ā ļø No domain configured for deployment'));
|
|
225
|
-
console.error(chalk.gray('For Clodo services: add deployment.domains in clodo-service-manifest.json'));
|
|
226
|
-
console.error(chalk.gray('For detected CF services: define routes in wrangler.toml'));
|
|
227
51
|
}
|
|
228
|
-
process.exit(1);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Step 4: Validate domain configuration
|
|
232
|
-
console.log(chalk.cyan('\nš Validating domain configuration...\n'));
|
|
233
|
-
const validation = router.validateConfiguration({
|
|
234
|
-
domains: [selectedDomain],
|
|
235
|
-
environment: mergedOptions.environment || 'production'
|
|
236
|
-
});
|
|
237
|
-
if (!validation.valid) {
|
|
238
|
-
console.error(chalk.red('ā Configuration validation failed:'));
|
|
239
|
-
validation.errors.forEach(err => {
|
|
240
|
-
console.error(chalk.yellow(` ⢠${err}`));
|
|
241
|
-
});
|
|
242
|
-
process.exit(1);
|
|
243
|
-
}
|
|
244
|
-
if (validation.warnings && validation.warnings.length > 0) {
|
|
245
|
-
console.log(chalk.yellow('ā ļø Configuration warnings:'));
|
|
246
|
-
validation.warnings.forEach(warn => {
|
|
247
|
-
console.log(chalk.gray(` ⢠${warn}`));
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Extract service metadata
|
|
252
|
-
const serviceName = manifest.serviceName || 'unknown-service';
|
|
253
|
-
const serviceType = manifest.serviceType || 'generic';
|
|
254
|
-
|
|
255
|
-
// Step 5: Detect existing resources and display deployment plan
|
|
256
|
-
const {
|
|
257
|
-
existingWorker,
|
|
258
|
-
existingDatabases,
|
|
259
|
-
resourceDetectionFailed
|
|
260
|
-
} = await detectExistingResources(serviceName, manifest, credentials, output, options.verbose);
|
|
261
|
-
displayDeploymentPlan({
|
|
262
|
-
serviceName,
|
|
263
|
-
serviceType,
|
|
264
|
-
selectedDomain,
|
|
265
|
-
environment: mergedOptions.environment,
|
|
266
|
-
credentials,
|
|
267
|
-
dryRun: options.dryRun,
|
|
268
|
-
existingWorker,
|
|
269
|
-
existingDatabases,
|
|
270
|
-
resourceDetectionFailed,
|
|
271
|
-
manifest
|
|
272
52
|
});
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
process.exit(0);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Step 5: Initialize MultiDomainOrchestrator for deployment
|
|
281
|
-
// REFACTORED (Task 3.2): Direct orchestrator integration instead of external deployer
|
|
282
|
-
console.log(chalk.cyan('\nāļø Initializing orchestration system...\n'));
|
|
283
|
-
|
|
284
|
-
// Determine wrangler config path based on selected domain/zone
|
|
285
|
-
// For multi-customer deployments, use customer-specific config if available
|
|
286
|
-
let wranglerConfigPath;
|
|
287
|
-
const configDir = join(servicePath, 'config');
|
|
288
|
-
|
|
289
|
-
// Map domain to config file (customize this mapping for your setup)
|
|
290
|
-
if (selectedDomain === 'clodo.dev' || credentials.cloudflareSettings?.zoneName === 'clodo.dev') {
|
|
291
|
-
// Use config/wrangler.toml for clodo.dev domain
|
|
292
|
-
const clodoConfigPath = join(configDir, 'wrangler.toml');
|
|
293
|
-
if (existsSync(clodoConfigPath)) {
|
|
294
|
-
wranglerConfigPath = clodoConfigPath;
|
|
295
|
-
console.log(chalk.green(`ā Using clodo.dev config: ${clodoConfigPath}`));
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
// Add more domain mappings as needed:
|
|
299
|
-
// else if (selectedDomain === 'customer2.com') {
|
|
300
|
-
// wranglerConfigPath = path.join(configDir, 'customers', 'customer2-wrangler.toml');
|
|
301
|
-
// }
|
|
302
|
-
|
|
303
|
-
// If no specific config found, use default (root wrangler.toml)
|
|
304
|
-
if (!wranglerConfigPath) {
|
|
305
|
-
console.log(chalk.yellow(`ā¹ Using default wrangler.toml from service root`));
|
|
306
|
-
}
|
|
307
|
-
const orchestrator = new MultiDomainOrchestrator({
|
|
308
|
-
domains: [selectedDomain],
|
|
309
|
-
environment: mergedOptions.environment || 'production',
|
|
310
|
-
dryRun: options.dryRun || false,
|
|
311
|
-
skipTests: false,
|
|
312
|
-
parallelDeployments: 1,
|
|
313
|
-
// Single domain in this flow
|
|
314
|
-
servicePath: servicePath,
|
|
315
|
-
serviceName: serviceName,
|
|
316
|
-
// Pass service name for custom domain construction
|
|
317
|
-
wranglerConfigPath: wranglerConfigPath,
|
|
318
|
-
// Pass custom config path if found
|
|
319
|
-
// Use cloudflareSettings object for complete zone-specific configuration
|
|
320
|
-
cloudflareSettings: credentials.cloudflareSettings,
|
|
321
|
-
enablePersistence: !options.dryRun,
|
|
322
|
-
rollbackEnabled: !options.dryRun,
|
|
323
|
-
verbose: options.verbose
|
|
324
|
-
});
|
|
325
|
-
try {
|
|
326
|
-
await orchestrator.initialize();
|
|
327
|
-
} catch (err) {
|
|
328
|
-
console.error(chalk.red('ā Failed to initialize orchestrator'));
|
|
329
|
-
console.error(chalk.yellow(`Error: ${err.message}`));
|
|
330
|
-
if (process.env.DEBUG) {
|
|
331
|
-
console.error(chalk.gray(err.stack));
|
|
53
|
+
if (result.success) {
|
|
54
|
+
output.success(result.message);
|
|
55
|
+
if (result.deployedDomains && result.deployedDomains.length > 0) {
|
|
56
|
+
output.info(`Deployed to domains: ${result.deployedDomains.join(', ')}`);
|
|
332
57
|
}
|
|
58
|
+
} else {
|
|
59
|
+
output.error('Deployment failed');
|
|
333
60
|
process.exit(1);
|
|
334
61
|
}
|
|
335
|
-
|
|
336
|
-
// Step 7: Execute deployment via orchestrator
|
|
337
|
-
console.log(chalk.cyan('š Starting deployment via orchestrator...\n'));
|
|
338
|
-
let result;
|
|
339
|
-
try {
|
|
340
|
-
result = await orchestrator.deploySingleDomain(selectedDomain, {
|
|
341
|
-
manifest: manifest,
|
|
342
|
-
credentials: credentials,
|
|
343
|
-
dryRun: options.dryRun,
|
|
344
|
-
environment: mergedOptions.environment || 'production'
|
|
345
|
-
});
|
|
346
|
-
} catch (deployError) {
|
|
347
|
-
await handleDeploymentError(deployError, command, options);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// Step 8: Display deployment results
|
|
351
|
-
displayDeploymentResults({
|
|
352
|
-
result,
|
|
353
|
-
serviceName,
|
|
354
|
-
serviceType,
|
|
355
|
-
selectedDomain,
|
|
356
|
-
environment: mergedOptions.environment
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
// Step 9: Run post-deployment verification and health check
|
|
360
|
-
await runPostDeploymentVerification(serviceName, result, credentials, {
|
|
361
|
-
...options,
|
|
362
|
-
serviceName,
|
|
363
|
-
customDomain: selectedDomain !== 'workers.cloudflare.com' ? selectedDomain : null
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
// Step 10: Display next steps
|
|
367
|
-
displayNextSteps({
|
|
368
|
-
result,
|
|
369
|
-
selectedDomain,
|
|
370
|
-
serviceName,
|
|
371
|
-
dryRun: options.dryRun
|
|
372
|
-
});
|
|
373
|
-
if (process.env.DEBUG && result.details) {
|
|
374
|
-
console.log(chalk.gray('š Full Result:'));
|
|
375
|
-
console.log(chalk.gray(JSON.stringify(result, null, 2)));
|
|
376
|
-
}
|
|
377
62
|
} catch (error) {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
console.error(chalk.yellow('\nš” Credential Issue:'));
|
|
381
|
-
console.error(chalk.white(' Check your API token, account ID, and zone ID'));
|
|
382
|
-
console.error(chalk.white(' Visit: https://dash.cloudflare.com/profile/api-tokens'));
|
|
383
|
-
}
|
|
384
|
-
if (error.message.includes('domain') || error.message.includes('zone')) {
|
|
385
|
-
console.error(chalk.yellow('\nš” Domain Issue:'));
|
|
386
|
-
console.error(chalk.white(' Verify domain exists in Cloudflare'));
|
|
387
|
-
console.error(chalk.white(' Check API token has zone:read permissions'));
|
|
388
|
-
}
|
|
389
|
-
if (error.message.includes('orchestration') || error.message.includes('initialization')) {
|
|
390
|
-
console.error(chalk.yellow('\nš” Orchestration Issue:'));
|
|
391
|
-
console.error(chalk.white(' Check MultiDomainOrchestrator configuration'));
|
|
392
|
-
console.error(chalk.white(' Verify all modular components loaded correctly'));
|
|
393
|
-
}
|
|
394
|
-
if (process.env.DEBUG) {
|
|
395
|
-
console.error(chalk.gray('\nFull Stack Trace:'));
|
|
396
|
-
console.error(chalk.gray(error.stack));
|
|
397
|
-
} else {
|
|
398
|
-
console.error(chalk.gray('Run with DEBUG=1 for full stack trace'));
|
|
399
|
-
}
|
|
63
|
+
const output = new (await import('../../lib/shared/utils/output-formatter.js')).OutputFormatter(options || {});
|
|
64
|
+
output.error(`Deployment failed: ${error.message}`);
|
|
400
65
|
process.exit(1);
|
|
401
66
|
}
|
|
402
67
|
});
|