@aws/ml-container-creator 1.0.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -1
- package/bin/cli.js +57 -0
- package/config/agent.json +16 -0
- package/infra/ci-harness/lib/ci-harness-stack.ts +43 -0
- package/package.json +5 -2
- package/pyproject.toml +3 -0
- package/servers/agent-knowledge/index.js +592 -0
- package/servers/agent-knowledge/package.json +15 -0
- package/servers/base-image-picker/index.js +65 -18
- package/servers/instance-sizer/index.js +32 -0
- package/servers/lib/catalogs/fleet-drivers.json +38 -0
- package/servers/lib/catalogs/model-arch-support.json +51 -0
- package/servers/lib/catalogs/model-servers.json +2842 -1730
- package/servers/lib/schemas/image-catalog.schema.json +12 -0
- package/src/agent/__init__.py +2 -0
- package/src/agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/agent/__pycache__/config_loader.cpython-312.pyc +0 -0
- package/src/agent/__pycache__/context.cpython-312.pyc +0 -0
- package/src/agent/__pycache__/health_check.cpython-312.pyc +0 -0
- package/src/agent/agent.py +513 -0
- package/src/agent/config_loader.py +215 -0
- package/src/agent/context.py +380 -0
- package/src/agent/data/capability-matrix.json +106 -0
- package/src/agent/health_check.py +341 -0
- package/src/agent/prompts/system.md +173 -0
- package/src/agent/requirements-agent.txt +3 -0
- package/src/app.js +6 -4
- package/src/lib/generated/cli-options.js +1 -1
- package/src/lib/generated/parameter-matrix.js +1 -1
- package/src/lib/generated/validation-rules.js +1 -1
- package/src/lib/mcp-query-runner.js +110 -3
- package/src/lib/prompt-runner.js +66 -22
- package/src/lib/template-variable-resolver.js +8 -0
- package/src/lib/train-config-builder.js +339 -0
- package/src/lib/tune-config-state.js +89 -68
- package/templates/do/.benchmark_writer.py +3 -0
- package/templates/do/.eval_helper.py +409 -0
- package/templates/do/.register_helper.py +185 -11
- package/templates/do/.train_build_request.py +102 -113
- package/templates/do/.train_helper.py +433 -0
- package/templates/do/__pycache__/.register_helper.cpython-312.pyc +0 -0
- package/templates/do/adapter +157 -0
- package/templates/do/benchmark +60 -3
- package/templates/do/config +6 -1
- package/templates/do/deploy.d/managed-inference.ejs +83 -0
- package/templates/do/evaluate +272 -0
- package/templates/do/lib/resolve-instance.sh +155 -0
- package/templates/do/register +5 -0
- package/templates/do/test +1 -0
- package/templates/do/train +879 -126
- package/templates/do/training/config.yaml +83 -11
- package/templates/do/training/dpo/accelerate_config.yaml +24 -0
- package/templates/do/training/dpo/defaults.yaml +26 -0
- package/templates/do/training/dpo/prompts.json +8 -0
- package/templates/do/training/dpo/train.py +363 -0
- package/templates/do/training/sft/accelerate_config.yaml +22 -0
- package/templates/do/training/sft/defaults.yaml +18 -0
- package/templates/do/training/sft/prompts.json +7 -0
- package/templates/do/training/sft/train.py +310 -0
- package/templates/do/tune +11 -2
- package/src/lib/auto-prompt-builder.js +0 -172
- package/src/lib/cli-handler.js +0 -529
- package/src/lib/community-reports-validator.js +0 -91
- package/src/lib/configuration-exporter.js +0 -204
- package/src/lib/dataset-slug.js +0 -152
- package/src/lib/docker-introspection-validator.js +0 -51
- package/src/lib/known-flags-validator.js +0 -200
- package/src/lib/schema-validator.js +0 -157
- package/src/lib/train-config-parser.js +0 -136
- package/src/lib/train-config-persistence.js +0 -143
- package/src/lib/train-config-validator.js +0 -112
- package/src/lib/train-feedback.js +0 -46
- package/src/lib/train-idempotency.js +0 -97
- package/src/lib/train-request-builder.js +0 -120
- package/src/lib/tune-dataset-validator.js +0 -279
- package/src/lib/tune-output-resolver.js +0 -66
- package/templates/do/.train_poll_parser.py +0 -135
- package/templates/do/.train_status_parser.py +0 -187
- /package/templates/do/training/{train.py → custom/train.py} +0 -0
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration Exporter
|
|
3
|
-
*
|
|
4
|
-
* Handles prompting users to export configurations for community contribution
|
|
5
|
-
* and saving exported configurations to files.
|
|
6
|
-
*
|
|
7
|
-
* Requirements: 7.1, 7.2, 7.3, 7.4
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import fs from 'fs';
|
|
11
|
-
import path from 'path';
|
|
12
|
-
import { runPrompts } from '../prompt-adapter.js';
|
|
13
|
-
|
|
14
|
-
export default class ConfigurationExporter {
|
|
15
|
-
constructor() {
|
|
16
|
-
// No generator dependency — uses console.log and runPrompts directly
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Detect if configuration should be offered for export
|
|
21
|
-
* Offers export for experimental or unknown configurations
|
|
22
|
-
*
|
|
23
|
-
* @param {Object} config - Configuration profile
|
|
24
|
-
* @returns {boolean} Whether to offer export
|
|
25
|
-
*
|
|
26
|
-
* Requirements: 7.1
|
|
27
|
-
*/
|
|
28
|
-
shouldOfferExport(config) {
|
|
29
|
-
if (!config) return false;
|
|
30
|
-
|
|
31
|
-
const validationLevel = config.validationLevel || 'unknown';
|
|
32
|
-
|
|
33
|
-
// Offer export for experimental or unknown configurations
|
|
34
|
-
return validationLevel === 'experimental' || validationLevel === 'unknown';
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Prompt user to export configuration
|
|
39
|
-
* Captures testing notes and deployment results
|
|
40
|
-
*
|
|
41
|
-
* @param {Object} config - Configuration profile to export
|
|
42
|
-
* @returns {Promise<Object|null>} Export data or null if user declined
|
|
43
|
-
*
|
|
44
|
-
* Requirements: 7.1, 7.2, 7.3, 7.4
|
|
45
|
-
*/
|
|
46
|
-
async promptForExport(config) {
|
|
47
|
-
if (!this.shouldOfferExport(config)) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
console.log('\n📤 Configuration Export');
|
|
52
|
-
console.log('━'.repeat(50));
|
|
53
|
-
console.log(`This configuration has validation level: ${config.validationLevel || 'unknown'}`);
|
|
54
|
-
console.log('If you successfully deploy and test this configuration, please consider');
|
|
55
|
-
console.log('sharing it with the community to help others!');
|
|
56
|
-
console.log('');
|
|
57
|
-
|
|
58
|
-
// Ask if user wants to export
|
|
59
|
-
const { wantsToExport } = await runPrompts([
|
|
60
|
-
{
|
|
61
|
-
type: 'confirm',
|
|
62
|
-
name: 'wantsToExport',
|
|
63
|
-
message: 'Would you like to export this configuration for community contribution?',
|
|
64
|
-
default: false
|
|
65
|
-
}
|
|
66
|
-
]);
|
|
67
|
-
|
|
68
|
-
if (!wantsToExport) {
|
|
69
|
-
console.log('Skipping export. You can always export later after testing.');
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Collect testing information
|
|
74
|
-
const exportData = await runPrompts([
|
|
75
|
-
{
|
|
76
|
-
type: 'input',
|
|
77
|
-
name: 'instanceType',
|
|
78
|
-
message: 'What instance type did you use (or plan to use) for testing?',
|
|
79
|
-
default: config.recommendedInstanceTypes?.[0] || 'ml.g5.xlarge',
|
|
80
|
-
validate: (input) => {
|
|
81
|
-
if (!input || input.trim() === '') {
|
|
82
|
-
return 'Instance type is required';
|
|
83
|
-
}
|
|
84
|
-
return true;
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
type: 'confirm',
|
|
89
|
-
name: 'deploymentSuccess',
|
|
90
|
-
message: 'Did the deployment succeed?',
|
|
91
|
-
default: false
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
type: 'confirm',
|
|
95
|
-
name: 'inferenceSuccess',
|
|
96
|
-
message: 'Did inference work correctly?',
|
|
97
|
-
default: false,
|
|
98
|
-
when: (answers) => answers.deploymentSuccess
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
type: 'input',
|
|
102
|
-
name: 'testingNotes',
|
|
103
|
-
message: 'Any notes about your testing experience? (optional)',
|
|
104
|
-
default: ''
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
type: 'input',
|
|
108
|
-
name: 'testerName',
|
|
109
|
-
message: 'Your name or GitHub handle (optional, for attribution):',
|
|
110
|
-
default: 'Anonymous'
|
|
111
|
-
}
|
|
112
|
-
]);
|
|
113
|
-
|
|
114
|
-
return exportData;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Save exported configuration to file
|
|
119
|
-
* Creates a file in the project directory with export instructions
|
|
120
|
-
*
|
|
121
|
-
* @param {Object} exportResult - Result from ConfigurationManager.exportConfiguration()
|
|
122
|
-
* @param {string} destinationPath - Destination directory path
|
|
123
|
-
*
|
|
124
|
-
* Requirements: 7.2, 7.3, 7.5, 7.6
|
|
125
|
-
*/
|
|
126
|
-
saveExportToFile(exportResult, destinationPath) {
|
|
127
|
-
const { registryType, configEntry, submissionInstructions, metadata } = exportResult;
|
|
128
|
-
|
|
129
|
-
// Create export filename
|
|
130
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
|
|
131
|
-
const filename = `config-export-${registryType}-${timestamp}.md`;
|
|
132
|
-
const filepath = path.join(destinationPath, filename);
|
|
133
|
-
|
|
134
|
-
// Create export file content
|
|
135
|
-
const content = `${submissionInstructions}
|
|
136
|
-
|
|
137
|
-
## Export Metadata
|
|
138
|
-
|
|
139
|
-
\`\`\`json
|
|
140
|
-
${JSON.stringify(metadata, null, 2)}
|
|
141
|
-
\`\`\`
|
|
142
|
-
|
|
143
|
-
## Configuration Entry (JSON)
|
|
144
|
-
|
|
145
|
-
\`\`\`json
|
|
146
|
-
${JSON.stringify(configEntry, null, 2)}
|
|
147
|
-
\`\`\`
|
|
148
|
-
`;
|
|
149
|
-
|
|
150
|
-
// Write file
|
|
151
|
-
fs.writeFileSync(filepath, content, 'utf-8');
|
|
152
|
-
|
|
153
|
-
return filename;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Display export success message
|
|
158
|
-
*
|
|
159
|
-
* @param {string} filename - Name of exported file
|
|
160
|
-
*/
|
|
161
|
-
displayExportSuccess(filename) {
|
|
162
|
-
console.log('');
|
|
163
|
-
console.log('✅ Configuration exported successfully!');
|
|
164
|
-
console.log(`📄 Export saved to: ${filename}`);
|
|
165
|
-
console.log('');
|
|
166
|
-
console.log('Next steps:');
|
|
167
|
-
console.log('1. Review the export file');
|
|
168
|
-
console.log('2. Test your deployment');
|
|
169
|
-
console.log('3. Submit via GitHub issue or pull request');
|
|
170
|
-
console.log('4. Help the community! 🎉');
|
|
171
|
-
console.log('');
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Complete export workflow
|
|
176
|
-
* Prompts user, exports configuration, and saves to file
|
|
177
|
-
*
|
|
178
|
-
* @param {Object} config - Configuration profile
|
|
179
|
-
* @param {Object} configurationManager - ConfigurationManager instance
|
|
180
|
-
* @param {string} destinationPath - Destination directory path
|
|
181
|
-
* @returns {Promise<boolean>} Whether export was completed
|
|
182
|
-
*
|
|
183
|
-
* Requirements: 7.1, 7.2, 7.3, 7.4, 7.5, 7.6
|
|
184
|
-
*/
|
|
185
|
-
async exportWorkflow(config, configurationManager, destinationPath) {
|
|
186
|
-
// Prompt for export
|
|
187
|
-
const exportData = await this.promptForExport(config);
|
|
188
|
-
|
|
189
|
-
if (!exportData) {
|
|
190
|
-
return false;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Export configuration
|
|
194
|
-
const exportResult = configurationManager.exportConfiguration(config, exportData);
|
|
195
|
-
|
|
196
|
-
// Save to file
|
|
197
|
-
const filename = this.saveExportToFile(exportResult, destinationPath);
|
|
198
|
-
|
|
199
|
-
// Display success message
|
|
200
|
-
this.displayExportSuccess(filename);
|
|
201
|
-
|
|
202
|
-
return true;
|
|
203
|
-
}
|
|
204
|
-
}
|
package/src/lib/dataset-slug.js
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
-
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Dataset Slug Derivation
|
|
6
|
-
*
|
|
7
|
-
* Derives a deterministic, short slug from a dataset URI for use in
|
|
8
|
-
* tuning-job-aware adapter naming conventions.
|
|
9
|
-
*
|
|
10
|
-
* Slugification rules:
|
|
11
|
-
* - Lowercase
|
|
12
|
-
* - Strip non-alphanumeric characters (keep hyphens)
|
|
13
|
-
* - Truncate to 20 characters
|
|
14
|
-
* - Replace consecutive hyphens with single hyphen
|
|
15
|
-
* - Strip leading/trailing hyphens
|
|
16
|
-
*
|
|
17
|
-
* Examples:
|
|
18
|
-
* hf://org/name -> "name"
|
|
19
|
-
* hf://tatsu-lab/alpaca -> "alpaca"
|
|
20
|
-
* hf://Open-Orca/OpenOrca -> "openorca"
|
|
21
|
-
* s3://bucket/path/file.jsonl -> "file"
|
|
22
|
-
*
|
|
23
|
-
* Requirements: US-4 (AC-4.2)
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Derive a dataset slug from a dataset URI.
|
|
28
|
-
*
|
|
29
|
-
* @param {string} datasetUri - Dataset URI (s3://... or hf://...)
|
|
30
|
-
* @returns {string} The derived slug, or empty string if extraction fails
|
|
31
|
-
*/
|
|
32
|
-
export function deriveDatasetSlug(datasetUri) {
|
|
33
|
-
if (!datasetUri || typeof datasetUri !== 'string') {
|
|
34
|
-
return '';
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
let rawName = '';
|
|
38
|
-
|
|
39
|
-
if (datasetUri.startsWith('hf://')) {
|
|
40
|
-
// hf://org/name[/split][?file=pattern]
|
|
41
|
-
// Extract the dataset name (second path component)
|
|
42
|
-
const hfPath = datasetUri.slice(5); // remove "hf://"
|
|
43
|
-
const withoutQuery = hfPath.split('?')[0]; // remove ?file=...
|
|
44
|
-
const parts = withoutQuery.split('/');
|
|
45
|
-
// parts[0] = org, parts[1] = name, parts[2+] = split
|
|
46
|
-
rawName = parts[1] || parts[0] || '';
|
|
47
|
-
} else if (datasetUri.startsWith('s3://')) {
|
|
48
|
-
// s3://bucket/path/file.jsonl -> slug from filename (without extension)
|
|
49
|
-
const s3Path = datasetUri.slice(5); // remove "s3://"
|
|
50
|
-
const parts = s3Path.split('/');
|
|
51
|
-
const filename = parts[parts.length - 1] || '';
|
|
52
|
-
// Remove file extension
|
|
53
|
-
const dotIndex = filename.lastIndexOf('.');
|
|
54
|
-
rawName = dotIndex > 0 ? filename.substring(0, dotIndex) : filename;
|
|
55
|
-
} else {
|
|
56
|
-
// Unknown format — try to extract last path component
|
|
57
|
-
const parts = datasetUri.split('/');
|
|
58
|
-
rawName = parts[parts.length - 1] || '';
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return slugify(rawName);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Apply slugification rules to a raw name.
|
|
66
|
-
*
|
|
67
|
-
* @param {string} raw - Raw name to slugify
|
|
68
|
-
* @returns {string} Slugified string
|
|
69
|
-
*/
|
|
70
|
-
export function slugify(raw) {
|
|
71
|
-
if (!raw) return '';
|
|
72
|
-
|
|
73
|
-
let slug = raw
|
|
74
|
-
.toLowerCase() // lowercase
|
|
75
|
-
.replace(/[^a-z0-9-]/g, '') // strip non-alphanumeric (keep hyphens)
|
|
76
|
-
.replace(/-{2,}/g, '-') // replace consecutive hyphens
|
|
77
|
-
.replace(/^-+/, '') // strip leading hyphens
|
|
78
|
-
.replace(/-+$/, ''); // strip trailing hyphens
|
|
79
|
-
|
|
80
|
-
// Truncate to 20 chars
|
|
81
|
-
if (slug.length > 20) {
|
|
82
|
-
slug = slug.substring(0, 20);
|
|
83
|
-
// Don't end on a hyphen after truncation
|
|
84
|
-
slug = slug.replace(/-+$/, '');
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return slug;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Resolve a --from-tune argument to the appropriate config variable name.
|
|
92
|
-
*
|
|
93
|
-
* Resolution rules:
|
|
94
|
-
* - No arg (empty/null) -> TUNE_OUTPUT_PATH_LATEST
|
|
95
|
-
* - technique only (e.g., "sft") -> TUNE_ADAPTER_PATH_SFT
|
|
96
|
-
* - technique-dataset compound (e.g., "sft-alpaca") -> TUNE_ADAPTER_PATH_SFT_ALPACA
|
|
97
|
-
*
|
|
98
|
-
* @param {string} fromTuneArg - The --from-tune argument value
|
|
99
|
-
* @param {function} configVarExists - Function that checks if a config var exists
|
|
100
|
-
* @returns {{ varName: string, technique: string, slug: string, isCompound: boolean, fallback: string|null }}
|
|
101
|
-
*/
|
|
102
|
-
export function resolveFromTuneVar(fromTuneArg, configVarExists) {
|
|
103
|
-
if (!fromTuneArg) {
|
|
104
|
-
return {
|
|
105
|
-
varName: 'TUNE_OUTPUT_PATH_LATEST',
|
|
106
|
-
technique: '',
|
|
107
|
-
slug: '',
|
|
108
|
-
isCompound: false,
|
|
109
|
-
fallback: null
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const upper = fromTuneArg.toUpperCase();
|
|
114
|
-
|
|
115
|
-
// Check if argument contains a hyphen — potential compound key
|
|
116
|
-
const hyphenIndex = fromTuneArg.indexOf('-');
|
|
117
|
-
if (hyphenIndex > 0) {
|
|
118
|
-
const technique = fromTuneArg.substring(0, hyphenIndex);
|
|
119
|
-
const slug = fromTuneArg.substring(hyphenIndex + 1);
|
|
120
|
-
const techniqueUpper = technique.toUpperCase();
|
|
121
|
-
const slugUpper = slug.toUpperCase().replace(/-/g, '_');
|
|
122
|
-
const compoundVar = `TUNE_ADAPTER_PATH_${techniqueUpper}_${slugUpper}`;
|
|
123
|
-
|
|
124
|
-
if (configVarExists(compoundVar)) {
|
|
125
|
-
return {
|
|
126
|
-
varName: compoundVar,
|
|
127
|
-
technique,
|
|
128
|
-
slug,
|
|
129
|
-
isCompound: true,
|
|
130
|
-
fallback: null
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Compound key doesn't exist — fallback to technique-only
|
|
135
|
-
return {
|
|
136
|
-
varName: `TUNE_ADAPTER_PATH_${techniqueUpper}`,
|
|
137
|
-
technique,
|
|
138
|
-
slug,
|
|
139
|
-
isCompound: false,
|
|
140
|
-
fallback: compoundVar // the compound var that was tried but didn't exist
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// No hyphen — technique-only
|
|
145
|
-
return {
|
|
146
|
-
varName: `TUNE_ADAPTER_PATH_${upper}`,
|
|
147
|
-
technique: fromTuneArg,
|
|
148
|
-
slug: '',
|
|
149
|
-
isCompound: false,
|
|
150
|
-
fallback: null
|
|
151
|
-
};
|
|
152
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Docker Introspection Validator Strategy (Opt-in)
|
|
3
|
-
*
|
|
4
|
-
* Validates environment variables by introspecting Docker images.
|
|
5
|
-
* This is an experimental strategy that requires Docker to be available.
|
|
6
|
-
*
|
|
7
|
-
* Requirements: 13.11, 13.18
|
|
8
|
-
*/
|
|
9
|
-
export default class DockerIntrospectionValidator {
|
|
10
|
-
/**
|
|
11
|
-
* Create a new DockerIntrospectionValidator.
|
|
12
|
-
*/
|
|
13
|
-
constructor() {
|
|
14
|
-
this.name = 'docker-introspection';
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Validate environment variables using Docker introspection.
|
|
19
|
-
*
|
|
20
|
-
* Note: This is an experimental feature and not tested in CI/CD.
|
|
21
|
-
* It requires Docker to be available and the framework image to be pullable.
|
|
22
|
-
*
|
|
23
|
-
* @param {string} framework - Framework name
|
|
24
|
-
* @param {string} version - Framework version
|
|
25
|
-
* @param {Object} envVars - Environment variables to validate
|
|
26
|
-
* @returns {Object} ValidationResult
|
|
27
|
-
* @returns {Array<Object>} ValidationResult.warnings - Warning messages
|
|
28
|
-
* @returns {Array<Object>} ValidationResult.errors - Error messages
|
|
29
|
-
*/
|
|
30
|
-
async validate(_framework, _version, _envVars) {
|
|
31
|
-
const warnings = [];
|
|
32
|
-
const errors = [];
|
|
33
|
-
|
|
34
|
-
// Add experimental warning
|
|
35
|
-
warnings.push({
|
|
36
|
-
key: null,
|
|
37
|
-
message: 'Docker introspection validation is experimental and not tested in CI/CD'
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// Docker introspection implementation would go here
|
|
41
|
-
// This is a placeholder for the opt-in experimental feature
|
|
42
|
-
// Actual implementation would:
|
|
43
|
-
// 1. Pull the framework Docker image
|
|
44
|
-
// 2. Run a container with the env vars
|
|
45
|
-
// 3. Check if the container starts successfully
|
|
46
|
-
// 4. Parse any error messages from the container logs
|
|
47
|
-
|
|
48
|
-
// For now, just return the experimental warning
|
|
49
|
-
return { warnings, errors };
|
|
50
|
-
}
|
|
51
|
-
}
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Known Flags Validator Strategy
|
|
3
|
-
*
|
|
4
|
-
* Validates environment variables against a registry of known flags for each framework.
|
|
5
|
-
* Checks variable names, types, and range constraints.
|
|
6
|
-
*
|
|
7
|
-
* Requirements: 13.9, 13.13, 13.14, 13.15
|
|
8
|
-
*/
|
|
9
|
-
export default class KnownFlagsValidator {
|
|
10
|
-
/**
|
|
11
|
-
* Create a new KnownFlagsValidator.
|
|
12
|
-
*
|
|
13
|
-
* @param {Object} frameworkFlags - Framework flags registry
|
|
14
|
-
*/
|
|
15
|
-
constructor(frameworkFlags = {}) {
|
|
16
|
-
this.frameworkFlags = frameworkFlags;
|
|
17
|
-
this.name = 'known-flags-registry';
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Validate environment variables against known flags registry.
|
|
22
|
-
*
|
|
23
|
-
* @param {string} framework - Framework name
|
|
24
|
-
* @param {string} version - Framework version
|
|
25
|
-
* @param {Object} envVars - Environment variables to validate
|
|
26
|
-
* @returns {Object} ValidationResult
|
|
27
|
-
* @returns {Array<Object>} ValidationResult.warnings - Warning messages
|
|
28
|
-
* @returns {Array<Object>} ValidationResult.errors - Error messages
|
|
29
|
-
*/
|
|
30
|
-
async validate(framework, version, envVars) {
|
|
31
|
-
const warnings = [];
|
|
32
|
-
const errors = [];
|
|
33
|
-
|
|
34
|
-
// Get known flags for this framework version
|
|
35
|
-
const knownFlags = this.getKnownFlags(framework, version);
|
|
36
|
-
|
|
37
|
-
if (!knownFlags || Object.keys(knownFlags).length === 0) {
|
|
38
|
-
// No known flags data available
|
|
39
|
-
return { warnings, errors };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Validate each environment variable
|
|
43
|
-
for (const [key, value] of Object.entries(envVars)) {
|
|
44
|
-
const flagSpec = knownFlags[key];
|
|
45
|
-
|
|
46
|
-
if (!flagSpec) {
|
|
47
|
-
// Unknown flag - might be valid but not in our registry
|
|
48
|
-
warnings.push({
|
|
49
|
-
key,
|
|
50
|
-
message: `Unknown environment variable '${key}' for ${framework} ${version}`
|
|
51
|
-
});
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Check if flag is deprecated
|
|
56
|
-
if (flagSpec.deprecated) {
|
|
57
|
-
warnings.push({
|
|
58
|
-
key,
|
|
59
|
-
message: `Environment variable '${key}' is deprecated. ${flagSpec.deprecationMessage || ''}`
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
if (flagSpec.replacement) {
|
|
63
|
-
warnings.push({
|
|
64
|
-
key,
|
|
65
|
-
message: `Consider using '${flagSpec.replacement}' instead of '${key}'`
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Validate type
|
|
71
|
-
const typeError = this.validateType(key, value, flagSpec.type);
|
|
72
|
-
if (typeError) {
|
|
73
|
-
errors.push(typeError);
|
|
74
|
-
continue; // Skip range validation if type is wrong
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Validate range constraints
|
|
78
|
-
const rangeError = this.validateRange(key, value, flagSpec);
|
|
79
|
-
if (rangeError) {
|
|
80
|
-
errors.push(rangeError);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return { warnings, errors };
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Get known flags for a framework version.
|
|
89
|
-
*
|
|
90
|
-
* @param {string} framework - Framework name
|
|
91
|
-
* @param {string} version - Framework version
|
|
92
|
-
* @returns {Object|null} Known flags specification
|
|
93
|
-
* @private
|
|
94
|
-
*/
|
|
95
|
-
getKnownFlags(framework, version) {
|
|
96
|
-
if (!this.frameworkFlags[framework]) {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Try exact version match first
|
|
101
|
-
if (this.frameworkFlags[framework][version]) {
|
|
102
|
-
return this.frameworkFlags[framework][version];
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Try to find closest version (simplified - just use 'default' if available)
|
|
106
|
-
if (this.frameworkFlags[framework].default) {
|
|
107
|
-
return this.frameworkFlags[framework].default;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return null;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Validate environment variable type.
|
|
115
|
-
*
|
|
116
|
-
* @param {string} key - Variable name
|
|
117
|
-
* @param {string} value - Variable value
|
|
118
|
-
* @param {string} expectedType - Expected type (integer, float, string, boolean)
|
|
119
|
-
* @returns {Object|null} Error object or null if valid
|
|
120
|
-
* @private
|
|
121
|
-
*/
|
|
122
|
-
validateType(key, value, expectedType) {
|
|
123
|
-
if (!expectedType) {
|
|
124
|
-
return null; // No type constraint
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
switch (expectedType) {
|
|
128
|
-
case 'integer':
|
|
129
|
-
if (!/^-?\d+$/.test(value)) {
|
|
130
|
-
return {
|
|
131
|
-
key,
|
|
132
|
-
message: `Environment variable '${key}' must be an integer, got '${value}'`
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
break;
|
|
136
|
-
|
|
137
|
-
case 'float':
|
|
138
|
-
if (!/^-?\d+(\.\d+)?$/.test(value)) {
|
|
139
|
-
return {
|
|
140
|
-
key,
|
|
141
|
-
message: `Environment variable '${key}' must be a float, got '${value}'`
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
break;
|
|
145
|
-
|
|
146
|
-
case 'boolean':
|
|
147
|
-
if (!['true', 'false', '0', '1', 'yes', 'no'].includes(value.toLowerCase())) {
|
|
148
|
-
return {
|
|
149
|
-
key,
|
|
150
|
-
message: `Environment variable '${key}' must be a boolean (true/false, 0/1, yes/no), got '${value}'`
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
break;
|
|
154
|
-
|
|
155
|
-
case 'string':
|
|
156
|
-
// String is always valid
|
|
157
|
-
break;
|
|
158
|
-
|
|
159
|
-
default:
|
|
160
|
-
// Unknown type - skip validation
|
|
161
|
-
break;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Validate environment variable range constraints.
|
|
169
|
-
*
|
|
170
|
-
* @param {string} key - Variable name
|
|
171
|
-
* @param {string} value - Variable value
|
|
172
|
-
* @param {Object} flagSpec - Flag specification with min/max constraints
|
|
173
|
-
* @returns {Object|null} Error object or null if valid
|
|
174
|
-
* @private
|
|
175
|
-
*/
|
|
176
|
-
validateRange(key, value, flagSpec) {
|
|
177
|
-
// Only validate range for numeric types
|
|
178
|
-
if (flagSpec.type !== 'integer' && flagSpec.type !== 'float') {
|
|
179
|
-
return null;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const numValue = parseFloat(value);
|
|
183
|
-
|
|
184
|
-
if (flagSpec.min !== undefined && numValue < flagSpec.min) {
|
|
185
|
-
return {
|
|
186
|
-
key,
|
|
187
|
-
message: `Environment variable '${key}' must be >= ${flagSpec.min}, got ${value}`
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (flagSpec.max !== undefined && numValue > flagSpec.max) {
|
|
192
|
-
return {
|
|
193
|
-
key,
|
|
194
|
-
message: `Environment variable '${key}' must be <= ${flagSpec.max}, got ${value}`
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return null;
|
|
199
|
-
}
|
|
200
|
-
}
|