aios-core 2.1.4 → 2.1.6
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/.aios-core/development/tasks/analyze-brownfield.md +456 -456
- package/.aios-core/development/tasks/setup-project-docs.md +440 -444
- package/.aios-core/infrastructure/scripts/documentation-integrity/brownfield-analyzer.js +501 -501
- package/.aios-core/infrastructure/scripts/documentation-integrity/config-generator.js +368 -329
- package/.aios-core/infrastructure/scripts/documentation-integrity/deployment-config-loader.js +308 -282
- package/.aios-core/infrastructure/scripts/documentation-integrity/doc-generator.js +331 -331
- package/.aios-core/infrastructure/scripts/documentation-integrity/gitignore-generator.js +312 -312
- package/.aios-core/infrastructure/scripts/documentation-integrity/index.js +74 -74
- package/.aios-core/infrastructure/scripts/documentation-integrity/mode-detector.js +389 -358
- package/.aios-core/infrastructure/scripts/llm-routing/install-llm-routing.js +6 -6
- package/.aios-core/infrastructure/templates/core-config/core-config-brownfield.tmpl.yaml +176 -182
- package/.aios-core/infrastructure/templates/core-config/core-config-greenfield.tmpl.yaml +127 -127
- package/.aios-core/infrastructure/templates/project-docs/coding-standards-tmpl.md +346 -346
- package/.aios-core/infrastructure/templates/project-docs/source-tree-tmpl.md +177 -177
- package/.aios-core/infrastructure/templates/project-docs/tech-stack-tmpl.md +267 -267
- package/package.json +1 -1
- package/packages/installer/src/config/templates/env-template.js +2 -2
- package/packages/installer/src/wizard/wizard.js +1 -1
- package/packages/installer/tests/integration/environment-configuration.test.js +2 -1
- package/packages/installer/tests/unit/env-template.test.js +3 -2
- package/src/wizard/index.js +2 -2
- package/.aios-core/development/tasks/validate-structure.md +0 -243
- package/.aios-core/infrastructure/scripts/source-tree-guardian/index.js +0 -375
- package/.aios-core/infrastructure/scripts/source-tree-guardian/manifest-generator.js +0 -410
- package/.aios-core/infrastructure/scripts/source-tree-guardian/rules/naming-rules.yaml +0 -285
- package/.aios-core/infrastructure/scripts/source-tree-guardian/rules/placement-rules.yaml +0 -262
- package/.aios-core/infrastructure/scripts/source-tree-guardian/validator.js +0 -468
package/.aios-core/infrastructure/scripts/documentation-integrity/deployment-config-loader.js
CHANGED
|
@@ -1,282 +1,308 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deployment Config Loader
|
|
3
|
-
*
|
|
4
|
-
* Shared utility for loading deployment configuration from core-config.yaml.
|
|
5
|
-
* Implements the Configuration-Driven Architecture pattern.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* const { loadDeploymentConfig } = require('./deployment-config-loader');
|
|
9
|
-
* const config = loadDeploymentConfig(projectRoot);
|
|
10
|
-
*
|
|
11
|
-
* @module documentation-integrity/deployment-config-loader
|
|
12
|
-
* @version 1.0.0
|
|
13
|
-
* @story 6.9
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
const fs = require('fs');
|
|
17
|
-
const path = require('path');
|
|
18
|
-
const yaml = require('yaml');
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Default deployment configuration
|
|
22
|
-
* Used when core-config.yaml doesn't exist or deployment section is missing
|
|
23
|
-
*
|
|
24
|
-
* @type {Object}
|
|
25
|
-
*/
|
|
26
|
-
const DEFAULT_DEPLOYMENT_CONFIG = {
|
|
27
|
-
workflow: 'staging-first',
|
|
28
|
-
|
|
29
|
-
branches: {
|
|
30
|
-
staging_targets: ['feature/*', 'fix/*', 'docs/*', 'chore/*', 'refactor/*', 'test/*'],
|
|
31
|
-
production_targets: ['hotfix/*'],
|
|
32
|
-
staging_branch: 'staging',
|
|
33
|
-
production_branch: 'main',
|
|
34
|
-
default_target: 'staging',
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
environments: {
|
|
38
|
-
staging: {
|
|
39
|
-
name: 'Staging',
|
|
40
|
-
auto_deploy: true,
|
|
41
|
-
platform: null,
|
|
42
|
-
url: null,
|
|
43
|
-
promotion_message: 'After validation, create PR to main for production',
|
|
44
|
-
},
|
|
45
|
-
production: {
|
|
46
|
-
name: 'Production',
|
|
47
|
-
auto_deploy: true,
|
|
48
|
-
platform: null,
|
|
49
|
-
url: null,
|
|
50
|
-
promotion_message: 'This is the final production deployment',
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
quality_gates: {
|
|
55
|
-
lint: true,
|
|
56
|
-
typecheck: true,
|
|
57
|
-
tests: true,
|
|
58
|
-
security_scan: false,
|
|
59
|
-
min_coverage: 50,
|
|
60
|
-
},
|
|
61
|
-
|
|
62
|
-
pr_defaults: {
|
|
63
|
-
auto_assign_reviewers: false,
|
|
64
|
-
draft_by_default: false,
|
|
65
|
-
include_deployment_info: true,
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Loads deployment configuration from core-config.yaml
|
|
71
|
-
*
|
|
72
|
-
* @param {string} projectRoot - Project root directory
|
|
73
|
-
* @returns {Object} Deployment configuration (merged with defaults)
|
|
74
|
-
*/
|
|
75
|
-
function loadDeploymentConfig(projectRoot) {
|
|
76
|
-
const configPath = path.join(projectRoot, '.aios-core', 'core-config.yaml');
|
|
77
|
-
|
|
78
|
-
if (!fs.existsSync(configPath)) {
|
|
79
|
-
console.warn(`[deployment-config-loader] core-config.yaml not found at ${configPath}`);
|
|
80
|
-
console.warn('[deployment-config-loader] Using default deployment configuration');
|
|
81
|
-
return { ...DEFAULT_DEPLOYMENT_CONFIG };
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
86
|
-
const config = yaml.parse(configContent);
|
|
87
|
-
|
|
88
|
-
if (!config || !config.deployment) {
|
|
89
|
-
console.warn('[deployment-config-loader] No deployment section in core-config.yaml');
|
|
90
|
-
console.warn('[deployment-config-loader] Using default deployment configuration');
|
|
91
|
-
return { ...DEFAULT_DEPLOYMENT_CONFIG };
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Deep merge with defaults to ensure all required fields exist
|
|
95
|
-
return deepMerge(DEFAULT_DEPLOYMENT_CONFIG, config.deployment);
|
|
96
|
-
} catch (error) {
|
|
97
|
-
console.error(`[deployment-config-loader] Error loading config: ${error.message}`);
|
|
98
|
-
console.warn('[deployment-config-loader] Using default deployment configuration');
|
|
99
|
-
return { ...DEFAULT_DEPLOYMENT_CONFIG };
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Loads project configuration from core-config.yaml
|
|
105
|
-
*
|
|
106
|
-
* @param {string} projectRoot - Project root directory
|
|
107
|
-
* @returns {Object|null} Project configuration or null if not found
|
|
108
|
-
*/
|
|
109
|
-
function loadProjectConfig(projectRoot) {
|
|
110
|
-
const configPath = path.join(projectRoot, '.aios-core', 'core-config.yaml');
|
|
111
|
-
|
|
112
|
-
if (!fs.existsSync(configPath)) {
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
try {
|
|
117
|
-
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
118
|
-
const config = yaml.parse(configContent);
|
|
119
|
-
return config.project || null;
|
|
120
|
-
} catch (error) {
|
|
121
|
-
console.error(`[deployment-config-loader] Error loading project config: ${error.message}`);
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Gets the target branch for a given source branch
|
|
128
|
-
*
|
|
129
|
-
* @param {string} sourceBranch - Source branch name
|
|
130
|
-
* @param {Object} deploymentConfig - Deployment configuration
|
|
131
|
-
* @returns {string} Target branch name
|
|
132
|
-
*/
|
|
133
|
-
function getTargetBranch(sourceBranch, deploymentConfig) {
|
|
134
|
-
const { branches, workflow } = deploymentConfig;
|
|
135
|
-
|
|
136
|
-
// Check if it's a staging branch (used for promotion)
|
|
137
|
-
if (sourceBranch === branches.staging_branch) {
|
|
138
|
-
return branches.production_branch;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Check production targets (hotfix/* etc.)
|
|
142
|
-
for (const pattern of branches.production_targets || []) {
|
|
143
|
-
if (matchesBranchPattern(sourceBranch, pattern)) {
|
|
144
|
-
return branches.production_branch;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Check staging targets
|
|
149
|
-
for (const pattern of branches.staging_targets || []) {
|
|
150
|
-
if (matchesBranchPattern(sourceBranch, pattern)) {
|
|
151
|
-
// If direct-to-main workflow, target production
|
|
152
|
-
if (workflow === 'direct-to-main') {
|
|
153
|
-
return branches.production_branch;
|
|
154
|
-
}
|
|
155
|
-
return branches.staging_branch || branches.production_branch;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Default target
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
*
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
* @
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Deployment Config Loader
|
|
3
|
+
*
|
|
4
|
+
* Shared utility for loading deployment configuration from core-config.yaml.
|
|
5
|
+
* Implements the Configuration-Driven Architecture pattern.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const { loadDeploymentConfig } = require('./deployment-config-loader');
|
|
9
|
+
* const config = loadDeploymentConfig(projectRoot);
|
|
10
|
+
*
|
|
11
|
+
* @module documentation-integrity/deployment-config-loader
|
|
12
|
+
* @version 1.0.0
|
|
13
|
+
* @story 6.9
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const yaml = require('yaml');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Default deployment configuration
|
|
22
|
+
* Used when core-config.yaml doesn't exist or deployment section is missing
|
|
23
|
+
*
|
|
24
|
+
* @type {Object}
|
|
25
|
+
*/
|
|
26
|
+
const DEFAULT_DEPLOYMENT_CONFIG = {
|
|
27
|
+
workflow: 'staging-first',
|
|
28
|
+
|
|
29
|
+
branches: {
|
|
30
|
+
staging_targets: ['feature/*', 'fix/*', 'docs/*', 'chore/*', 'refactor/*', 'test/*'],
|
|
31
|
+
production_targets: ['hotfix/*'],
|
|
32
|
+
staging_branch: 'staging',
|
|
33
|
+
production_branch: 'main',
|
|
34
|
+
default_target: 'staging',
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
environments: {
|
|
38
|
+
staging: {
|
|
39
|
+
name: 'Staging',
|
|
40
|
+
auto_deploy: true,
|
|
41
|
+
platform: null,
|
|
42
|
+
url: null,
|
|
43
|
+
promotion_message: 'After validation, create PR to main for production',
|
|
44
|
+
},
|
|
45
|
+
production: {
|
|
46
|
+
name: 'Production',
|
|
47
|
+
auto_deploy: true,
|
|
48
|
+
platform: null,
|
|
49
|
+
url: null,
|
|
50
|
+
promotion_message: 'This is the final production deployment',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
quality_gates: {
|
|
55
|
+
lint: true,
|
|
56
|
+
typecheck: true,
|
|
57
|
+
tests: true,
|
|
58
|
+
security_scan: false,
|
|
59
|
+
min_coverage: 50,
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
pr_defaults: {
|
|
63
|
+
auto_assign_reviewers: false,
|
|
64
|
+
draft_by_default: false,
|
|
65
|
+
include_deployment_info: true,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Loads deployment configuration from core-config.yaml
|
|
71
|
+
*
|
|
72
|
+
* @param {string} projectRoot - Project root directory
|
|
73
|
+
* @returns {Object} Deployment configuration (merged with defaults)
|
|
74
|
+
*/
|
|
75
|
+
function loadDeploymentConfig(projectRoot) {
|
|
76
|
+
const configPath = path.join(projectRoot, '.aios-core', 'core-config.yaml');
|
|
77
|
+
|
|
78
|
+
if (!fs.existsSync(configPath)) {
|
|
79
|
+
console.warn(`[deployment-config-loader] core-config.yaml not found at ${configPath}`);
|
|
80
|
+
console.warn('[deployment-config-loader] Using default deployment configuration');
|
|
81
|
+
return { ...DEFAULT_DEPLOYMENT_CONFIG };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
86
|
+
const config = yaml.parse(configContent);
|
|
87
|
+
|
|
88
|
+
if (!config || !config.deployment) {
|
|
89
|
+
console.warn('[deployment-config-loader] No deployment section in core-config.yaml');
|
|
90
|
+
console.warn('[deployment-config-loader] Using default deployment configuration');
|
|
91
|
+
return { ...DEFAULT_DEPLOYMENT_CONFIG };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Deep merge with defaults to ensure all required fields exist
|
|
95
|
+
return deepMerge(DEFAULT_DEPLOYMENT_CONFIG, config.deployment);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error(`[deployment-config-loader] Error loading config: ${error.message}`);
|
|
98
|
+
console.warn('[deployment-config-loader] Using default deployment configuration');
|
|
99
|
+
return { ...DEFAULT_DEPLOYMENT_CONFIG };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Loads project configuration from core-config.yaml
|
|
105
|
+
*
|
|
106
|
+
* @param {string} projectRoot - Project root directory
|
|
107
|
+
* @returns {Object|null} Project configuration or null if not found
|
|
108
|
+
*/
|
|
109
|
+
function loadProjectConfig(projectRoot) {
|
|
110
|
+
const configPath = path.join(projectRoot, '.aios-core', 'core-config.yaml');
|
|
111
|
+
|
|
112
|
+
if (!fs.existsSync(configPath)) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
118
|
+
const config = yaml.parse(configContent);
|
|
119
|
+
return config.project || null;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error(`[deployment-config-loader] Error loading project config: ${error.message}`);
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Gets the target branch for a given source branch
|
|
128
|
+
*
|
|
129
|
+
* @param {string} sourceBranch - Source branch name
|
|
130
|
+
* @param {Object} deploymentConfig - Deployment configuration
|
|
131
|
+
* @returns {string} Target branch name
|
|
132
|
+
*/
|
|
133
|
+
function getTargetBranch(sourceBranch, deploymentConfig) {
|
|
134
|
+
const { branches, workflow } = deploymentConfig;
|
|
135
|
+
|
|
136
|
+
// Check if it's a staging branch (used for promotion)
|
|
137
|
+
if (sourceBranch === branches.staging_branch) {
|
|
138
|
+
return branches.production_branch;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Check production targets (hotfix/* etc.)
|
|
142
|
+
for (const pattern of branches.production_targets || []) {
|
|
143
|
+
if (matchesBranchPattern(sourceBranch, pattern)) {
|
|
144
|
+
return branches.production_branch;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Check staging targets
|
|
149
|
+
for (const pattern of branches.staging_targets || []) {
|
|
150
|
+
if (matchesBranchPattern(sourceBranch, pattern)) {
|
|
151
|
+
// If direct-to-main workflow, target production
|
|
152
|
+
if (workflow === 'direct-to-main') {
|
|
153
|
+
return branches.production_branch;
|
|
154
|
+
}
|
|
155
|
+
return branches.staging_branch || branches.production_branch;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Default target - resolve symbolic name to actual branch
|
|
160
|
+
const defaultTarget = (branches.default_target || 'production').toLowerCase();
|
|
161
|
+
const stagingBranch = branches.staging_branch;
|
|
162
|
+
const productionBranch = branches.production_branch;
|
|
163
|
+
|
|
164
|
+
// Handle symbolic names
|
|
165
|
+
if (defaultTarget === 'staging') {
|
|
166
|
+
return stagingBranch || productionBranch;
|
|
167
|
+
}
|
|
168
|
+
if (defaultTarget === 'production') {
|
|
169
|
+
return productionBranch;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Handle explicit branch names as fallback (for manually-edited configs)
|
|
173
|
+
if (stagingBranch && defaultTarget === stagingBranch.toLowerCase()) {
|
|
174
|
+
return stagingBranch;
|
|
175
|
+
}
|
|
176
|
+
if (productionBranch && defaultTarget === productionBranch.toLowerCase()) {
|
|
177
|
+
return productionBranch;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Conservative fallback with warning
|
|
181
|
+
console.warn(
|
|
182
|
+
`[deployment-config-loader] Unknown default_target "${branches.default_target}", falling back to production`,
|
|
183
|
+
);
|
|
184
|
+
return productionBranch;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Checks if a branch name matches a pattern
|
|
189
|
+
*
|
|
190
|
+
* @param {string} branchName - Branch name to check
|
|
191
|
+
* @param {string} pattern - Pattern to match (e.g., "feature/*")
|
|
192
|
+
* @returns {boolean} True if matches
|
|
193
|
+
*/
|
|
194
|
+
function matchesBranchPattern(branchName, pattern) {
|
|
195
|
+
// Escape regex metacharacters first, then convert glob wildcards
|
|
196
|
+
// Order matters: escape special chars, then convert * and ?
|
|
197
|
+
const regexPattern = pattern
|
|
198
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape regex metacharacters (except * and ?)
|
|
199
|
+
.replace(/\*/g, '.*') // Convert glob * to regex .*
|
|
200
|
+
.replace(/\?/g, '.'); // Convert glob ? to regex .
|
|
201
|
+
|
|
202
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
203
|
+
return regex.test(branchName);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Gets environment configuration by name
|
|
208
|
+
*
|
|
209
|
+
* @param {string} envName - Environment name (staging/production)
|
|
210
|
+
* @param {Object} deploymentConfig - Deployment configuration
|
|
211
|
+
* @returns {Object|null} Environment configuration
|
|
212
|
+
*/
|
|
213
|
+
function getEnvironmentConfig(envName, deploymentConfig) {
|
|
214
|
+
const normalized = envName.toLowerCase();
|
|
215
|
+
return deploymentConfig.environments?.[normalized] || null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Checks if quality gate is enabled
|
|
220
|
+
*
|
|
221
|
+
* @param {string} gateName - Gate name (lint, typecheck, tests, security_scan)
|
|
222
|
+
* @param {Object} deploymentConfig - Deployment configuration
|
|
223
|
+
* @returns {boolean} True if gate is enabled
|
|
224
|
+
*/
|
|
225
|
+
function isQualityGateEnabled(gateName, deploymentConfig) {
|
|
226
|
+
return deploymentConfig.quality_gates?.[gateName] === true;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Gets all enabled quality gates
|
|
231
|
+
*
|
|
232
|
+
* @param {Object} deploymentConfig - Deployment configuration
|
|
233
|
+
* @returns {string[]} List of enabled gate names
|
|
234
|
+
*/
|
|
235
|
+
function getEnabledQualityGates(deploymentConfig) {
|
|
236
|
+
const gates = deploymentConfig.quality_gates || {};
|
|
237
|
+
return Object.entries(gates)
|
|
238
|
+
.filter(([key, value]) => value === true && key !== 'min_coverage')
|
|
239
|
+
.map(([key]) => key);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Deep merge two objects
|
|
244
|
+
*
|
|
245
|
+
* @param {Object} target - Target object
|
|
246
|
+
* @param {Object} source - Source object
|
|
247
|
+
* @returns {Object} Merged object
|
|
248
|
+
*/
|
|
249
|
+
function deepMerge(target, source) {
|
|
250
|
+
const result = { ...target };
|
|
251
|
+
|
|
252
|
+
for (const key of Object.keys(source)) {
|
|
253
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
254
|
+
result[key] = deepMerge(target[key] || {}, source[key]);
|
|
255
|
+
} else if (source[key] !== undefined) {
|
|
256
|
+
result[key] = source[key];
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Validates deployment configuration
|
|
265
|
+
*
|
|
266
|
+
* @param {Object} config - Deployment configuration to validate
|
|
267
|
+
* @returns {Object} Validation result with isValid and errors
|
|
268
|
+
*/
|
|
269
|
+
function validateDeploymentConfig(config) {
|
|
270
|
+
const errors = [];
|
|
271
|
+
|
|
272
|
+
// Check workflow
|
|
273
|
+
if (!['staging-first', 'direct-to-main'].includes(config.workflow)) {
|
|
274
|
+
errors.push(`Invalid workflow: ${config.workflow}`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Check branches
|
|
278
|
+
if (!config.branches?.production_branch) {
|
|
279
|
+
errors.push('Missing production_branch');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (config.workflow === 'staging-first' && !config.branches?.staging_branch) {
|
|
283
|
+
errors.push('staging-first workflow requires staging_branch');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Check environments
|
|
287
|
+
if (!config.environments?.production) {
|
|
288
|
+
errors.push('Missing production environment configuration');
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
isValid: errors.length === 0,
|
|
293
|
+
errors,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
module.exports = {
|
|
298
|
+
loadDeploymentConfig,
|
|
299
|
+
loadProjectConfig,
|
|
300
|
+
getTargetBranch,
|
|
301
|
+
matchesBranchPattern,
|
|
302
|
+
getEnvironmentConfig,
|
|
303
|
+
isQualityGateEnabled,
|
|
304
|
+
getEnabledQualityGates,
|
|
305
|
+
validateDeploymentConfig,
|
|
306
|
+
deepMerge,
|
|
307
|
+
DEFAULT_DEPLOYMENT_CONFIG,
|
|
308
|
+
};
|