@celilo/cli 0.1.0
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/README.md +1566 -0
- package/bin/celilo +16 -0
- package/drizzle/0000_complex_puma.sql +179 -0
- package/drizzle/0001_dizzy_wolfpack.sql +2 -0
- package/drizzle/0002_web_routes.sql +16 -0
- package/drizzle/0003_backup_storage.sql +32 -0
- package/drizzle/meta/0000_snapshot.json +1151 -0
- package/drizzle/meta/0001_snapshot.json +1167 -0
- package/drizzle/meta/0002_snapshot.json +1257 -0
- package/drizzle/meta/_journal.json +27 -0
- package/package.json +64 -0
- package/schemas/system_config.json +106 -0
- package/src/__integration__/container-services-cli.integration.test.ts +246 -0
- package/src/ansible/dependencies.test.ts +309 -0
- package/src/ansible/dependencies.ts +896 -0
- package/src/ansible/inventory.test.ts +463 -0
- package/src/ansible/inventory.ts +445 -0
- package/src/ansible/secrets.ts +222 -0
- package/src/ansible/validation.test.ts +92 -0
- package/src/ansible/validation.ts +272 -0
- package/src/api-clients/digitalocean.ts +94 -0
- package/src/api-clients/proxmox.ts +655 -0
- package/src/capabilities/logging-wrapper.test.ts +217 -0
- package/src/capabilities/lookup.test.ts +149 -0
- package/src/capabilities/lookup.ts +89 -0
- package/src/capabilities/public-web-helpers.test.ts +198 -0
- package/src/capabilities/public-web-publish.test.ts +458 -0
- package/src/capabilities/registration.test.ts +395 -0
- package/src/capabilities/registration.ts +200 -0
- package/src/capabilities/route-validation.test.ts +121 -0
- package/src/capabilities/route-validation.ts +96 -0
- package/src/capabilities/secret-ref.test.ts +313 -0
- package/src/capabilities/secret-validation.ts +157 -0
- package/src/capabilities/secrets.test.ts +750 -0
- package/src/capabilities/secrets.ts +244 -0
- package/src/capabilities/validation.test.ts +613 -0
- package/src/capabilities/validation.ts +160 -0
- package/src/capabilities/well-known.test.ts +238 -0
- package/src/capabilities/well-known.ts +222 -0
- package/src/cli/cli.test.ts +654 -0
- package/src/cli/command-registry.ts +742 -0
- package/src/cli/command-tree-parser.test.ts +180 -0
- package/src/cli/command-tree-parser.ts +193 -0
- package/src/cli/commands/backup-create.ts +137 -0
- package/src/cli/commands/backup-delete.ts +74 -0
- package/src/cli/commands/backup-import.ts +97 -0
- package/src/cli/commands/backup-list.ts +132 -0
- package/src/cli/commands/backup-name.ts +73 -0
- package/src/cli/commands/backup-prune.ts +98 -0
- package/src/cli/commands/backup-restore.ts +122 -0
- package/src/cli/commands/capability-info.ts +121 -0
- package/src/cli/commands/capability-list.ts +47 -0
- package/src/cli/commands/completion.ts +87 -0
- package/src/cli/commands/hook-run.ts +176 -0
- package/src/cli/commands/ipam.ts +607 -0
- package/src/cli/commands/machine-add.ts +235 -0
- package/src/cli/commands/machine-earmark.ts +82 -0
- package/src/cli/commands/machine-list.ts +77 -0
- package/src/cli/commands/machine-remove.ts +90 -0
- package/src/cli/commands/machine-status.ts +131 -0
- package/src/cli/commands/module-audit.ts +51 -0
- package/src/cli/commands/module-build.ts +60 -0
- package/src/cli/commands/module-config.ts +170 -0
- package/src/cli/commands/module-deploy.ts +71 -0
- package/src/cli/commands/module-generate.ts +236 -0
- package/src/cli/commands/module-health.ts +108 -0
- package/src/cli/commands/module-import.ts +80 -0
- package/src/cli/commands/module-list.ts +43 -0
- package/src/cli/commands/module-logs.ts +73 -0
- package/src/cli/commands/module-remove.ts +162 -0
- package/src/cli/commands/module-show.ts +208 -0
- package/src/cli/commands/module-status.ts +131 -0
- package/src/cli/commands/module-types.ts +189 -0
- package/src/cli/commands/module-upgrade.ts +192 -0
- package/src/cli/commands/package.ts +68 -0
- package/src/cli/commands/secret-list.ts +99 -0
- package/src/cli/commands/secret-set.ts +134 -0
- package/src/cli/commands/service-add-digitalocean.ts +133 -0
- package/src/cli/commands/service-add-proxmox.ts +342 -0
- package/src/cli/commands/service-config-get.ts +83 -0
- package/src/cli/commands/service-config-set.ts +145 -0
- package/src/cli/commands/service-list.ts +74 -0
- package/src/cli/commands/service-reconfigure.ts +230 -0
- package/src/cli/commands/service-remove.ts +103 -0
- package/src/cli/commands/service-verify.ts +240 -0
- package/src/cli/commands/status.ts +216 -0
- package/src/cli/commands/storage-add-local.ts +106 -0
- package/src/cli/commands/storage-add-s3.ts +114 -0
- package/src/cli/commands/storage-list.ts +72 -0
- package/src/cli/commands/storage-remove.ts +54 -0
- package/src/cli/commands/storage-set-default.ts +44 -0
- package/src/cli/commands/storage-verify.ts +54 -0
- package/src/cli/commands/system-config.ts +168 -0
- package/src/cli/commands/system-init.ts +314 -0
- package/src/cli/commands/system-secret-get.ts +98 -0
- package/src/cli/commands/system-secret-set.ts +76 -0
- package/src/cli/commands/system-vault-password.ts +34 -0
- package/src/cli/completion.test.ts +37 -0
- package/src/cli/completion.ts +482 -0
- package/src/cli/fuel-gauge.test.ts +208 -0
- package/src/cli/fuel-gauge.ts +405 -0
- package/src/cli/generate-zsh-completion.test.ts +95 -0
- package/src/cli/generate-zsh-completion.ts +497 -0
- package/src/cli/index.ts +1583 -0
- package/src/cli/interactive-config.test.ts +201 -0
- package/src/cli/interactive-config.ts +62 -0
- package/src/cli/parser.test.ts +227 -0
- package/src/cli/parser.ts +244 -0
- package/src/cli/prompts.test.ts +33 -0
- package/src/cli/prompts.ts +121 -0
- package/src/cli/types.ts +38 -0
- package/src/cli/validators.test.ts +235 -0
- package/src/cli/validators.ts +188 -0
- package/src/config/env.ts +41 -0
- package/src/config/paths.test.ts +172 -0
- package/src/config/paths.ts +108 -0
- package/src/db/client.ts +190 -0
- package/src/db/migrate.ts +30 -0
- package/src/db/schema.test.ts +221 -0
- package/src/db/schema.ts +434 -0
- package/src/hooks/capability-loader-firewall.test.ts +246 -0
- package/src/hooks/capability-loader.test.ts +100 -0
- package/src/hooks/capability-loader.ts +520 -0
- package/src/hooks/define-hook.test.ts +488 -0
- package/src/hooks/executor.test.ts +462 -0
- package/src/hooks/executor.ts +469 -0
- package/src/hooks/logger.test.ts +54 -0
- package/src/hooks/logger.ts +95 -0
- package/src/hooks/test-fixtures/failing-hook.ts +13 -0
- package/src/hooks/test-fixtures/no-default-hook.ts +6 -0
- package/src/hooks/test-fixtures/success-hook.ts +20 -0
- package/src/hooks/test-fixtures/unbranded-hook.ts +11 -0
- package/src/hooks/test-fixtures/void-hook.ts +13 -0
- package/src/hooks/types.ts +89 -0
- package/src/infrastructure/property-extractor.test.ts +194 -0
- package/src/infrastructure/property-extractor.ts +151 -0
- package/src/ipam/allocator.test.ts +442 -0
- package/src/ipam/allocator.ts +369 -0
- package/src/ipam/auto-allocator.test.ts +247 -0
- package/src/ipam/auto-allocator.ts +270 -0
- package/src/ipam/subnet-parser.test.ts +107 -0
- package/src/ipam/subnet-parser.ts +136 -0
- package/src/manifest/contracts/index.ts +61 -0
- package/src/manifest/contracts/v1.ts +118 -0
- package/src/manifest/json-schema-roundtrip.test.ts +99 -0
- package/src/manifest/schema.ts +367 -0
- package/src/manifest/template-validator.test.ts +231 -0
- package/src/manifest/template-validator.ts +322 -0
- package/src/manifest/validate.test.ts +1180 -0
- package/src/manifest/validate.ts +415 -0
- package/src/module/import.test.ts +355 -0
- package/src/module/import.ts +676 -0
- package/src/module/packaging/audit.ts +169 -0
- package/src/module/packaging/build.ts +228 -0
- package/src/module/packaging/checksum.ts +41 -0
- package/src/module/packaging/extract.ts +234 -0
- package/src/module/packaging/signature.ts +47 -0
- package/src/secrets/encryption.test.ts +284 -0
- package/src/secrets/encryption.ts +162 -0
- package/src/secrets/generators.test.ts +112 -0
- package/src/secrets/generators.ts +127 -0
- package/src/secrets/master-key.test.ts +159 -0
- package/src/secrets/master-key.ts +114 -0
- package/src/secrets/storage.test.ts +115 -0
- package/src/secrets/storage.ts +106 -0
- package/src/secrets/vault.test.ts +35 -0
- package/src/secrets/vault.ts +42 -0
- package/src/services/backup-create.ts +532 -0
- package/src/services/backup-metadata.ts +198 -0
- package/src/services/backup-restore.ts +229 -0
- package/src/services/backup-retention.ts +84 -0
- package/src/services/backup-storage.ts +281 -0
- package/src/services/build-stream.test.ts +122 -0
- package/src/services/build-stream.ts +201 -0
- package/src/services/config-interview.ts +694 -0
- package/src/services/container-service.test.ts +298 -0
- package/src/services/container-service.ts +401 -0
- package/src/services/cross-module-data-manager.test.ts +405 -0
- package/src/services/cross-module-data-manager.ts +412 -0
- package/src/services/deploy-ansible.ts +88 -0
- package/src/services/deploy-planner.ts +153 -0
- package/src/services/deploy-preflight.ts +274 -0
- package/src/services/deploy-ssh.ts +131 -0
- package/src/services/deploy-terraform.test.ts +55 -0
- package/src/services/deploy-terraform.ts +445 -0
- package/src/services/deploy-validation.ts +311 -0
- package/src/services/dns-auto-register.ts +211 -0
- package/src/services/health-runner.ts +184 -0
- package/src/services/infrastructure-selector.test.ts +485 -0
- package/src/services/infrastructure-selector.ts +245 -0
- package/src/services/infrastructure-variable-resolver.test.ts +751 -0
- package/src/services/infrastructure-variable-resolver.ts +234 -0
- package/src/services/machine-detector.ts +328 -0
- package/src/services/machine-pool.test.ts +405 -0
- package/src/services/machine-pool.ts +316 -0
- package/src/services/manifest-validation.ts +120 -0
- package/src/services/module-build.test.ts +290 -0
- package/src/services/module-build.ts +431 -0
- package/src/services/module-config.test.ts +237 -0
- package/src/services/module-config.ts +298 -0
- package/src/services/module-deploy.ts +862 -0
- package/src/services/module-types-drift.test.ts +73 -0
- package/src/services/module-types-generator.test.ts +288 -0
- package/src/services/module-types-generator.ts +189 -0
- package/src/services/proxmox-state-recovery.ts +140 -0
- package/src/services/schema-validation.ts +155 -0
- package/src/services/secret-schema-loader.test.ts +311 -0
- package/src/services/secret-schema-loader.ts +239 -0
- package/src/services/ssh-key-manager.test.ts +283 -0
- package/src/services/ssh-key-manager.ts +193 -0
- package/src/services/storage-providers/local.ts +105 -0
- package/src/services/storage-providers/s3.ts +182 -0
- package/src/services/storage-providers/types.ts +24 -0
- package/src/services/system-config-schema-types.ts +25 -0
- package/src/services/system-config-validator.test.ts +160 -0
- package/src/services/system-config-validator.ts +74 -0
- package/src/services/system-init.test.ts +153 -0
- package/src/services/system-init.ts +253 -0
- package/src/services/terraform-safety.ts +174 -0
- package/src/services/zone-detector.test.ts +110 -0
- package/src/services/zone-detector.ts +102 -0
- package/src/services/zone-policy.test.ts +97 -0
- package/src/services/zone-policy.ts +126 -0
- package/src/templates/generator.test.ts +645 -0
- package/src/templates/generator.ts +1119 -0
- package/src/templates/types.ts +62 -0
- package/src/test-utils/INTERACTIVE_PROMPTS.md +167 -0
- package/src/test-utils/cli-context-interactive.test.ts +152 -0
- package/src/test-utils/cli-context-server.test.ts +66 -0
- package/src/test-utils/cli-context.test.ts +273 -0
- package/src/test-utils/cli-context.ts +677 -0
- package/src/test-utils/cli-result.test.ts +282 -0
- package/src/test-utils/cli-result.ts +241 -0
- package/src/test-utils/cli.ts +55 -0
- package/src/test-utils/completion-harness.test.ts +126 -0
- package/src/test-utils/completion-harness.ts +82 -0
- package/src/test-utils/database.test.ts +182 -0
- package/src/test-utils/database.ts +126 -0
- package/src/test-utils/filesystem.test.ts +208 -0
- package/src/test-utils/filesystem.ts +142 -0
- package/src/test-utils/fixtures.test.ts +123 -0
- package/src/test-utils/fixtures.ts +160 -0
- package/src/test-utils/golden-diff.ts +197 -0
- package/src/test-utils/index.ts +77 -0
- package/src/test-utils/integration.ts +81 -0
- package/src/test-utils/module-fixtures.ts +468 -0
- package/src/test-utils/modules.test.ts +144 -0
- package/src/test-utils/modules.ts +183 -0
- package/src/test-utils/setup-test-db.ts +90 -0
- package/src/test-utils/value-extractor.test.ts +231 -0
- package/src/test-utils/value-extractor.ts +228 -0
- package/src/types/infrastructure.ts +157 -0
- package/src/utils/shell.test.ts +365 -0
- package/src/utils/shell.ts +159 -0
- package/src/validation/schemas.ts +166 -0
- package/src/variables/ansible-resolver.test.ts +142 -0
- package/src/variables/ansible-resolver.ts +69 -0
- package/src/variables/capability-self-ref.test.ts +220 -0
- package/src/variables/context.test.ts +1265 -0
- package/src/variables/context.ts +624 -0
- package/src/variables/declarative-derivation.test.ts +743 -0
- package/src/variables/declarative-derivation.ts +200 -0
- package/src/variables/parser.test.ts +231 -0
- package/src/variables/parser.ts +76 -0
- package/src/variables/resolver.test.ts +458 -0
- package/src/variables/resolver.ts +282 -0
- package/src/variables/types.ts +59 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { parseVariables } from '../variables/parser';
|
|
4
|
+
import type { VariableReference } from '../variables/types';
|
|
5
|
+
import type { ModuleManifest } from './schema';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Template validation error
|
|
9
|
+
*/
|
|
10
|
+
export interface TemplateValidationError {
|
|
11
|
+
file: string;
|
|
12
|
+
variable: string;
|
|
13
|
+
error: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Template validation result
|
|
18
|
+
*/
|
|
19
|
+
export interface TemplateValidationResult {
|
|
20
|
+
success: boolean;
|
|
21
|
+
errors: TemplateValidationError[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get nested value from object using dot notation
|
|
26
|
+
* Returns undefined if path doesn't exist
|
|
27
|
+
*
|
|
28
|
+
* @param obj - Object to traverse
|
|
29
|
+
* @param path - Dot-separated path (e.g., 'requires.machine.cpu')
|
|
30
|
+
* @returns Value if found, undefined otherwise
|
|
31
|
+
*/
|
|
32
|
+
function getNestedValue(obj: Record<string, unknown>, path: string): unknown {
|
|
33
|
+
const parts = path.split('.');
|
|
34
|
+
let current: unknown = obj;
|
|
35
|
+
|
|
36
|
+
for (const part of parts) {
|
|
37
|
+
if (current === null || current === undefined) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
if (typeof current !== 'object') {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
current = (current as Record<string, unknown>)[part];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return current;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Check if a path exists in the manifest
|
|
51
|
+
*
|
|
52
|
+
* @param manifest - Module manifest
|
|
53
|
+
* @param path - Dot-separated path (e.g., 'requires.machine.cpu')
|
|
54
|
+
* @returns True if path exists in manifest
|
|
55
|
+
*/
|
|
56
|
+
function pathExistsInManifest(manifest: ModuleManifest, path: string): boolean {
|
|
57
|
+
// Convert manifest to a plain object we can traverse
|
|
58
|
+
const manifestObj = manifest as unknown as Record<string, unknown>;
|
|
59
|
+
const value = getNestedValue(manifestObj, path);
|
|
60
|
+
return value !== undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Auto-allocated variables that are injected during module generation
|
|
65
|
+
* These don't need to be declared in the manifest
|
|
66
|
+
*
|
|
67
|
+
* IPAM auto-allocation and zone-based derivation
|
|
68
|
+
*/
|
|
69
|
+
const AUTO_ALLOCATED_VARIABLES = new Set([
|
|
70
|
+
'vmid', // Auto-allocated by IPAM (container-based modules)
|
|
71
|
+
'container_ip', // Auto-allocated by IPAM (container-based modules)
|
|
72
|
+
'vlan', // Auto-derived from zone configuration
|
|
73
|
+
'gateway', // Auto-derived from zone configuration
|
|
74
|
+
'target_node', // Can be auto-derived from system config
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Check if a variable is declared in the manifest or auto-allocated
|
|
79
|
+
*
|
|
80
|
+
* @param manifest - Module manifest
|
|
81
|
+
* @param variableName - Variable name to check
|
|
82
|
+
* @returns True if variable is declared or auto-allocated
|
|
83
|
+
*/
|
|
84
|
+
function isVariableDeclared(manifest: ModuleManifest, variableName: string): boolean {
|
|
85
|
+
// Check if it's an auto-allocated variable
|
|
86
|
+
if (AUTO_ALLOCATED_VARIABLES.has(variableName)) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check in variables.owns
|
|
91
|
+
const declared = manifest.variables?.owns?.some((v) => v.name === variableName);
|
|
92
|
+
if (declared) return true;
|
|
93
|
+
|
|
94
|
+
// Check in variables.imports
|
|
95
|
+
const imported = manifest.variables?.imports?.some((v) => v.name === variableName);
|
|
96
|
+
if (imported) return true;
|
|
97
|
+
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Validate a $self: variable reference
|
|
103
|
+
*
|
|
104
|
+
* Policy function - validates variable path against manifest structure
|
|
105
|
+
*
|
|
106
|
+
* @param variable - Variable reference to validate
|
|
107
|
+
* @param manifest - Module manifest
|
|
108
|
+
* @returns Error message if invalid, null if valid
|
|
109
|
+
*/
|
|
110
|
+
function validateSelfVariable(
|
|
111
|
+
variable: VariableReference,
|
|
112
|
+
manifest: ModuleManifest,
|
|
113
|
+
): string | null {
|
|
114
|
+
const path = variable.path;
|
|
115
|
+
|
|
116
|
+
// Check if it's a direct variable reference (e.g., $self:hostname)
|
|
117
|
+
if (!path.includes('.')) {
|
|
118
|
+
if (isVariableDeclared(manifest, path)) {
|
|
119
|
+
return null; // Valid - variable is declared
|
|
120
|
+
}
|
|
121
|
+
// Check if it exists as a top-level manifest field
|
|
122
|
+
if (pathExistsInManifest(manifest, path)) {
|
|
123
|
+
return null; // Valid - exists in manifest
|
|
124
|
+
}
|
|
125
|
+
return `Self variable '${path}' not found in module configuration`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Check if it's a nested path (e.g., $self:requires.machine.cpu)
|
|
129
|
+
if (pathExistsInManifest(manifest, path)) {
|
|
130
|
+
return null; // Valid
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return `Self variable '${path}' not found in module configuration`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Validate a $capability: variable reference
|
|
138
|
+
*
|
|
139
|
+
* Policy function - validates capability references against manifest requirements
|
|
140
|
+
*
|
|
141
|
+
* @param variable - Variable reference to validate
|
|
142
|
+
* @param manifest - Module manifest
|
|
143
|
+
* @returns Error message if invalid, null if valid
|
|
144
|
+
*/
|
|
145
|
+
function validateCapabilityVariable(
|
|
146
|
+
variable: VariableReference,
|
|
147
|
+
manifest: ModuleManifest,
|
|
148
|
+
): string | null {
|
|
149
|
+
const [capabilityName, ...pathParts] = variable.path.split('.');
|
|
150
|
+
|
|
151
|
+
if (!capabilityName) {
|
|
152
|
+
return 'Capability variable must specify capability name (e.g., dns_registrar.primary_domain)';
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (pathParts.length === 0) {
|
|
156
|
+
return `Capability variable must specify data path (e.g., ${capabilityName}.nameserver)`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Check if module requires this capability
|
|
160
|
+
const requiresCapability = manifest.requires?.capabilities?.some(
|
|
161
|
+
(cap) => cap.name === capabilityName,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
if (!requiresCapability) {
|
|
165
|
+
return `Module references capability '${capabilityName}' but does not require it in manifest`;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return null; // Valid - capability is required
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Validate variable references in a template
|
|
173
|
+
*
|
|
174
|
+
* Policy function - validates template variables against manifest
|
|
175
|
+
*
|
|
176
|
+
* @param content - Template content
|
|
177
|
+
* @param manifest - Module manifest
|
|
178
|
+
* @param relativePath - Relative file path (for error reporting)
|
|
179
|
+
* @returns Array of validation errors
|
|
180
|
+
*/
|
|
181
|
+
function validateTemplateContent(
|
|
182
|
+
content: string,
|
|
183
|
+
manifest: ModuleManifest,
|
|
184
|
+
relativePath: string,
|
|
185
|
+
): TemplateValidationError[] {
|
|
186
|
+
const errors: TemplateValidationError[] = [];
|
|
187
|
+
const variables = parseVariables(content);
|
|
188
|
+
|
|
189
|
+
for (const variable of variables) {
|
|
190
|
+
let error: string | null = null;
|
|
191
|
+
|
|
192
|
+
switch (variable.type) {
|
|
193
|
+
case 'self':
|
|
194
|
+
error = validateSelfVariable(variable, manifest);
|
|
195
|
+
break;
|
|
196
|
+
|
|
197
|
+
case 'capability':
|
|
198
|
+
error = validateCapabilityVariable(variable, manifest);
|
|
199
|
+
break;
|
|
200
|
+
|
|
201
|
+
case 'system':
|
|
202
|
+
case 'system_secret':
|
|
203
|
+
case 'secret':
|
|
204
|
+
// These are validated at runtime, not import time
|
|
205
|
+
// System config and secrets might not exist yet during import
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (error) {
|
|
210
|
+
errors.push({
|
|
211
|
+
file: relativePath,
|
|
212
|
+
variable: variable.raw,
|
|
213
|
+
error,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return errors;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Find all template files in a directory recursively
|
|
223
|
+
*
|
|
224
|
+
* @param dirPath - Directory to search
|
|
225
|
+
* @param basePath - Base path for relative path calculation
|
|
226
|
+
* @returns Array of template file paths (relative to basePath)
|
|
227
|
+
*/
|
|
228
|
+
async function findTemplateFiles(dirPath: string, basePath: string): Promise<string[]> {
|
|
229
|
+
const templateFiles: string[] = [];
|
|
230
|
+
|
|
231
|
+
async function walkDir(currentPath: string) {
|
|
232
|
+
const entries = await readdir(currentPath, { withFileTypes: true });
|
|
233
|
+
|
|
234
|
+
for (const entry of entries) {
|
|
235
|
+
const fullPath = join(currentPath, entry.name);
|
|
236
|
+
|
|
237
|
+
if (entry.isDirectory()) {
|
|
238
|
+
await walkDir(fullPath);
|
|
239
|
+
} else if (entry.isFile() && entry.name.endsWith('.tpl')) {
|
|
240
|
+
// Store relative path from basePath
|
|
241
|
+
const relativePath = fullPath.substring(basePath.length + 1);
|
|
242
|
+
templateFiles.push(relativePath);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
await walkDir(dirPath);
|
|
248
|
+
return templateFiles;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Validate all template files in a module directory
|
|
253
|
+
*
|
|
254
|
+
* Policy function - validates template variable references against manifest
|
|
255
|
+
*
|
|
256
|
+
* @param modulePath - Path to module directory
|
|
257
|
+
* @param manifest - Validated module manifest
|
|
258
|
+
* @returns Validation result with errors if any
|
|
259
|
+
*/
|
|
260
|
+
export async function validateModuleTemplates(
|
|
261
|
+
modulePath: string,
|
|
262
|
+
manifest: ModuleManifest,
|
|
263
|
+
): Promise<TemplateValidationResult> {
|
|
264
|
+
const allErrors: TemplateValidationError[] = [];
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
// Find all .tpl files
|
|
268
|
+
const templateFiles = await findTemplateFiles(modulePath, modulePath);
|
|
269
|
+
|
|
270
|
+
// Validate each template file
|
|
271
|
+
for (const relativePath of templateFiles) {
|
|
272
|
+
const fullPath = join(modulePath, relativePath);
|
|
273
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
274
|
+
const errors = validateTemplateContent(content, manifest, relativePath);
|
|
275
|
+
allErrors.push(...errors);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
success: allErrors.length === 0,
|
|
280
|
+
errors: allErrors,
|
|
281
|
+
};
|
|
282
|
+
} catch (error) {
|
|
283
|
+
return {
|
|
284
|
+
success: false,
|
|
285
|
+
errors: [
|
|
286
|
+
{
|
|
287
|
+
file: '<directory>',
|
|
288
|
+
variable: '',
|
|
289
|
+
error: `Failed to validate templates: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
290
|
+
},
|
|
291
|
+
],
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Format template validation errors into a readable error message
|
|
298
|
+
*
|
|
299
|
+
* @param errors - Array of validation errors
|
|
300
|
+
* @returns Formatted error message
|
|
301
|
+
*/
|
|
302
|
+
export function formatTemplateValidationErrors(errors: TemplateValidationError[]): string {
|
|
303
|
+
const lines: string[] = ['Failed to validate template variables:'];
|
|
304
|
+
|
|
305
|
+
// Group errors by file
|
|
306
|
+
const errorsByFile = new Map<string, TemplateValidationError[]>();
|
|
307
|
+
for (const error of errors) {
|
|
308
|
+
const fileErrors = errorsByFile.get(error.file) || [];
|
|
309
|
+
fileErrors.push(error);
|
|
310
|
+
errorsByFile.set(error.file, fileErrors);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Format errors by file
|
|
314
|
+
for (const [file, fileErrors] of errorsByFile) {
|
|
315
|
+
lines.push(` ${file}:`);
|
|
316
|
+
for (const error of fileErrors) {
|
|
317
|
+
lines.push(` ${error.variable}: ${error.error}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return lines.join('\n');
|
|
322
|
+
}
|