@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,338 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Scaffolding utilities for project initialization
|
|
4
|
+
* Handles template processing and file generation
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.loadTemplate = loadTemplate;
|
|
8
|
+
exports.processTemplate = processTemplate;
|
|
9
|
+
exports.generateTerraformFiles = generateTerraformFiles;
|
|
10
|
+
exports.generateApplicationFiles = generateApplicationFiles;
|
|
11
|
+
exports.generateConfigFiles = generateConfigFiles;
|
|
12
|
+
exports.createProjectStructure = createProjectStructure;
|
|
13
|
+
exports.validateProjectName = validateProjectName;
|
|
14
|
+
exports.validateProvider = validateProvider;
|
|
15
|
+
exports.validateLanguage = validateLanguage;
|
|
16
|
+
exports.isDirectoryEmpty = isDirectoryEmpty;
|
|
17
|
+
exports.buildTemplateVariables = buildTemplateVariables;
|
|
18
|
+
const fs_1 = require("fs");
|
|
19
|
+
const path_1 = require("path");
|
|
20
|
+
const logger_1 = require("./logger");
|
|
21
|
+
const errors_1 = require("../core/errors");
|
|
22
|
+
/**
|
|
23
|
+
* Template processing functions
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Load a template file from the templates directory
|
|
27
|
+
* @param templatePath - Path to template file relative to templates directory
|
|
28
|
+
* @returns Template content as string
|
|
29
|
+
*/
|
|
30
|
+
function loadTemplate(templatePath) {
|
|
31
|
+
// Get template directory (works in both source and built code)
|
|
32
|
+
const templatesDir = (0, path_1.join)(__dirname, '..', 'templates');
|
|
33
|
+
// Prevent path traversal attacks
|
|
34
|
+
// Reject any path that contains path traversal sequences
|
|
35
|
+
if (templatePath.includes('..') || templatePath.includes('~')) {
|
|
36
|
+
throw new errors_1.ConfigError(`Invalid template path: ${templatePath}`);
|
|
37
|
+
}
|
|
38
|
+
// Join and resolve to get absolute path, then verify it's within templates directory
|
|
39
|
+
const fullPath = (0, path_1.join)(templatesDir, templatePath);
|
|
40
|
+
// Additional safety: verify the resolved path is within templates directory
|
|
41
|
+
// by checking that it starts with the templates directory path
|
|
42
|
+
if (!fullPath.startsWith(templatesDir)) {
|
|
43
|
+
throw new errors_1.ConfigError(`Invalid template path: ${templatePath}`);
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
return (0, fs_1.readFileSync)(fullPath, 'utf8');
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
logger_1.Logger.error(`Failed to load template ${templatePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
50
|
+
throw new errors_1.ConfigError(`Template file not found: ${templatePath}.\n` +
|
|
51
|
+
`This may indicate a corrupted installation. Try reinstalling terraflow.`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Process a template string by replacing variable placeholders
|
|
56
|
+
* @param template - Template content with placeholders
|
|
57
|
+
* @param variables - Object mapping variable names to values
|
|
58
|
+
* @returns Processed template with variables replaced
|
|
59
|
+
*/
|
|
60
|
+
function processTemplate(template, variables) {
|
|
61
|
+
let result = template;
|
|
62
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
63
|
+
const regex = new RegExp(`<${key}>`, 'g');
|
|
64
|
+
result = result.replace(regex, value);
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* File generation functions
|
|
70
|
+
*/
|
|
71
|
+
/**
|
|
72
|
+
* Generate Terraform files for the project
|
|
73
|
+
* @param projectDir - Root directory of the project
|
|
74
|
+
* @param provider - Cloud provider (aws, azure, gcp)
|
|
75
|
+
* @param projectName - Name of the project
|
|
76
|
+
*/
|
|
77
|
+
async function generateTerraformFiles(projectDir, provider, projectName) {
|
|
78
|
+
const terraformDir = (0, path_1.join)(projectDir, 'terraform');
|
|
79
|
+
const templatesDir = 'terraform';
|
|
80
|
+
logger_1.Logger.debug(`Generating Terraform files for provider: ${provider}`);
|
|
81
|
+
// Create terraform directory if it doesn't exist
|
|
82
|
+
(0, fs_1.mkdirSync)(terraformDir, { recursive: true });
|
|
83
|
+
// _init.tf - provider-specific
|
|
84
|
+
const initTemplate = loadTemplate((0, path_1.join)(templatesDir, provider, '_init.tf.template'));
|
|
85
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(terraformDir, '_init.tf'), initTemplate);
|
|
86
|
+
// inputs.tf - provider-specific
|
|
87
|
+
const inputsTemplate = loadTemplate((0, path_1.join)(templatesDir, provider, 'inputs.tf.template'));
|
|
88
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(terraformDir, 'inputs.tf'), inputsTemplate);
|
|
89
|
+
// locals.tf - common with project name replacement
|
|
90
|
+
const localsTemplate = loadTemplate((0, path_1.join)(templatesDir, 'locals.tf.template'));
|
|
91
|
+
const localsContent = processTemplate(localsTemplate, { 'project-name': projectName });
|
|
92
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(terraformDir, 'locals.tf'), localsContent);
|
|
93
|
+
// main.tf - common
|
|
94
|
+
const mainTemplate = loadTemplate((0, path_1.join)(templatesDir, 'main.tf.template'));
|
|
95
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(terraformDir, 'main.tf'), mainTemplate);
|
|
96
|
+
// outputs.tf - common
|
|
97
|
+
const outputsTemplate = loadTemplate((0, path_1.join)(templatesDir, 'outputs.tf.template'));
|
|
98
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(terraformDir, 'outputs.tf'), outputsTemplate);
|
|
99
|
+
// Create modules directory
|
|
100
|
+
const modulesDir = (0, path_1.join)(terraformDir, 'modules');
|
|
101
|
+
(0, fs_1.mkdirSync)(modulesDir, { recursive: true });
|
|
102
|
+
// modules/inputs.tf
|
|
103
|
+
const moduleInputsTemplate = loadTemplate((0, path_1.join)(templatesDir, 'modules', 'inputs.tf.template'));
|
|
104
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(modulesDir, 'inputs.tf'), moduleInputsTemplate);
|
|
105
|
+
// modules/main.tf
|
|
106
|
+
const moduleMainTemplate = loadTemplate((0, path_1.join)(templatesDir, 'modules', 'main.tf.template'));
|
|
107
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(modulesDir, 'main.tf'), moduleMainTemplate);
|
|
108
|
+
// modules/outputs.tf
|
|
109
|
+
const moduleOutputsTemplate = loadTemplate((0, path_1.join)(templatesDir, 'modules', 'outputs.tf.template'));
|
|
110
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(modulesDir, 'outputs.tf'), moduleOutputsTemplate);
|
|
111
|
+
logger_1.Logger.debug('Terraform files generated successfully');
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Get file extension for main file based on language
|
|
115
|
+
* @param language - Programming language (javascript, typescript, python, go)
|
|
116
|
+
* @returns File extension (e.g., '.js', '.ts', '.py', '.go')
|
|
117
|
+
* @example
|
|
118
|
+
* getMainExtension('typescript') // returns '.ts'
|
|
119
|
+
* getMainExtension('python') // returns '.py'
|
|
120
|
+
*/
|
|
121
|
+
function getMainExtension(language) {
|
|
122
|
+
switch (language) {
|
|
123
|
+
case 'typescript':
|
|
124
|
+
return '.ts';
|
|
125
|
+
case 'python':
|
|
126
|
+
return '.py';
|
|
127
|
+
case 'go':
|
|
128
|
+
return '.go';
|
|
129
|
+
default:
|
|
130
|
+
return '.js';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get test file name based on language
|
|
135
|
+
* @param language - Programming language (javascript, typescript, python, go)
|
|
136
|
+
* @returns Test file name (e.g., 'index.spec.js', 'test_main.py', 'main_test.go')
|
|
137
|
+
* @example
|
|
138
|
+
* getTestFileName('python') // returns 'test_main.py'
|
|
139
|
+
* getTestFileName('go') // returns 'main_test.go'
|
|
140
|
+
*/
|
|
141
|
+
function getTestFileName(language) {
|
|
142
|
+
switch (language) {
|
|
143
|
+
case 'python':
|
|
144
|
+
return 'test_main.py';
|
|
145
|
+
case 'go':
|
|
146
|
+
return 'main_test.go';
|
|
147
|
+
case 'typescript':
|
|
148
|
+
return 'index.spec.ts';
|
|
149
|
+
default:
|
|
150
|
+
return 'index.spec.js';
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Generate application files for the project
|
|
155
|
+
* @param projectDir - Root directory of the project
|
|
156
|
+
* @param language - Programming language (javascript, typescript, python, go)
|
|
157
|
+
* @param projectName - Name of the project
|
|
158
|
+
*/
|
|
159
|
+
async function generateApplicationFiles(projectDir, language, projectName) {
|
|
160
|
+
const srcDir = (0, path_1.join)(projectDir, 'src');
|
|
161
|
+
const templatesDir = 'application';
|
|
162
|
+
logger_1.Logger.debug(`Generating application files for language: ${language}`);
|
|
163
|
+
// Create src directories
|
|
164
|
+
(0, fs_1.mkdirSync)((0, path_1.join)(srcDir, 'main'), { recursive: true });
|
|
165
|
+
(0, fs_1.mkdirSync)((0, path_1.join)(srcDir, 'test'), { recursive: true });
|
|
166
|
+
// Main file
|
|
167
|
+
const mainTemplate = loadTemplate((0, path_1.join)(templatesDir, language, 'main.template'));
|
|
168
|
+
const mainContent = processTemplate(mainTemplate, { 'project-name': projectName });
|
|
169
|
+
const mainExt = getMainExtension(language);
|
|
170
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(srcDir, 'main', `index${mainExt}`), mainContent);
|
|
171
|
+
// Test file
|
|
172
|
+
const testTemplate = loadTemplate((0, path_1.join)(templatesDir, language, 'test.template'));
|
|
173
|
+
const testContent = processTemplate(testTemplate, { 'project-name': projectName });
|
|
174
|
+
const testFileName = getTestFileName(language);
|
|
175
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(srcDir, 'test', testFileName), testContent);
|
|
176
|
+
// Language-specific config files
|
|
177
|
+
if (language === 'typescript') {
|
|
178
|
+
const tsconfigTemplate = loadTemplate((0, path_1.join)(templatesDir, language, 'tsconfig.json.template'));
|
|
179
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(projectDir, 'tsconfig.json'), tsconfigTemplate);
|
|
180
|
+
}
|
|
181
|
+
else if (language === 'python') {
|
|
182
|
+
const requirementsTemplate = loadTemplate((0, path_1.join)(templatesDir, language, 'requirements.txt.template'));
|
|
183
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(projectDir, 'requirements.txt'), requirementsTemplate);
|
|
184
|
+
}
|
|
185
|
+
else if (language === 'go') {
|
|
186
|
+
const goModTemplate = loadTemplate((0, path_1.join)(templatesDir, language, 'go.mod.template'));
|
|
187
|
+
const goModContent = processTemplate(goModTemplate, { 'project-name': projectName });
|
|
188
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(projectDir, 'go.mod'), goModContent);
|
|
189
|
+
}
|
|
190
|
+
logger_1.Logger.debug('Application files generated successfully');
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Map cloud provider to Terraform backend type
|
|
194
|
+
* @param provider - Cloud provider name (aws, azure, gcp)
|
|
195
|
+
* @returns Backend type (s3, azurerm, gcs, or local)
|
|
196
|
+
* @example
|
|
197
|
+
* getBackendType('aws') // returns 's3'
|
|
198
|
+
* getBackendType('azure') // returns 'azurerm'
|
|
199
|
+
* getBackendType('gcp') // returns 'gcs'
|
|
200
|
+
*/
|
|
201
|
+
function getBackendType(provider) {
|
|
202
|
+
switch (provider) {
|
|
203
|
+
case 'aws':
|
|
204
|
+
return 's3';
|
|
205
|
+
case 'azure':
|
|
206
|
+
return 'azurerm';
|
|
207
|
+
case 'gcp':
|
|
208
|
+
return 'gcs';
|
|
209
|
+
default:
|
|
210
|
+
return 'local';
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Generate configuration files for the project
|
|
215
|
+
* @param projectDir - Root directory of the project
|
|
216
|
+
* @param provider - Cloud provider (aws, azure, gcp)
|
|
217
|
+
* @param language - Programming language (javascript, typescript, python, go)
|
|
218
|
+
* @param projectName - Name of the project
|
|
219
|
+
*/
|
|
220
|
+
async function generateConfigFiles(projectDir, provider, language, projectName) {
|
|
221
|
+
const templatesDir = 'config';
|
|
222
|
+
logger_1.Logger.debug(`Generating configuration files for provider: ${provider}, language: ${language}`);
|
|
223
|
+
// .tfwconfig.yml
|
|
224
|
+
const tfwconfigTemplate = loadTemplate((0, path_1.join)(templatesDir, 'tfwconfig.yml.template'));
|
|
225
|
+
const backendType = getBackendType(provider);
|
|
226
|
+
const tfwconfigContent = processTemplate(tfwconfigTemplate, {
|
|
227
|
+
'project-name': projectName,
|
|
228
|
+
provider: backendType,
|
|
229
|
+
});
|
|
230
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(projectDir, '.tfwconfig.yml'), tfwconfigContent);
|
|
231
|
+
// .env.example
|
|
232
|
+
const envExampleTemplate = loadTemplate((0, path_1.join)(templatesDir, 'env.example.template'));
|
|
233
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(projectDir, '.env.example'), envExampleTemplate);
|
|
234
|
+
// .gitignore
|
|
235
|
+
const gitignoreTemplate = loadTemplate((0, path_1.join)(templatesDir, 'gitignore.template'));
|
|
236
|
+
// gitignore template already includes all languages, no processing needed
|
|
237
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(projectDir, '.gitignore'), gitignoreTemplate);
|
|
238
|
+
// README.md
|
|
239
|
+
const readmeTemplate = loadTemplate((0, path_1.join)(templatesDir, 'README.md.template'));
|
|
240
|
+
const readmeContent = processTemplate(readmeTemplate, {
|
|
241
|
+
'project-name': projectName,
|
|
242
|
+
provider: provider, // Use original provider name for README
|
|
243
|
+
});
|
|
244
|
+
(0, fs_1.writeFileSync)((0, path_1.join)(projectDir, 'README.md'), readmeContent);
|
|
245
|
+
logger_1.Logger.debug('Configuration files generated successfully');
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Project structure creation
|
|
249
|
+
*/
|
|
250
|
+
/**
|
|
251
|
+
* Create the complete project directory structure
|
|
252
|
+
* @param projectDir - Root directory of the project
|
|
253
|
+
*/
|
|
254
|
+
async function createProjectStructure(projectDir) {
|
|
255
|
+
logger_1.Logger.debug(`Creating project structure in: ${projectDir}`);
|
|
256
|
+
const dirs = [
|
|
257
|
+
(0, path_1.join)(projectDir, 'src', 'main'),
|
|
258
|
+
(0, path_1.join)(projectDir, 'src', 'test'),
|
|
259
|
+
(0, path_1.join)(projectDir, 'terraform', 'modules'),
|
|
260
|
+
];
|
|
261
|
+
for (const dir of dirs) {
|
|
262
|
+
try {
|
|
263
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
logger_1.Logger.error(`Failed to create directory ${dir}: ${error instanceof Error ? error.message : String(error)}`);
|
|
267
|
+
throw new errors_1.ConfigError(`Failed to create directory: ${dir}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
logger_1.Logger.debug('Project structure created successfully');
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Validation helpers
|
|
274
|
+
*/
|
|
275
|
+
/**
|
|
276
|
+
* Validate project name format
|
|
277
|
+
* @param name - Project name to validate
|
|
278
|
+
* @returns true if valid, false otherwise
|
|
279
|
+
*/
|
|
280
|
+
function validateProjectName(name) {
|
|
281
|
+
const PROJECT_NAME_REGEX = /^[a-zA-Z0-9_-]+$/;
|
|
282
|
+
return PROJECT_NAME_REGEX.test(name);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Validate cloud provider
|
|
286
|
+
* @param provider - Provider name to validate
|
|
287
|
+
* @returns true if valid, false otherwise
|
|
288
|
+
*/
|
|
289
|
+
function validateProvider(provider) {
|
|
290
|
+
const VALID_PROVIDERS = ['aws', 'azure', 'gcp'];
|
|
291
|
+
return VALID_PROVIDERS.includes(provider);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Validate programming language
|
|
295
|
+
* @param language - Language name to validate
|
|
296
|
+
* @returns true if valid, false otherwise
|
|
297
|
+
*/
|
|
298
|
+
function validateLanguage(language) {
|
|
299
|
+
const VALID_LANGUAGES = ['javascript', 'typescript', 'python', 'go'];
|
|
300
|
+
return VALID_LANGUAGES.includes(language);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Check if a directory is empty
|
|
304
|
+
* @param dir - Directory path to check
|
|
305
|
+
* @returns true if directory is empty or doesn't exist, false otherwise
|
|
306
|
+
*/
|
|
307
|
+
async function isDirectoryEmpty(dir) {
|
|
308
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
try {
|
|
312
|
+
const files = (0, fs_1.readdirSync)(dir);
|
|
313
|
+
return files.length === 0;
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
logger_1.Logger.error(`Failed to read directory ${dir}: ${error instanceof Error ? error.message : String(error)}`);
|
|
317
|
+
throw new errors_1.ConfigError(`Failed to read directory: ${dir}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Template variable builder
|
|
322
|
+
*/
|
|
323
|
+
/**
|
|
324
|
+
* Build template variables object from project parameters
|
|
325
|
+
* @param projectName - Name of the project
|
|
326
|
+
* @param provider - Cloud provider (aws, azure, gcp)
|
|
327
|
+
* @param language - Programming language (javascript, typescript, python, go)
|
|
328
|
+
* @returns Object with all template variable mappings
|
|
329
|
+
*/
|
|
330
|
+
function buildTemplateVariables(projectName, provider, _language) {
|
|
331
|
+
const backendType = getBackendType(provider);
|
|
332
|
+
return {
|
|
333
|
+
'project-name': projectName,
|
|
334
|
+
provider: backendType,
|
|
335
|
+
'provider-name': provider, // Keep original provider name for README
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
//# sourceMappingURL=scaffolding.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Variable templating utility
|
|
3
|
+
* Resolves template variables in configuration
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Template resolution utility
|
|
7
|
+
*/
|
|
8
|
+
export declare class TemplateUtils {
|
|
9
|
+
/**
|
|
10
|
+
* Resolve template variables in a string
|
|
11
|
+
* Supports ${VAR} syntax
|
|
12
|
+
* @param template - Template string with ${VAR} placeholders
|
|
13
|
+
* @param vars - Variables to substitute
|
|
14
|
+
* @returns Resolved string
|
|
15
|
+
*/
|
|
16
|
+
static resolve(template: string, vars: Record<string, string>): string;
|
|
17
|
+
/**
|
|
18
|
+
* Resolve all template variables in an object recursively
|
|
19
|
+
* @param obj - Object to resolve templates in
|
|
20
|
+
* @param vars - Variables to substitute
|
|
21
|
+
* @returns Object with templates resolved
|
|
22
|
+
*/
|
|
23
|
+
static resolveObject<T extends Record<string, unknown>>(obj: T, vars: Record<string, string>): T;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=templates.d.ts.map
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Variable templating utility
|
|
4
|
+
* Resolves template variables in configuration
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.TemplateUtils = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Template resolution utility
|
|
10
|
+
*/
|
|
11
|
+
class TemplateUtils {
|
|
12
|
+
/**
|
|
13
|
+
* Resolve template variables in a string
|
|
14
|
+
* Supports ${VAR} syntax
|
|
15
|
+
* @param template - Template string with ${VAR} placeholders
|
|
16
|
+
* @param vars - Variables to substitute
|
|
17
|
+
* @returns Resolved string
|
|
18
|
+
*/
|
|
19
|
+
static resolve(template, vars) {
|
|
20
|
+
if (typeof template !== 'string') {
|
|
21
|
+
return template;
|
|
22
|
+
}
|
|
23
|
+
return template.replace(/\$\{([^}]+)\}/g, (match, varName) => {
|
|
24
|
+
const trimmedVarName = varName.trim();
|
|
25
|
+
if (vars[trimmedVarName] !== undefined) {
|
|
26
|
+
return vars[trimmedVarName];
|
|
27
|
+
}
|
|
28
|
+
// If variable not found, return the original placeholder
|
|
29
|
+
return match;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Resolve all template variables in an object recursively
|
|
34
|
+
* @param obj - Object to resolve templates in
|
|
35
|
+
* @param vars - Variables to substitute
|
|
36
|
+
* @returns Object with templates resolved
|
|
37
|
+
*/
|
|
38
|
+
static resolveObject(obj, vars) {
|
|
39
|
+
if (obj === null || obj === undefined) {
|
|
40
|
+
return obj;
|
|
41
|
+
}
|
|
42
|
+
if (typeof obj === 'string') {
|
|
43
|
+
return TemplateUtils.resolve(obj, vars);
|
|
44
|
+
}
|
|
45
|
+
if (Array.isArray(obj)) {
|
|
46
|
+
return obj.map((item) => TemplateUtils.resolveObject(item, vars));
|
|
47
|
+
}
|
|
48
|
+
if (typeof obj === 'object') {
|
|
49
|
+
const resolved = {};
|
|
50
|
+
for (const key in obj) {
|
|
51
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
52
|
+
const value = obj[key];
|
|
53
|
+
if (typeof value === 'string') {
|
|
54
|
+
resolved[key] = TemplateUtils.resolve(value, vars);
|
|
55
|
+
}
|
|
56
|
+
else if (typeof value === 'object' && value !== null) {
|
|
57
|
+
resolved[key] = TemplateUtils.resolveObject(value, vars);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
resolved[key] = value;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return resolved;
|
|
65
|
+
}
|
|
66
|
+
return obj;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
exports.TemplateUtils = TemplateUtils;
|
|
70
|
+
//# sourceMappingURL=templates.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@salte-common/terraflow",
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"description": "Opinionated Terraform workflow CLI with multi-cloud support",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"terraflow": "./bin/terraflow.js",
|
|
9
|
+
"tf": "./bin/tf.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc && npm run copy-templates",
|
|
13
|
+
"copy-templates": "cp -r src/templates dist/templates",
|
|
14
|
+
"test": "jest",
|
|
15
|
+
"test:coverage": "jest --coverage",
|
|
16
|
+
"lint": "eslint src --ext .ts",
|
|
17
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
18
|
+
"prepublishOnly": "npm run build && npm test"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"terraform",
|
|
22
|
+
"infrastructure",
|
|
23
|
+
"iac",
|
|
24
|
+
"devops",
|
|
25
|
+
"cli",
|
|
26
|
+
"aws",
|
|
27
|
+
"azure",
|
|
28
|
+
"gcp"
|
|
29
|
+
],
|
|
30
|
+
"author": "Dave Sibiski",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "https://github.com/salte-common/terraflow.git"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@aws-sdk/client-secrets-manager": "^3.956.0",
|
|
41
|
+
"@aws-sdk/client-sts": "^3.956.0",
|
|
42
|
+
"commander": "^11.0.0",
|
|
43
|
+
"dotenv": "^16.0.0",
|
|
44
|
+
"js-yaml": "^4.1.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/jest": "^29.0.0",
|
|
48
|
+
"@types/js-yaml": "^4.0.5",
|
|
49
|
+
"@types/node": "^20.0.0",
|
|
50
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
51
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
52
|
+
"eslint": "^8.0.0",
|
|
53
|
+
"eslint-config-prettier": "^9.0.0",
|
|
54
|
+
"eslint-plugin-prettier": "^5.0.0",
|
|
55
|
+
"jest": "^29.0.0",
|
|
56
|
+
"prettier": "^3.0.0",
|
|
57
|
+
"ts-jest": "^29.0.0",
|
|
58
|
+
"typescript": "^5.0.0"
|
|
59
|
+
}
|
|
60
|
+
}
|