qa360 1.4.5 → 2.0.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/README.md +1 -1
- package/dist/commands/ai.d.ts +41 -0
- package/dist/commands/ai.js +499 -0
- package/dist/commands/ask.js +12 -12
- package/dist/commands/coverage.d.ts +8 -0
- package/dist/commands/coverage.js +252 -0
- package/dist/commands/explain.d.ts +27 -0
- package/dist/commands/explain.js +630 -0
- package/dist/commands/flakiness.d.ts +73 -0
- package/dist/commands/flakiness.js +435 -0
- package/dist/commands/generate.d.ts +66 -0
- package/dist/commands/generate.js +438 -0
- package/dist/commands/init.d.ts +56 -9
- package/dist/commands/init.js +217 -10
- package/dist/commands/monitor.d.ts +27 -0
- package/dist/commands/monitor.js +225 -0
- package/dist/commands/ollama.d.ts +40 -0
- package/dist/commands/ollama.js +301 -0
- package/dist/commands/pack.d.ts +37 -9
- package/dist/commands/pack.js +240 -141
- package/dist/commands/regression.d.ts +8 -0
- package/dist/commands/regression.js +340 -0
- package/dist/commands/repair.d.ts +26 -0
- package/dist/commands/repair.js +307 -0
- package/dist/commands/retry.d.ts +43 -0
- package/dist/commands/retry.js +275 -0
- package/dist/commands/run.d.ts +8 -3
- package/dist/commands/run.js +45 -31
- package/dist/commands/slo.d.ts +8 -0
- package/dist/commands/slo.js +327 -0
- package/dist/core/adapters/playwright-native-api.d.ts +183 -0
- package/dist/core/adapters/playwright-native-api.js +461 -0
- package/dist/core/adapters/playwright-ui.d.ts +7 -0
- package/dist/core/adapters/playwright-ui.js +29 -1
- package/dist/core/ai/anthropic-provider.d.ts +50 -0
- package/dist/core/ai/anthropic-provider.js +211 -0
- package/dist/core/ai/deepseek-provider.d.ts +81 -0
- package/dist/core/ai/deepseek-provider.js +254 -0
- package/dist/core/ai/index.d.ts +60 -0
- package/dist/core/ai/index.js +18 -0
- package/dist/core/ai/llm-client.d.ts +45 -0
- package/dist/core/ai/llm-client.js +7 -0
- package/dist/core/ai/mock-provider.d.ts +49 -0
- package/dist/core/ai/mock-provider.js +121 -0
- package/dist/core/ai/ollama-provider.d.ts +78 -0
- package/dist/core/ai/ollama-provider.js +192 -0
- package/dist/core/ai/openai-provider.d.ts +48 -0
- package/dist/core/ai/openai-provider.js +188 -0
- package/dist/core/ai/provider-factory.d.ts +160 -0
- package/dist/core/ai/provider-factory.js +269 -0
- package/dist/core/auth/api-key-provider.d.ts +16 -0
- package/dist/core/auth/api-key-provider.js +63 -0
- package/dist/core/auth/aws-iam-provider.d.ts +35 -0
- package/dist/core/auth/aws-iam-provider.js +177 -0
- package/dist/core/auth/azure-ad-provider.d.ts +15 -0
- package/dist/core/auth/azure-ad-provider.js +99 -0
- package/dist/core/auth/basic-auth-provider.d.ts +26 -0
- package/dist/core/auth/basic-auth-provider.js +111 -0
- package/dist/core/auth/gcp-adc-provider.d.ts +27 -0
- package/dist/core/auth/gcp-adc-provider.js +126 -0
- package/dist/core/auth/index.d.ts +238 -0
- package/dist/core/auth/index.js +82 -0
- package/dist/core/auth/jwt-provider.d.ts +19 -0
- package/dist/core/auth/jwt-provider.js +160 -0
- package/dist/core/auth/manager.d.ts +84 -0
- package/dist/core/auth/manager.js +230 -0
- package/dist/core/auth/oauth2-provider.d.ts +17 -0
- package/dist/core/auth/oauth2-provider.js +114 -0
- package/dist/core/auth/totp-provider.d.ts +31 -0
- package/dist/core/auth/totp-provider.js +134 -0
- package/dist/core/auth/ui-login-provider.d.ts +26 -0
- package/dist/core/auth/ui-login-provider.js +198 -0
- package/dist/core/cache/index.d.ts +7 -0
- package/dist/core/cache/index.js +6 -0
- package/dist/core/cache/lru-cache.d.ts +203 -0
- package/dist/core/cache/lru-cache.js +397 -0
- package/dist/core/coverage/analyzer.d.ts +101 -0
- package/dist/core/coverage/analyzer.js +415 -0
- package/dist/core/coverage/collector.d.ts +74 -0
- package/dist/core/coverage/collector.js +459 -0
- package/dist/core/coverage/config.d.ts +37 -0
- package/dist/core/coverage/config.js +156 -0
- package/dist/core/coverage/index.d.ts +11 -0
- package/dist/core/coverage/index.js +15 -0
- package/dist/core/coverage/types.d.ts +267 -0
- package/dist/core/coverage/types.js +6 -0
- package/dist/core/coverage/vault.d.ts +95 -0
- package/dist/core/coverage/vault.js +405 -0
- package/dist/core/dashboard/assets.d.ts +6 -0
- package/dist/core/dashboard/assets.js +690 -0
- package/dist/core/dashboard/index.d.ts +6 -0
- package/dist/core/dashboard/index.js +5 -0
- package/dist/core/dashboard/server.d.ts +72 -0
- package/dist/core/dashboard/server.js +354 -0
- package/dist/core/dashboard/types.d.ts +70 -0
- package/dist/core/dashboard/types.js +5 -0
- package/dist/core/discoverer/index.d.ts +115 -0
- package/dist/core/discoverer/index.js +250 -0
- package/dist/core/flakiness/index.d.ts +228 -0
- package/dist/core/flakiness/index.js +384 -0
- package/dist/core/generation/code-formatter.d.ts +111 -0
- package/dist/core/generation/code-formatter.js +307 -0
- package/dist/core/generation/code-generator.d.ts +144 -0
- package/dist/core/generation/code-generator.js +293 -0
- package/dist/core/generation/generator.d.ts +40 -0
- package/dist/core/generation/generator.js +76 -0
- package/dist/core/generation/index.d.ts +30 -0
- package/dist/core/generation/index.js +28 -0
- package/dist/core/generation/pack-generator.d.ts +107 -0
- package/dist/core/generation/pack-generator.js +416 -0
- package/dist/core/generation/prompt-builder.d.ts +132 -0
- package/dist/core/generation/prompt-builder.js +672 -0
- package/dist/core/generation/source-analyzer.d.ts +213 -0
- package/dist/core/generation/source-analyzer.js +657 -0
- package/dist/core/generation/test-optimizer.d.ts +117 -0
- package/dist/core/generation/test-optimizer.js +328 -0
- package/dist/core/generation/types.d.ts +214 -0
- package/dist/core/generation/types.js +4 -0
- package/dist/core/index.d.ts +23 -1
- package/dist/core/index.js +39 -0
- package/dist/core/pack/validator.js +31 -1
- package/dist/core/pack-v2/index.d.ts +9 -0
- package/dist/core/pack-v2/index.js +8 -0
- package/dist/core/pack-v2/loader.d.ts +62 -0
- package/dist/core/pack-v2/loader.js +231 -0
- package/dist/core/pack-v2/migrator.d.ts +56 -0
- package/dist/core/pack-v2/migrator.js +455 -0
- package/dist/core/pack-v2/validator.d.ts +61 -0
- package/dist/core/pack-v2/validator.js +577 -0
- package/dist/core/regression/detector.d.ts +107 -0
- package/dist/core/regression/detector.js +497 -0
- package/dist/core/regression/index.d.ts +9 -0
- package/dist/core/regression/index.js +11 -0
- package/dist/core/regression/trend-analyzer.d.ts +102 -0
- package/dist/core/regression/trend-analyzer.js +345 -0
- package/dist/core/regression/types.d.ts +222 -0
- package/dist/core/regression/types.js +7 -0
- package/dist/core/regression/vault.d.ts +87 -0
- package/dist/core/regression/vault.js +289 -0
- package/dist/core/repair/engine/fixer.d.ts +24 -0
- package/dist/core/repair/engine/fixer.js +226 -0
- package/dist/core/repair/engine/suggestion-engine.d.ts +18 -0
- package/dist/core/repair/engine/suggestion-engine.js +187 -0
- package/dist/core/repair/index.d.ts +10 -0
- package/dist/core/repair/index.js +13 -0
- package/dist/core/repair/repairer.d.ts +90 -0
- package/dist/core/repair/repairer.js +284 -0
- package/dist/core/repair/types.d.ts +91 -0
- package/dist/core/repair/types.js +6 -0
- package/dist/core/repair/utils/error-analyzer.d.ts +28 -0
- package/dist/core/repair/utils/error-analyzer.js +264 -0
- package/dist/core/retry/flakiness-integration.d.ts +60 -0
- package/dist/core/retry/flakiness-integration.js +228 -0
- package/dist/core/retry/index.d.ts +14 -0
- package/dist/core/retry/index.js +16 -0
- package/dist/core/retry/retry-engine.d.ts +80 -0
- package/dist/core/retry/retry-engine.js +296 -0
- package/dist/core/retry/types.d.ts +178 -0
- package/dist/core/retry/types.js +52 -0
- package/dist/core/retry/vault.d.ts +77 -0
- package/dist/core/retry/vault.js +304 -0
- package/dist/core/runner/e2e-helpers.d.ts +102 -0
- package/dist/core/runner/e2e-helpers.js +153 -0
- package/dist/core/runner/phase3-runner.d.ts +101 -2
- package/dist/core/runner/phase3-runner.js +559 -24
- package/dist/core/self-healing/assertion-healer.d.ts +97 -0
- package/dist/core/self-healing/assertion-healer.js +371 -0
- package/dist/core/self-healing/engine.d.ts +122 -0
- package/dist/core/self-healing/engine.js +538 -0
- package/dist/core/self-healing/index.d.ts +10 -0
- package/dist/core/self-healing/index.js +11 -0
- package/dist/core/self-healing/selector-healer.d.ts +103 -0
- package/dist/core/self-healing/selector-healer.js +372 -0
- package/dist/core/self-healing/types.d.ts +152 -0
- package/dist/core/self-healing/types.js +6 -0
- package/dist/core/slo/config.d.ts +107 -0
- package/dist/core/slo/config.js +360 -0
- package/dist/core/slo/index.d.ts +11 -0
- package/dist/core/slo/index.js +15 -0
- package/dist/core/slo/sli-calculator.d.ts +92 -0
- package/dist/core/slo/sli-calculator.js +364 -0
- package/dist/core/slo/slo-tracker.d.ts +148 -0
- package/dist/core/slo/slo-tracker.js +379 -0
- package/dist/core/slo/types.d.ts +281 -0
- package/dist/core/slo/types.js +7 -0
- package/dist/core/slo/vault.d.ts +102 -0
- package/dist/core/slo/vault.js +427 -0
- package/dist/core/tui/index.d.ts +7 -0
- package/dist/core/tui/index.js +6 -0
- package/dist/core/tui/monitor.d.ts +92 -0
- package/dist/core/tui/monitor.js +271 -0
- package/dist/core/tui/renderer.d.ts +33 -0
- package/dist/core/tui/renderer.js +218 -0
- package/dist/core/tui/types.d.ts +63 -0
- package/dist/core/tui/types.js +5 -0
- package/dist/core/types/pack-v2.d.ts +425 -0
- package/dist/core/types/pack-v2.js +8 -0
- package/dist/core/vault/index.d.ts +116 -0
- package/dist/core/vault/index.js +400 -5
- package/dist/core/watch/index.d.ts +7 -0
- package/dist/core/watch/index.js +6 -0
- package/dist/core/watch/watch-mode.d.ts +213 -0
- package/dist/core/watch/watch-mode.js +389 -0
- package/dist/index.js +68 -68
- package/dist/utils/config.d.ts +5 -0
- package/dist/utils/config.js +136 -0
- package/package.json +5 -1
- package/dist/core/adapters/playwright-api.d.ts +0 -82
- package/dist/core/adapters/playwright-api.js +0 -264
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Pack v1 to v2 Migrator
|
|
3
|
+
*
|
|
4
|
+
* Migrates Pack v1 configurations to Pack v2 format.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Map v1 quality gates to v2 adapters
|
|
8
|
+
*/
|
|
9
|
+
const GATE_TO_ADAPTER = {
|
|
10
|
+
'api_smoke': 'playwright-api',
|
|
11
|
+
'ui': 'playwright-ui',
|
|
12
|
+
'a11y': 'playwright-ui',
|
|
13
|
+
'perf': 'k6-perf',
|
|
14
|
+
'sast': 'semgrep-sast',
|
|
15
|
+
'dast': 'zap-dast',
|
|
16
|
+
'secrets': 'gitleaks-secrets',
|
|
17
|
+
'deps': 'osv-deps'
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Migrate Pack v1 to v2
|
|
21
|
+
*/
|
|
22
|
+
export class PackMigrator {
|
|
23
|
+
/**
|
|
24
|
+
* Migrate a v1 pack to v2 format
|
|
25
|
+
*/
|
|
26
|
+
migrate(v1Pack) {
|
|
27
|
+
const changes = [];
|
|
28
|
+
const warnings = [];
|
|
29
|
+
// Create base v2 structure
|
|
30
|
+
const v2Pack = {
|
|
31
|
+
version: 2,
|
|
32
|
+
name: v1Pack.name,
|
|
33
|
+
description: v1Pack.description,
|
|
34
|
+
gates: {}
|
|
35
|
+
};
|
|
36
|
+
// Migrate environment variables to v2 variables
|
|
37
|
+
if (v1Pack.environment) {
|
|
38
|
+
v2Pack.variables = { ...v1Pack.environment };
|
|
39
|
+
changes.push('Renamed environment to variables');
|
|
40
|
+
}
|
|
41
|
+
// Migrate metadata
|
|
42
|
+
if (v1Pack.observability) {
|
|
43
|
+
v2Pack.metadata = {
|
|
44
|
+
observability: v1Pack.observability
|
|
45
|
+
};
|
|
46
|
+
changes.push('Moved observability to metadata.observability');
|
|
47
|
+
}
|
|
48
|
+
// Migrate gates
|
|
49
|
+
const gatesResult = this.migrateGates(v1Pack);
|
|
50
|
+
v2Pack.gates = gatesResult.gates;
|
|
51
|
+
changes.push(...gatesResult.changes);
|
|
52
|
+
warnings.push(...gatesResult.warnings);
|
|
53
|
+
// Migrate auth (from environment secrets)
|
|
54
|
+
const authResult = this.migrateAuth(v1Pack);
|
|
55
|
+
if (authResult.auth) {
|
|
56
|
+
v2Pack.auth = authResult.auth;
|
|
57
|
+
changes.push(...authResult.changes);
|
|
58
|
+
warnings.push(...authResult.warnings);
|
|
59
|
+
}
|
|
60
|
+
// Migrate hooks
|
|
61
|
+
if (v1Pack.hooks) {
|
|
62
|
+
const hooksResult = this.migrateHooks(v1Pack.hooks);
|
|
63
|
+
v2Pack.hooks = hooksResult.hooks;
|
|
64
|
+
changes.push(...hooksResult.changes);
|
|
65
|
+
warnings.push(...hooksResult.warnings);
|
|
66
|
+
}
|
|
67
|
+
// Migrate execution config
|
|
68
|
+
const execResult = this.migrateExecution(v1Pack);
|
|
69
|
+
if (execResult.execution) {
|
|
70
|
+
v2Pack.execution = execResult.execution;
|
|
71
|
+
changes.push(...execResult.changes);
|
|
72
|
+
}
|
|
73
|
+
// Migrate budgets to individual gates
|
|
74
|
+
if (v1Pack.budgets) {
|
|
75
|
+
this.migrateBudgetsToGates(v1Pack, v2Pack);
|
|
76
|
+
changes.push('Migrated budgets to individual gate configurations');
|
|
77
|
+
}
|
|
78
|
+
// Migrate security config to individual gates
|
|
79
|
+
if (v1Pack.security) {
|
|
80
|
+
this.migrateSecurityToGates(v1Pack, v2Pack);
|
|
81
|
+
changes.push('Migrated security to individual gate configurations');
|
|
82
|
+
}
|
|
83
|
+
// Migrate targets to gate configs
|
|
84
|
+
if (v1Pack.targets) {
|
|
85
|
+
this.migrateTargetsToGates(v1Pack, v2Pack);
|
|
86
|
+
changes.push('Migrated targets to individual gate configurations');
|
|
87
|
+
}
|
|
88
|
+
// Add data profile to metadata
|
|
89
|
+
if (v1Pack.data) {
|
|
90
|
+
v2Pack.metadata = v2Pack.metadata || {};
|
|
91
|
+
v2Pack.metadata.data = v1Pack.data;
|
|
92
|
+
changes.push('Moved data to metadata.data');
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
success: true,
|
|
96
|
+
migrated: v2Pack,
|
|
97
|
+
changes,
|
|
98
|
+
warnings
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Migrate gates from v1 to v2
|
|
103
|
+
*/
|
|
104
|
+
migrateGates(v1Pack) {
|
|
105
|
+
const gates = {};
|
|
106
|
+
const changes = [];
|
|
107
|
+
const warnings = [];
|
|
108
|
+
for (const gate of v1Pack.gates) {
|
|
109
|
+
const adapter = GATE_TO_ADAPTER[gate] || gate;
|
|
110
|
+
const gateName = this.sanitizeGateName(gate);
|
|
111
|
+
gates[gateName] = {
|
|
112
|
+
adapter,
|
|
113
|
+
enabled: true
|
|
114
|
+
};
|
|
115
|
+
changes.push(`Migrated gate "${gate}" to "${gateName}" with adapter "${adapter}"`);
|
|
116
|
+
}
|
|
117
|
+
// Handle a11y as separate gate from ui
|
|
118
|
+
if (v1Pack.gates.includes('ui') && v1Pack.gates.includes('a11y')) {
|
|
119
|
+
// Both exist, keep separate
|
|
120
|
+
if (gates['ui'] && gates['a11y']) {
|
|
121
|
+
gates['a11y'].adapter = 'playwright-ui';
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else if (v1Pack.gates.includes('a11y') && !v1Pack.gates.includes('ui')) {
|
|
125
|
+
// Only a11y, rename to a11y
|
|
126
|
+
if (gates['ui']) {
|
|
127
|
+
gates['a11y'] = gates['ui'];
|
|
128
|
+
delete gates['ui'];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return { gates, changes, warnings };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Migrate authentication from v1 to v2
|
|
135
|
+
*/
|
|
136
|
+
migrateAuth(v1Pack) {
|
|
137
|
+
const changes = [];
|
|
138
|
+
const warnings = [];
|
|
139
|
+
// Check for auth-related environment variables
|
|
140
|
+
const authProfiles = {};
|
|
141
|
+
let apiAuth;
|
|
142
|
+
let uiAuth;
|
|
143
|
+
// Detect JWT/Bearer token
|
|
144
|
+
const jwtToken = this.findSecretInEnv(v1Pack, ['JWT_TOKEN', 'JWT', 'TOKEN', 'BEARER', 'AUTH_TOKEN']);
|
|
145
|
+
if (jwtToken) {
|
|
146
|
+
const profileName = 'jwt-default';
|
|
147
|
+
authProfiles[profileName] = {
|
|
148
|
+
type: 'bearer',
|
|
149
|
+
config: {
|
|
150
|
+
token: jwtToken
|
|
151
|
+
},
|
|
152
|
+
cache: {
|
|
153
|
+
enabled: true,
|
|
154
|
+
ttl: 3600
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
apiAuth = profileName;
|
|
158
|
+
changes.push('Created JWT bearer auth profile from environment variables');
|
|
159
|
+
}
|
|
160
|
+
// Detect Basic auth
|
|
161
|
+
const username = this.findSecretInEnv(v1Pack, ['USERNAME', 'USER', 'API_USER']);
|
|
162
|
+
const password = this.findSecretInEnv(v1Pack, ['PASSWORD', 'PASS', 'API_PASSWORD', 'SECRET']);
|
|
163
|
+
if (username && password) {
|
|
164
|
+
const profileName = 'basic-default';
|
|
165
|
+
authProfiles[profileName] = {
|
|
166
|
+
type: 'basic',
|
|
167
|
+
config: {
|
|
168
|
+
username,
|
|
169
|
+
password
|
|
170
|
+
},
|
|
171
|
+
cache: {
|
|
172
|
+
enabled: true
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
apiAuth = profileName;
|
|
176
|
+
changes.push('Created Basic auth profile from environment variables');
|
|
177
|
+
}
|
|
178
|
+
// Detect API key
|
|
179
|
+
const apiKey = this.findSecretInEnv(v1Pack, ['API_KEY', 'APIKEY', 'X_API_KEY']);
|
|
180
|
+
if (apiKey) {
|
|
181
|
+
const profileName = 'api-key-default';
|
|
182
|
+
authProfiles[profileName] = {
|
|
183
|
+
type: 'api_key',
|
|
184
|
+
config: {
|
|
185
|
+
key: apiKey
|
|
186
|
+
},
|
|
187
|
+
cache: {
|
|
188
|
+
enabled: true
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
apiAuth = profileName;
|
|
192
|
+
changes.push('Created API key auth profile from environment variables');
|
|
193
|
+
}
|
|
194
|
+
if (Object.keys(authProfiles).length === 0) {
|
|
195
|
+
return { changes, warnings };
|
|
196
|
+
}
|
|
197
|
+
warnings.push('Review auth profiles and replace secret references with actual secret management');
|
|
198
|
+
return {
|
|
199
|
+
auth: {
|
|
200
|
+
api: apiAuth,
|
|
201
|
+
ui: uiAuth,
|
|
202
|
+
profiles: authProfiles
|
|
203
|
+
},
|
|
204
|
+
changes,
|
|
205
|
+
warnings
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Migrate hooks from v1 to v2
|
|
210
|
+
*/
|
|
211
|
+
migrateHooks(v1Hooks) {
|
|
212
|
+
const hooks = {};
|
|
213
|
+
const changes = [];
|
|
214
|
+
const warnings = [];
|
|
215
|
+
const hookTypes = ['beforeAll', 'beforeEach', 'afterEach', 'afterAll'];
|
|
216
|
+
for (const hookType of hookTypes) {
|
|
217
|
+
const hooksForType = v1Hooks[hookType];
|
|
218
|
+
if (!hooksForType || hooksForType.length === 0)
|
|
219
|
+
continue;
|
|
220
|
+
hooks[hookType] = hooksForType.map((h) => {
|
|
221
|
+
// Convert string hooks to run type
|
|
222
|
+
if (typeof h === 'string') {
|
|
223
|
+
// Detect special hook patterns
|
|
224
|
+
if (h.startsWith('docker-compose') || h.startsWith('compose')) {
|
|
225
|
+
const action = h.includes('up') ? 'up' : 'down';
|
|
226
|
+
changes.push(`Converted hook "${h}" to docker compose type`);
|
|
227
|
+
return {
|
|
228
|
+
type: 'docker',
|
|
229
|
+
compose: {
|
|
230
|
+
command: action
|
|
231
|
+
},
|
|
232
|
+
timeout: 60000
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
else if (h.startsWith('wait-on') || h.startsWith('wait_on')) {
|
|
236
|
+
const resource = h.split(/\s+/).slice(1).join(' ');
|
|
237
|
+
changes.push(`Converted hook "${h}" to wait_on type`);
|
|
238
|
+
return {
|
|
239
|
+
type: 'wait_on',
|
|
240
|
+
wait_for: {
|
|
241
|
+
resource,
|
|
242
|
+
timeout: 30000
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
return {
|
|
248
|
+
type: 'run',
|
|
249
|
+
command: h,
|
|
250
|
+
timeout: 30000
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Convert object hooks
|
|
255
|
+
if (typeof h === 'object') {
|
|
256
|
+
if (h.run) {
|
|
257
|
+
return {
|
|
258
|
+
type: 'run',
|
|
259
|
+
command: h.run,
|
|
260
|
+
timeout: h.timeout || 30000,
|
|
261
|
+
cwd: h.cwd,
|
|
262
|
+
env: h.env
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
else if (h.compose) {
|
|
266
|
+
return {
|
|
267
|
+
type: 'docker',
|
|
268
|
+
compose: {
|
|
269
|
+
command: h.compose,
|
|
270
|
+
file: h.file,
|
|
271
|
+
services: h.services
|
|
272
|
+
},
|
|
273
|
+
timeout: h.timeout || 60000
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
else if (h.wait_on) {
|
|
277
|
+
return {
|
|
278
|
+
type: 'wait_on',
|
|
279
|
+
wait_for: {
|
|
280
|
+
resource: h.wait_on,
|
|
281
|
+
timeout: h.timeout || 30000,
|
|
282
|
+
method: h.method
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return h;
|
|
288
|
+
});
|
|
289
|
+
changes.push(`Migrated hooks.${hookType} with ${hooks[hookType].length} hook(s)`);
|
|
290
|
+
}
|
|
291
|
+
return { hooks, changes, warnings };
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Migrate execution config from v1 to v2
|
|
295
|
+
*/
|
|
296
|
+
migrateExecution(v1Pack) {
|
|
297
|
+
const changes = [];
|
|
298
|
+
if (!v1Pack.execution) {
|
|
299
|
+
return { changes };
|
|
300
|
+
}
|
|
301
|
+
const v1Exec = v1Pack.execution;
|
|
302
|
+
const execution = {
|
|
303
|
+
on_failure: v1Exec.on_failure || 'stop',
|
|
304
|
+
hook_timeout_ms: v1Exec.hook_timeout_ms,
|
|
305
|
+
compose_file: v1Exec.compose_file,
|
|
306
|
+
default_timeout: v1Exec.timeout,
|
|
307
|
+
default_retries: v1Exec.max_retries,
|
|
308
|
+
limits: v1Exec.limits,
|
|
309
|
+
parallel: false
|
|
310
|
+
};
|
|
311
|
+
changes.push('Migrated execution configuration');
|
|
312
|
+
return { execution, changes };
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Migrate budgets to individual gates
|
|
316
|
+
*/
|
|
317
|
+
migrateBudgetsToGates(v1Pack, v2Pack) {
|
|
318
|
+
if (!v1Pack.budgets)
|
|
319
|
+
return;
|
|
320
|
+
for (const [gateName, gate] of Object.entries(v2Pack.gates)) {
|
|
321
|
+
const budgets = {};
|
|
322
|
+
// Map v1 budget names to v2 gate budgets
|
|
323
|
+
if (v1Pack.budgets.perf_p95_ms) {
|
|
324
|
+
budgets.p95_ms = v1Pack.budgets.perf_p95_ms;
|
|
325
|
+
}
|
|
326
|
+
if (v1Pack.budgets.a11y_min) {
|
|
327
|
+
budgets.a11y_min = v1Pack.budgets.a11y_min;
|
|
328
|
+
}
|
|
329
|
+
if (Object.keys(budgets).length > 0) {
|
|
330
|
+
gate.budgets = budgets;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Migrate security config to individual gates
|
|
336
|
+
*/
|
|
337
|
+
migrateSecurityToGates(v1Pack, v2Pack) {
|
|
338
|
+
if (!v1Pack.security)
|
|
339
|
+
return;
|
|
340
|
+
const security = v1Pack.security;
|
|
341
|
+
// SAST budget - create gate if not exists
|
|
342
|
+
if (security.sast) {
|
|
343
|
+
if (!v2Pack.gates['sast']) {
|
|
344
|
+
v2Pack.gates['sast'] = { adapter: 'semgrep-sast' };
|
|
345
|
+
}
|
|
346
|
+
v2Pack.gates['sast'].budgets = {
|
|
347
|
+
max_critical: security.sast.max_critical,
|
|
348
|
+
max_high: security.sast.max_high,
|
|
349
|
+
max_medium: security.sast.max_medium
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
// DAST budget - create gate if not exists
|
|
353
|
+
if (security.dast) {
|
|
354
|
+
if (!v2Pack.gates['dast']) {
|
|
355
|
+
v2Pack.gates['dast'] = { adapter: 'zap-dast' };
|
|
356
|
+
}
|
|
357
|
+
v2Pack.gates['dast'].budgets = {
|
|
358
|
+
max_critical: security.dast.max_critical,
|
|
359
|
+
max_high: security.dast.max_high,
|
|
360
|
+
max_medium: security.dast.max_medium
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
// Secrets budget - create gate if not exists
|
|
364
|
+
if (security.secrets) {
|
|
365
|
+
if (!v2Pack.gates['secrets']) {
|
|
366
|
+
v2Pack.gates['secrets'] = { adapter: 'gitleaks-secrets' };
|
|
367
|
+
}
|
|
368
|
+
v2Pack.gates['secrets'].budgets = {
|
|
369
|
+
max_critical: security.secrets.max_findings,
|
|
370
|
+
max_high: security.secrets.max_findings
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
// Dependencies budget - create gate if not exists
|
|
374
|
+
if (security.deps) {
|
|
375
|
+
if (!v2Pack.gates['deps']) {
|
|
376
|
+
v2Pack.gates['deps'] = { adapter: 'osv-deps' };
|
|
377
|
+
}
|
|
378
|
+
v2Pack.gates['deps'].budgets = {
|
|
379
|
+
max_critical: security.deps.max_critical,
|
|
380
|
+
max_high: security.deps.max_high,
|
|
381
|
+
max_medium: security.deps.max_medium
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Migrate targets to gate configs
|
|
387
|
+
*/
|
|
388
|
+
migrateTargetsToGates(v1Pack, v2Pack) {
|
|
389
|
+
if (!v1Pack.targets)
|
|
390
|
+
return;
|
|
391
|
+
// API target - api_smoke gets renamed to smoke in v2
|
|
392
|
+
if (v1Pack.targets.api && v2Pack.gates['smoke']) {
|
|
393
|
+
const gate = v2Pack.gates['smoke'];
|
|
394
|
+
gate.config = gate.config || {};
|
|
395
|
+
gate.config.baseUrl = v1Pack.targets.api.baseUrl;
|
|
396
|
+
gate.config.smoke = v1Pack.targets.api.smoke;
|
|
397
|
+
}
|
|
398
|
+
// Web target
|
|
399
|
+
if (v1Pack.targets.web && v2Pack.gates['ui']) {
|
|
400
|
+
const gate = v2Pack.gates['ui'];
|
|
401
|
+
gate.config = gate.config || {};
|
|
402
|
+
gate.config.baseUrl = v1Pack.targets.web.baseUrl;
|
|
403
|
+
gate.config.pages = v1Pack.targets.web.pages;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Find a secret in environment variables by trying common names
|
|
408
|
+
*/
|
|
409
|
+
findSecretInEnv(pack, names) {
|
|
410
|
+
if (!pack.environment)
|
|
411
|
+
return undefined;
|
|
412
|
+
for (const name of names) {
|
|
413
|
+
// Try exact match
|
|
414
|
+
if (pack.environment[name]) {
|
|
415
|
+
return `\${{ secrets.${name} }}`;
|
|
416
|
+
}
|
|
417
|
+
// Try lowercase
|
|
418
|
+
const lowerName = name.toLowerCase();
|
|
419
|
+
if (pack.environment[lowerName]) {
|
|
420
|
+
return `\${{ secrets.${lowerName} }}`;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return undefined;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Sanitize gate name for v2 format
|
|
427
|
+
*/
|
|
428
|
+
sanitizeGateName(gate) {
|
|
429
|
+
// Map v1 gate names to v2 conventions
|
|
430
|
+
const nameMap = {
|
|
431
|
+
'api_smoke': 'smoke',
|
|
432
|
+
'a11y': 'accessibility'
|
|
433
|
+
};
|
|
434
|
+
return nameMap[gate] || gate.replace(/-/g, '_');
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Detect pack version
|
|
438
|
+
*/
|
|
439
|
+
detectVersion(pack) {
|
|
440
|
+
if (pack.version === 2)
|
|
441
|
+
return 2;
|
|
442
|
+
if (pack.version === 1)
|
|
443
|
+
return 1;
|
|
444
|
+
// Heuristic detection
|
|
445
|
+
if (pack.gates && Array.isArray(pack.gates))
|
|
446
|
+
return 1;
|
|
447
|
+
if (pack.gates && typeof pack.gates === 'object' && !Array.isArray(pack.gates)) {
|
|
448
|
+
// Check for v2 structure
|
|
449
|
+
const firstGate = Object.values(pack.gates)[0];
|
|
450
|
+
if (firstGate?.test_files || firstGate?.adapter)
|
|
451
|
+
return 2;
|
|
452
|
+
}
|
|
453
|
+
return 'unknown';
|
|
454
|
+
}
|
|
455
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QA360 Pack v2 Validator
|
|
3
|
+
*
|
|
4
|
+
* Validates pack.yml v2 files with test_files patterns and auth profiles.
|
|
5
|
+
*/
|
|
6
|
+
import { PackValidationResultV2 } from '../types/pack-v2.js';
|
|
7
|
+
export declare class PackValidatorV2 {
|
|
8
|
+
private packPath;
|
|
9
|
+
private baseDir;
|
|
10
|
+
constructor(packPath: string);
|
|
11
|
+
/**
|
|
12
|
+
* Validate a Pack v2 configuration
|
|
13
|
+
*/
|
|
14
|
+
validate(pack: any, options?: {
|
|
15
|
+
checkFilesExist?: boolean;
|
|
16
|
+
}): Promise<PackValidationResultV2>;
|
|
17
|
+
/**
|
|
18
|
+
* Validate basic structure
|
|
19
|
+
*/
|
|
20
|
+
private validateStructure;
|
|
21
|
+
/**
|
|
22
|
+
* Validate required fields
|
|
23
|
+
*/
|
|
24
|
+
private validateRequiredFields;
|
|
25
|
+
/**
|
|
26
|
+
* Validate auth configuration
|
|
27
|
+
*/
|
|
28
|
+
private validateAuthConfig;
|
|
29
|
+
/**
|
|
30
|
+
* Validate a single auth profile
|
|
31
|
+
*/
|
|
32
|
+
private validateAuthProfile;
|
|
33
|
+
/**
|
|
34
|
+
* Validate gates configuration
|
|
35
|
+
*/
|
|
36
|
+
private validateGates;
|
|
37
|
+
/**
|
|
38
|
+
* Validate a test file pattern
|
|
39
|
+
*/
|
|
40
|
+
private validateTestPattern;
|
|
41
|
+
/**
|
|
42
|
+
* Validate gate budgets
|
|
43
|
+
*/
|
|
44
|
+
private validateGateBudgets;
|
|
45
|
+
/**
|
|
46
|
+
* Validate hooks configuration
|
|
47
|
+
*/
|
|
48
|
+
private validateHooks;
|
|
49
|
+
/**
|
|
50
|
+
* Validate a single hook
|
|
51
|
+
*/
|
|
52
|
+
private validateHook;
|
|
53
|
+
/**
|
|
54
|
+
* Validate execution configuration
|
|
55
|
+
*/
|
|
56
|
+
private validateExecutionConfig;
|
|
57
|
+
/**
|
|
58
|
+
* Validate business rules
|
|
59
|
+
*/
|
|
60
|
+
private validateBusinessRules;
|
|
61
|
+
}
|