@salte-common/terraflow 0.1.0-alpha.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/LICENSE +22 -0
- package/README.md +278 -0
- package/RELEASE_SUMMARY.md +53 -0
- package/STANDARDS_COMPLIANCE.md +85 -0
- package/bin/terraflow.js +3 -0
- package/bin/tf.js +3 -0
- package/dist/commands/apply.d.ts +7 -0
- package/dist/commands/apply.js +12 -0
- package/dist/commands/base.d.ts +7 -0
- package/dist/commands/base.js +12 -0
- package/dist/commands/config.d.ts +25 -0
- package/dist/commands/config.js +354 -0
- package/dist/commands/destroy.d.ts +7 -0
- package/dist/commands/destroy.js +12 -0
- package/dist/commands/init.d.ts +68 -0
- package/dist/commands/init.js +131 -0
- package/dist/commands/plan.d.ts +7 -0
- package/dist/commands/plan.js +12 -0
- package/dist/core/backend-state.d.ts +25 -0
- package/dist/core/backend-state.js +77 -0
- package/dist/core/config.d.ts +83 -0
- package/dist/core/config.js +295 -0
- package/dist/core/context.d.ts +52 -0
- package/dist/core/context.js +192 -0
- package/dist/core/environment.d.ts +62 -0
- package/dist/core/environment.js +205 -0
- package/dist/core/errors.d.ts +22 -0
- package/dist/core/errors.js +36 -0
- package/dist/core/plugin-loader.d.ts +21 -0
- package/dist/core/plugin-loader.js +136 -0
- package/dist/core/terraform.d.ts +45 -0
- package/dist/core/terraform.js +247 -0
- package/dist/core/validator.d.ts +103 -0
- package/dist/core/validator.js +304 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +184 -0
- package/dist/plugins/auth/aws-assume-role.d.ts +10 -0
- package/dist/plugins/auth/aws-assume-role.js +110 -0
- package/dist/plugins/auth/azure-service-principal.d.ts +10 -0
- package/dist/plugins/auth/azure-service-principal.js +99 -0
- package/dist/plugins/auth/gcp-service-account.d.ts +10 -0
- package/dist/plugins/auth/gcp-service-account.js +105 -0
- package/dist/plugins/backends/azurerm.d.ts +10 -0
- package/dist/plugins/backends/azurerm.js +117 -0
- package/dist/plugins/backends/gcs.d.ts +10 -0
- package/dist/plugins/backends/gcs.js +75 -0
- package/dist/plugins/backends/local.d.ts +11 -0
- package/dist/plugins/backends/local.js +37 -0
- package/dist/plugins/backends/s3.d.ts +10 -0
- package/dist/plugins/backends/s3.js +185 -0
- package/dist/plugins/secrets/aws-secrets.d.ts +12 -0
- package/dist/plugins/secrets/aws-secrets.js +125 -0
- package/dist/plugins/secrets/azure-keyvault.d.ts +12 -0
- package/dist/plugins/secrets/azure-keyvault.js +178 -0
- package/dist/plugins/secrets/env.d.ts +24 -0
- package/dist/plugins/secrets/env.js +62 -0
- package/dist/plugins/secrets/gcp-secret-manager.d.ts +12 -0
- package/dist/plugins/secrets/gcp-secret-manager.js +157 -0
- package/dist/templates/application/go/go.mod.template +4 -0
- package/dist/templates/application/go/main.template +8 -0
- package/dist/templates/application/go/test.template +11 -0
- package/dist/templates/application/javascript/main.template +14 -0
- package/dist/templates/application/javascript/test.template +8 -0
- package/dist/templates/application/python/main.template +13 -0
- package/dist/templates/application/python/requirements.txt.template +3 -0
- package/dist/templates/application/python/test.template +8 -0
- package/dist/templates/application/typescript/main.template +14 -0
- package/dist/templates/application/typescript/test.template +8 -0
- package/dist/templates/application/typescript/tsconfig.json.template +20 -0
- package/dist/templates/config/README.md.template +82 -0
- package/dist/templates/config/env.example.template +22 -0
- package/dist/templates/config/gitignore.template +40 -0
- package/dist/templates/config/tfwconfig.yml.template +69 -0
- package/dist/templates/templates/application/go/go.mod.template +4 -0
- package/dist/templates/templates/application/go/main.template +8 -0
- package/dist/templates/templates/application/go/test.template +11 -0
- package/dist/templates/templates/application/javascript/main.template +14 -0
- package/dist/templates/templates/application/javascript/test.template +8 -0
- package/dist/templates/templates/application/python/main.template +13 -0
- package/dist/templates/templates/application/python/requirements.txt.template +3 -0
- package/dist/templates/templates/application/python/test.template +8 -0
- package/dist/templates/templates/application/typescript/main.template +14 -0
- package/dist/templates/templates/application/typescript/test.template +8 -0
- package/dist/templates/templates/application/typescript/tsconfig.json.template +20 -0
- package/dist/templates/templates/config/README.md.template +82 -0
- package/dist/templates/templates/config/env.example.template +22 -0
- package/dist/templates/templates/config/gitignore.template +40 -0
- package/dist/templates/templates/config/tfwconfig.yml.template +69 -0
- package/dist/templates/templates/terraform/aws/_init.tf.template +24 -0
- package/dist/templates/templates/terraform/aws/inputs.tf.template +11 -0
- package/dist/templates/templates/terraform/azure/_init.tf.template +19 -0
- package/dist/templates/templates/terraform/azure/inputs.tf.template +11 -0
- package/dist/templates/templates/terraform/gcp/_init.tf.template +20 -0
- package/dist/templates/templates/terraform/gcp/inputs.tf.template +16 -0
- package/dist/templates/templates/terraform/locals.tf.template +9 -0
- package/dist/templates/templates/terraform/main.tf.template +8 -0
- package/dist/templates/templates/terraform/modules/inputs.tf.template +5 -0
- package/dist/templates/templates/terraform/modules/main.tf.template +2 -0
- package/dist/templates/templates/terraform/modules/outputs.tf.template +2 -0
- package/dist/templates/templates/terraform/outputs.tf.template +6 -0
- package/dist/templates/terraform/aws/_init.tf.template +24 -0
- package/dist/templates/terraform/aws/inputs.tf.template +11 -0
- package/dist/templates/terraform/azure/_init.tf.template +19 -0
- package/dist/templates/terraform/azure/inputs.tf.template +11 -0
- package/dist/templates/terraform/gcp/_init.tf.template +20 -0
- package/dist/templates/terraform/gcp/inputs.tf.template +16 -0
- package/dist/templates/terraform/locals.tf.template +9 -0
- package/dist/templates/terraform/main.tf.template +8 -0
- package/dist/templates/terraform/modules/inputs.tf.template +5 -0
- package/dist/templates/terraform/modules/main.tf.template +2 -0
- package/dist/templates/terraform/modules/outputs.tf.template +2 -0
- package/dist/templates/terraform/outputs.tf.template +6 -0
- package/dist/types/config.d.ts +92 -0
- package/dist/types/config.js +6 -0
- package/dist/types/context.d.ts +59 -0
- package/dist/types/context.js +6 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.js +23 -0
- package/dist/types/plugins.d.ts +77 -0
- package/dist/types/plugins.js +6 -0
- package/dist/utils/cloud.d.ts +43 -0
- package/dist/utils/cloud.js +150 -0
- package/dist/utils/git.d.ts +88 -0
- package/dist/utils/git.js +258 -0
- package/dist/utils/logger.d.ts +67 -0
- package/dist/utils/logger.js +121 -0
- package/dist/utils/scaffolding.d.ts +92 -0
- package/dist/utils/scaffolding.js +338 -0
- package/dist/utils/templates.d.ts +25 -0
- package/dist/utils/templates.js +70 -0
- package/package.json +60 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Config command handler
|
|
4
|
+
* Manages Terraflow configuration
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.ConfigCommand = void 0;
|
|
11
|
+
const fs_1 = require("fs");
|
|
12
|
+
const path_1 = require("path");
|
|
13
|
+
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
14
|
+
const config_1 = require("../core/config");
|
|
15
|
+
const logger_1 = require("../utils/logger");
|
|
16
|
+
/**
|
|
17
|
+
* Fields that should always be masked
|
|
18
|
+
* These are exact field names that contain sensitive data
|
|
19
|
+
*/
|
|
20
|
+
const ALWAYS_MASKED_FIELDS = [
|
|
21
|
+
'client_secret',
|
|
22
|
+
'secret_access_key',
|
|
23
|
+
'session_token',
|
|
24
|
+
'access_key_id',
|
|
25
|
+
'access_key',
|
|
26
|
+
'api_key',
|
|
27
|
+
'password',
|
|
28
|
+
'secret',
|
|
29
|
+
'key', // Only mask if it's clearly a credential key, not a generic key field
|
|
30
|
+
'token',
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Sensitive field patterns that should be masked
|
|
34
|
+
*/
|
|
35
|
+
const SENSITIVE_PATTERNS = [
|
|
36
|
+
/password$/i,
|
|
37
|
+
/secret$/i,
|
|
38
|
+
/.*_secret$/i,
|
|
39
|
+
/.*_key$/i, // But not role_arn, kms_key_id, etc.
|
|
40
|
+
/token$/i,
|
|
41
|
+
/credential$/i,
|
|
42
|
+
/access.*key$/i,
|
|
43
|
+
/session.*token$/i,
|
|
44
|
+
/client.*secret$/i,
|
|
45
|
+
];
|
|
46
|
+
/**
|
|
47
|
+
* Fields that should NOT be masked (even if they match patterns)
|
|
48
|
+
*/
|
|
49
|
+
const EXCLUDED_FIELDS = ['role_arn', 'kms_key_id', 'key', 'key_file', 'key_id'];
|
|
50
|
+
/**
|
|
51
|
+
* Check if a field name indicates sensitive data
|
|
52
|
+
*/
|
|
53
|
+
function isSensitiveField(fieldName) {
|
|
54
|
+
// Check if field is explicitly excluded
|
|
55
|
+
if (EXCLUDED_FIELDS.includes(fieldName)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const lowerName = fieldName.toLowerCase();
|
|
59
|
+
// Check exact matches first (these are always sensitive)
|
|
60
|
+
if (ALWAYS_MASKED_FIELDS.includes(lowerName)) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
// For 'key', only mask if it's clearly a credential (not key_file, key_id, etc.)
|
|
64
|
+
if (lowerName === 'key') {
|
|
65
|
+
// Don't mask generic 'key' fields - too many false positives
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
// Check patterns (e.g., client_secret, access_key, session_token)
|
|
69
|
+
return SENSITIVE_PATTERNS.some((pattern) => pattern.test(fieldName));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Mask sensitive values in an object recursively
|
|
73
|
+
*/
|
|
74
|
+
function maskSensitiveValues(obj, path = '') {
|
|
75
|
+
if (obj === null || obj === undefined) {
|
|
76
|
+
return obj;
|
|
77
|
+
}
|
|
78
|
+
if (Array.isArray(obj)) {
|
|
79
|
+
return obj.map((item, index) => maskSensitiveValues(item, `${path}[${index}]`));
|
|
80
|
+
}
|
|
81
|
+
if (typeof obj === 'object') {
|
|
82
|
+
const masked = {};
|
|
83
|
+
for (const key in obj) {
|
|
84
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
85
|
+
const fullPath = path ? `${path}.${key}` : key;
|
|
86
|
+
const value = obj[key];
|
|
87
|
+
if (isSensitiveField(key) || isSensitiveField(fullPath)) {
|
|
88
|
+
// Mask the value
|
|
89
|
+
if (typeof value === 'string' && value.length > 0) {
|
|
90
|
+
masked[key] = '***MASKED***';
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
masked[key] = value;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// Recursively mask nested objects
|
|
98
|
+
masked[key] = maskSensitiveValues(value, fullPath);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return masked;
|
|
103
|
+
}
|
|
104
|
+
return obj;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Config command handler class
|
|
108
|
+
*/
|
|
109
|
+
class ConfigCommand {
|
|
110
|
+
/**
|
|
111
|
+
* Show resolved configuration
|
|
112
|
+
* Displays merged configuration with source tracking and masked sensitive values
|
|
113
|
+
*/
|
|
114
|
+
static async show(cliOptions = {}) {
|
|
115
|
+
try {
|
|
116
|
+
// Load configuration
|
|
117
|
+
const config = await config_1.ConfigManager.load(cliOptions);
|
|
118
|
+
// Mask sensitive values
|
|
119
|
+
const maskedConfig = maskSensitiveValues(config);
|
|
120
|
+
// Format as YAML
|
|
121
|
+
const yamlOutput = js_yaml_1.default.dump(maskedConfig, {
|
|
122
|
+
indent: 2,
|
|
123
|
+
lineWidth: 120,
|
|
124
|
+
quotingType: '"',
|
|
125
|
+
forceQuotes: false,
|
|
126
|
+
});
|
|
127
|
+
logger_1.Logger.info('Resolved configuration:');
|
|
128
|
+
logger_1.Logger.info('');
|
|
129
|
+
logger_1.Logger.info(yamlOutput);
|
|
130
|
+
// Show configuration sources
|
|
131
|
+
logger_1.Logger.info('');
|
|
132
|
+
logger_1.Logger.info('Configuration sources:');
|
|
133
|
+
logger_1.Logger.info(' CLI: Command-line arguments (highest priority)');
|
|
134
|
+
logger_1.Logger.info(' ENV: Environment variables');
|
|
135
|
+
logger_1.Logger.info(' FILE: Configuration file (.tfwconfig.yml)');
|
|
136
|
+
logger_1.Logger.info(' DEFAULT: Hard-coded defaults (lowest priority)');
|
|
137
|
+
logger_1.Logger.info('');
|
|
138
|
+
logger_1.Logger.info('Note: Sensitive values are masked with ***MASKED***');
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
logger_1.Logger.error(`Failed to show configuration: ${error instanceof Error ? error.message : String(error)}`);
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Generate skeleton configuration file
|
|
147
|
+
* Creates a .tfwconfig.yml with commented examples
|
|
148
|
+
*/
|
|
149
|
+
static async init(outputPath, workingDir = process.cwd()) {
|
|
150
|
+
try {
|
|
151
|
+
const configPath = outputPath || (0, path_1.join)(workingDir, '.tfwconfig.yml');
|
|
152
|
+
// Check if file already exists
|
|
153
|
+
if ((0, fs_1.existsSync)(configPath)) {
|
|
154
|
+
logger_1.Logger.warn(`Configuration file already exists at ${configPath}`);
|
|
155
|
+
logger_1.Logger.warn('Use --output flag to specify a different filename');
|
|
156
|
+
throw new Error('Configuration file already exists');
|
|
157
|
+
}
|
|
158
|
+
// Generate skeleton
|
|
159
|
+
const skeleton = ConfigCommand.generateConfigSkeleton();
|
|
160
|
+
// Ensure directory exists
|
|
161
|
+
const dir = (0, path_1.dirname)(configPath);
|
|
162
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
163
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
164
|
+
}
|
|
165
|
+
// Write file
|
|
166
|
+
(0, fs_1.writeFileSync)(configPath, skeleton, 'utf8');
|
|
167
|
+
logger_1.Logger.success(`✅ Configuration skeleton created at ${configPath}`);
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
logger_1.Logger.error(`Failed to create config file: ${error instanceof Error ? error.message : String(error)}`);
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Generate skeleton configuration file content
|
|
176
|
+
*/
|
|
177
|
+
static generateConfigSkeleton() {
|
|
178
|
+
return `# Terraflow Configuration File
|
|
179
|
+
# This file defines your Terraflow configuration
|
|
180
|
+
# See https://github.com/salte-common/terraflow/blob/main/docs/configuration.md for full documentation
|
|
181
|
+
|
|
182
|
+
# Global Settings
|
|
183
|
+
# ===============
|
|
184
|
+
|
|
185
|
+
# Workspace name (optional - will be derived if not specified)
|
|
186
|
+
# Priority: CLI > ENV > Tag > Branch > Hostname
|
|
187
|
+
workspace: development
|
|
188
|
+
|
|
189
|
+
# Terraform working directory (default: ./terraform)
|
|
190
|
+
# This is where your Terraform files (.tf) should be located
|
|
191
|
+
working-dir: ./terraform
|
|
192
|
+
|
|
193
|
+
# Skip git commit check for destructive operations
|
|
194
|
+
# Set to true to skip validation that requires a clean git working directory
|
|
195
|
+
skip-commit-check: false
|
|
196
|
+
|
|
197
|
+
# Backend Configuration
|
|
198
|
+
# ====================
|
|
199
|
+
# Terraform backend for state storage
|
|
200
|
+
# Options: local | s3 | azurerm | gcs
|
|
201
|
+
|
|
202
|
+
backend:
|
|
203
|
+
# Backend type
|
|
204
|
+
type: local
|
|
205
|
+
|
|
206
|
+
# Backend-specific configuration
|
|
207
|
+
config:
|
|
208
|
+
# Local backend (no additional config needed)
|
|
209
|
+
# State is stored in terraform.tfstate in the working directory
|
|
210
|
+
|
|
211
|
+
# S3 Backend Example:
|
|
212
|
+
# type: s3
|
|
213
|
+
# config:
|
|
214
|
+
# bucket: my-terraform-state-bucket
|
|
215
|
+
# key: terraform.tfstate
|
|
216
|
+
# region: us-east-1
|
|
217
|
+
# encrypt: true # Always recommended
|
|
218
|
+
# dynamodb_table: terraform-statelock # For state locking
|
|
219
|
+
# kms_key_id: arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012 # Optional: KMS encryption
|
|
220
|
+
|
|
221
|
+
# Azure RM Backend Example:
|
|
222
|
+
# type: azurerm
|
|
223
|
+
# config:
|
|
224
|
+
# resource_group_name: terraform-state-rg
|
|
225
|
+
# storage_account_name: terraformstate
|
|
226
|
+
# container_name: terraform-state
|
|
227
|
+
# key: terraform.tfstate
|
|
228
|
+
|
|
229
|
+
# GCS Backend Example:
|
|
230
|
+
# type: gcs
|
|
231
|
+
# config:
|
|
232
|
+
# bucket: terraform-state-bucket
|
|
233
|
+
# prefix: terraform/state
|
|
234
|
+
# credentials: /path/to/service-account-key.json # Optional: path to service account key
|
|
235
|
+
|
|
236
|
+
# Secrets Management
|
|
237
|
+
# ==================
|
|
238
|
+
# Secrets provider for retrieving Terraform variables
|
|
239
|
+
# Options: env | aws-secrets | azure-keyvault | gcp-secret-manager
|
|
240
|
+
|
|
241
|
+
# secrets:
|
|
242
|
+
# # Secrets provider type
|
|
243
|
+
# provider: env
|
|
244
|
+
#
|
|
245
|
+
# # Provider-specific configuration
|
|
246
|
+
# config:
|
|
247
|
+
# # Environment secrets (no additional config needed)
|
|
248
|
+
# # Secrets are loaded from .env file or existing environment variables
|
|
249
|
+
# # Use TF_VAR_* prefix in .env file for Terraform variables
|
|
250
|
+
#
|
|
251
|
+
# # AWS Secrets Manager Example:
|
|
252
|
+
# # provider: aws-secrets
|
|
253
|
+
# # config:
|
|
254
|
+
# # secret_name: myapp/terraform-vars
|
|
255
|
+
# # region: us-east-1 # Optional: uses AWS_REGION if not specified
|
|
256
|
+
#
|
|
257
|
+
# # Azure Key Vault Example:
|
|
258
|
+
# # provider: azure-keyvault
|
|
259
|
+
# # config:
|
|
260
|
+
# # vault_name: my-keyvault
|
|
261
|
+
# # secret_name: terraform-vars
|
|
262
|
+
#
|
|
263
|
+
# # GCP Secret Manager Example:
|
|
264
|
+
# # provider: gcp-secret-manager
|
|
265
|
+
# # config:
|
|
266
|
+
# # project_id: my-gcp-project
|
|
267
|
+
# # secret_id: terraform-vars
|
|
268
|
+
|
|
269
|
+
# Authentication
|
|
270
|
+
# ==============
|
|
271
|
+
# Cloud provider authentication configuration
|
|
272
|
+
# Used to assume roles or authenticate with cloud providers
|
|
273
|
+
|
|
274
|
+
# auth:
|
|
275
|
+
# # AWS Assume Role Example:
|
|
276
|
+
# # assume_role:
|
|
277
|
+
# # role_arn: arn:aws:iam::123456789012:role/terraform-role
|
|
278
|
+
# # session_name: terraflow-session # Optional: default is "terraflow-session"
|
|
279
|
+
# # duration: 3600 # Optional: session duration in seconds (default: 3600)
|
|
280
|
+
#
|
|
281
|
+
# # Azure Service Principal Example:
|
|
282
|
+
# # service_principal:
|
|
283
|
+
# # client_id: your-client-id
|
|
284
|
+
# # tenant_id: your-tenant-id
|
|
285
|
+
# # client_secret: your-client-secret # Optional: can use Azure CLI or managed identity
|
|
286
|
+
#
|
|
287
|
+
# # GCP Service Account Example:
|
|
288
|
+
# # service_account:
|
|
289
|
+
# # key_file: /path/to/service-account-key.json
|
|
290
|
+
|
|
291
|
+
# Terraform Variables
|
|
292
|
+
# ===================
|
|
293
|
+
# Variables passed to Terraform as TF_VAR_* environment variables
|
|
294
|
+
# These are converted automatically - no TF_VAR_ prefix needed in config
|
|
295
|
+
|
|
296
|
+
# variables:
|
|
297
|
+
# environment: development
|
|
298
|
+
# region: us-east-1
|
|
299
|
+
# instance_count: 3
|
|
300
|
+
|
|
301
|
+
# Workspace Derivation Strategy
|
|
302
|
+
# =============================
|
|
303
|
+
# Order of precedence for workspace name resolution
|
|
304
|
+
# Default: [cli, env, tag, branch, hostname]
|
|
305
|
+
|
|
306
|
+
# workspace_strategy:
|
|
307
|
+
# - cli # Command-line --workspace flag
|
|
308
|
+
# - env # TERRAFLOW_WORKSPACE environment variable
|
|
309
|
+
# - tag # Git tag (if on a tag)
|
|
310
|
+
# - branch # Git branch (if not ephemeral)
|
|
311
|
+
# - hostname # System hostname (fallback)
|
|
312
|
+
|
|
313
|
+
# Validation Configuration
|
|
314
|
+
# ========================
|
|
315
|
+
# Validation rules for Terraform operations
|
|
316
|
+
|
|
317
|
+
# validations:
|
|
318
|
+
# # Require git commit before apply/destroy
|
|
319
|
+
# require_git_commit: true
|
|
320
|
+
#
|
|
321
|
+
# # List of allowed workspace names (empty = allow all)
|
|
322
|
+
# allowed_workspaces:
|
|
323
|
+
# - development
|
|
324
|
+
# - staging
|
|
325
|
+
# - production
|
|
326
|
+
|
|
327
|
+
# Logging Configuration
|
|
328
|
+
# =====================
|
|
329
|
+
# Control logging behavior
|
|
330
|
+
|
|
331
|
+
# logging:
|
|
332
|
+
# # Log level: error | warn | info | debug
|
|
333
|
+
# level: info
|
|
334
|
+
#
|
|
335
|
+
# # Enable Terraform log output
|
|
336
|
+
# terraform_log: false
|
|
337
|
+
#
|
|
338
|
+
# # Terraform log level: TRACE | DEBUG | INFO | WARN | ERROR
|
|
339
|
+
# terraform_log_level: TRACE
|
|
340
|
+
|
|
341
|
+
# Template Variables
|
|
342
|
+
# ==================
|
|
343
|
+
# Configuration values support template variables using \${VAR} syntax
|
|
344
|
+
# Available variables:
|
|
345
|
+
# - Environment variables (e.g., \${AWS_REGION})
|
|
346
|
+
# - Cloud provider info (e.g., \${AWS_ACCOUNT_ID}, \${AZURE_SUBSCRIPTION_ID}, \${GCP_PROJECT_ID})
|
|
347
|
+
# - VCS info (e.g., \${GITHUB_REPOSITORY}, \${GIT_BRANCH}, \${GIT_COMMIT_SHA})
|
|
348
|
+
# Example:
|
|
349
|
+
# bucket: \${AWS_REGION}-\${AWS_ACCOUNT_ID}-terraform-state
|
|
350
|
+
`;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
exports.ConfigCommand = ConfigCommand;
|
|
354
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Destroy command handler
|
|
4
|
+
* Executes terraform destroy with Terraflow enhancements
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.DestroyCommand = void 0;
|
|
8
|
+
// TODO: Implement destroy command
|
|
9
|
+
class DestroyCommand {
|
|
10
|
+
}
|
|
11
|
+
exports.DestroyCommand = DestroyCommand;
|
|
12
|
+
//# sourceMappingURL=destroy.js.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Init command handler
|
|
3
|
+
* Scaffolds a new infrastructure project with opinionated defaults
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Init command options
|
|
7
|
+
*/
|
|
8
|
+
export interface InitOptions {
|
|
9
|
+
provider?: string;
|
|
10
|
+
language?: string;
|
|
11
|
+
workingDir?: string;
|
|
12
|
+
force?: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Init command handler for project scaffolding
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* // Create AWS project with JavaScript
|
|
20
|
+
* await InitCommand.execute('my-project', {
|
|
21
|
+
* provider: 'aws',
|
|
22
|
+
* language: 'javascript'
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* // Create Azure project with TypeScript
|
|
26
|
+
* await InitCommand.execute('my-project', {
|
|
27
|
+
* provider: 'azure',
|
|
28
|
+
* language: 'typescript'
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare class InitCommand {
|
|
33
|
+
/**
|
|
34
|
+
* Execute the init command to scaffold a new infrastructure project
|
|
35
|
+
*
|
|
36
|
+
* Creates a complete project structure with:
|
|
37
|
+
* - Terraform configuration files for the specified cloud provider
|
|
38
|
+
* - Application code templates in the specified language
|
|
39
|
+
* - Pre-configured `.tfwconfig.yml` with backend settings
|
|
40
|
+
* - Example `.env.example` file
|
|
41
|
+
* - Complete `.gitignore` and `README.md`
|
|
42
|
+
*
|
|
43
|
+
* @param projectName - Name of the project to create (optional, defaults to current directory)
|
|
44
|
+
* @param options - Init command options
|
|
45
|
+
* @param options.provider - Cloud provider: 'aws', 'azure', or 'gcp' (default: 'aws')
|
|
46
|
+
* @param options.language - Application language: 'javascript', 'typescript', 'python', or 'go' (default: 'javascript')
|
|
47
|
+
* @param options.workingDir - Directory where to create the project (default: current directory)
|
|
48
|
+
* @param options.force - Overwrite existing files if present (default: false)
|
|
49
|
+
* @throws {ConfigError} If validation fails (invalid project name, provider, language, or non-empty directory)
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* // Create project in current directory
|
|
54
|
+
* await InitCommand.execute(undefined, { provider: 'aws' });
|
|
55
|
+
*
|
|
56
|
+
* // Create named project
|
|
57
|
+
* await InitCommand.execute('my-infrastructure', {
|
|
58
|
+
* provider: 'gcp',
|
|
59
|
+
* language: 'python'
|
|
60
|
+
* });
|
|
61
|
+
*
|
|
62
|
+
* // Force overwrite existing files
|
|
63
|
+
* await InitCommand.execute('my-project', { force: true });
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
static execute(projectName: string | undefined, options?: InitOptions): Promise<void>;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Init command handler
|
|
4
|
+
* Scaffolds a new infrastructure project with opinionated defaults
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.InitCommand = void 0;
|
|
8
|
+
const path_1 = require("path");
|
|
9
|
+
const logger_1 = require("../utils/logger");
|
|
10
|
+
const errors_1 = require("../core/errors");
|
|
11
|
+
const scaffolding_1 = require("../utils/scaffolding");
|
|
12
|
+
/**
|
|
13
|
+
* Init command handler for project scaffolding
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // Create AWS project with JavaScript
|
|
18
|
+
* await InitCommand.execute('my-project', {
|
|
19
|
+
* provider: 'aws',
|
|
20
|
+
* language: 'javascript'
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Create Azure project with TypeScript
|
|
24
|
+
* await InitCommand.execute('my-project', {
|
|
25
|
+
* provider: 'azure',
|
|
26
|
+
* language: 'typescript'
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
class InitCommand {
|
|
31
|
+
/**
|
|
32
|
+
* Execute the init command to scaffold a new infrastructure project
|
|
33
|
+
*
|
|
34
|
+
* Creates a complete project structure with:
|
|
35
|
+
* - Terraform configuration files for the specified cloud provider
|
|
36
|
+
* - Application code templates in the specified language
|
|
37
|
+
* - Pre-configured `.tfwconfig.yml` with backend settings
|
|
38
|
+
* - Example `.env.example` file
|
|
39
|
+
* - Complete `.gitignore` and `README.md`
|
|
40
|
+
*
|
|
41
|
+
* @param projectName - Name of the project to create (optional, defaults to current directory)
|
|
42
|
+
* @param options - Init command options
|
|
43
|
+
* @param options.provider - Cloud provider: 'aws', 'azure', or 'gcp' (default: 'aws')
|
|
44
|
+
* @param options.language - Application language: 'javascript', 'typescript', 'python', or 'go' (default: 'javascript')
|
|
45
|
+
* @param options.workingDir - Directory where to create the project (default: current directory)
|
|
46
|
+
* @param options.force - Overwrite existing files if present (default: false)
|
|
47
|
+
* @throws {ConfigError} If validation fails (invalid project name, provider, language, or non-empty directory)
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* // Create project in current directory
|
|
52
|
+
* await InitCommand.execute(undefined, { provider: 'aws' });
|
|
53
|
+
*
|
|
54
|
+
* // Create named project
|
|
55
|
+
* await InitCommand.execute('my-infrastructure', {
|
|
56
|
+
* provider: 'gcp',
|
|
57
|
+
* language: 'python'
|
|
58
|
+
* });
|
|
59
|
+
*
|
|
60
|
+
* // Force overwrite existing files
|
|
61
|
+
* await InitCommand.execute('my-project', { force: true });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
static async execute(projectName, options = {}) {
|
|
65
|
+
const provider = options.provider || 'aws';
|
|
66
|
+
const language = options.language || 'javascript';
|
|
67
|
+
const workingDir = options.workingDir || process.cwd();
|
|
68
|
+
const force = options.force || false;
|
|
69
|
+
// Validate provider
|
|
70
|
+
if (!(0, scaffolding_1.validateProvider)(provider)) {
|
|
71
|
+
throw new errors_1.ConfigError(`Invalid provider "${provider}". Must be one of: aws, azure, gcp.\n` +
|
|
72
|
+
`Example: terraflow init my-project --provider aws`);
|
|
73
|
+
}
|
|
74
|
+
// Validate language
|
|
75
|
+
if (!(0, scaffolding_1.validateLanguage)(language)) {
|
|
76
|
+
throw new errors_1.ConfigError(`Invalid language "${language}". Must be one of: javascript, typescript, python, go.\n` +
|
|
77
|
+
`Example: terraflow init my-project --language typescript`);
|
|
78
|
+
}
|
|
79
|
+
// Determine project directory
|
|
80
|
+
const projectDir = projectName ? (0, path_1.resolve)(workingDir, projectName) : (0, path_1.resolve)(workingDir);
|
|
81
|
+
// Validate project name if provided
|
|
82
|
+
if (projectName && !(0, scaffolding_1.validateProjectName)(projectName)) {
|
|
83
|
+
throw new errors_1.ConfigError(`Invalid project name "${projectName}". Project name must contain only alphanumeric characters, hyphens, and underscores.\n` +
|
|
84
|
+
`Valid examples: "my-project", "my_project", "project123"\n` +
|
|
85
|
+
`Invalid examples: "my project" (spaces), "my.project" (dots), "my/project" (slashes)`);
|
|
86
|
+
}
|
|
87
|
+
// Check if directory exists and is not empty
|
|
88
|
+
const isEmpty = await (0, scaffolding_1.isDirectoryEmpty)(projectDir);
|
|
89
|
+
if (!isEmpty && !force) {
|
|
90
|
+
throw new errors_1.ConfigError(`Directory "${projectDir}" is not empty. Use --force to overwrite existing files.\n` +
|
|
91
|
+
`Warning: Using --force will overwrite existing files in the target directory.\n` +
|
|
92
|
+
`Example: terraflow init ${projectName || 'my-project'} --force`);
|
|
93
|
+
}
|
|
94
|
+
logger_1.Logger.info(`🚀 Initializing project "${projectName || 'current directory'}"...`);
|
|
95
|
+
logger_1.Logger.info(` Provider: ${provider}`);
|
|
96
|
+
logger_1.Logger.info(` Language: ${language}`);
|
|
97
|
+
// Create project structure
|
|
98
|
+
await (0, scaffolding_1.createProjectStructure)(projectDir);
|
|
99
|
+
// Generate files
|
|
100
|
+
const finalProjectName = projectName || 'project';
|
|
101
|
+
await (0, scaffolding_1.generateTerraformFiles)(projectDir, provider, finalProjectName);
|
|
102
|
+
await (0, scaffolding_1.generateApplicationFiles)(projectDir, language, finalProjectName);
|
|
103
|
+
await (0, scaffolding_1.generateConfigFiles)(projectDir, provider, language, finalProjectName);
|
|
104
|
+
logger_1.Logger.info(`✅ Project "${projectName || 'current directory'}" initialized successfully!`);
|
|
105
|
+
logger_1.Logger.info('');
|
|
106
|
+
logger_1.Logger.info('Next steps:');
|
|
107
|
+
if (projectName) {
|
|
108
|
+
logger_1.Logger.info(` 1. cd ${projectName}`);
|
|
109
|
+
logger_1.Logger.info(' 2. cp .env.example .env');
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
logger_1.Logger.info(' 1. cp .env.example .env');
|
|
113
|
+
logger_1.Logger.info(' 2. Edit .env with your credentials');
|
|
114
|
+
}
|
|
115
|
+
if (projectName) {
|
|
116
|
+
logger_1.Logger.info(' 3. Edit .env with your credentials');
|
|
117
|
+
logger_1.Logger.info(' 4. Review and update .tfwconfig.yml');
|
|
118
|
+
logger_1.Logger.info(' 5. terraflow init');
|
|
119
|
+
logger_1.Logger.info(' 6. terraflow plan');
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
logger_1.Logger.info(' 3. Review and update .tfwconfig.yml');
|
|
123
|
+
logger_1.Logger.info(' 4. terraflow init');
|
|
124
|
+
logger_1.Logger.info(' 5. terraflow plan');
|
|
125
|
+
}
|
|
126
|
+
logger_1.Logger.info('');
|
|
127
|
+
logger_1.Logger.info('Documentation: ./README.md');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
exports.InitCommand = InitCommand;
|
|
131
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Plan command handler
|
|
4
|
+
* Executes terraform plan with Terraflow enhancements
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.PlanCommand = void 0;
|
|
8
|
+
// TODO: Implement plan command
|
|
9
|
+
class PlanCommand {
|
|
10
|
+
}
|
|
11
|
+
exports.PlanCommand = PlanCommand;
|
|
12
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backend state management
|
|
3
|
+
* Stores previous backend configuration to detect migrations
|
|
4
|
+
*/
|
|
5
|
+
import type { BackendConfig } from '../types/config';
|
|
6
|
+
/**
|
|
7
|
+
* Load previous backend state
|
|
8
|
+
* @param workingDir - Working directory
|
|
9
|
+
* @returns Previous backend configuration or null
|
|
10
|
+
*/
|
|
11
|
+
export declare function loadBackendState(workingDir: string): BackendConfig | null;
|
|
12
|
+
/**
|
|
13
|
+
* Save backend state
|
|
14
|
+
* @param workingDir - Working directory
|
|
15
|
+
* @param backend - Backend configuration to save
|
|
16
|
+
*/
|
|
17
|
+
export declare function saveBackendState(workingDir: string, backend: BackendConfig): void;
|
|
18
|
+
/**
|
|
19
|
+
* Detect if backend has changed
|
|
20
|
+
* @param workingDir - Working directory
|
|
21
|
+
* @param currentBackend - Current backend configuration
|
|
22
|
+
* @returns Previous backend type if changed, null otherwise
|
|
23
|
+
*/
|
|
24
|
+
export declare function detectBackendMigration(workingDir: string, currentBackend: BackendConfig): string | null;
|
|
25
|
+
//# sourceMappingURL=backend-state.d.ts.map
|