@tamyla/clodo-framework 3.1.27 ā 3.2.1
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 +22 -0
- package/dist/cli/clodo-simple.js +1 -1
- package/dist/cli/commands/assess.js +1 -1
- package/dist/cli/commands/create.js +1 -1
- package/dist/cli/commands/deploy.js +59 -21
- package/dist/cli/commands/diagnose.js +1 -1
- package/dist/cli/commands/update.js +1 -1
- package/dist/cli/commands/validate.js +1 -1
- package/dist/cli/security-cli.js +1 -1
- package/dist/config/.env.staging.example +25 -0
- package/dist/deployment/index.js +2 -2
- package/dist/deployment/orchestration/BaseDeploymentOrchestrator.js +1 -1
- package/dist/deployment/orchestration/UnifiedDeploymentOrchestrator.js +2 -4
- package/dist/index.js +11 -12
- package/dist/lib/deployment/orchestration/BaseDeploymentOrchestrator.js +1 -1
- package/dist/lib/shared/cloudflare/ops.js +103 -17
- package/dist/lib/shared/deployment/credential-collector.js +134 -1
- package/dist/lib/shared/deployment/index.js +7 -3
- 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/workflows/index.js +13 -0
- package/dist/lib/shared/deployment/workflows/interactive-confirmation.js +1 -1
- package/dist/lib/shared/deployment/workflows/interactive-deployment-coordinator.js +229 -0
- package/dist/lib/shared/deployment/workflows/interactive-domain-info-gatherer.js +1 -1
- package/dist/lib/shared/deployment/workflows/interactive-secret-workflow.js +3 -3
- package/dist/lib/shared/deployment/workflows/interactive-testing-workflow.js +2 -2
- package/dist/lib/shared/deployment/workflows/interactive-validation.js +2 -2
- package/dist/lib/shared/monitoring/health-checker.js +11 -5
- package/dist/lib/shared/validation/ValidationRegistry.js +1 -1
- package/dist/utils/deployment/index.js +2 -1
- package/dist/utils/health-checker.js +53 -76
- package/dist/utils/index.js +3 -0
- package/package.json +4 -11
- package/templates/generic/src/worker/index.js +1 -1
- package/templates/static-site/src/worker/index.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
## [3.2.1](https://github.com/tamylaa/clodo-framework/compare/v3.2.0...v3.2.1) (2025-12-05)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* enhance deploy command with prerequisite checking and organize project structure ([660c5ab](https://github.com/tamylaa/clodo-framework/commit/660c5ab82546ca1a80b42c1d4cbc6d3972cc6203))
|
|
7
|
+
|
|
8
|
+
# [3.2.0](https://github.com/tamylaa/clodo-framework/compare/v3.1.27...v3.2.0) (2025-12-03)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* add pre-publish import validation and remove non-existent exports ([a41fa7d](https://github.com/tamylaa/clodo-framework/commit/a41fa7df5c1ce05d34efe7e62e68d1c2fa8ea89c))
|
|
14
|
+
* correct CLI import paths for npm distribution ([ba68e0a](https://github.com/tamylaa/clodo-framework/commit/ba68e0adfa4c3bf36584b8bd37310ef616f79722))
|
|
15
|
+
* enable missing exports and remove lib-dependent modules from npm ([1645a58](https://github.com/tamylaa/clodo-framework/commit/1645a58d1e1f5d7126fd02766e9b4d006fa45be7))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* add comprehensive pre-publish and downstream install tests ([b1e8a25](https://github.com/tamylaa/clodo-framework/commit/b1e8a25b3acae840b66f728aa55dd5c24af0914f))
|
|
21
|
+
* add proper CLI regression tests to downstream install test ([4cde4bc](https://github.com/tamylaa/clodo-framework/commit/4cde4bc08f0290f0432815b2c8cfa60247bc8dfd))
|
|
22
|
+
|
|
1
23
|
## [3.1.27](https://github.com/tamylaa/clodo-framework/compare/v3.1.26...v3.1.27) (2025-12-02)
|
|
2
24
|
|
|
3
25
|
|
package/dist/cli/clodo-simple.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import chalk from 'chalk';
|
|
7
7
|
import path from 'path';
|
|
8
|
-
import { ServiceOrchestrator } from '
|
|
8
|
+
import { ServiceOrchestrator } from '@tamyla/clodo-framework';
|
|
9
9
|
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
10
10
|
import { ServiceConfigManager } from '../../lib/shared/utils/service-config-manager.js';
|
|
11
11
|
export function registerAssessCommand(program) {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import chalk from 'chalk';
|
|
9
|
-
import { Clodo } from '
|
|
9
|
+
import { Clodo } from '@tamyla/clodo-framework';
|
|
10
10
|
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
11
11
|
import { ConfigLoader } from '../../lib/shared/utils/config-loader.js';
|
|
12
12
|
export function registerCreateCommand(program) {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { Clodo } from '
|
|
2
|
+
import { Clodo } from '@tamyla/clodo-framework';
|
|
3
3
|
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
4
4
|
import { ConfigLoader } from '../../lib/shared/utils/config-loader.js';
|
|
5
|
+
import { InteractiveDeploymentCoordinator } from '../../lib/shared/deployment/workflows/interactive-deployment-coordinator.js';
|
|
5
6
|
export function registerDeployCommand(program) {
|
|
6
|
-
const command = program.command('deploy').description('Deploy a Clodo service with
|
|
7
|
+
const command = program.command('deploy').description('Deploy a Clodo service with interactive configuration and validation')
|
|
7
8
|
// Cloudflare-specific options
|
|
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
|
|
9
|
+
.option('--token <token>', 'Cloudflare API token (or set CLOUDFLARE_API_TOKEN env var)').option('--account-id <id>', 'Cloudflare account ID (or set CLOUDFLARE_ACCOUNT_ID env var)').option('--zone-id <id>', 'Cloudflare zone ID (or set CLOUDFLARE_ZONE_ID env var)').option('--domain <domain>', 'Specific domain to deploy to').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', '.').option('--check-prereqs', 'Check deployment prerequisites before starting').option('--check-auth', 'Check Wrangler authentication status').option('--check-network', 'Check network connectivity to Cloudflare');
|
|
9
10
|
|
|
10
11
|
// Add standard options (--verbose, --quiet, --json, --no-color, --config-file)
|
|
11
12
|
StandardOptions.define(command).action(async options => {
|
|
@@ -38,26 +39,63 @@ export function registerDeployCommand(program) {
|
|
|
38
39
|
// Merge config file defaults with CLI options (CLI takes precedence)
|
|
39
40
|
const mergedOptions = configLoader.merge(configFileData, options);
|
|
40
41
|
|
|
41
|
-
//
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
42
|
+
// Determine if interactive mode should be enabled
|
|
43
|
+
const interactive = !mergedOptions.nonInteractive && !mergedOptions.yes;
|
|
44
|
+
if (interactive) {
|
|
45
|
+
console.log(chalk.cyan('\nš Interactive Clodo Service Deployment'));
|
|
46
|
+
console.log(chalk.gray('ā'.repeat(60)));
|
|
47
|
+
console.log(chalk.white('Welcome to the interactive deployment wizard!\n'));
|
|
48
|
+
|
|
49
|
+
// Use the interactive deployment coordinator
|
|
50
|
+
const coordinator = new InteractiveDeploymentCoordinator({
|
|
51
|
+
servicePath: mergedOptions.servicePath || '.',
|
|
52
|
+
environment: mergedOptions.environment || 'production',
|
|
53
|
+
domain: mergedOptions.domain,
|
|
54
|
+
dryRun: mergedOptions.dryRun || false,
|
|
55
|
+
credentials: {
|
|
56
|
+
token: mergedOptions.token,
|
|
57
|
+
accountId: mergedOptions.accountId,
|
|
58
|
+
zoneId: mergedOptions.zoneId
|
|
59
|
+
},
|
|
60
|
+
checkPrereqs: mergedOptions.checkPrereqs,
|
|
61
|
+
checkAuth: mergedOptions.checkAuth,
|
|
62
|
+
checkNetwork: mergedOptions.checkNetwork,
|
|
63
|
+
verbose: mergedOptions.verbose,
|
|
64
|
+
quiet: mergedOptions.quiet,
|
|
65
|
+
json: mergedOptions.json
|
|
66
|
+
});
|
|
67
|
+
const result = await coordinator.runInteractiveDeployment();
|
|
68
|
+
if (result.success) {
|
|
69
|
+
output.success(result.message);
|
|
70
|
+
if (result.deployedDomains && result.deployedDomains.length > 0) {
|
|
71
|
+
output.info(`Deployed to domains: ${result.deployedDomains.join(', ')}`);
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
output.error('Interactive deployment failed');
|
|
75
|
+
process.exit(1);
|
|
57
76
|
}
|
|
58
77
|
} else {
|
|
59
|
-
|
|
60
|
-
|
|
78
|
+
// Use simple API for deployment (non-interactive/CI mode)
|
|
79
|
+
const result = await Clodo.deploy({
|
|
80
|
+
servicePath: mergedOptions.servicePath || '.',
|
|
81
|
+
environment: mergedOptions.environment || 'production',
|
|
82
|
+
domain: mergedOptions.domain,
|
|
83
|
+
dryRun: mergedOptions.dryRun || false,
|
|
84
|
+
credentials: {
|
|
85
|
+
token: mergedOptions.token,
|
|
86
|
+
accountId: mergedOptions.accountId,
|
|
87
|
+
zoneId: mergedOptions.zoneId
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
if (result.success) {
|
|
91
|
+
output.success(result.message);
|
|
92
|
+
if (result.deployedDomains && result.deployedDomains.length > 0) {
|
|
93
|
+
output.info(`Deployed to domains: ${result.deployedDomains.join(', ')}`);
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
output.error('Deployment failed');
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
61
99
|
}
|
|
62
100
|
} catch (error) {
|
|
63
101
|
const output = new (await import('../../lib/shared/utils/output-formatter.js')).OutputFormatter(options || {});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { ServiceOrchestrator } from '
|
|
3
|
+
import { ServiceOrchestrator } from '@tamyla/clodo-framework';
|
|
4
4
|
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
5
5
|
import { ServiceConfigManager } from '../../lib/shared/utils/service-config-manager.js';
|
|
6
6
|
export function registerDiagnoseCommand(program) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import path from 'path';
|
|
7
|
-
import { ServiceOrchestrator } from '
|
|
7
|
+
import { ServiceOrchestrator } from '@tamyla/clodo-framework';
|
|
8
8
|
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
9
9
|
import { ServiceConfigManager } from '../../lib/shared/utils/service-config-manager.js';
|
|
10
10
|
export function registerUpdateCommand(program) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { Clodo } from '
|
|
2
|
+
import { Clodo } from '@tamyla/clodo-framework';
|
|
3
3
|
import { StandardOptions } from '../../lib/shared/utils/cli-options.js';
|
|
4
4
|
export function registerValidateCommand(program) {
|
|
5
5
|
const command = program.command('validate <service-path>').description('Validate an existing service configuration').option('--export-report <file>', 'Export validation report to JSON file');
|
package/dist/cli/security-cli.js
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Staging Environment Configuration
|
|
2
|
+
# Copy this file to .env.staging and fill in your actual Cloudflare credentials
|
|
3
|
+
# DO NOT commit this file to version control
|
|
4
|
+
|
|
5
|
+
# Cloudflare API Credentials for Staging Environment
|
|
6
|
+
CLOUDFLARE_API_TOKEN=your_staging_cloudflare_api_token_here
|
|
7
|
+
CLOUDFLARE_ACCOUNT_ID=your_staging_cloudflare_account_id_here
|
|
8
|
+
CLOUDFLARE_ZONE_ID=your_staging_cloudflare_zone_id_here
|
|
9
|
+
|
|
10
|
+
# Staging Domain Configuration
|
|
11
|
+
STAGING_DOMAIN=staging.example.com
|
|
12
|
+
STAGING_ZONE_ID=${CLOUDFLARE_ZONE_ID}
|
|
13
|
+
|
|
14
|
+
# Database Configuration (if different from development)
|
|
15
|
+
STAGING_DATABASE_ID=your_staging_database_id_here
|
|
16
|
+
STAGING_DATABASE_NAME=api2-com-staging-db
|
|
17
|
+
|
|
18
|
+
# Monitoring and Alerting
|
|
19
|
+
STAGING_METRICS_ENDPOINT=https://metrics-staging.example.com
|
|
20
|
+
STAGING_ALERT_EMAIL=staging-alerts@example.com
|
|
21
|
+
|
|
22
|
+
# Security Settings
|
|
23
|
+
STAGING_WAF_ENABLED=true
|
|
24
|
+
STAGING_RATE_LIMIT=500
|
|
25
|
+
STAGING_CORS_ORIGINS=https://staging.example.com,https://app-staging.example.com
|
package/dist/deployment/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// Deployment Module
|
|
2
2
|
// Core deployment components for the Clodo Framework
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// NOTE: WranglerDeployer has lib/ dependencies not available in npm distribution
|
|
5
|
+
// export { WranglerDeployer } from './wrangler-deployer.js';
|
|
5
6
|
|
|
6
7
|
// Orchestration Framework - Public API for downstream consumers
|
|
7
8
|
export { BaseDeploymentOrchestrator } from './orchestration/BaseDeploymentOrchestrator.js';
|
|
8
9
|
export { SingleServiceOrchestrator } from './orchestration/SingleServiceOrchestrator.js';
|
|
9
10
|
export { PortfolioOrchestrator } from './orchestration/PortfolioOrchestrator.js';
|
|
10
|
-
export { EnterpriseOrchestrator } from './orchestration/EnterpriseOrchestrator.js';
|
|
11
11
|
export { UnifiedDeploymentOrchestrator } from './orchestration/UnifiedDeploymentOrchestrator.js';
|
|
@@ -36,8 +36,7 @@
|
|
|
36
36
|
import { BaseDeploymentOrchestrator } from './BaseDeploymentOrchestrator.js';
|
|
37
37
|
import { SingleServiceOrchestrator } from './SingleServiceOrchestrator.js';
|
|
38
38
|
import { PortfolioOrchestrator } from './PortfolioOrchestrator.js';
|
|
39
|
-
import {
|
|
40
|
-
import { ErrorHandler } from '../../shared/utils/ErrorHandler.js';
|
|
39
|
+
import { ErrorHandler } from '../../../lib/shared/utils/ErrorHandler.js';
|
|
41
40
|
|
|
42
41
|
/**
|
|
43
42
|
* Capability descriptors - Defines what each capability provides
|
|
@@ -204,8 +203,7 @@ export class UnifiedDeploymentOrchestrator extends BaseDeploymentOrchestrator {
|
|
|
204
203
|
// System adapters for backward compatibility
|
|
205
204
|
this.systemAdapters = {
|
|
206
205
|
single: SingleServiceOrchestrator,
|
|
207
|
-
portfolio: PortfolioOrchestrator
|
|
208
|
-
enterprise: EnterpriseOrchestrator
|
|
206
|
+
portfolio: PortfolioOrchestrator
|
|
209
207
|
};
|
|
210
208
|
|
|
211
209
|
// Deployment context
|
package/dist/index.js
CHANGED
|
@@ -21,22 +21,21 @@ export * from './modules/ModuleManager.js';
|
|
|
21
21
|
export * from './routing/EnhancedRouter.js';
|
|
22
22
|
export * from './handlers/GenericRouteHandler.js';
|
|
23
23
|
|
|
24
|
-
// Deployment components
|
|
25
|
-
|
|
24
|
+
// Deployment components
|
|
25
|
+
export { DeploymentValidator } from './deployment/validator.js';
|
|
26
|
+
export { DeploymentAuditor } from './deployment/auditor.js';
|
|
27
|
+
// NOTE: WranglerDeployer has lib/ dependencies not available in npm distribution
|
|
26
28
|
// export { WranglerDeployer } from './deployment/wrangler-deployer.js';
|
|
27
29
|
|
|
28
30
|
// Security components
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
export { SecurityCLI } from './security/SecurityCLI.js';
|
|
32
|
+
export { ConfigurationValidator } from './security/ConfigurationValidator.js';
|
|
33
|
+
export { SecretGenerator } from './security/SecretGenerator.js';
|
|
31
34
|
|
|
32
|
-
// Service management components
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// export { InputHandler } from './service-management/handlers/InputHandler.js';
|
|
37
|
-
// export { ConfirmationHandler } from './service-management/handlers/ConfirmationHandler.js';
|
|
38
|
-
// export { GenerationHandler } from './service-management/handlers/GenerationHandler.js';
|
|
39
|
-
// export { ValidationHandler } from './service-management/handlers/ValidationHandler.js';
|
|
35
|
+
// Service management components
|
|
36
|
+
export { ServiceCreator } from './service-management/ServiceCreator.js';
|
|
37
|
+
export { ServiceOrchestrator } from './service-management/ServiceOrchestrator.js';
|
|
38
|
+
export { InputCollector } from './service-management/InputCollector.js';
|
|
40
39
|
|
|
41
40
|
// Framework version info
|
|
42
41
|
export const FRAMEWORK_VERSION = '1.0.0';
|
|
@@ -242,7 +242,7 @@ export async function listDatabases(options = {}) {
|
|
|
242
242
|
if (apiToken && accountId) {
|
|
243
243
|
const {
|
|
244
244
|
CloudflareAPI
|
|
245
|
-
} = await import('
|
|
245
|
+
} = await import('@tamyla/clodo-framework/utils/cloudflare');
|
|
246
246
|
const cf = new CloudflareAPI(apiToken);
|
|
247
247
|
return await cf.listD1Databases(accountId);
|
|
248
248
|
}
|
|
@@ -267,7 +267,7 @@ export async function databaseExists(databaseName, options = {}) {
|
|
|
267
267
|
if (apiToken && accountId) {
|
|
268
268
|
const {
|
|
269
269
|
CloudflareAPI
|
|
270
|
-
} = await import('
|
|
270
|
+
} = await import('@tamyla/clodo-framework/utils/cloudflare');
|
|
271
271
|
const cf = new CloudflareAPI(apiToken);
|
|
272
272
|
return await cf.d1DatabaseExists(accountId, databaseName);
|
|
273
273
|
}
|
|
@@ -290,7 +290,7 @@ export async function createDatabase(name, options = {}) {
|
|
|
290
290
|
if (apiToken && accountId) {
|
|
291
291
|
const {
|
|
292
292
|
CloudflareAPI
|
|
293
|
-
} = await import('
|
|
293
|
+
} = await import('@tamyla/clodo-framework/utils/cloudflare');
|
|
294
294
|
const cf = new CloudflareAPI(apiToken);
|
|
295
295
|
const result = await cf.createD1Database(accountId, name);
|
|
296
296
|
return result.uuid; // Return UUID to match CLI behavior
|
|
@@ -405,7 +405,7 @@ export async function getDatabaseId(databaseName, options = {}) {
|
|
|
405
405
|
if (apiToken && accountId) {
|
|
406
406
|
const {
|
|
407
407
|
CloudflareAPI
|
|
408
|
-
} = await import('
|
|
408
|
+
} = await import('@tamyla/clodo-framework/utils/cloudflare');
|
|
409
409
|
const cf = new CloudflareAPI(apiToken);
|
|
410
410
|
const db = await cf.getD1Database(accountId, databaseName);
|
|
411
411
|
return db?.uuid || null;
|
|
@@ -430,33 +430,119 @@ export async function getDatabaseId(databaseName, options = {}) {
|
|
|
430
430
|
}
|
|
431
431
|
|
|
432
432
|
// Validate prerequisites
|
|
433
|
-
export async function validatePrerequisites() {
|
|
433
|
+
export async function validatePrerequisites(options = {}) {
|
|
434
|
+
const {
|
|
435
|
+
checkAuth = false,
|
|
436
|
+
checkNetwork = false,
|
|
437
|
+
verbose = false
|
|
438
|
+
} = options;
|
|
434
439
|
const checks = [{
|
|
435
440
|
name: 'Node.js',
|
|
436
|
-
command: 'node --version'
|
|
441
|
+
command: 'node --version',
|
|
442
|
+
required: true,
|
|
443
|
+
description: 'Required for running the framework and build processes',
|
|
444
|
+
minVersion: '18.0.0'
|
|
437
445
|
}, {
|
|
438
446
|
name: 'NPM',
|
|
439
|
-
command: 'npm --version'
|
|
447
|
+
command: 'npm --version',
|
|
448
|
+
required: true,
|
|
449
|
+
description: 'Required for package management and scripts',
|
|
450
|
+
minVersion: '8.0.0'
|
|
440
451
|
}, {
|
|
441
452
|
name: 'Wrangler',
|
|
442
|
-
command: 'npx wrangler --version'
|
|
453
|
+
command: 'npx wrangler --version',
|
|
454
|
+
required: true,
|
|
455
|
+
description: 'Cloudflare CLI for Workers and D1 database management',
|
|
456
|
+
minVersion: '3.0.0'
|
|
443
457
|
}];
|
|
458
|
+
|
|
459
|
+
// Add optional checks
|
|
460
|
+
if (checkAuth) {
|
|
461
|
+
checks.push({
|
|
462
|
+
name: 'Wrangler Auth',
|
|
463
|
+
command: 'npx wrangler whoami',
|
|
464
|
+
required: false,
|
|
465
|
+
description: 'Cloudflare authentication (optional - can authenticate during deployment)',
|
|
466
|
+
async: true
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
if (checkNetwork) {
|
|
470
|
+
checks.push({
|
|
471
|
+
name: 'Network',
|
|
472
|
+
command: null,
|
|
473
|
+
required: false,
|
|
474
|
+
description: 'Internet connectivity to Cloudflare API',
|
|
475
|
+
async: true,
|
|
476
|
+
checkFunction: async () => {
|
|
477
|
+
try {
|
|
478
|
+
const response = await fetch('https://api.cloudflare.com/client/v4/user/tokens/verify', {
|
|
479
|
+
method: 'GET',
|
|
480
|
+
headers: {
|
|
481
|
+
'Authorization': 'Bearer test'
|
|
482
|
+
},
|
|
483
|
+
signal: AbortSignal.timeout(5000)
|
|
484
|
+
});
|
|
485
|
+
return {
|
|
486
|
+
status: 'ok',
|
|
487
|
+
version: 'Connected'
|
|
488
|
+
};
|
|
489
|
+
} catch (error) {
|
|
490
|
+
if (error.name === 'AbortError') {
|
|
491
|
+
return {
|
|
492
|
+
status: 'failed',
|
|
493
|
+
error: 'Connection timeout'
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
return {
|
|
497
|
+
status: 'ok',
|
|
498
|
+
version: 'Connected (API responded)'
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
}
|
|
444
504
|
const results = [];
|
|
445
505
|
for (const check of checks) {
|
|
446
506
|
try {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
507
|
+
let result;
|
|
508
|
+
if (check.async && check.checkFunction) {
|
|
509
|
+
result = await check.checkFunction();
|
|
510
|
+
} else if (check.command) {
|
|
511
|
+
const {
|
|
512
|
+
stdout: version
|
|
513
|
+
} = await execAsync(check.command, {
|
|
514
|
+
encoding: 'utf8',
|
|
515
|
+
timeout: 10000
|
|
516
|
+
});
|
|
517
|
+
result = {
|
|
518
|
+
status: 'ok',
|
|
519
|
+
version: version.trim()
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
// Version checking for critical components
|
|
523
|
+
if (check.minVersion && check.name === 'Node.js') {
|
|
524
|
+
const versionNum = version.trim().replace('v', '');
|
|
525
|
+
if (versionNum < check.minVersion) {
|
|
526
|
+
result = {
|
|
527
|
+
status: 'warning',
|
|
528
|
+
version: version.trim(),
|
|
529
|
+
warning: `Node.js ${check.minVersion}+ recommended, found ${versionNum}`
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
} else {
|
|
534
|
+
result = {
|
|
535
|
+
status: 'skipped',
|
|
536
|
+
version: 'N/A'
|
|
537
|
+
};
|
|
538
|
+
}
|
|
452
539
|
results.push({
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
version: version.trim()
|
|
540
|
+
...check,
|
|
541
|
+
...result
|
|
456
542
|
});
|
|
457
543
|
} catch (error) {
|
|
458
544
|
results.push({
|
|
459
|
-
|
|
545
|
+
...check,
|
|
460
546
|
status: 'failed',
|
|
461
547
|
error: error.message
|
|
462
548
|
});
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
import chalk from 'chalk';
|
|
12
12
|
import { askUser, askPassword, askChoice, closePrompts } from '../utils/interactive-prompts.js';
|
|
13
13
|
import { ApiTokenManager } from '../security/api-token-manager.js';
|
|
14
|
-
import { CloudflareAPI } from '
|
|
14
|
+
import { CloudflareAPI } from '@tamyla/clodo-framework/utils/cloudflare';
|
|
15
|
+
import { validatePrerequisites } from '../cloudflare/ops.js';
|
|
15
16
|
export class DeploymentCredentialCollector {
|
|
16
17
|
constructor(options = {}) {
|
|
17
18
|
this.servicePath = options.servicePath || '.';
|
|
@@ -29,6 +30,10 @@ export class DeploymentCredentialCollector {
|
|
|
29
30
|
* 4. Return complete credential set
|
|
30
31
|
*/
|
|
31
32
|
async collectCredentials(options = {}) {
|
|
33
|
+
// First, check prerequisites
|
|
34
|
+
if (!this.quiet) {
|
|
35
|
+
await this.displayPrerequisites(options);
|
|
36
|
+
}
|
|
32
37
|
const startCredentials = {
|
|
33
38
|
token: options.token || process.env.CLOUDFLARE_API_TOKEN || null,
|
|
34
39
|
accountId: options.accountId || process.env.CLOUDFLARE_ACCOUNT_ID || null,
|
|
@@ -36,6 +41,11 @@ export class DeploymentCredentialCollector {
|
|
|
36
41
|
zoneName: null // Will be populated when fetching zone
|
|
37
42
|
};
|
|
38
43
|
|
|
44
|
+
// Show credential status if not quiet
|
|
45
|
+
if (!this.quiet) {
|
|
46
|
+
this.displayCredentialStatus(startCredentials, options);
|
|
47
|
+
}
|
|
48
|
+
|
|
39
49
|
// All credentials provided - quick path
|
|
40
50
|
if (startCredentials.token && startCredentials.accountId && startCredentials.zoneId) {
|
|
41
51
|
if (!this.quiet) {
|
|
@@ -247,5 +257,128 @@ export class DeploymentCredentialCollector {
|
|
|
247
257
|
cleanup() {
|
|
248
258
|
closePrompts();
|
|
249
259
|
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Display prerequisite check results
|
|
263
|
+
* @param {Object} options - Options for prerequisite checking
|
|
264
|
+
*/
|
|
265
|
+
async displayPrerequisites(options = {}) {
|
|
266
|
+
console.log(chalk.cyan('\nš§ Deployment Prerequisites\n'));
|
|
267
|
+
try {
|
|
268
|
+
const results = await validatePrerequisites({
|
|
269
|
+
checkAuth: options.checkAuth || false,
|
|
270
|
+
checkNetwork: options.checkNetwork || false,
|
|
271
|
+
verbose: !this.quiet
|
|
272
|
+
});
|
|
273
|
+
let allOk = true;
|
|
274
|
+
let hasWarnings = false;
|
|
275
|
+
results.forEach(result => {
|
|
276
|
+
const icon = result.status === 'ok' ? 'ā
' : result.status === 'warning' ? 'ā ļø' : result.status === 'failed' ? 'ā' : 'āļø';
|
|
277
|
+
const color = result.status === 'ok' ? chalk.green : result.status === 'warning' ? chalk.yellow : result.status === 'failed' ? chalk.red : chalk.gray;
|
|
278
|
+
console.log(`${icon} ${color(result.name)}: ${result.version || 'N/A'}`);
|
|
279
|
+
if (result.description) {
|
|
280
|
+
console.log(` ${chalk.gray(result.description)}`);
|
|
281
|
+
}
|
|
282
|
+
if (result.warning) {
|
|
283
|
+
console.log(` ${chalk.yellow('ā ļø ' + result.warning)}`);
|
|
284
|
+
hasWarnings = true;
|
|
285
|
+
}
|
|
286
|
+
if (result.error) {
|
|
287
|
+
console.log(` ${chalk.red('ā ' + result.error)}`);
|
|
288
|
+
allOk = false;
|
|
289
|
+
}
|
|
290
|
+
if (result.status === 'failed' && result.required) {
|
|
291
|
+
console.log(` ${chalk.red('š” Required for deployment - please install/update ' + result.name.toLowerCase())}`);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
console.log('');
|
|
295
|
+
|
|
296
|
+
// Summary
|
|
297
|
+
if (allOk && !hasWarnings) {
|
|
298
|
+
console.log(chalk.green('š All prerequisites met - ready for deployment!\n'));
|
|
299
|
+
} else if (allOk && hasWarnings) {
|
|
300
|
+
console.log(chalk.yellow('ā ļø Prerequisites OK but with warnings - deployment may proceed\n'));
|
|
301
|
+
} else {
|
|
302
|
+
console.log(chalk.red('ā Some required prerequisites are missing - please resolve before deploying\n'));
|
|
303
|
+
}
|
|
304
|
+
} catch (error) {
|
|
305
|
+
console.log(chalk.yellow(`ā ļø Could not check prerequisites: ${error.message}\n`));
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Display credential status showing what's available vs missing
|
|
311
|
+
* @param {Object} credentials - Current credential state
|
|
312
|
+
* @param {Object} options - CLI options
|
|
313
|
+
*/
|
|
314
|
+
displayCredentialStatus(credentials, options) {
|
|
315
|
+
console.log(chalk.cyan('\nš Credential Status\n'));
|
|
316
|
+
const status = {
|
|
317
|
+
token: {
|
|
318
|
+
available: !!credentials.token,
|
|
319
|
+
source: this.getCredentialSource('token', credentials.token, options.token),
|
|
320
|
+
required: true
|
|
321
|
+
},
|
|
322
|
+
accountId: {
|
|
323
|
+
available: !!credentials.accountId,
|
|
324
|
+
source: this.getCredentialSource('accountId', credentials.accountId, options.accountId),
|
|
325
|
+
required: false
|
|
326
|
+
},
|
|
327
|
+
zoneId: {
|
|
328
|
+
available: !!credentials.zoneId,
|
|
329
|
+
source: this.getCredentialSource('zoneId', credentials.zoneId, options.zoneId),
|
|
330
|
+
required: false
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// Display each credential
|
|
335
|
+
Object.entries(status).forEach(([key, info]) => {
|
|
336
|
+
const icon = info.available ? 'ā
' : 'ā';
|
|
337
|
+
const color = info.available ? chalk.green : chalk.red;
|
|
338
|
+
const label = key === 'token' ? 'API Token' : key === 'accountId' ? 'Account ID' : 'Zone ID';
|
|
339
|
+
const envVar = key === 'token' ? 'CLOUDFLARE_API_TOKEN' : key === 'accountId' ? 'CLOUDFLARE_ACCOUNT_ID' : 'CLOUDFLARE_ZONE_ID';
|
|
340
|
+
console.log(`${icon} ${color(label)}: ${info.available ? info.source : 'Missing'}`);
|
|
341
|
+
if (!info.available) {
|
|
342
|
+
console.log(` ${chalk.gray(`Set via --${key.replace('Id', '-id')} flag or ${envVar} environment variable`)}`);
|
|
343
|
+
console.log(` ${chalk.gray('To set environment variable:')}`);
|
|
344
|
+
console.log(` ${chalk.gray(` Windows PowerShell: $env:${envVar}="your-value-here"`)}`);
|
|
345
|
+
console.log(` ${chalk.gray(` Windows CMD: set ${envVar}=your-value-here`)}`);
|
|
346
|
+
console.log(` ${chalk.gray(` Linux/macOS: export ${envVar}="your-value-here"`)}`);
|
|
347
|
+
console.log(` ${chalk.gray(` Or add to .env file: ${envVar}=your-value-here`)}`);
|
|
348
|
+
console.log('');
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
console.log('');
|
|
352
|
+
|
|
353
|
+
// Summary
|
|
354
|
+
const availableCount = Object.values(status).filter(s => s.available).length;
|
|
355
|
+
const totalCount = Object.keys(status).length;
|
|
356
|
+
if (availableCount === totalCount) {
|
|
357
|
+
console.log(chalk.green('š All credentials available - ready for deployment!\n'));
|
|
358
|
+
} else if (availableCount >= 1) {
|
|
359
|
+
console.log(chalk.yellow(`ā ļø ${availableCount}/${totalCount} credentials available. Missing credentials will be fetched interactively.\n`));
|
|
360
|
+
} else {
|
|
361
|
+
console.log(chalk.red('ā No credentials found. Interactive collection required.\n'));
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Get the source of a credential (flag, env var, or none)
|
|
367
|
+
* @param {string} key - Credential key
|
|
368
|
+
* @param {*} value - Credential value
|
|
369
|
+
* @param {*} flagValue - CLI flag value
|
|
370
|
+
* @returns {string} Source description
|
|
371
|
+
*/
|
|
372
|
+
getCredentialSource(key, value, flagValue) {
|
|
373
|
+
if (!value) return 'Not set';
|
|
374
|
+
|
|
375
|
+
// Check if it came from CLI flag
|
|
376
|
+
if (flagValue) return 'CLI flag';
|
|
377
|
+
|
|
378
|
+
// Check if it came from environment variable
|
|
379
|
+
const envVar = key === 'token' ? 'CLOUDFLARE_API_TOKEN' : key === 'accountId' ? 'CLOUDFLARE_ACCOUNT_ID' : 'CLOUDFLARE_ZONE_ID';
|
|
380
|
+
if (process.env[envVar]) return 'Environment variable';
|
|
381
|
+
return 'Unknown source';
|
|
382
|
+
}
|
|
250
383
|
}
|
|
251
384
|
export default DeploymentCredentialCollector;
|