@fromeroc9/testform 1.0.2 → 1.0.4
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/dist/action/index.js +1 -1
- package/dist/action.js +60 -0
- package/dist/adapters/github.js +467 -0
- package/dist/adapters/resources.js +363 -0
- package/dist/cli/index.js +3 -3
- package/dist/commands/apply.js +390 -0
- package/dist/commands/destroy.js +85 -0
- package/dist/commands/diff.js +131 -0
- package/dist/commands/fmt.js +166 -0
- package/dist/commands/force-unlock.js +55 -0
- package/dist/commands/generate.js +143 -0
- package/dist/commands/graph.js +159 -0
- package/dist/commands/import.js +222 -0
- package/dist/commands/init.js +167 -0
- package/dist/commands/login.js +71 -0
- package/dist/commands/logout.js +20 -0
- package/dist/commands/plan.js +250 -0
- package/dist/commands/refresh.js +165 -0
- package/dist/commands/report.js +724 -0
- package/dist/commands/show.js +61 -0
- package/dist/commands/state.js +197 -0
- package/dist/commands/taint.js +49 -0
- package/dist/commands/validate.js +128 -0
- package/dist/commands/workspace.js +102 -0
- package/dist/const.js +105 -0
- package/dist/core/backends/azurerm.js +201 -0
- package/dist/core/backends/backend.js +2 -0
- package/dist/core/backends/gcs.js +200 -0
- package/dist/core/backends/local.js +162 -0
- package/dist/core/backends/s3.js +224 -0
- package/dist/core/command-context.js +59 -0
- package/dist/core/config.js +131 -0
- package/dist/core/credentials.js +53 -0
- package/dist/core/parser.js +62 -0
- package/dist/core/parsers/base-parser.js +215 -0
- package/dist/core/parsers/testcase-parser.js +115 -0
- package/dist/core/parsers/testplan-parser.js +41 -0
- package/dist/core/parsers/testrun-parser.js +43 -0
- package/dist/core/policy.js +341 -0
- package/dist/core/prompt.js +109 -0
- package/dist/core/state.js +185 -0
- package/dist/core/utils.js +94 -0
- package/dist/core/variables.js +108 -0
- package/dist/core/workspace.js +56 -0
- package/dist/help.js +797 -0
- package/dist/index.js +650 -0
- package/dist/logger.js +134 -0
- package/dist/notify.js +36 -0
- package/dist/types.js +2 -0
- package/package.json +2 -2
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Pure utility functions shared across TestForm commands.
|
|
4
|
+
*
|
|
5
|
+
* Small, stateless helpers that eliminate common one-liner duplications
|
|
6
|
+
* found across command files.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.formatHclValue = void 0;
|
|
10
|
+
exports.elapsedSeconds = elapsedSeconds;
|
|
11
|
+
exports.formatIdentityDisplay = formatIdentityDisplay;
|
|
12
|
+
exports.formatResourceAddress = formatResourceAddress;
|
|
13
|
+
/**
|
|
14
|
+
* Calculates seconds elapsed since a given timestamp and returns a
|
|
15
|
+
* human-readable string (e.g. `"3"`).
|
|
16
|
+
*
|
|
17
|
+
* @param startTime - The start timestamp as returned by `Date.now()`.
|
|
18
|
+
* @returns Elapsed seconds as a fixed-point string with no decimals.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* const start = Date.now();
|
|
22
|
+
* // ... async work ...
|
|
23
|
+
* console.log(`Done in ${elapsedSeconds(start)}s`);
|
|
24
|
+
*/
|
|
25
|
+
function elapsedSeconds(startTime) {
|
|
26
|
+
return ((Date.now() - startTime) / 1000).toFixed(0);
|
|
27
|
+
}
|
|
28
|
+
function formatIdentityDisplay(identity) {
|
|
29
|
+
const parts = identity.split('::');
|
|
30
|
+
if (parts.length > 0) {
|
|
31
|
+
let filePath = parts[0];
|
|
32
|
+
const lastSlash = Math.max(filePath.lastIndexOf('/'), filePath.lastIndexOf('\\'));
|
|
33
|
+
if (lastSlash !== -1) {
|
|
34
|
+
filePath = filePath.substring(lastSlash + 1);
|
|
35
|
+
}
|
|
36
|
+
const lastDot = filePath.lastIndexOf('.');
|
|
37
|
+
if (lastDot !== -1) {
|
|
38
|
+
filePath = filePath.substring(0, lastDot);
|
|
39
|
+
}
|
|
40
|
+
parts[0] = filePath;
|
|
41
|
+
return parts.join('::');
|
|
42
|
+
}
|
|
43
|
+
return identity;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Formats a resource address string.
|
|
47
|
+
*
|
|
48
|
+
* @param type - The resource type (e.g. `'github_testcase'`).
|
|
49
|
+
* @param identity - The resource identity (e.g. `'tc1.case.feature::@[tc1]'`).
|
|
50
|
+
* @returns A formatted address like `"github_testcase.tc1::@[tc1]"`.
|
|
51
|
+
*/
|
|
52
|
+
function formatResourceAddress(type, identity) {
|
|
53
|
+
return `${type}.${formatIdentityDisplay(identity)}`;
|
|
54
|
+
}
|
|
55
|
+
const formatHclValue = (value, indentLevel) => {
|
|
56
|
+
const indent = ' '.repeat(indentLevel);
|
|
57
|
+
if (value === null || value === undefined)
|
|
58
|
+
return 'null';
|
|
59
|
+
if (typeof value === 'string') {
|
|
60
|
+
if (value.includes('\n')) {
|
|
61
|
+
const paddedValue = value.split('\n').map(line => line ? `${indent} ${line}` : `${indent} `).join('\n');
|
|
62
|
+
return `<<-EOT\n${paddedValue}\n${indent}EOT`;
|
|
63
|
+
}
|
|
64
|
+
return JSON.stringify(value);
|
|
65
|
+
}
|
|
66
|
+
if (Array.isArray(value)) {
|
|
67
|
+
if (value.length === 0)
|
|
68
|
+
return '[]';
|
|
69
|
+
const items = value.map(v => (0, exports.formatHclValue)(v, indentLevel + 1));
|
|
70
|
+
return `[\n${indent} ${items.join(`,\n${indent} `)},\n${indent}]`;
|
|
71
|
+
}
|
|
72
|
+
if (typeof value === 'object') {
|
|
73
|
+
const keys = Object.keys(value);
|
|
74
|
+
if (keys.length === 0)
|
|
75
|
+
return '{}';
|
|
76
|
+
let maxKeyLen = 0;
|
|
77
|
+
for (const k of keys) {
|
|
78
|
+
const strK = JSON.stringify(k);
|
|
79
|
+
if (strK.length > maxKeyLen)
|
|
80
|
+
maxKeyLen = strK.length;
|
|
81
|
+
}
|
|
82
|
+
let out = `{\n`;
|
|
83
|
+
for (const k of keys) {
|
|
84
|
+
const strK = JSON.stringify(k);
|
|
85
|
+
const padding = ' '.repeat(maxKeyLen - strK.length);
|
|
86
|
+
const val = Object.prototype.hasOwnProperty.call(value, k) ? value[k] : undefined;
|
|
87
|
+
out += `${indent} ${strK}${padding} = ${(0, exports.formatHclValue)(val, indentLevel + 1)}\n`;
|
|
88
|
+
}
|
|
89
|
+
out += `${indent}}`;
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
return JSON.stringify(value);
|
|
93
|
+
};
|
|
94
|
+
exports.formatHclValue = formatHclValue;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VariableParser = void 0;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const notify_1 = require("../notify");
|
|
6
|
+
class VariableParser {
|
|
7
|
+
vars = {};
|
|
8
|
+
workDir;
|
|
9
|
+
constructor(varArgs, varFileArgs, workDir = '.') {
|
|
10
|
+
this.workDir = workDir;
|
|
11
|
+
this.parseVarFiles(varFileArgs);
|
|
12
|
+
this.parseVars(varArgs);
|
|
13
|
+
}
|
|
14
|
+
parseVars(varArgs) {
|
|
15
|
+
if (!varArgs)
|
|
16
|
+
return;
|
|
17
|
+
const items = Array.isArray(varArgs) ? varArgs : [varArgs];
|
|
18
|
+
for (const item of items) {
|
|
19
|
+
const idx = item.indexOf('=');
|
|
20
|
+
if (idx === -1) {
|
|
21
|
+
notify_1.notify.push({
|
|
22
|
+
type: 'warning',
|
|
23
|
+
title: `Invalid variable format: ${item}`,
|
|
24
|
+
detail: ['Variables must be in the format key=value']
|
|
25
|
+
});
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const key = item.substring(0, idx).trim();
|
|
29
|
+
const value = item.substring(idx + 1).trim();
|
|
30
|
+
this.vars[key] = value;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
parseVarFiles(varFileArgs) {
|
|
34
|
+
if (!varFileArgs)
|
|
35
|
+
return;
|
|
36
|
+
const items = Array.isArray(varFileArgs) ? varFileArgs : [varFileArgs];
|
|
37
|
+
for (let filePath of items) {
|
|
38
|
+
const path = require('path');
|
|
39
|
+
filePath = path.resolve(this.workDir, filePath);
|
|
40
|
+
if (!(0, fs_1.existsSync)(filePath)) {
|
|
41
|
+
notify_1.notify.push({
|
|
42
|
+
type: 'error',
|
|
43
|
+
title: `Variable file not found: ${filePath}`,
|
|
44
|
+
detail: [],
|
|
45
|
+
close: true
|
|
46
|
+
});
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
const content = (0, fs_1.readFileSync)(filePath, 'utf-8');
|
|
51
|
+
let fileVars = {};
|
|
52
|
+
if (filePath.endsWith('.json')) {
|
|
53
|
+
fileVars = JSON.parse(content);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// Simple tfvars format parser (key = "value")
|
|
57
|
+
const lines = content.split('\n');
|
|
58
|
+
for (const line of lines) {
|
|
59
|
+
const trimmed = line.trim();
|
|
60
|
+
if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('//'))
|
|
61
|
+
continue;
|
|
62
|
+
const idx = trimmed.indexOf('=');
|
|
63
|
+
if (idx !== -1) {
|
|
64
|
+
const key = trimmed.substring(0, idx).trim();
|
|
65
|
+
let value = trimmed.substring(idx + 1).trim();
|
|
66
|
+
// Remove surrounding quotes if present
|
|
67
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
68
|
+
value = value.substring(1, value.length - 1);
|
|
69
|
+
}
|
|
70
|
+
else if (value.startsWith("'") && value.endsWith("'")) {
|
|
71
|
+
value = value.substring(1, value.length - 1);
|
|
72
|
+
}
|
|
73
|
+
fileVars[key] = value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Merge into existing vars
|
|
78
|
+
this.vars = { ...this.vars, ...fileVars };
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
notify_1.notify.push({
|
|
82
|
+
type: 'error',
|
|
83
|
+
title: `Failed to parse variable file: ${filePath}`,
|
|
84
|
+
detail: [error.message],
|
|
85
|
+
close: true
|
|
86
|
+
});
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
getVars() {
|
|
92
|
+
return this.vars;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Replaces ${var.name} in the given template string
|
|
96
|
+
* with the corresponding values from the variables.
|
|
97
|
+
*/
|
|
98
|
+
applyToTemplate(template) {
|
|
99
|
+
let result = template;
|
|
100
|
+
// Match ${var.NAME}
|
|
101
|
+
const regex = /\$\{var\.([a-zA-Z0-9_-]+)\}/g;
|
|
102
|
+
result = result.replace(regex, (match, key) => {
|
|
103
|
+
return this.vars[key] !== undefined ? this.vars[key] : match;
|
|
104
|
+
});
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.VariableParser = VariableParser;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WorkspaceManager = void 0;
|
|
4
|
+
const path_1 = require("path");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
class WorkspaceManager {
|
|
7
|
+
envPath;
|
|
8
|
+
statePath;
|
|
9
|
+
constructor(dir) {
|
|
10
|
+
const testformDir = (0, path_1.join)(dir, '.testform');
|
|
11
|
+
if (!(0, fs_1.existsSync)(testformDir)) {
|
|
12
|
+
(0, fs_1.mkdirSync)(testformDir, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
this.envPath = (0, path_1.join)(testformDir, 'environment');
|
|
15
|
+
this.statePath = (0, path_1.join)(testformDir, 'testform.state');
|
|
16
|
+
}
|
|
17
|
+
getCurrentWorkspace() {
|
|
18
|
+
if ((0, fs_1.existsSync)(this.envPath) && (0, fs_1.statSync)(this.envPath).isFile()) {
|
|
19
|
+
const content = (0, fs_1.readFileSync)(this.envPath, 'utf8').trim();
|
|
20
|
+
if (content.length > 0) {
|
|
21
|
+
return content;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return 'default';
|
|
25
|
+
}
|
|
26
|
+
setCurrentWorkspace(name) {
|
|
27
|
+
(0, fs_1.writeFileSync)(this.envPath, name, 'utf8');
|
|
28
|
+
}
|
|
29
|
+
getActiveBackend() {
|
|
30
|
+
if ((0, fs_1.existsSync)(this.statePath) && (0, fs_1.statSync)(this.statePath).isFile()) {
|
|
31
|
+
try {
|
|
32
|
+
const content = (0, fs_1.readFileSync)(this.statePath, 'utf8');
|
|
33
|
+
const parsed = JSON.parse(content);
|
|
34
|
+
return parsed.backend || null;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
setActiveBackend(backend) {
|
|
43
|
+
let stateObj = { version: 3 };
|
|
44
|
+
if ((0, fs_1.existsSync)(this.statePath) && (0, fs_1.statSync)(this.statePath).isFile()) {
|
|
45
|
+
try {
|
|
46
|
+
stateObj = JSON.parse((0, fs_1.readFileSync)(this.statePath, 'utf8'));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Ignore parse errors, just overwrite
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
stateObj.backend = backend;
|
|
53
|
+
(0, fs_1.writeFileSync)(this.statePath, JSON.stringify(stateObj, null, 2), 'utf8');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.WorkspaceManager = WorkspaceManager;
|